9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

commit e5f0f74b8a25039c8e02ea45d97b979a79010ee3
Author: garbeam@wmii.de <unknown>
Date:   Sun, 20 Nov 2005 18:27:27 +0200

initial import

Diffstat:
ALICENSE | 287+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AMakefile | 35+++++++++++++++++++++++++++++++++++
AREADME | 30++++++++++++++++++++++++++++++
Aawk/Makefile | 47+++++++++++++++++++++++++++++++++++++++++++++++
Aawk/awk.1 | 527+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/awk.h | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/awkgram.y | 488+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/lex.c | 569+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/lib.c | 686+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/main.c | 197+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/maketab.c | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/parse.c | 271+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/proctab.c | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/proto.h | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/re.c | 325+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/run.c | 1892+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aawk/tran.c | 434+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abasename/Makefile | 36++++++++++++++++++++++++++++++++++++
Abasename/basename.1 | 35+++++++++++++++++++++++++++++++++++
Abasename/basename.c | 41+++++++++++++++++++++++++++++++++++++++++
Abc/Makefile | 46++++++++++++++++++++++++++++++++++++++++++++++
Abc/bc.1 | 292+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abc/bc.y | 985+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acat/Makefile | 36++++++++++++++++++++++++++++++++++++
Acat/cat.1 | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acat/cat.c | 36++++++++++++++++++++++++++++++++++++
Acleanname/Makefile | 36++++++++++++++++++++++++++++++++++++
Acleanname/cleanname.1 | 32++++++++++++++++++++++++++++++++
Acleanname/cleanname.c | 44++++++++++++++++++++++++++++++++++++++++++++
Aconfig.mk | 15+++++++++++++++
Adate/Makefile | 36++++++++++++++++++++++++++++++++++++
Adate/date.1 | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adate/date.c | 30++++++++++++++++++++++++++++++
Aecho/Makefile | 36++++++++++++++++++++++++++++++++++++
Aecho/echo.1 | 26++++++++++++++++++++++++++
Aecho/echo.c | 38++++++++++++++++++++++++++++++++++++++
Agrep/Makefile | 46++++++++++++++++++++++++++++++++++++++++++++++
Agrep/comp.c | 277+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agrep/grep.1 | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agrep/grep.h | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agrep/grep.y | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agrep/main.c | 263+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agrep/sub.c | 317+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/LICENSE | 19+++++++++++++++++++
Alib9/Makefile | 208+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/_exits.c | 10++++++++++
Alib9/_p9dialparse.c | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/_p9dir.c | 231+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/_p9translate.c | 46++++++++++++++++++++++++++++++++++++++++++++++
Alib9/announce.c | 152+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/argv0.c | 9+++++++++
Alib9/atexit.c | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/atnotify.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/atoi.c | 9+++++++++
Alib9/atol.c | 9+++++++++
Alib9/atoll.c | 9+++++++++
Alib9/await.c | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio.h | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/_lib9.h | 12++++++++++++
Alib9/bio/bbuffered.c | 21+++++++++++++++++++++
Alib9/bio/bcat.c | 46++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/bfildes.c | 9+++++++++
Alib9/bio/bflush.c | 34++++++++++++++++++++++++++++++++++
Alib9/bio/bgetc.c | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/bgetd.c | 37+++++++++++++++++++++++++++++++++++++
Alib9/bio/bgetrune.c | 47+++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/binit.c | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/boffset.c | 26++++++++++++++++++++++++++
Alib9/bio/bprint.c | 14++++++++++++++
Alib9/bio/bputc.c | 20++++++++++++++++++++
Alib9/bio/bputrune.c | 23+++++++++++++++++++++++
Alib9/bio/brdline.c | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/brdstr.c | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/bread.c | 46++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/bseek.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/bvprint.c | 39+++++++++++++++++++++++++++++++++++++++
Alib9/bio/bwrite.c | 39+++++++++++++++++++++++++++++++++++++++
Alib9/bio/lib9.std.h | 20++++++++++++++++++++
Alib9/cistrcmp.c | 26++++++++++++++++++++++++++
Alib9/cistrncmp.c | 28++++++++++++++++++++++++++++
Alib9/cistrstr.c | 23+++++++++++++++++++++++
Alib9/cleanname.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/convD2M.c | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/convM2D.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/convM2S.c | 323+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/convS2M.c | 399+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/create.c | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/ctime.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/date.c | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/debugmalloc.c | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/dial.c | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/dirfstat.c | 29+++++++++++++++++++++++++++++
Alib9/dirfwstat.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/dirmodefmt.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/dirread.c | 188+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/dirstat.c | 32++++++++++++++++++++++++++++++++
Alib9/dirwstat.c | 19+++++++++++++++++++
Alib9/dup.c | 12++++++++++++
Alib9/encodefmt.c | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/errstr.c | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/exec.c | 9+++++++++
Alib9/execl.c | 29+++++++++++++++++++++++++++++
Alib9/fcall.h | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fcallfmt.c | 253+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt.h | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/LICENSE | 19+++++++++++++++++++
Alib9/fmt/charstod.c | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/dofmt.c | 552+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/dorfmt.c | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/errfmt.c | 28++++++++++++++++++++++++++++
Alib9/fmt/fltfmt.c | 394+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/fmt.c | 231+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/fmtdef.h | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/fmtfd.c | 46++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/fmtfdflush.c | 34++++++++++++++++++++++++++++++++++
Alib9/fmt/fmtlock.c | 27+++++++++++++++++++++++++++
Alib9/fmt/fmtprint.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/fmtquote.c | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/fmtrune.c | 40++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/fmtstr.c | 27+++++++++++++++++++++++++++
Alib9/fmt/fmtvprint.c | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/fprint.c | 29+++++++++++++++++++++++++++++
Alib9/fmt/nan64.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/plan9.h | 33+++++++++++++++++++++++++++++++++
Alib9/fmt/portdate | 30++++++++++++++++++++++++++++++
Alib9/fmt/pow10.c | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/print.c | 29+++++++++++++++++++++++++++++
Alib9/fmt/runefmtstr.c | 27+++++++++++++++++++++++++++
Alib9/fmt/runeseprint.c | 30++++++++++++++++++++++++++++++
Alib9/fmt/runesmprint.c | 30++++++++++++++++++++++++++++++
Alib9/fmt/runesnprint.c | 31+++++++++++++++++++++++++++++++
Alib9/fmt/runesprint.c | 30++++++++++++++++++++++++++++++
Alib9/fmt/runevseprint.c | 40++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/runevsmprint.c | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/runevsnprint.c | 39+++++++++++++++++++++++++++++++++++++++
Alib9/fmt/seprint.c | 29+++++++++++++++++++++++++++++
Alib9/fmt/smprint.c | 29+++++++++++++++++++++++++++++
Alib9/fmt/snprint.c | 30++++++++++++++++++++++++++++++
Alib9/fmt/sprint.c | 39+++++++++++++++++++++++++++++++++++++++
Alib9/fmt/strtod.c | 532+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/strtod.h | 4++++
Alib9/fmt/test.c | 44++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/test2.c | 9+++++++++
Alib9/fmt/test3.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/vfprint.c | 33+++++++++++++++++++++++++++++++++
Alib9/fmt/vseprint.c | 39+++++++++++++++++++++++++++++++++++++++
Alib9/fmt/vsmprint.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/vsnprint.c | 39+++++++++++++++++++++++++++++++++++++++
Alib9/fmtlock2.c | 16++++++++++++++++
Alib9/fork.c | 22++++++++++++++++++++++
Alib9/get9root.c | 18++++++++++++++++++
Alib9/getcallerpc-386.c | 7+++++++
Alib9/getcallerpc-PowerMacintosh.c | 7+++++++
Alib9/getcallerpc-power.c | 11+++++++++++
Alib9/getcallerpc-ppc.c | 7+++++++
Alib9/getcallerpc-sun4u.s | 5+++++
Alib9/getcallerpc-x86_64.c | 7+++++++
Alib9/getenv.c | 26++++++++++++++++++++++++++
Alib9/getfields.c | 36++++++++++++++++++++++++++++++++++++
Alib9/getnetconn.c | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/getns.c | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/getuser.c | 16++++++++++++++++
Alib9/getwd.c | 10++++++++++
Alib9/jmp-FreeBSD.s | 3+++
Alib9/jmp.c | 17+++++++++++++++++
Alib9/lib9.h | 17+++++++++++++++++
Alib9/libc.h | 871+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/lnrand.c | 18++++++++++++++++++
Alib9/lock.c | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/lrand.c | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/main.c | 13+++++++++++++
Alib9/malloc.c | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/malloctag.c | 19+++++++++++++++++++
Alib9/mallocz.c | 15+++++++++++++++
Alib9/nan.c | 27+++++++++++++++++++++++++++
Alib9/nan.h | 4++++
Alib9/needsrcquote.c | 12++++++++++++
Alib9/needstack.c | 8++++++++
Alib9/netmkaddr.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/notify.c | 272+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/nrand.c | 19+++++++++++++++++++
Alib9/nulldir.c | 9+++++++++
Alib9/open.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/opentemp.c | 15+++++++++++++++
Alib9/pipe.c | 15+++++++++++++++
Alib9/portdate | 30++++++++++++++++++++++++++++++
Alib9/post9p.c | 46++++++++++++++++++++++++++++++++++++++++++++++
Alib9/postnote.c | 34++++++++++++++++++++++++++++++++++
Alib9/priv.c | 32++++++++++++++++++++++++++++++++
Alib9/qlock.c | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/quote.c | 136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/rand.c | 10++++++++++
Alib9/read9pmsg.c | 31+++++++++++++++++++++++++++++++
Alib9/readcons.c | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/readn.c | 22++++++++++++++++++++++
Alib9/regex/README | 7+++++++
Alib9/regex/cvt | 11+++++++++++
Alib9/regex/lib9.h | 2++
Alib9/regex/lib9.std.h | 10++++++++++
Alib9/regex/mkfile | 25+++++++++++++++++++++++++
Alib9/regex/regaux.c | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/regex/regcomp.c | 555+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/regex/regcomp.h | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/regex/regerror.c | 14++++++++++++++
Alib9/regex/regexec.c | 231+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/regex/regsub.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/regex/rregexec.c | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/regex/rregsub.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/regex/test.c | 46++++++++++++++++++++++++++++++++++++++++++++++
Alib9/regex/test2.c | 20++++++++++++++++++++
Alib9/regexp.7 | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/regexp.h | 1+
Alib9/regexp9.h | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/rfork.c | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/searchpath.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/seek.c | 8++++++++
Alib9/sendfd.c | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/sleep.c | 35+++++++++++++++++++++++++++++++++++
Alib9/st4nkO6D | 1+
Alib9/strdup.c | 17+++++++++++++++++
Alib9/strecpy.c | 16++++++++++++++++
Alib9/sysfatal.c | 26++++++++++++++++++++++++++
Alib9/syslog.c | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/sysname.c | 30++++++++++++++++++++++++++++++
Alib9/testfork.c | 21+++++++++++++++++++++
Alib9/time.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/tokenize.c | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/truerand.c | 25+++++++++++++++++++++++++
Alib9/u.h | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/u16.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/u32.c | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/u64.c | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/udp.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/unsharp.c | 44++++++++++++++++++++++++++++++++++++++++++++
Alib9/utf.h | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/utf/LICENSE | 13+++++++++++++
Alib9/utf/plan9.h | 29+++++++++++++++++++++++++++++
Alib9/utf/portdate | 20++++++++++++++++++++
Alib9/utf/rune.c | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/utf/runestrcat.c | 25+++++++++++++++++++++++++
Alib9/utf/runestrchr.c | 35+++++++++++++++++++++++++++++++++++
Alib9/utf/runestrcmp.c | 35+++++++++++++++++++++++++++++++++++
Alib9/utf/runestrcpy.c | 28++++++++++++++++++++++++++++
Alib9/utf/runestrdup.c | 30++++++++++++++++++++++++++++++
Alib9/utf/runestrecpy.c | 32++++++++++++++++++++++++++++++++
Alib9/utf/runestrlen.c | 24++++++++++++++++++++++++
Alib9/utf/runestrncat.c | 32++++++++++++++++++++++++++++++++
Alib9/utf/runestrncmp.c | 37+++++++++++++++++++++++++++++++++++++
Alib9/utf/runestrncpy.c | 33+++++++++++++++++++++++++++++++++
Alib9/utf/runestrrchr.c | 30++++++++++++++++++++++++++++++
Alib9/utf/runestrstr.c | 44++++++++++++++++++++++++++++++++++++++++++++
Alib9/utf/runetype.c | 1151+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/utf/utfdef.h | 33+++++++++++++++++++++++++++++++++
Alib9/utf/utfecpy.c | 36++++++++++++++++++++++++++++++++++++
Alib9/utf/utflen.c | 37+++++++++++++++++++++++++++++++++++++
Alib9/utf/utfnlen.c | 41+++++++++++++++++++++++++++++++++++++++++
Alib9/utf/utfrrune.c | 45+++++++++++++++++++++++++++++++++++++++++++++
Alib9/utf/utfrune.c | 44++++++++++++++++++++++++++++++++++++++++++++
Alib9/utf/utfutf.c | 41+++++++++++++++++++++++++++++++++++++++++
Alib9/wait.c | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/waitpid.c | 20++++++++++++++++++++
Arc/Makefile | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/code.c | 430+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/exec.c | 946+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/exec.h | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/fmtquote.c | 162+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/fns.h | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/getflags.c | 217+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/getflags.h | 7+++++++
Arc/glob.c | 211+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/havefork.c | 222+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/haventfork.c | 211+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/here.c | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/io.c | 182+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/io.h | 30++++++++++++++++++++++++++++++
Arc/lex.c | 324+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/pcmd.c | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/pfnc.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/plan9ish.c | 549+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/rc.1 | 992+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/rc.h | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/rcmain | 39+++++++++++++++++++++++++++++++++++++++
Arc/simple.c | 445+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/subr.c | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/syn.y | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/trap.c | 34++++++++++++++++++++++++++++++++++
Arc/tree.c | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/unixcrap.c | 211+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/var.c | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arc/y.tab.h | 30++++++++++++++++++++++++++++++
Ased/Makefile | 36++++++++++++++++++++++++++++++++++++
Ased/sed.1 | 385+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ased/sed.c | 1447+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aseq/Makefile | 36++++++++++++++++++++++++++++++++++++
Aseq/seq.1 | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aseq/seq.c | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asleep/Makefile | 37+++++++++++++++++++++++++++++++++++++
Asleep/sleep.1 | 31+++++++++++++++++++++++++++++++
Asleep/sleep.c | 13+++++++++++++
Asort/Makefile | 36++++++++++++++++++++++++++++++++++++
Asort/sort.1 | 262+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asort/sort.c | 1767+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atee/Makefile | 36++++++++++++++++++++++++++++++++++++
Atee/tee.1 | 28++++++++++++++++++++++++++++
Atee/tee.c | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/Makefile | 36++++++++++++++++++++++++++++++++++++
Atest/test.1 | 211+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/test.c | 292+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atouch/Makefile | 36++++++++++++++++++++++++++++++++++++
Atouch/touch.1 | 35+++++++++++++++++++++++++++++++++++
Atouch/touch.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atr/Makefile | 36++++++++++++++++++++++++++++++++++++
Atr/tr.1 | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atr/tr.c | 356+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Auniq/Makefile | 36++++++++++++++++++++++++++++++++++++
Auniq/uniq.1 | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Auniq/uniq.c | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ayacc/9yacc | 4++++
Ayacc/Makefile | 27+++++++++++++++++++++++++++
Ayacc/yacc.1 | 176+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ayacc/yacc.c | 2980+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ayacc/yaccpar | 276+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ayacc/yaccpars | 273+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
323 files changed, 41871 insertions(+), 0 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -0,0 +1,287 @@ +The rare bits touched by Anselm R. Garbe are under following LICENSE: + +MIT/X Consortium License + +(C)opyright MMV Anselm R. Garbe <garbeam at gmail dot 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. + +=================================================================== + +The Plan 9 software is provided under the terms of the +Lucent Public License, Version 1.02, reproduced below, +with the following notable exceptions: + +1. No right is granted to create derivative works of or + to redistribute (other than with the Plan 9 Operating System) + the screen imprinter fonts identified in subdirectory + /lib/font/bit/lucida and printer fonts (Lucida Sans Unicode, Lucida + Sans Italic, Lucida Sans Demibold, Lucida Typewriter, Lucida Sans + Typewriter83), identified in subdirectory /sys/lib/postscript/font. + These directories contain material copyrights by B&H Inc. and Y&Y Inc. + +2. The printer fonts identified in subdirectory /sys/lib/ghostscript/font + are subject to the GNU GPL, reproduced in the file /LICENSE.gpl. + +3. The ghostscript program in the subdirectory /sys/src/cmd/gs is + covered by the Aladdin Free Public License, reproduced in the file + /LICENSE.afpl. + +Other, less notable exceptions are marked in the file tree with +COPYING, COPYRIGHT, or LICENSE files. + +=================================================================== + +Lucent Public License Version 1.02 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE +PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original + Program, and + b. in the case of each Contributor, + + i. changes to the Program, and + ii. additions to the Program; + + where such changes and/or additions to the Program were added to the + Program by such Contributor itself or anyone acting on such + Contributor's behalf, and the Contributor explicitly consents, in + accordance with Section 3C, to characterization of the changes and/or + additions as Contributions. + +"Contributor" means LUCENT and any other entity that has Contributed a +Contribution to the Program. + +"Distributor" means a Recipient that distributes the Program, +modifications to the Program, or any part thereof. + +"Licensed Patents" mean patent claims licensable by a Contributor +which are necessarily infringed by the use or sale of its Contribution +alone or when combined with the Program. + +"Original Program" means the original version of the software +accompanying this Agreement as released by LUCENT, including source +code, object code and documentation, if any. + +"Program" means the Original Program and Contributions or any part +thereof + +"Recipient" means anyone who receives the Program under this +Agreement, including all Contributors. + +2. GRANT OF RIGHTS + + a. Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free copyright + license to reproduce, prepare derivative works of, publicly display, + publicly perform, distribute and sublicense the Contribution of such + Contributor, if any, and such derivative works, in source code and + object code form. + + b. Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free patent + license under Licensed Patents to make, use, sell, offer to sell, + import and otherwise transfer the Contribution of such Contributor, if + any, in source code and object code form. The patent license granted + by a Contributor shall also apply to the combination of the + Contribution of that Contributor and the Program if, at the time the + Contribution is added by the Contributor, such addition of the + Contribution causes such combination to be covered by the Licensed + Patents. The patent license granted by a Contributor shall not apply + to (i) any other combinations which include the Contribution, nor to + (ii) Contributions of other Contributors. No hardware per se is + licensed hereunder. + + c. Recipient understands that although each Contributor grants the + licenses to its Contributions set forth herein, no assurances are + provided by any Contributor that the Program does not infringe the + patent or other intellectual property rights of any other entity. Each + Contributor disclaims any liability to Recipient for claims brought by + any other entity based on infringement of intellectual property rights + or otherwise. As a condition to exercising the rights and licenses + granted hereunder, each Recipient hereby assumes sole responsibility + to secure any other intellectual property rights needed, if any. For + example, if a third party patent license is required to allow + Recipient to distribute the Program, it is Recipient's responsibility + to acquire that license before distributing the Program. + + d. Each Contributor represents that to its knowledge it has sufficient + copyright rights in its Contribution, if any, to grant the copyright + license set forth in this Agreement. + +3. REQUIREMENTS + +A. Distributor may choose to distribute the Program in any form under +this Agreement or under its own license agreement, provided that: + + a. it complies with the terms and conditions of this Agreement; + + b. if the Program is distributed in source code or other tangible + form, a copy of this Agreement or Distributor's own license agreement + is included with each copy of the Program; and + + c. if distributed under Distributor's own license agreement, such + license agreement: + + i. effectively disclaims on behalf of all Contributors all warranties + and conditions, express and implied, including warranties or + conditions of title and non-infringement, and implied warranties or + conditions of merchantability and fitness for a particular purpose; + ii. effectively excludes on behalf of all Contributors all liability + for damages, including direct, indirect, special, incidental and + consequential damages, such as lost profits; and + iii. states that any provisions which differ from this Agreement are + offered by that Contributor alone and not by any other party. + +B. Each Distributor must include the following in a conspicuous + location in the Program: + + Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights + Reserved. + +C. In addition, each Contributor must identify itself as the +originator of its Contribution in a manner that reasonably allows +subsequent Recipients to identify the originator of the Contribution. +Also, each Contributor must agree that the additions and/or changes +are intended to be a Contribution. Once a Contribution is contributed, +it may not thereafter be revoked. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain +responsibilities with respect to end users, business partners and the +like. While this license is intended to facilitate the commercial use +of the Program, the Distributor who includes the Program in a +commercial product offering should do so in a manner which does not +create potential liability for Contributors. Therefore, if a +Distributor includes the Program in a commercial product offering, +such Distributor ("Commercial Distributor") hereby agrees to defend +and indemnify every Contributor ("Indemnified Contributor") against +any losses, damages and costs (collectively"Losses") arising from +claims, lawsuits and other legal actions brought by a third party +against the Indemnified Contributor to the extent caused by the acts +or omissions of such Commercial Distributor in connection with its +distribution of the Program in a commercial product offering. The +obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. +In order to qualify, an Indemnified Contributor must: a) promptly +notify the Commercial Distributor in writing of such claim, and b) +allow the Commercial Distributor to control, and cooperate with the +Commercial Distributor in, the defense and any related settlement +negotiations. The Indemnified Contributor may participate in any such +claim at its own expense. + +For example, a Distributor might include the Program in a commercial +product offering, Product X. That Distributor is then a Commercial +Distributor. If that Commercial Distributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Distributor's responsibility +alone. Under this section, the Commercial Distributor would have to +defend claims against the Contributors related to those performance +claims and warranties, and if a court requires any Contributor to pay +any damages as a result, the Commercial Distributor must pay those +damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS +PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY +WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY +OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement, including but not limited to +the risks and costs of program errors, compliance with applicable +laws, damage to or loss of data, programs or equipment, and +unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR +ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING +WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR +DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. EXPORT CONTROL + +Recipient agrees that Recipient alone is responsible for compliance +with the United States export administration regulations (and the +export control laws and regulation of any other countries). + +8. GENERAL + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further +action by the parties hereto, such provision shall be reformed to the +minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against a Contributor with +respect to a patent applicable to software (including a cross-claim or +counterclaim in a lawsuit), then any patent licenses granted by that +Contributor to such Recipient under this Agreement shall terminate as +of the date such litigation is filed. In addition, if Recipient +institutes patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Program +itself (excluding combinations of the Program with other software or +hardware) infringes such Recipient's patent(s), then such Recipient's +rights granted under Section 2(b) shall terminate as of the date such +litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of +time after becoming aware of such noncompliance. If all Recipient's +rights under this Agreement terminate, Recipient agrees to cease use +and distribution of the Program as soon as reasonably practicable. +However, Recipient's obligations under this Agreement and any licenses +granted by Recipient relating to the Program shall continue and +survive. + +LUCENT may publish new versions (including revisions) of this +Agreement from time to time. Each new version of the Agreement will be +given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new +version of the Agreement is published, Contributor may elect to +distribute the Program (including its Contributions) under the new +version. No one other than LUCENT has the right to modify this +Agreement. Except as expressly stated in Sections 2(a) and 2(b) above, +Recipient receives no rights or licenses to the intellectual property +of any Contributor under this Agreement, whether expressly, by +implication, estoppel or otherwise. All rights in the Program not +expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and +the intellectual property laws of the United States of America. No +party to this Agreement will bring a legal action under this Agreement +more than one year after the cause of action arose. Each party waives +its rights to a jury trial in any resulting litigation. + diff --git a/Makefile b/Makefile @@ -0,0 +1,35 @@ +# 9base - awk basename cat cleanname echo grep rc sed seq sleep sort tee +# test touch tr uniq from Plan 9 + +include config.mk + +SUBDIRS = lib9 yacc awk basename bc cat cleanname date echo grep rc \ + sed seq sleep sort tee test touch tr uniq + +all: + @echo 9base build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + @chmod 755 yacc/9yacc + @for i in ${SUBDIRS}; do cd $$i; ${MAKE} || exit; cd ..; done; + +clean: + @for i in ${SUBDIRS}; do cd $$i; ${MAKE} clean || exit; cd ..; done + @echo cleaned 9base + +install: all + @for i in ${SUBDIRS}; do cd $$i; ${MAKE} install || exit; cd ..; done + @echo installed 9base to ${DESTDIR}${PREFIX} + +uninstall: + @for i in ${SUBDIRS}; do cd $$i; ${MAKE} uninstall || exit; cd ..; done + @echo uninstalled 9base + +dist: clean + @mkdir -p 9base-${VERSION} + @cp -R Makefile README LICENSE config.mk ${SUBDIRS} 9base-${VERSION} + @tar -cf 9base-${VERSION}.tar 9base-${VERSION} + @gzip 9base-${VERSION}.tar + @rm -rf 9base-${VERSION} + @echo created distribution 9base-${VERSION}.tar.gz diff --git a/README b/README @@ -0,0 +1,30 @@ +Abstract +-------- +This is a port of various original Plan 9 tools for Unix, based on +plan9port [1], mk-with-libs.tgz [2], and wmii [3]. See the LICENSE +file for license details. + +Requirements +------------ +In order to build 9base you need yacc or bison. + +Installation +------------ +Edit config.mk to match your local setup and execute 'make install' +(if necessary as root). By default, 9rc is installed into its own +hierarchy, /usr/local/9. This is done to avoid conflicts with the +standard tools of your system. You can then put /usr/local/9/bin at +the end of your PATH. + +Credits +------- +Many thanks go to Lucent, the Bell Labs which developed this fine +stuff and to Russ Cox for his plan9port. + +References +---------- +[1] http://swtch.com/plan9port/ +[2] http://swtch.com/plan9port/unix/ +[3] http://wmii.de + +--Anselm R. Garbe diff --git a/awk/Makefile b/awk/Makefile @@ -0,0 +1,47 @@ +# awk - awk unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = awk + +OFILES = re.o lex.o main.o parse.o proctab.o tran.o lib.o run.o y.tab.o + +YFILES = awkgram.y + +MANFILES = awk.1 + +all: + @if [ ! -f y.tab.c ]; then \ + ${MAKE} -f Makefile depend;\ + fi + @${MAKE} -f Makefile ${TARG} + @echo built ${TARG} + + +depend: + @echo YACC ${YFILES} + @${YACC} -d ${YFILES} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} y.tab.c y.tab.h + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -lm -L${PREFIX}/lib -L../lib9 -l9 diff --git a/awk/awk.1 b/awk/awk.1 @@ -0,0 +1,527 @@ +.TH AWK 1 +.SH NAME +awk \- pattern-directed scanning and processing language +.SH SYNOPSIS +.B awk +[ +.BI -F fs +] +[ +.BI -v +.I var=value +] +[ +.BI -mr n +] +[ +.BI -mf n +] +[ +.B -f +.I prog +[ +.I prog +] +[ +.I file ... +] +.SH DESCRIPTION +.I Awk +scans each input +.I file +for lines that match any of a set of patterns specified literally in +.IR prog +or in one or more files +specified as +.B -f +.IR file . +With each pattern +there can be an associated action that will be performed +when a line of a +.I file +matches the pattern. +Each line is matched against the +pattern portion of every pattern-action statement; +the associated action is performed for each matched pattern. +The file name +.L - +means the standard input. +Any +.IR file +of the form +.I var=value +is treated as an assignment, not a file name, +and is executed at the time it would have been opened if it were a file name. +The option +.B -v +followed by +.I var=value +is an assignment to be done before +.I prog +is executed; +any number of +.B -v +options may be present. +.B \-F +.IR fs +option defines the input field separator to be the regular expression +.IR fs . +.PP +An input line is normally made up of fields separated by white space, +or by regular expression +.BR FS . +The fields are denoted +.BR $1 , +.BR $2 , +\&..., while +.B $0 +refers to the entire line. +If +.BR FS +is null, the input line is split into one field per character. +.PP +To compensate for inadequate implementation of storage management, +the +.B \-mr +option can be used to set the maximum size of the input record, +and the +.B \-mf +option to set the maximum number of fields. +.PP +A pattern-action statement has the form +.IP +.IB pattern " { " action " } +.PP +A missing +.BI { " action " } +means print the line; +a missing pattern always matches. +Pattern-action statements are separated by newlines or semicolons. +.PP +An action is a sequence of statements. +A statement can be one of the following: +.PP +.EX +.ta \w'\fLdelete array[expression]'u +if(\fI expression \fP)\fI statement \fP\fR[ \fPelse\fI statement \fP\fR]\fP +while(\fI expression \fP)\fI statement\fP +for(\fI expression \fP;\fI expression \fP;\fI expression \fP)\fI statement\fP +for(\fI var \fPin\fI array \fP)\fI statement\fP +do\fI statement \fPwhile(\fI expression \fP) +break +continue +{\fR [\fP\fI statement ... \fP\fR] \fP} +\fIexpression\fP #\fR commonly\fP\fI var = expression\fP +print\fR [ \fP\fIexpression-list \fP\fR] \fP\fR[ \fP>\fI expression \fP\fR]\fP +printf\fI format \fP\fR[ \fP,\fI expression-list \fP\fR] \fP\fR[ \fP>\fI expression \fP\fR]\fP +return\fR [ \fP\fIexpression \fP\fR]\fP +next #\fR skip remaining patterns on this input line\fP +nextfile #\fR skip rest of this file, open next, start at top\fP +delete\fI array\fP[\fI expression \fP] #\fR delete an array element\fP +delete\fI array\fP #\fR delete all elements of array\fP +exit\fR [ \fP\fIexpression \fP\fR]\fP #\fR exit immediately; status is \fP\fIexpression\fP +.EE +.DT +.PP +Statements are terminated by +semicolons, newlines or right braces. +An empty +.I expression-list +stands for +.BR $0 . +String constants are quoted \&\fL"\ "\fR, +with the usual C escapes recognized within. +Expressions take on string or numeric values as appropriate, +and are built using the operators +.B + \- * / % ^ +(exponentiation), and concatenation (indicated by white space). +The operators +.B +! ++ \-\- += \-= *= /= %= ^= > >= < <= == != ?: +are also available in expressions. +Variables may be scalars, array elements +(denoted +.IB x [ i ] ) +or fields. +Variables are initialized to the null string. +Array subscripts may be any string, +not necessarily numeric; +this allows for a form of associative memory. +Multiple subscripts such as +.B [i,j,k] +are permitted; the constituents are concatenated, +separated by the value of +.BR SUBSEP . +.PP +The +.B print +statement prints its arguments on the standard output +(or on a file if +.BI > file +or +.BI >> file +is present or on a pipe if +.BI | cmd +is present), separated by the current output field separator, +and terminated by the output record separator. +.I file +and +.I cmd +may be literal names or parenthesized expressions; +identical string values in different statements denote +the same open file. +The +.B printf +statement formats its expression list according to the format +(see +.IR fprintf (2)) . +The built-in function +.BI close( expr ) +closes the file or pipe +.IR expr . +The built-in function +.BI fflush( expr ) +flushes any buffered output for the file or pipe +.IR expr . +.PP +The mathematical functions +.BR exp , +.BR log , +.BR sqrt , +.BR sin , +.BR cos , +and +.BR atan2 +are built in. +Other built-in functions: +.TF length +.TP +.B length +the length of its argument +taken as a string, +or of +.B $0 +if no argument. +.TP +.B rand +random number on (0,1) +.TP +.B srand +sets seed for +.B rand +and returns the previous seed. +.TP +.B int +truncates to an integer value +.TP +.B utf +converts its numerical argument, a character number, to a +.SM UTF +string +.TP +.BI substr( s , " m" , " n\fL) +the +.IR n -character +substring of +.I s +that begins at position +.IR m +counted from 1. +.TP +.BI index( s , " t" ) +the position in +.I s +where the string +.I t +occurs, or 0 if it does not. +.TP +.BI match( s , " r" ) +the position in +.I s +where the regular expression +.I r +occurs, or 0 if it does not. +The variables +.B RSTART +and +.B RLENGTH +are set to the position and length of the matched string. +.TP +.BI split( s , " a" , " fs\fL) +splits the string +.I s +into array elements +.IB a [1]\f1, +.IB a [2]\f1, +\&..., +.IB a [ n ]\f1, +and returns +.IR n . +The separation is done with the regular expression +.I fs +or with the field separator +.B FS +if +.I fs +is not given. +An empty string as field separator splits the string +into one array element per character. +.TP +.BI sub( r , " t" , " s\fL) +substitutes +.I t +for the first occurrence of the regular expression +.I r +in the string +.IR s . +If +.I s +is not given, +.B $0 +is used. +.TP +.B gsub +same as +.B sub +except that all occurrences of the regular expression +are replaced; +.B sub +and +.B gsub +return the number of replacements. +.TP +.BI sprintf( fmt , " expr" , " ...\fL) +the string resulting from formatting +.I expr ... +according to the +.I printf +format +.I fmt +.TP +.BI system( cmd ) +executes +.I cmd +and returns its exit status +.TP +.BI tolower( str ) +returns a copy of +.I str +with all upper-case characters translated to their +corresponding lower-case equivalents. +.TP +.BI toupper( str ) +returns a copy of +.I str +with all lower-case characters translated to their +corresponding upper-case equivalents. +.PD +.PP +The ``function'' +.B getline +sets +.B $0 +to the next input record from the current input file; +.B getline +.BI < file +sets +.B $0 +to the next record from +.IR file . +.B getline +.I x +sets variable +.I x +instead. +Finally, +.IB cmd " | getline +pipes the output of +.I cmd +into +.BR getline ; +each call of +.B getline +returns the next line of output from +.IR cmd . +In all cases, +.B getline +returns 1 for a successful input, +0 for end of file, and \-1 for an error. +.PP +Patterns are arbitrary Boolean combinations +(with +.BR "! || &&" ) +of regular expressions and +relational expressions. +Regular expressions are as in +.IR regexp (6). +Isolated regular expressions +in a pattern apply to the entire line. +Regular expressions may also occur in +relational expressions, using the operators +.BR ~ +and +.BR !~ . +.BI / re / +is a constant regular expression; +any string (constant or variable) may be used +as a regular expression, except in the position of an isolated regular expression +in a pattern. +.PP +A pattern may consist of two patterns separated by a comma; +in this case, the action is performed for all lines +from an occurrence of the first pattern +though an occurrence of the second. +.PP +A relational expression is one of the following: +.IP +.I expression matchop regular-expression +.br +.I expression relop expression +.br +.IB expression " in " array-name +.br +.BI ( expr , expr,... ") in " array-name +.PP +where a +.I relop +is any of the six relational operators in C, +and a +.I matchop +is either +.B ~ +(matches) +or +.B !~ +(does not match). +A conditional is an arithmetic expression, +a relational expression, +or a Boolean combination +of these. +.PP +The special patterns +.B BEGIN +and +.B END +may be used to capture control before the first input line is read +and after the last. +.B BEGIN +and +.B END +do not combine with other patterns. +.PP +Variable names with special meanings: +.TF FILENAME +.TP +.B CONVFMT +conversion format used when converting numbers +(default +.BR "%.6g" ) +.TP +.B FS +regular expression used to separate fields; also settable +by option +.BI \-F fs\f1. +.TP +.BR NF +number of fields in the current record +.TP +.B NR +ordinal number of the current record +.TP +.B FNR +ordinal number of the current record in the current file +.TP +.B FILENAME +the name of the current input file +.TP +.B RS +input record separator (default newline) +.TP +.B OFS +output field separator (default blank) +.TP +.B ORS +output record separator (default newline) +.TP +.B OFMT +output format for numbers (default +.BR "%.6g" ) +.TP +.B SUBSEP +separates multiple subscripts (default 034) +.TP +.B ARGC +argument count, assignable +.TP +.B ARGV +argument array, assignable; +non-null members are taken as file names +.TP +.B ENVIRON +array of environment variables; subscripts are names. +.PD +.PP +Functions may be defined (at the position of a pattern-action statement) thus: +.IP +.L +function foo(a, b, c) { ...; return x } +.PP +Parameters are passed by value if scalar and by reference if array name; +functions may be called recursively. +Parameters are local to the function; all other variables are global. +Thus local variables may be created by providing excess parameters in +the function definition. +.SH EXAMPLES +.TP +.L +length($0) > 72 +Print lines longer than 72 characters. +.TP +.L +{ print $2, $1 } +Print first two fields in opposite order. +.PP +.EX +BEGIN { FS = ",[ \et]*|[ \et]+" } + { print $2, $1 } +.EE +.ns +.IP +Same, with input fields separated by comma and/or blanks and tabs. +.PP +.EX + { s += $1 } +END { print "sum is", s, " average is", s/NR } +.EE +.ns +.IP +Add up first column, print sum and average. +.TP +.L +/start/, /stop/ +Print all lines between start/stop pairs. +.PP +.EX +BEGIN { # Simulate echo(1) + for (i = 1; i < ARGC; i++) printf "%s ", ARGV[i] + printf "\en" + exit } +.EE +.SH SOURCE +.B /sys/src/cmd/awk +.SH SEE ALSO +.IR sed (1), +.IR regexp (6), +.br +A. V. Aho, B. W. Kernighan, P. J. Weinberger, +.I +The AWK Programming Language, +Addison-Wesley, 1988. ISBN 0-201-07981-X +.SH BUGS +There are no explicit conversions between numbers and strings. +To force an expression to be treated as a number add 0 to it; +to force it to be treated as a string concatenate +\&\fL""\fP to it. +.br +The scope rules for variables in functions are a botch; +the syntax is worse. diff --git a/awk/awk.h b/awk/awk.h @@ -0,0 +1,184 @@ +/* +Copyright (c) Lucent Technologies 1997 + All Rights Reserved + +*/ + +typedef double Awkfloat; + +/* unsigned char is more trouble than it's worth */ + +typedef unsigned char uschar; + +#define xfree(a) { if ((a) != NULL) { free((char *) a); a = NULL; } } + +#define DEBUG +#ifdef DEBUG + /* uses have to be doubly parenthesized */ +# define dprintf(x) if (dbg) printf x +#else +# define dprintf(x) +#endif + +extern char errbuf[]; + +extern int compile_time; /* 1 if compiling, 0 if running */ +extern int safe; /* 0 => unsafe, 1 => safe */ + +#define RECSIZE (8 * 1024) /* sets limit on records, fields, etc., etc. */ +extern int recsize; /* size of current record, orig RECSIZE */ + +extern char **FS; +extern char **RS; +extern char **ORS; +extern char **OFS; +extern char **OFMT; +extern Awkfloat *NR; +extern Awkfloat *FNR; +extern Awkfloat *NF; +extern char **FILENAME; +extern char **SUBSEP; +extern Awkfloat *RSTART; +extern Awkfloat *RLENGTH; + +extern char *record; /* points to $0 */ +extern int lineno; /* line number in awk program */ +extern int errorflag; /* 1 if error has occurred */ +extern int donefld; /* 1 if record broken into fields */ +extern int donerec; /* 1 if record is valid (no fld has changed */ +extern char inputFS[]; /* FS at time of input, for field splitting */ + +extern int dbg; + +extern char *patbeg; /* beginning of pattern matched */ +extern int patlen; /* length of pattern matched. set in b.c */ + +/* Cell: all information about a variable or constant */ + +typedef struct Cell { + uschar ctype; /* OCELL, OBOOL, OJUMP, etc. */ + uschar csub; /* CCON, CTEMP, CFLD, etc. */ + char *nval; /* name, for variables only */ + char *sval; /* string value */ + Awkfloat fval; /* value as number */ + int tval; /* type info: STR|NUM|ARR|FCN|FLD|CON|DONTFREE */ + struct Cell *cnext; /* ptr to next if chained */ +} Cell; + +typedef struct Array { /* symbol table array */ + int nelem; /* elements in table right now */ + int size; /* size of tab */ + Cell **tab; /* hash table pointers */ +} Array; + +#define NSYMTAB 50 /* initial size of a symbol table */ +extern Array *symtab; + +extern Cell *nrloc; /* NR */ +extern Cell *fnrloc; /* FNR */ +extern Cell *nfloc; /* NF */ +extern Cell *rstartloc; /* RSTART */ +extern Cell *rlengthloc; /* RLENGTH */ + +/* Cell.tval values: */ +#define NUM 01 /* number value is valid */ +#define STR 02 /* string value is valid */ +#define DONTFREE 04 /* string space is not freeable */ +#define CON 010 /* this is a constant */ +#define ARR 020 /* this is an array */ +#define FCN 040 /* this is a function name */ +#define FLD 0100 /* this is a field $1, $2, ... */ +#define REC 0200 /* this is $0 */ + + +/* function types */ +#define FLENGTH 1 +#define FSQRT 2 +#define FEXP 3 +#define FLOG 4 +#define FINT 5 +#define FSYSTEM 6 +#define FRAND 7 +#define FSRAND 8 +#define FSIN 9 +#define FCOS 10 +#define FATAN 11 +#define FTOUPPER 12 +#define FTOLOWER 13 +#define FFLUSH 14 +#define FUTF 15 + +/* Node: parse tree is made of nodes, with Cell's at bottom */ + +typedef struct Node { + int ntype; + struct Node *nnext; + int lineno; + int nobj; + struct Node *narg[1]; /* variable: actual size set by calling malloc */ +} Node; + +#define NIL ((Node *) 0) + +extern Node *winner; +extern Node *nullstat; +extern Node *nullnode; + +/* ctypes */ +#define OCELL 1 +#define OBOOL 2 +#define OJUMP 3 + +/* Cell subtypes: csub */ +#define CFREE 7 +#define CCOPY 6 +#define CCON 5 +#define CTEMP 4 +#define CNAME 3 +#define CVAR 2 +#define CFLD 1 +#define CUNK 0 + +/* bool subtypes */ +#define BTRUE 11 +#define BFALSE 12 + +/* jump subtypes */ +#define JEXIT 21 +#define JNEXT 22 +#define JBREAK 23 +#define JCONT 24 +#define JRET 25 +#define JNEXTFILE 26 + +/* node types */ +#define NVALUE 1 +#define NSTAT 2 +#define NEXPR 3 + + +extern int pairstack[], paircnt; + +#define notlegal(n) (n <= FIRSTTOKEN || n >= LASTTOKEN || proctab[n-FIRSTTOKEN] == nullproc) +#define isvalue(n) ((n)->ntype == NVALUE) +#define isexpr(n) ((n)->ntype == NEXPR) +#define isjump(n) ((n)->ctype == OJUMP) +#define isexit(n) ((n)->csub == JEXIT) +#define isbreak(n) ((n)->csub == JBREAK) +#define iscont(n) ((n)->csub == JCONT) +#define isnext(n) ((n)->csub == JNEXT) +#define isnextfile(n) ((n)->csub == JNEXTFILE) +#define isret(n) ((n)->csub == JRET) +#define isrec(n) ((n)->tval & REC) +#define isfld(n) ((n)->tval & FLD) +#define isstr(n) ((n)->tval & STR) +#define isnum(n) ((n)->tval & NUM) +#define isarr(n) ((n)->tval & ARR) +#define isfcn(n) ((n)->tval & FCN) +#define istrue(n) ((n)->csub == BTRUE) +#define istemp(n) ((n)->csub == CTEMP) +#define isargument(n) ((n)->nobj == ARG) +/* #define freeable(p) (!((p)->tval & DONTFREE)) */ +#define freeable(p) ( ((p)->tval & (STR|DONTFREE)) == STR ) + +#include "proto.h" diff --git a/awk/awkgram.y b/awk/awkgram.y @@ -0,0 +1,488 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +%{ +#include <stdio.h> +#include <string.h> +#include "awk.h" + +#define makedfa(a,b) compre(a) + +void checkdup(Node *list, Cell *item); +int yywrap(void) { return(1); } + +Node *beginloc = 0; +Node *endloc = 0; +int infunc = 0; /* = 1 if in arglist or body of func */ +int inloop = 0; /* = 1 if in while, for, do */ +char *curfname = 0; /* current function name */ +Node *arglist = 0; /* list of args for current function */ +%} + +%union { + Node *p; + Cell *cp; + int i; + char *s; +} + +%token <i> FIRSTTOKEN /* must be first */ +%token <p> PROGRAM PASTAT PASTAT2 XBEGIN XEND +%token <i> NL ',' '{' '(' '|' ';' '/' ')' '}' '[' ']' +%token <i> ARRAY +%token <i> MATCH NOTMATCH MATCHOP +%token <i> FINAL DOT ALL CCL NCCL CHAR OR STAR QUEST PLUS +%token <i> AND BOR APPEND EQ GE GT LE LT NE IN +%token <i> ARG BLTIN BREAK CLOSE CONTINUE DELETE DO EXIT FOR FUNC +%token <i> SUB GSUB IF INDEX LSUBSTR MATCHFCN NEXT NEXTFILE +%token <i> ADD MINUS MULT DIVIDE MOD +%token <i> ASSIGN ASGNOP ADDEQ SUBEQ MULTEQ DIVEQ MODEQ POWEQ +%token <i> PRINT PRINTF SPRINTF +%token <p> ELSE INTEST CONDEXPR +%token <i> POSTINCR PREINCR POSTDECR PREDECR +%token <cp> VAR IVAR VARNF CALL NUMBER STRING +%token <s> REGEXPR + +%type <p> pas pattern ppattern plist pplist patlist prarg term re +%type <p> pa_pat pa_stat pa_stats +%type <s> reg_expr +%type <p> simple_stmt opt_simple_stmt stmt stmtlist +%type <p> var varname funcname varlist +%type <p> for if else while +%type <i> do st +%type <i> pst opt_pst lbrace rbrace rparen comma nl opt_nl and bor +%type <i> subop print + +%right ASGNOP +%right '?' +%right ':' +%left BOR +%left AND +%left GETLINE +%nonassoc APPEND EQ GE GT LE LT NE MATCHOP IN '|' +%left ARG BLTIN BREAK CALL CLOSE CONTINUE DELETE DO EXIT FOR FUNC +%left GSUB IF INDEX LSUBSTR MATCHFCN NEXT NUMBER +%left PRINT PRINTF RETURN SPLIT SPRINTF STRING SUB SUBSTR +%left REGEXPR VAR VARNF IVAR WHILE '(' +%left CAT +%left '+' '-' +%left '*' '/' '%' +%left NOT UMINUS +%right POWER +%right DECR INCR +%left INDIRECT +%token LASTTOKEN /* must be last */ + +%% + +program: + pas { if (errorflag==0) + winner = (Node *)stat3(PROGRAM, beginloc, $1, endloc); } + | error { yyclearin; bracecheck(); SYNTAX("bailing out"); } + ; + +and: + AND | and NL + ; + +bor: + BOR | bor NL + ; + +comma: + ',' | comma NL + ; + +do: + DO | do NL + ; + +else: + ELSE | else NL + ; + +for: + FOR '(' opt_simple_stmt ';' opt_nl pattern ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt + { --inloop; $$ = stat4(FOR, $3, notnull($6), $9, $12); } + | FOR '(' opt_simple_stmt ';' ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt + { --inloop; $$ = stat4(FOR, $3, NIL, $7, $10); } + | FOR '(' varname IN varname rparen {inloop++;} stmt + { --inloop; $$ = stat3(IN, $3, makearr($5), $8); } + ; + +funcname: + VAR { setfname($1); } + | CALL { setfname($1); } + ; + +if: + IF '(' pattern rparen { $$ = notnull($3); } + ; + +lbrace: + '{' | lbrace NL + ; + +nl: + NL | nl NL + ; + +opt_nl: + /* empty */ { $$ = 0; } + | nl + ; + +opt_pst: + /* empty */ { $$ = 0; } + | pst + ; + + +opt_simple_stmt: + /* empty */ { $$ = 0; } + | simple_stmt + ; + +pas: + opt_pst { $$ = 0; } + | opt_pst pa_stats opt_pst { $$ = $2; } + ; + +pa_pat: + pattern { $$ = notnull($1); } + ; + +pa_stat: + pa_pat { $$ = stat2(PASTAT, $1, stat2(PRINT, rectonode(), NIL)); } + | pa_pat lbrace stmtlist '}' { $$ = stat2(PASTAT, $1, $3); } + | pa_pat ',' pa_pat { $$ = pa2stat($1, $3, stat2(PRINT, rectonode(), NIL)); } + | pa_pat ',' pa_pat lbrace stmtlist '}' { $$ = pa2stat($1, $3, $5); } + | lbrace stmtlist '}' { $$ = stat2(PASTAT, NIL, $2); } + | XBEGIN lbrace stmtlist '}' + { beginloc = linkum(beginloc, $3); $$ = 0; } + | XEND lbrace stmtlist '}' + { endloc = linkum(endloc, $3); $$ = 0; } + | FUNC funcname '(' varlist rparen {infunc++;} lbrace stmtlist '}' + { infunc--; curfname=0; defn((Cell *)$2, $4, $8); $$ = 0; } + ; + +pa_stats: + pa_stat + | pa_stats opt_pst pa_stat { $$ = linkum($1, $3); } + ; + +patlist: + pattern + | patlist comma pattern { $$ = linkum($1, $3); } + ; + +ppattern: + var ASGNOP ppattern { $$ = op2($2, $1, $3); } + | ppattern '?' ppattern ':' ppattern %prec '?' + { $$ = op3(CONDEXPR, notnull($1), $3, $5); } + | ppattern bor ppattern %prec BOR + { $$ = op2(BOR, notnull($1), notnull($3)); } + | ppattern and ppattern %prec AND + { $$ = op2(AND, notnull($1), notnull($3)); } + | ppattern MATCHOP reg_expr { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); } + | ppattern MATCHOP ppattern + { if (constnode($3)) + $$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0)); + else + $$ = op3($2, (Node *)1, $1, $3); } + | ppattern IN varname { $$ = op2(INTEST, $1, makearr($3)); } + | '(' plist ')' IN varname { $$ = op2(INTEST, $2, makearr($5)); } + | ppattern term %prec CAT { $$ = op2(CAT, $1, $2); } + | re + | term + ; + +pattern: + var ASGNOP pattern { $$ = op2($2, $1, $3); } + | pattern '?' pattern ':' pattern %prec '?' + { $$ = op3(CONDEXPR, notnull($1), $3, $5); } + | pattern bor pattern %prec BOR + { $$ = op2(BOR, notnull($1), notnull($3)); } + | pattern and pattern %prec AND + { $$ = op2(AND, notnull($1), notnull($3)); } + | pattern EQ pattern { $$ = op2($2, $1, $3); } + | pattern GE pattern { $$ = op2($2, $1, $3); } + | pattern GT pattern { $$ = op2($2, $1, $3); } + | pattern LE pattern { $$ = op2($2, $1, $3); } + | pattern LT pattern { $$ = op2($2, $1, $3); } + | pattern NE pattern { $$ = op2($2, $1, $3); } + | pattern MATCHOP reg_expr { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); } + | pattern MATCHOP pattern + { if (constnode($3)) + $$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0)); + else + $$ = op3($2, (Node *)1, $1, $3); } + | pattern IN varname { $$ = op2(INTEST, $1, makearr($3)); } + | '(' plist ')' IN varname { $$ = op2(INTEST, $2, makearr($5)); } + | pattern '|' GETLINE var { + if (safe) SYNTAX("cmd | getline is unsafe"); + else $$ = op3(GETLINE, $4, itonp($2), $1); } + | pattern '|' GETLINE { + if (safe) SYNTAX("cmd | getline is unsafe"); + else $$ = op3(GETLINE, (Node*)0, itonp($2), $1); } + | pattern term %prec CAT { $$ = op2(CAT, $1, $2); } + | re + | term + ; + +plist: + pattern comma pattern { $$ = linkum($1, $3); } + | plist comma pattern { $$ = linkum($1, $3); } + ; + +pplist: + ppattern + | pplist comma ppattern { $$ = linkum($1, $3); } + ; + +prarg: + /* empty */ { $$ = rectonode(); } + | pplist + | '(' plist ')' { $$ = $2; } + ; + +print: + PRINT | PRINTF + ; + +pst: + NL | ';' | pst NL | pst ';' + ; + +rbrace: + '}' | rbrace NL + ; + +re: + reg_expr + { $$ = op3(MATCH, NIL, rectonode(), (Node*)makedfa($1, 0)); } + | NOT re { $$ = op1(NOT, notnull($2)); } + ; + +reg_expr: + '/' {startreg();} REGEXPR '/' { $$ = $3; } + ; + +rparen: + ')' | rparen NL + ; + +simple_stmt: + print prarg '|' term { + if (safe) SYNTAX("print | is unsafe"); + else $$ = stat3($1, $2, itonp($3), $4); } + | print prarg APPEND term { + if (safe) SYNTAX("print >> is unsafe"); + else $$ = stat3($1, $2, itonp($3), $4); } + | print prarg GT term { + if (safe) SYNTAX("print > is unsafe"); + else $$ = stat3($1, $2, itonp($3), $4); } + | print prarg { $$ = stat3($1, $2, NIL, NIL); } + | DELETE varname '[' patlist ']' { $$ = stat2(DELETE, makearr($2), $4); } + | DELETE varname { $$ = stat2(DELETE, makearr($2), 0); } + | pattern { $$ = exptostat($1); } + | error { yyclearin; SYNTAX("illegal statement"); } + ; + +st: + nl + | ';' opt_nl + ; + +stmt: + BREAK st { if (!inloop) SYNTAX("break illegal outside of loops"); + $$ = stat1(BREAK, NIL); } + | CLOSE pattern st { $$ = stat1(CLOSE, $2); } + | CONTINUE st { if (!inloop) SYNTAX("continue illegal outside of loops"); + $$ = stat1(CONTINUE, NIL); } + | do {inloop++;} stmt {--inloop;} WHILE '(' pattern ')' st + { $$ = stat2(DO, $3, notnull($7)); } + | EXIT pattern st { $$ = stat1(EXIT, $2); } + | EXIT st { $$ = stat1(EXIT, NIL); } + | for + | if stmt else stmt { $$ = stat3(IF, $1, $2, $4); } + | if stmt { $$ = stat3(IF, $1, $2, NIL); } + | lbrace stmtlist rbrace { $$ = $2; } + | NEXT st { if (infunc) + SYNTAX("next is illegal inside a function"); + $$ = stat1(NEXT, NIL); } + | NEXTFILE st { if (infunc) + SYNTAX("nextfile is illegal inside a function"); + $$ = stat1(NEXTFILE, NIL); } + | RETURN pattern st { $$ = stat1(RETURN, $2); } + | RETURN st { $$ = stat1(RETURN, NIL); } + | simple_stmt st + | while {inloop++;} stmt { --inloop; $$ = stat2(WHILE, $1, $3); } + | ';' opt_nl { $$ = 0; } + ; + +stmtlist: + stmt + | stmtlist stmt { $$ = linkum($1, $2); } + ; + +subop: + SUB | GSUB + ; + +term: + term '/' ASGNOP term { $$ = op2(DIVEQ, $1, $4); } + | term '+' term { $$ = op2(ADD, $1, $3); } + | term '-' term { $$ = op2(MINUS, $1, $3); } + | term '*' term { $$ = op2(MULT, $1, $3); } + | term '/' term { $$ = op2(DIVIDE, $1, $3); } + | term '%' term { $$ = op2(MOD, $1, $3); } + | term POWER term { $$ = op2(POWER, $1, $3); } + | '-' term %prec UMINUS { $$ = op1(UMINUS, $2); } + | '+' term %prec UMINUS { $$ = $2; } + | NOT term %prec UMINUS { $$ = op1(NOT, notnull($2)); } + | BLTIN '(' ')' { $$ = op2(BLTIN, itonp($1), rectonode()); } + | BLTIN '(' patlist ')' { $$ = op2(BLTIN, itonp($1), $3); } + | BLTIN { $$ = op2(BLTIN, itonp($1), rectonode()); } + | CALL '(' ')' { $$ = op2(CALL, celltonode($1,CVAR), NIL); } + | CALL '(' patlist ')' { $$ = op2(CALL, celltonode($1,CVAR), $3); } + | DECR var { $$ = op1(PREDECR, $2); } + | INCR var { $$ = op1(PREINCR, $2); } + | var DECR { $$ = op1(POSTDECR, $1); } + | var INCR { $$ = op1(POSTINCR, $1); } + | GETLINE var LT term { $$ = op3(GETLINE, $2, itonp($3), $4); } + | GETLINE LT term { $$ = op3(GETLINE, NIL, itonp($2), $3); } + | GETLINE var { $$ = op3(GETLINE, $2, NIL, NIL); } + | GETLINE { $$ = op3(GETLINE, NIL, NIL, NIL); } + | INDEX '(' pattern comma pattern ')' + { $$ = op2(INDEX, $3, $5); } + | INDEX '(' pattern comma reg_expr ')' + { SYNTAX("index() doesn't permit regular expressions"); + $$ = op2(INDEX, $3, (Node*)$5); } + | '(' pattern ')' { $$ = $2; } + | MATCHFCN '(' pattern comma reg_expr ')' + { $$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa($5, 1)); } + | MATCHFCN '(' pattern comma pattern ')' + { if (constnode($5)) + $$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa(strnode($5), 1)); + else + $$ = op3(MATCHFCN, (Node *)1, $3, $5); } + | NUMBER { $$ = celltonode($1, CCON); } + | SPLIT '(' pattern comma varname comma pattern ')' /* string */ + { $$ = op4(SPLIT, $3, makearr($5), $7, (Node*)STRING); } + | SPLIT '(' pattern comma varname comma reg_expr ')' /* const /regexp/ */ + { $$ = op4(SPLIT, $3, makearr($5), (Node*)makedfa($7, 1), (Node *)REGEXPR); } + | SPLIT '(' pattern comma varname ')' + { $$ = op4(SPLIT, $3, makearr($5), NIL, (Node*)STRING); } /* default */ + | SPRINTF '(' patlist ')' { $$ = op1($1, $3); } + | STRING { $$ = celltonode($1, CCON); } + | subop '(' reg_expr comma pattern ')' + { $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, rectonode()); } + | subop '(' pattern comma pattern ')' + { if (constnode($3)) + $$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, rectonode()); + else + $$ = op4($1, (Node *)1, $3, $5, rectonode()); } + | subop '(' reg_expr comma pattern comma var ')' + { $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, $7); } + | subop '(' pattern comma pattern comma var ')' + { if (constnode($3)) + $$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, $7); + else + $$ = op4($1, (Node *)1, $3, $5, $7); } + | SUBSTR '(' pattern comma pattern comma pattern ')' + { $$ = op3(SUBSTR, $3, $5, $7); } + | SUBSTR '(' pattern comma pattern ')' + { $$ = op3(SUBSTR, $3, $5, NIL); } + | var + ; + +var: + varname + | varname '[' patlist ']' { $$ = op2(ARRAY, makearr($1), $3); } + | IVAR { $$ = op1(INDIRECT, celltonode($1, CVAR)); } + | INDIRECT term { $$ = op1(INDIRECT, $2); } + ; + +varlist: + /* nothing */ { arglist = $$ = 0; } + | VAR { arglist = $$ = celltonode($1,CVAR); } + | varlist comma VAR { + checkdup($1, $3); + arglist = $$ = linkum($1,celltonode($3,CVAR)); } + ; + +varname: + VAR { $$ = celltonode($1, CVAR); } + | ARG { $$ = op1(ARG, itonp($1)); } + | VARNF { $$ = op1(VARNF, (Node *) $1); } + ; + + +while: + WHILE '(' pattern rparen { $$ = notnull($3); } + ; + +%% + +void setfname(Cell *p) +{ + if (isarr(p)) + SYNTAX("%s is an array, not a function", p->nval); + else if (isfcn(p)) + SYNTAX("you can't define function %s more than once", p->nval); + curfname = p->nval; +} + +int constnode(Node *p) +{ + return isvalue(p) && ((Cell *) (p->narg[0]))->csub == CCON; +} + +char *strnode(Node *p) +{ + return ((Cell *)(p->narg[0]))->sval; +} + +Node *notnull(Node *n) +{ + switch (n->nobj) { + case LE: case LT: case EQ: case NE: case GT: case GE: + case BOR: case AND: case NOT: + return n; + default: + return op2(NE, n, nullnode); + } +} + +void checkdup(Node *vl, Cell *cp) /* check if name already in list */ +{ + char *s = cp->nval; + for ( ; vl; vl = vl->nnext) { + if (strcmp(s, ((Cell *)(vl->narg[0]))->nval) == 0) { + SYNTAX("duplicate argument %s", s); + break; + } + } +} diff --git a/awk/lex.c b/awk/lex.c @@ -0,0 +1,569 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "awk.h" +#include "y.tab.h" + +extern YYSTYPE yylval; +extern int infunc; + +int lineno = 1; +int bracecnt = 0; +int brackcnt = 0; +int parencnt = 0; + +typedef struct Keyword { + char *word; + int sub; + int type; +} Keyword; + +Keyword keywords[] ={ /* keep sorted: binary searched */ + { "BEGIN", XBEGIN, XBEGIN }, + { "END", XEND, XEND }, + { "NF", VARNF, VARNF }, + { "atan2", FATAN, BLTIN }, + { "break", BREAK, BREAK }, + { "close", CLOSE, CLOSE }, + { "continue", CONTINUE, CONTINUE }, + { "cos", FCOS, BLTIN }, + { "delete", DELETE, DELETE }, + { "do", DO, DO }, + { "else", ELSE, ELSE }, + { "exit", EXIT, EXIT }, + { "exp", FEXP, BLTIN }, + { "fflush", FFLUSH, BLTIN }, + { "for", FOR, FOR }, + { "func", FUNC, FUNC }, + { "function", FUNC, FUNC }, + { "getline", GETLINE, GETLINE }, + { "gsub", GSUB, GSUB }, + { "if", IF, IF }, + { "in", IN, IN }, + { "index", INDEX, INDEX }, + { "int", FINT, BLTIN }, + { "length", FLENGTH, BLTIN }, + { "log", FLOG, BLTIN }, + { "match", MATCHFCN, MATCHFCN }, + { "next", NEXT, NEXT }, + { "nextfile", NEXTFILE, NEXTFILE }, + { "print", PRINT, PRINT }, + { "printf", PRINTF, PRINTF }, + { "rand", FRAND, BLTIN }, + { "return", RETURN, RETURN }, + { "sin", FSIN, BLTIN }, + { "split", SPLIT, SPLIT }, + { "sprintf", SPRINTF, SPRINTF }, + { "sqrt", FSQRT, BLTIN }, + { "srand", FSRAND, BLTIN }, + { "sub", SUB, SUB }, + { "substr", SUBSTR, SUBSTR }, + { "system", FSYSTEM, BLTIN }, + { "tolower", FTOLOWER, BLTIN }, + { "toupper", FTOUPPER, BLTIN }, + { "while", WHILE, WHILE }, + { "utf", FUTF, BLTIN }, +}; + +#define DEBUG +#ifdef DEBUG +#define RET(x) { if(dbg)printf("lex %s\n", tokname(x)); return(x); } +#else +#define RET(x) return(x) +#endif + +int peek(void) +{ + int c = input(); + unput(c); + return c; +} + +int gettok(char **pbuf, int *psz) /* get next input token */ +{ + int c; + char *buf = *pbuf; + int sz = *psz; + char *bp = buf; + + c = input(); + if (c == 0) + return 0; + buf[0] = c; + buf[1] = 0; + if (!isalnum(c) && c != '.' && c != '_') + return c; + + *bp++ = c; + if (isalpha(c) || c == '_') { /* it's a varname */ + for ( ; (c = input()) != 0; ) { + if (bp-buf >= sz) + if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, 0)) + FATAL( "out of space for name %.10s...", buf ); + if (isalnum(c) || c == '_') + *bp++ = c; + else { + *bp = 0; + unput(c); + break; + } + } + } else { /* it's a number */ + char *rem; + /* read input until can't be a number */ + for ( ; (c = input()) != 0; ) { + if (bp-buf >= sz) + if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, 0)) + FATAL( "out of space for number %.10s...", buf ); + if (isdigit(c) || c == 'e' || c == 'E' + || c == '.' || c == '+' || c == '-') + *bp++ = c; + else { + unput(c); + break; + } + } + *bp = 0; + strtod(buf, &rem); /* parse the number */ + unputstr(rem); /* put rest back for later */ + rem[0] = 0; + } + *pbuf = buf; + *psz = sz; + return buf[0]; +} + +int word(char *); +int string(void); +int regexpr(void); +int sc = 0; /* 1 => return a } right now */ +int reg = 0; /* 1 => return a REGEXPR now */ + +int yylex(void) +{ + int c; + static char *buf = 0; + static int bufsize = 500; + + if (buf == 0 && (buf = (char *) malloc(bufsize)) == NULL) + FATAL( "out of space in yylex" ); + if (sc) { + sc = 0; + RET('}'); + } + if (reg) { + reg = 0; + return regexpr(); + } + for (;;) { + c = gettok(&buf, &bufsize); + if (c == 0) + return 0; + if (isalpha(c) || c == '_') + return word(buf); + if (isdigit(c) || c == '.') { + yylval.cp = setsymtab(buf, tostring(buf), atof(buf), CON|NUM, symtab); + /* should this also have STR set? */ + RET(NUMBER); + } + + yylval.i = c; + switch (c) { + case '\n': /* {EOL} */ + RET(NL); + case '\r': /* assume \n is coming */ + case ' ': /* {WS}+ */ + case '\t': + break; + case '#': /* #.* strip comments */ + while ((c = input()) != '\n' && c != 0) + ; + unput(c); + break; + case ';': + RET(';'); + case '\\': + if (peek() == '\n') { + input(); + } else if (peek() == '\r') { + input(); input(); /* \n */ + lineno++; + } else { + RET(c); + } + break; + case '&': + if (peek() == '&') { + input(); RET(AND); + } else + RET('&'); + case '|': + if (peek() == '|') { + input(); RET(BOR); + } else + RET('|'); + case '!': + if (peek() == '=') { + input(); yylval.i = NE; RET(NE); + } else if (peek() == '~') { + input(); yylval.i = NOTMATCH; RET(MATCHOP); + } else + RET(NOT); + case '~': + yylval.i = MATCH; + RET(MATCHOP); + case '<': + if (peek() == '=') { + input(); yylval.i = LE; RET(LE); + } else { + yylval.i = LT; RET(LT); + } + case '=': + if (peek() == '=') { + input(); yylval.i = EQ; RET(EQ); + } else { + yylval.i = ASSIGN; RET(ASGNOP); + } + case '>': + if (peek() == '=') { + input(); yylval.i = GE; RET(GE); + } else if (peek() == '>') { + input(); yylval.i = APPEND; RET(APPEND); + } else { + yylval.i = GT; RET(GT); + } + case '+': + if (peek() == '+') { + input(); yylval.i = INCR; RET(INCR); + } else if (peek() == '=') { + input(); yylval.i = ADDEQ; RET(ASGNOP); + } else + RET('+'); + case '-': + if (peek() == '-') { + input(); yylval.i = DECR; RET(DECR); + } else if (peek() == '=') { + input(); yylval.i = SUBEQ; RET(ASGNOP); + } else + RET('-'); + case '*': + if (peek() == '=') { /* *= */ + input(); yylval.i = MULTEQ; RET(ASGNOP); + } else if (peek() == '*') { /* ** or **= */ + input(); /* eat 2nd * */ + if (peek() == '=') { + input(); yylval.i = POWEQ; RET(ASGNOP); + } else { + RET(POWER); + } + } else + RET('*'); + case '/': + RET('/'); + case '%': + if (peek() == '=') { + input(); yylval.i = MODEQ; RET(ASGNOP); + } else + RET('%'); + case '^': + if (peek() == '=') { + input(); yylval.i = POWEQ; RET(ASGNOP); + } else + RET(POWER); + + case '$': + /* BUG: awkward, if not wrong */ + c = gettok(&buf, &bufsize); + if (c == '(' || c == '[' || (infunc && isarg(buf) >= 0)) { + unputstr(buf); + RET(INDIRECT); + } else if (isalpha(c)) { + if (strcmp(buf, "NF") == 0) { /* very special */ + unputstr("(NF)"); + RET(INDIRECT); + } + yylval.cp = setsymtab(buf, "", 0.0, STR|NUM, symtab); + RET(IVAR); + } else { + unputstr(buf); + RET(INDIRECT); + } + + case '}': + if (--bracecnt < 0) + SYNTAX( "extra }" ); + sc = 1; + RET(';'); + case ']': + if (--brackcnt < 0) + SYNTAX( "extra ]" ); + RET(']'); + case ')': + if (--parencnt < 0) + SYNTAX( "extra )" ); + RET(')'); + case '{': + bracecnt++; + RET('{'); + case '[': + brackcnt++; + RET('['); + case '(': + parencnt++; + RET('('); + + case '"': + return string(); /* BUG: should be like tran.c ? */ + + default: + RET(c); + } + } +} + +int string(void) +{ + int c, n; + char *s, *bp; + static char *buf = 0; + static int bufsz = 500; + + if (buf == 0 && (buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of space for strings"); + for (bp = buf; (c = input()) != '"'; ) { + if (!adjbuf(&buf, &bufsz, bp-buf+2, 500, &bp, 0)) + FATAL("out of space for string %.10s...", buf); + switch (c) { + case '\n': + case '\r': + case 0: + SYNTAX( "non-terminated string %.10s...", buf ); + lineno++; + break; + case '\\': + c = input(); + switch (c) { + case '"': *bp++ = '"'; break; + case 'n': *bp++ = '\n'; break; + case 't': *bp++ = '\t'; break; + case 'f': *bp++ = '\f'; break; + case 'r': *bp++ = '\r'; break; + case 'b': *bp++ = '\b'; break; + case 'v': *bp++ = '\v'; break; + case 'a': *bp++ = '\007'; break; + case '\\': *bp++ = '\\'; break; + + case '0': case '1': case '2': /* octal: \d \dd \ddd */ + case '3': case '4': case '5': case '6': case '7': + n = c - '0'; + if ((c = peek()) >= '0' && c < '8') { + n = 8 * n + input() - '0'; + if ((c = peek()) >= '0' && c < '8') + n = 8 * n + input() - '0'; + } + *bp++ = n; + break; + + case 'x': /* hex \x0-9a-fA-F + */ + { char xbuf[100], *px; + for (px = xbuf; (c = input()) != 0 && px-xbuf < 100-2; ) { + if (isdigit(c) + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F')) + *px++ = c; + else + break; + } + *px = 0; + unput(c); + sscanf(xbuf, "%x", &n); + *bp++ = n; + break; + } + + default: + *bp++ = c; + break; + } + break; + default: + *bp++ = c; + break; + } + } + *bp = 0; + s = tostring(buf); + *bp++ = ' '; *bp++ = 0; + yylval.cp = setsymtab(buf, s, 0.0, CON|STR|DONTFREE, symtab); + RET(STRING); +} + + +int binsearch(char *w, Keyword *kp, int n) +{ + int cond, low, mid, high; + + low = 0; + high = n - 1; + while (low <= high) { + mid = (low + high) / 2; + if ((cond = strcmp(w, kp[mid].word)) < 0) + high = mid - 1; + else if (cond > 0) + low = mid + 1; + else + return mid; + } + return -1; +} + +int word(char *w) +{ + Keyword *kp; + int c, n; + + n = binsearch(w, keywords, sizeof(keywords)/sizeof(keywords[0])); + kp = keywords + n; + if (n != -1) { /* found in table */ + yylval.i = kp->sub; + switch (kp->type) { /* special handling */ + case FSYSTEM: + if (safe) + SYNTAX( "system is unsafe" ); + RET(kp->type); + case FUNC: + if (infunc) + SYNTAX( "illegal nested function" ); + RET(kp->type); + case RETURN: + if (!infunc) + SYNTAX( "return not in function" ); + RET(kp->type); + case VARNF: + yylval.cp = setsymtab("NF", "", 0.0, NUM, symtab); + RET(VARNF); + default: + RET(kp->type); + } + } + c = peek(); /* look for '(' */ + if (c != '(' && infunc && (n=isarg(w)) >= 0) { + yylval.i = n; + RET(ARG); + } else { + yylval.cp = setsymtab(w, "", 0.0, STR|NUM|DONTFREE, symtab); + if (c == '(') { + RET(CALL); + } else { + RET(VAR); + } + } +} + +void startreg(void) /* next call to yyles will return a regular expression */ +{ + reg = 1; +} + +int regexpr(void) +{ + int c; + static char *buf = 0; + static int bufsz = 500; + char *bp; + + if (buf == 0 && (buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of space for rex expr"); + bp = buf; + for ( ; (c = input()) != '/' && c != 0; ) { + if (!adjbuf(&buf, &bufsz, bp-buf+3, 500, &bp, 0)) + FATAL("out of space for reg expr %.10s...", buf); + if (c == '\n') { + SYNTAX( "newline in regular expression %.10s...", buf ); + unput('\n'); + break; + } else if (c == '\\') { + *bp++ = '\\'; + *bp++ = input(); + } else { + *bp++ = c; + } + } + *bp = 0; + yylval.s = tostring(buf); + unput('/'); + RET(REGEXPR); +} + +/* low-level lexical stuff, sort of inherited from lex */ + +char ebuf[300]; +char *ep = ebuf; +char yysbuf[100]; /* pushback buffer */ +char *yysptr = yysbuf; +FILE *yyin = 0; + +int input(void) /* get next lexical input character */ +{ + int c; + extern char *lexprog; + + if (yysptr > yysbuf) + c = *--yysptr; + else if (lexprog != NULL) { /* awk '...' */ + if ((c = *lexprog) != 0) + lexprog++; + } else /* awk -f ... */ + c = pgetc(); + if (c == '\n') + lineno++; + else if (c == EOF) + c = 0; + if (ep >= ebuf + sizeof ebuf) + ep = ebuf; + return *ep++ = c; +} + +void unput(int c) /* put lexical character back on input */ +{ + if (c == '\n') + lineno--; + if (yysptr >= yysbuf + sizeof(yysbuf)) + FATAL("pushed back too much: %.20s...", yysbuf); + *yysptr++ = c; + if (--ep < ebuf) + ep = ebuf + sizeof(ebuf) - 1; +} + +void unputstr(char *s) /* put a string back on input */ +{ + int i; + + for (i = strlen(s)-1; i >= 0; i--) + unput(s[i]); +} diff --git a/awk/lib.c b/awk/lib.c @@ -0,0 +1,686 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +#define DEBUG +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <stdarg.h> +#include "awk.h" +#include "y.tab.h" + +FILE *infile = NULL; +char *file = ""; +char *record; +int recsize = RECSIZE; +char *fields; +int fieldssize = RECSIZE; + +Cell **fldtab; /* pointers to Cells */ +char inputFS[100] = " "; + +#define MAXFLD 200 +int nfields = MAXFLD; /* last allocated slot for $i */ + +int donefld; /* 1 = implies rec broken into fields */ +int donerec; /* 1 = record is valid (no flds have changed) */ + +int lastfld = 0; /* last used field */ +int argno = 1; /* current input argument number */ +extern Awkfloat *ARGC; + +static Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE }; +static Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE }; + +void recinit(unsigned int n) +{ + record = (char *) malloc(n); + fields = (char *) malloc(n); + fldtab = (Cell **) malloc((nfields+1) * sizeof(Cell *)); + if (record == NULL || fields == NULL || fldtab == NULL) + FATAL("out of space for $0 and fields"); + fldtab[0] = (Cell *) malloc(sizeof (Cell)); + *fldtab[0] = dollar0; + fldtab[0]->sval = record; + fldtab[0]->nval = tostring("0"); + makefields(1, nfields); +} + +void makefields(int n1, int n2) /* create $n1..$n2 inclusive */ +{ + char temp[50]; + int i; + + for (i = n1; i <= n2; i++) { + fldtab[i] = (Cell *) malloc(sizeof (struct Cell)); + if (fldtab[i] == NULL) + FATAL("out of space in makefields %d", i); + *fldtab[i] = dollar1; + sprintf(temp, "%d", i); + fldtab[i]->nval = tostring(temp); + } +} + +void initgetrec(void) +{ + int i; + char *p; + + for (i = 1; i < *ARGC; i++) { + if (!isclvar(p = getargv(i))) { /* find 1st real filename */ + setsval(lookup("FILENAME", symtab), getargv(i)); + return; + } + setclvar(p); /* a commandline assignment before filename */ + argno++; + } + infile = stdin; /* no filenames, so use stdin */ +} + +int getrec(char **pbuf, int *pbufsize, int isrecord) /* get next input record */ +{ /* note: cares whether buf == record */ + int c; + static int firsttime = 1; + char *buf = *pbuf; + int bufsize = *pbufsize; + + if (firsttime) { + firsttime = 0; + initgetrec(); + } + dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n", + *RS, *FS, *ARGC, *FILENAME) ); + if (isrecord) { + donefld = 0; + donerec = 1; + } + buf[0] = 0; + while (argno < *ARGC || infile == stdin) { + dprintf( ("argno=%d, file=|%s|\n", argno, file) ); + if (infile == NULL) { /* have to open a new file */ + file = getargv(argno); + if (*file == '\0') { /* it's been zapped */ + argno++; + continue; + } + if (isclvar(file)) { /* a var=value arg */ + setclvar(file); + argno++; + continue; + } + *FILENAME = file; + dprintf( ("opening file %s\n", file) ); + if (*file == '-' && *(file+1) == '\0') + infile = stdin; + else if ((infile = fopen(file, "r")) == NULL) + FATAL("can't open file %s", file); + setfval(fnrloc, 0.0); + } + c = readrec(&buf, &bufsize, infile); + if (c != 0 || buf[0] != '\0') { /* normal record */ + if (isrecord) { + if (freeable(fldtab[0])) + xfree(fldtab[0]->sval); + fldtab[0]->sval = buf; /* buf == record */ + fldtab[0]->tval = REC | STR | DONTFREE; + if (is_number(fldtab[0]->sval)) { + fldtab[0]->fval = atof(fldtab[0]->sval); + fldtab[0]->tval |= NUM; + } + } + setfval(nrloc, nrloc->fval+1); + setfval(fnrloc, fnrloc->fval+1); + *pbuf = buf; + *pbufsize = bufsize; + return 1; + } + /* EOF arrived on this file; set up next */ + if (infile != stdin) + fclose(infile); + infile = NULL; + argno++; + } + *pbuf = buf; + *pbufsize = bufsize; + return 0; /* true end of file */ +} + +void nextfile(void) +{ + if (infile != stdin) + fclose(infile); + infile = NULL; + argno++; +} + +int readrec(char **pbuf, int *pbufsize, FILE *inf) /* read one record into buf */ +{ + int sep, c; + char *rr, *buf = *pbuf; + int bufsize = *pbufsize; + + if (strlen(*FS) >= sizeof(inputFS)) + FATAL("field separator %.10s... is too long", *FS); + strcpy(inputFS, *FS); /* for subsequent field splitting */ + if ((sep = **RS) == 0) { + sep = '\n'; + while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */ + ; + if (c != EOF) + ungetc(c, inf); + } + for (rr = buf; ; ) { + for (; (c=getc(inf)) != sep && c != EOF; ) { + if (rr-buf+1 > bufsize) + if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1")) + FATAL("input record `%.30s...' too long", buf); + *rr++ = c; + } + if (**RS == sep || c == EOF) + break; + if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */ + break; + if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2")) + FATAL("input record `%.30s...' too long", buf); + *rr++ = '\n'; + *rr++ = c; + } + if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3")) + FATAL("input record `%.30s...' too long", buf); + *rr = 0; + dprintf( ("readrec saw <%s>, returns %d\n", buf, c == EOF && rr == buf ? 0 : 1) ); + *pbuf = buf; + *pbufsize = bufsize; + return c == EOF && rr == buf ? 0 : 1; +} + +char *getargv(int n) /* get ARGV[n] */ +{ + Cell *x; + char *s, temp[50]; + extern Array *ARGVtab; + + sprintf(temp, "%d", n); + x = setsymtab(temp, "", 0.0, STR, ARGVtab); + s = getsval(x); + dprintf( ("getargv(%d) returns |%s|\n", n, s) ); + return s; +} + +void setclvar(char *s) /* set var=value from s */ +{ + char *p; + Cell *q; + + for (p=s; *p != '='; p++) + ; + *p++ = 0; + p = qstring(p, '\0'); + q = setsymtab(s, p, 0.0, STR, symtab); + setsval(q, p); + if (is_number(q->sval)) { + q->fval = atof(q->sval); + q->tval |= NUM; + } + dprintf( ("command line set %s to |%s|\n", s, p) ); +} + + +void fldbld(void) /* create fields from current record */ +{ + /* this relies on having fields[] the same length as $0 */ + /* the fields are all stored in this one array with \0's */ + char *r, *fr, sep; + Cell *p; + int i, j, n; + + if (donefld) + return; + if (!isstr(fldtab[0])) + getsval(fldtab[0]); + r = fldtab[0]->sval; + n = strlen(r); + if (n > fieldssize) { + xfree(fields); + if ((fields = (char *) malloc(n+1)) == NULL) + FATAL("out of space for fields in fldbld %d", n); + fieldssize = n; + } + fr = fields; + i = 0; /* number of fields accumulated here */ + if (strlen(inputFS) > 1) { /* it's a regular expression */ + i = refldbld(r, inputFS); + } else if ((sep = *inputFS) == ' ') { /* default whitespace */ + for (i = 0; ; ) { + while (*r == ' ' || *r == '\t' || *r == '\n') + r++; + if (*r == 0) + break; + i++; + if (i > nfields) + growfldtab(i); + if (freeable(fldtab[i])) + xfree(fldtab[i]->sval); + fldtab[i]->sval = fr; + fldtab[i]->tval = FLD | STR | DONTFREE; + do + *fr++ = *r++; + while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0'); + *fr++ = 0; + } + *fr = 0; + } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */ + for (i = 0; *r != 0; r++) { + char buf[2]; + i++; + if (i > nfields) + growfldtab(i); + if (freeable(fldtab[i])) + xfree(fldtab[i]->sval); + buf[0] = *r; + buf[1] = 0; + fldtab[i]->sval = tostring(buf); + fldtab[i]->tval = FLD | STR; + } + *fr = 0; + } else if (*r != 0) { /* if 0, it's a null field */ + for (;;) { + i++; + if (i > nfields) + growfldtab(i); + if (freeable(fldtab[i])) + xfree(fldtab[i]->sval); + fldtab[i]->sval = fr; + fldtab[i]->tval = FLD | STR | DONTFREE; + while (*r != sep && *r != '\n' && *r != '\0') /* \n is always a separator */ + *fr++ = *r++; + *fr++ = 0; + if (*r++ == 0) + break; + } + *fr = 0; + } + if (i > nfields) + FATAL("record `%.30s...' has too many fields; can't happen", r); + cleanfld(i+1, lastfld); /* clean out junk from previous record */ + lastfld = i; + donefld = 1; + for (j = 1; j <= lastfld; j++) { + p = fldtab[j]; + if(is_number(p->sval)) { + p->fval = atof(p->sval); + p->tval |= NUM; + } + } + setfval(nfloc, (Awkfloat) lastfld); + if (dbg) { + for (j = 0; j <= lastfld; j++) { + p = fldtab[j]; + printf("field %d (%s): |%s|\n", j, p->nval, p->sval); + } + } +} + +void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */ +{ /* nvals remain intact */ + Cell *p; + int i; + + for (i = n1; i <= n2; i++) { + p = fldtab[i]; + if (freeable(p)) + xfree(p->sval); + p->sval = ""; + p->tval = FLD | STR | DONTFREE; + } +} + +void newfld(int n) /* add field n after end of existing lastfld */ +{ + if (n > nfields) + growfldtab(n); + cleanfld(lastfld+1, n); + lastfld = n; + setfval(nfloc, (Awkfloat) n); +} + +Cell *fieldadr(int n) /* get nth field */ +{ + if (n < 0) + FATAL("trying to access field %d", n); + if (n > nfields) /* fields after NF are empty */ + growfldtab(n); /* but does not increase NF */ + return(fldtab[n]); +} + +void growfldtab(int n) /* make new fields up to at least $n */ +{ + int nf = 2 * nfields; + + if (n > nf) + nf = n; + fldtab = (Cell **) realloc(fldtab, (nf+1) * (sizeof (struct Cell *))); + if (fldtab == NULL) + FATAL("out of space creating %d fields", nf); + makefields(nfields+1, nf); + nfields = nf; +} + +int refldbld(char *rec, char *fs) /* build fields from reg expr in FS */ +{ + /* this relies on having fields[] the same length as $0 */ + /* the fields are all stored in this one array with \0's */ + char *fr; + void *p; + int i, n; + + n = strlen(rec); + if (n > fieldssize) { + xfree(fields); + if ((fields = (char *) malloc(n+1)) == NULL) + FATAL("out of space for fields in refldbld %d", n); + fieldssize = n; + } + fr = fields; + *fr = '\0'; + if (*rec == '\0') + return 0; + p = compre(fs); + dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) ); + for (i = 1; ; i++) { + if (i > nfields) + growfldtab(i); + if (freeable(fldtab[i])) + xfree(fldtab[i]->sval); + fldtab[i]->tval = FLD | STR | DONTFREE; + fldtab[i]->sval = fr; + dprintf( ("refldbld: i=%d\n", i) ); + if (nematch(p, rec, rec)) { + dprintf( ("match %s (%d chars)\n", patbeg, patlen) ); + strncpy(fr, rec, patbeg-rec); + fr += patbeg - rec + 1; + *(fr-1) = '\0'; + rec = patbeg + patlen; + } else { + dprintf( ("no match %s\n", rec) ); + strcpy(fr, rec); + break; + } + } + return i; +} + +void recbld(void) /* create $0 from $1..$NF if necessary */ +{ + int i; + char *r, *p; + + if (donerec == 1) + return; + r = record; + for (i = 1; i <= *NF; i++) { + p = getsval(fldtab[i]); + if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1")) + FATAL("created $0 `%.30s...' too long", record); + while ((*r = *p++) != 0) + r++; + if (i < *NF) { + if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2")) + FATAL("created $0 `%.30s...' too long", record); + for (p = *OFS; (*r = *p++) != 0; ) + r++; + } + } + if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3")) + FATAL("built giant record `%.30s...'", record); + *r = '\0'; + dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) ); + + if (freeable(fldtab[0])) + xfree(fldtab[0]->sval); + fldtab[0]->tval = REC | STR | DONTFREE; + fldtab[0]->sval = record; + + dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) ); + dprintf( ("recbld = |%s|\n", record) ); + donerec = 1; +} + +int errorflag = 0; + +void yyerror(char *s) +{ + SYNTAX(s); +} + +void SYNTAX(char *fmt, ...) +{ + extern char *cmdname, *curfname; + static int been_here = 0; + va_list varg; + + if (been_here++ > 2) + return; + fprintf(stderr, "%s: ", cmdname); + va_start(varg, fmt); + vfprintf(stderr, fmt, varg); + va_end(varg); + if(compile_time == 1 && cursource() != NULL) + fprintf(stderr, " at %s:%d", cursource(), lineno); + else + fprintf(stderr, " at line %d", lineno); + if (curfname != NULL) + fprintf(stderr, " in function %s", curfname); + fprintf(stderr, "\n"); + errorflag = 2; + eprint(); +} + +void fpecatch(int n) +{ + FATAL("floating point exception %d", n); +} + +extern int bracecnt, brackcnt, parencnt; + +void bracecheck(void) +{ + int c; + static int beenhere = 0; + + if (beenhere++) + return; + while ((c = input()) != EOF && c != '\0') + bclass(c); + bcheck2(bracecnt, '{', '}'); + bcheck2(brackcnt, '[', ']'); + bcheck2(parencnt, '(', ')'); +} + +void bcheck2(int n, int c1, int c2) +{ + if (n == 1) + fprintf(stderr, "\tmissing %c\n", c2); + else if (n > 1) + fprintf(stderr, "\t%d missing %c's\n", n, c2); + else if (n == -1) + fprintf(stderr, "\textra %c\n", c2); + else if (n < -1) + fprintf(stderr, "\t%d extra %c's\n", -n, c2); +} + +void FATAL(char *fmt, ...) +{ + extern char *cmdname; + va_list varg; + + fflush(stdout); + fprintf(stderr, "%s: ", cmdname); + va_start(varg, fmt); + vfprintf(stderr, fmt, varg); + va_end(varg); + error(); + if (dbg > 1) /* core dump if serious debugging on */ + abort(); + exit(2); +} + +void WARNING(char *fmt, ...) +{ + extern char *cmdname; + va_list varg; + + fflush(stdout); + fprintf(stderr, "%s: ", cmdname); + va_start(varg, fmt); + vfprintf(stderr, fmt, varg); + va_end(varg); + error(); +} + +void error() +{ + extern Node *curnode; + int line; + + fprintf(stderr, "\n"); + if (compile_time != 2 && NR && *NR > 0) { + if (strcmp(*FILENAME, "-") != 0) + fprintf(stderr, " input record %s:%d", *FILENAME, (int) (*FNR)); + else + fprintf(stderr, " input record number %d", (int) (*FNR)); + fprintf(stderr, "\n"); + } + if (compile_time != 2 && curnode) + line = curnode->lineno; + else if (compile_time != 2 && lineno) + line = lineno; + else + line = -1; + if (compile_time == 1 && cursource() != NULL){ + if(line >= 0) + fprintf(stderr, " source %s:%d", cursource(), line); + else + fprintf(stderr, " source file %s", cursource()); + }else if(line >= 0) + fprintf(stderr, " source line %d", line); + fprintf(stderr, "\n"); + eprint(); +} + +void eprint(void) /* try to print context around error */ +{ + char *p, *q; + int c; + static int been_here = 0; + extern char ebuf[], *ep; + + if (compile_time == 2 || compile_time == 0 || been_here++ > 0) + return; + p = ep - 1; + if (p > ebuf && *p == '\n') + p--; + for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--) + ; + while (*p == '\n') + p++; + fprintf(stderr, " context is\n\t"); + for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--) + ; + for ( ; p < q; p++) + if (*p) + putc(*p, stderr); + fprintf(stderr, " >>> "); + for ( ; p < ep; p++) + if (*p) + putc(*p, stderr); + fprintf(stderr, " <<< "); + if (*ep) + while ((c = input()) != '\n' && c != '\0' && c != EOF) { + putc(c, stderr); + bclass(c); + } + putc('\n', stderr); + ep = ebuf; +} + +void bclass(int c) +{ + switch (c) { + case '{': bracecnt++; break; + case '}': bracecnt--; break; + case '[': brackcnt++; break; + case ']': brackcnt--; break; + case '(': parencnt++; break; + case ')': parencnt--; break; + } +} + +double errcheck(double x, char *s) +{ + + if (errno == EDOM) { + errno = 0; + WARNING("%s argument out of domain", s); + x = 1; + } else if (errno == ERANGE) { + errno = 0; + WARNING("%s result out of range", s); + x = 1; + } + return x; +} + +int isclvar(char *s) /* is s of form var=something ? */ +{ + char *os = s; + + if (!isalpha(*s) && *s != '_') + return 0; + for ( ; *s; s++) + if (!(isalnum(*s) || *s == '_')) + break; + return *s == '=' && s > os && *(s+1) != '='; +} + +/* strtod is supposed to be a proper test of what's a valid number */ + +#include <math.h> +int is_number(char *s) +{ + double r; + char *ep; + 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,197 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +char *version = "version 19990602"; + +#define DEBUG +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include "awk.h" +#include "y.tab.h" + +extern char **environ; +extern int nfields; + +int dbg = 0; +char *cmdname; /* gets argv[0] for error messages */ +extern FILE *yyin; /* lex input file */ +char *lexprog; /* points to program argument if it exists */ +extern int errorflag; /* non-zero if any syntax errors; set by yyerror */ +int compile_time = 2; /* for error printing: */ + /* 2 = cmdline, 1 = compile, 0 = running */ + +char *pfile[20]; /* program filenames from -f's */ +int npfile = 0; /* number of filenames */ +int curpfile = 0; /* current filename */ + +int safe = 0; /* 1 => "safe" mode */ + +int main(int argc, char *argv[]) +{ + char *fs = NULL, *marg; + int temp; + + cmdname = argv[0]; + if (argc == 1) { + fprintf(stderr, "Usage: %s [-f programfile | 'program'] [-Ffieldsep] [-v var=value] [files]\n", cmdname); + exit(1); + } + signal(SIGFPE, fpecatch); + yyin = NULL; + symtab = makesymtab(NSYMTAB); + while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') { + if (strcmp(argv[1], "--") == 0) { /* explicit end of args */ + argc--; + argv++; + break; + } + switch (argv[1][1]) { + case 's': + if (strcmp(argv[1], "-safe") == 0) + safe = 1; + break; + case 'f': /* next argument is program filename */ + argc--; + argv++; + if (argc <= 1) + FATAL("no program filename"); + pfile[npfile++] = argv[1]; + break; + case 'F': /* set field separator */ + if (argv[1][2] != 0) { /* arg is -Fsomething */ + if (argv[1][2] == 't' && argv[1][3] == 0) /* wart: t=>\t */ + fs = "\t"; + else if (argv[1][2] != 0) + fs = &argv[1][2]; + } else { /* arg is -F something */ + argc--; argv++; + if (argc > 1 && argv[1][0] == 't' && argv[1][1] == 0) /* wart: t=>\t */ + fs = "\t"; + else if (argc > 1 && argv[1][0] != 0) + fs = &argv[1][0]; + } + if (fs == NULL || *fs == '\0') + WARNING("field separator FS is empty"); + break; + case 'v': /* -v a=1 to be done NOW. one -v for each */ + if (argv[1][2] == '\0' && --argc > 1 && isclvar((++argv)[1])) + setclvar(argv[1]); + break; + case 'm': /* more memory: -mr=record, -mf=fields */ + /* no longer needed */ + marg = argv[1]; + if (argv[1][3]) + temp = atoi(&argv[1][3]); + else { + argv++; argc--; + temp = atoi(&argv[1][0]); + } + switch (marg[2]) { + case 'r': recsize = temp; break; + case 'f': nfields = temp; break; + default: FATAL("unknown option %s\n", marg); + } + break; + case 'd': + dbg = atoi(&argv[1][2]); + if (dbg == 0) + dbg = 1; + printf("awk %s\n", version); + break; + case 'V': /* added for exptools "standard" */ + printf("awk %s\n", version); + exit(0); + break; + default: + WARNING("unknown option %s ignored", argv[1]); + break; + } + argc--; + argv++; + } + /* argv[1] is now the first argument */ + if (npfile == 0) { /* no -f; first argument is program */ + if (argc <= 1) { + if (dbg) + exit(0); + FATAL("no program given"); + } + dprintf( ("program = |%s|\n", argv[1]) ); + lexprog = argv[1]; + argc--; + argv++; + } + recinit(recsize); + syminit(); + compile_time = 1; + argv[0] = cmdname; /* put prog name at front of arglist */ + dprintf( ("argc=%d, argv[0]=%s\n", argc, argv[0]) ); + arginit(argc, argv); + if (!safe) + envinit(environ); + yyparse(); + if (fs) + *FS = qstring(fs, '\0'); + dprintf( ("errorflag=%d\n", errorflag) ); + if (errorflag == 0) { + compile_time = 0; + run(winner); + } else + bracecheck(); + return(errorflag); +} + +int pgetc(void) /* get 1 character from awk program */ +{ + int c; + + for (;;) { + if (yyin == NULL) { + if (curpfile >= npfile) + return EOF; + if (strcmp(pfile[curpfile], "-") == 0) + yyin = stdin; + else if ((yyin = fopen(pfile[curpfile], "r")) == NULL) + FATAL("can't open file %s", pfile[curpfile]); + lineno = 1; + } + if ((c = getc(yyin)) != EOF) + return c; + if (yyin != stdin) + fclose(yyin); + yyin = NULL; + curpfile++; + } +} + +char *cursource(void) /* current source file name */ +{ + if (npfile > 0) + return pfile[curpfile]; + else + return NULL; +} diff --git a/awk/maketab.c b/awk/maketab.c @@ -0,0 +1,168 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +/* + * this program makes the table to link function names + * and type indices that is used by execute() in run.c. + * it finds the indices in y.tab.h, produced by yacc. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "awk.h" +#include "y.tab.h" + +struct xx +{ int token; + char *name; + char *pname; +} proc[] = { + { PROGRAM, "program", NULL }, + { BOR, "boolop", " || " }, + { AND, "boolop", " && " }, + { NOT, "boolop", " !" }, + { NE, "relop", " != " }, + { EQ, "relop", " == " }, + { LE, "relop", " <= " }, + { LT, "relop", " < " }, + { GE, "relop", " >= " }, + { GT, "relop", " > " }, + { ARRAY, "array", NULL }, + { INDIRECT, "indirect", "$(" }, + { SUBSTR, "substr", "substr" }, + { SUB, "sub", "sub" }, + { GSUB, "gsub", "gsub" }, + { INDEX, "sindex", "sindex" }, + { SPRINTF, "awksprintf", "sprintf " }, + { ADD, "arith", " + " }, + { MINUS, "arith", " - " }, + { MULT, "arith", " * " }, + { DIVIDE, "arith", " / " }, + { MOD, "arith", " % " }, + { UMINUS, "arith", " -" }, + { POWER, "arith", " **" }, + { PREINCR, "incrdecr", "++" }, + { POSTINCR, "incrdecr", "++" }, + { PREDECR, "incrdecr", "--" }, + { POSTDECR, "incrdecr", "--" }, + { CAT, "cat", " " }, + { PASTAT, "pastat", NULL }, + { PASTAT2, "dopa2", NULL }, + { MATCH, "matchop", " ~ " }, + { NOTMATCH, "matchop", " !~ " }, + { MATCHFCN, "matchop", "matchop" }, + { INTEST, "intest", "intest" }, + { PRINTF, "awkprintf", "printf" }, + { PRINT, "printstat", "print" }, + { CLOSE, "closefile", "closefile" }, + { DELETE, "awkdelete", "awkdelete" }, + { SPLIT, "split", "split" }, + { ASSIGN, "assign", " = " }, + { ADDEQ, "assign", " += " }, + { SUBEQ, "assign", " -= " }, + { MULTEQ, "assign", " *= " }, + { DIVEQ, "assign", " /= " }, + { MODEQ, "assign", " %= " }, + { POWEQ, "assign", " ^= " }, + { CONDEXPR, "condexpr", " ?: " }, + { IF, "ifstat", "if(" }, + { WHILE, "whilestat", "while(" }, + { FOR, "forstat", "for(" }, + { DO, "dostat", "do" }, + { IN, "instat", "instat" }, + { NEXT, "jump", "next" }, + { NEXTFILE, "jump", "nextfile" }, + { EXIT, "jump", "exit" }, + { BREAK, "jump", "break" }, + { CONTINUE, "jump", "continue" }, + { RETURN, "jump", "ret" }, + { BLTIN, "bltin", "bltin" }, + { CALL, "call", "call" }, + { ARG, "arg", "arg" }, + { VARNF, "getnf", "NF" }, + { GETLINE, "getline", "getline" }, + { 0, "", "" }, +}; + +#define SIZE (LASTTOKEN - FIRSTTOKEN + 1) +char *table[SIZE]; +char *names[SIZE]; + +int main(int argc, char *argv[]) +{ + struct xx *p; + int i, n, tok; + char c; + FILE *fp; + char buf[200], name[200], def[200]; + + printf("#include <stdio.h>\n"); + printf("#include \"awk.h\"\n"); + printf("#include \"y.tab.h\"\n\n"); + for (i = SIZE; --i >= 0; ) + names[i] = ""; + + if ((fp = fopen("y.tab.h", "r")) == NULL) { + fprintf(stderr, "maketab can't open y.tab.h!\n"); + exit(1); + } + printf("static char *printname[%d] = {\n", SIZE); + i = 0; + while (fgets(buf, sizeof buf, fp) != NULL) { + n = sscanf(buf, "%1c %s %s %d", &c, def, name, &tok); + if (c != '#' || (n != 4 && strcmp(def,"define") != 0)) /* not a valid #define */ + continue; + if (tok < FIRSTTOKEN || tok > LASTTOKEN) { + fprintf(stderr, "maketab funny token %d %s\n", tok, buf); + exit(1); + } + names[tok-FIRSTTOKEN] = (char *) malloc(strlen(name)+1); + strcpy(names[tok-FIRSTTOKEN], name); + printf("\t(char *) \"%s\",\t/* %d */\n", name, tok); + i++; + } + printf("};\n\n"); + + for (p=proc; p->token!=0; p++) + table[p->token-FIRSTTOKEN] = p->name; + printf("\nCell *(*proctab[%d])(Node **, int) = {\n", SIZE); + for (i=0; i<SIZE; i++) + if (table[i]==0) + printf("\tnullproc,\t/* %s */\n", names[i]); + else + printf("\t%s,\t/* %s */\n", table[i], names[i]); + printf("};\n\n"); + + printf("char *tokname(int n)\n"); /* print a tokname() function */ + printf("{\n"); + printf(" static char buf[100];\n\n"); + printf(" if (n < FIRSTTOKEN || n > LASTTOKEN) {\n"); + printf(" sprintf(buf, \"token %%d\", n);\n"); + printf(" return buf;\n"); + printf(" }\n"); + printf(" return printname[n-FIRSTTOKEN];\n"); + printf("}\n"); + return 0; +} diff --git a/awk/parse.c b/awk/parse.c @@ -0,0 +1,271 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +#define DEBUG +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "awk.h" +#include "y.tab.h" + +Node *nodealloc(int n) +{ + Node *x; + + x = (Node *) malloc(sizeof(Node) + (n-1)*sizeof(Node *)); + if (x == NULL) + FATAL("out of space in nodealloc"); + x->nnext = NULL; + x->lineno = lineno; + return(x); +} + +Node *exptostat(Node *a) +{ + a->ntype = NSTAT; + return(a); +} + +Node *node1(int a, Node *b) +{ + Node *x; + + x = nodealloc(1); + x->nobj = a; + x->narg[0]=b; + return(x); +} + +Node *node2(int a, Node *b, Node *c) +{ + Node *x; + + x = nodealloc(2); + x->nobj = a; + x->narg[0] = b; + x->narg[1] = c; + return(x); +} + +Node *node3(int a, Node *b, Node *c, Node *d) +{ + Node *x; + + x = nodealloc(3); + x->nobj = a; + x->narg[0] = b; + x->narg[1] = c; + x->narg[2] = d; + return(x); +} + +Node *node4(int a, Node *b, Node *c, Node *d, Node *e) +{ + Node *x; + + x = nodealloc(4); + x->nobj = a; + x->narg[0] = b; + x->narg[1] = c; + x->narg[2] = d; + x->narg[3] = e; + return(x); +} + +Node *stat1(int a, Node *b) +{ + Node *x; + + x = node1(a,b); + x->ntype = NSTAT; + return(x); +} + +Node *stat2(int a, Node *b, Node *c) +{ + Node *x; + + x = node2(a,b,c); + x->ntype = NSTAT; + return(x); +} + +Node *stat3(int a, Node *b, Node *c, Node *d) +{ + Node *x; + + x = node3(a,b,c,d); + x->ntype = NSTAT; + return(x); +} + +Node *stat4(int a, Node *b, Node *c, Node *d, Node *e) +{ + Node *x; + + x = node4(a,b,c,d,e); + x->ntype = NSTAT; + return(x); +} + +Node *op1(int a, Node *b) +{ + Node *x; + + x = node1(a,b); + x->ntype = NEXPR; + return(x); +} + +Node *op2(int a, Node *b, Node *c) +{ + Node *x; + + x = node2(a,b,c); + x->ntype = NEXPR; + return(x); +} + +Node *op3(int a, Node *b, Node *c, Node *d) +{ + Node *x; + + x = node3(a,b,c,d); + x->ntype = NEXPR; + return(x); +} + +Node *op4(int a, Node *b, Node *c, Node *d, Node *e) +{ + Node *x; + + x = node4(a,b,c,d,e); + x->ntype = NEXPR; + return(x); +} + +Node *celltonode(Cell *a, int b) +{ + Node *x; + + a->ctype = OCELL; + a->csub = b; + x = node1(0, (Node *) a); + x->ntype = NVALUE; + return(x); +} + +Node *rectonode(void) /* make $0 into a Node */ +{ + extern Cell *literal0; + return op1(INDIRECT, celltonode(literal0, CUNK)); +} + +Node *makearr(Node *p) +{ + Cell *cp; + + if (isvalue(p)) { + cp = (Cell *) (p->narg[0]); + if (isfcn(cp)) + SYNTAX( "%s is a function, not an array", cp->nval ); + else if (!isarr(cp)) { + xfree(cp->sval); + cp->sval = (char *) makesymtab(NSYMTAB); + cp->tval = ARR; + } + } + return p; +} + +#define PA2NUM 50 /* max number of pat,pat patterns allowed */ +int paircnt; /* number of them in use */ +int pairstack[PA2NUM]; /* state of each pat,pat */ + +Node *pa2stat(Node *a, Node *b, Node *c) /* pat, pat {...} */ +{ + Node *x; + + x = node4(PASTAT2, a, b, c, itonp(paircnt)); + if (paircnt++ >= PA2NUM) + SYNTAX( "limited to %d pat,pat statements", PA2NUM ); + x->ntype = NSTAT; + return(x); +} + +Node *linkum(Node *a, Node *b) +{ + Node *c; + + if (errorflag) /* don't link things that are wrong */ + return a; + if (a == NULL) + return(b); + else if (b == NULL) + return(a); + for (c = a; c->nnext != NULL; c = c->nnext) + ; + c->nnext = b; + return(a); +} + +void defn(Cell *v, Node *vl, Node *st) /* turn on FCN bit in definition, */ +{ /* body of function, arglist */ + Node *p; + int n; + + if (isarr(v)) { + SYNTAX( "`%s' is an array name and a function name", v->nval ); + return; + } + v->tval = FCN; + v->sval = (char *) st; + n = 0; /* count arguments */ + for (p = vl; p; p = p->nnext) + n++; + v->fval = n; + dprintf( ("defining func %s (%d args)\n", v->nval, n) ); +} + +int isarg(char *s) /* is s in argument list for current function? */ +{ /* return -1 if not, otherwise arg # */ + extern Node *arglist; + Node *p = arglist; + int n; + + for (n = 0; p != 0; p = p->nnext, n++) + if (strcmp(((Cell *)(p->narg[0]))->nval, s) == 0) + return n; + return -1; +} + +int ptoi(void *p) /* convert pointer to integer */ +{ + return (int) (long) p; /* swearing that p fits, of course */ +} + +Node *itonp(int i) /* and vice versa */ +{ + return (Node *) (long) i; +} diff --git a/awk/proctab.c b/awk/proctab.c @@ -0,0 +1,206 @@ +#include <stdio.h> +#include <math.h> +#include "awk.h" +#include "y.tab.h" + +static char *printname[92] = { + (char *) "FIRSTTOKEN", /* 57346 */ + (char *) "PROGRAM", /* 57347 */ + (char *) "PASTAT", /* 57348 */ + (char *) "PASTAT2", /* 57349 */ + (char *) "XBEGIN", /* 57350 */ + (char *) "XEND", /* 57351 */ + (char *) "NL", /* 57352 */ + (char *) "ARRAY", /* 57353 */ + (char *) "MATCH", /* 57354 */ + (char *) "NOTMATCH", /* 57355 */ + (char *) "MATCHOP", /* 57356 */ + (char *) "FINAL", /* 57357 */ + (char *) "DOT", /* 57358 */ + (char *) "ALL", /* 57359 */ + (char *) "CCL", /* 57360 */ + (char *) "NCCL", /* 57361 */ + (char *) "CHAR", /* 57362 */ + (char *) "OR", /* 57363 */ + (char *) "STAR", /* 57364 */ + (char *) "QUEST", /* 57365 */ + (char *) "PLUS", /* 57366 */ + (char *) "AND", /* 57367 */ + (char *) "BOR", /* 57368 */ + (char *) "APPEND", /* 57369 */ + (char *) "EQ", /* 57370 */ + (char *) "GE", /* 57371 */ + (char *) "GT", /* 57372 */ + (char *) "LE", /* 57373 */ + (char *) "LT", /* 57374 */ + (char *) "NE", /* 57375 */ + (char *) "IN", /* 57376 */ + (char *) "ARG", /* 57377 */ + (char *) "BLTIN", /* 57378 */ + (char *) "BREAK", /* 57379 */ + (char *) "CLOSE", /* 57380 */ + (char *) "CONTINUE", /* 57381 */ + (char *) "DELETE", /* 57382 */ + (char *) "DO", /* 57383 */ + (char *) "EXIT", /* 57384 */ + (char *) "FOR", /* 57385 */ + (char *) "FUNC", /* 57386 */ + (char *) "SUB", /* 57387 */ + (char *) "GSUB", /* 57388 */ + (char *) "IF", /* 57389 */ + (char *) "INDEX", /* 57390 */ + (char *) "LSUBSTR", /* 57391 */ + (char *) "MATCHFCN", /* 57392 */ + (char *) "NEXT", /* 57393 */ + (char *) "NEXTFILE", /* 57394 */ + (char *) "ADD", /* 57395 */ + (char *) "MINUS", /* 57396 */ + (char *) "MULT", /* 57397 */ + (char *) "DIVIDE", /* 57398 */ + (char *) "MOD", /* 57399 */ + (char *) "ASSIGN", /* 57400 */ + (char *) "ASGNOP", /* 57401 */ + (char *) "ADDEQ", /* 57402 */ + (char *) "SUBEQ", /* 57403 */ + (char *) "MULTEQ", /* 57404 */ + (char *) "DIVEQ", /* 57405 */ + (char *) "MODEQ", /* 57406 */ + (char *) "POWEQ", /* 57407 */ + (char *) "PRINT", /* 57408 */ + (char *) "PRINTF", /* 57409 */ + (char *) "SPRINTF", /* 57410 */ + (char *) "ELSE", /* 57411 */ + (char *) "INTEST", /* 57412 */ + (char *) "CONDEXPR", /* 57413 */ + (char *) "POSTINCR", /* 57414 */ + (char *) "PREINCR", /* 57415 */ + (char *) "POSTDECR", /* 57416 */ + (char *) "PREDECR", /* 57417 */ + (char *) "VAR", /* 57418 */ + (char *) "IVAR", /* 57419 */ + (char *) "VARNF", /* 57420 */ + (char *) "CALL", /* 57421 */ + (char *) "NUMBER", /* 57422 */ + (char *) "STRING", /* 57423 */ + (char *) "REGEXPR", /* 57424 */ + (char *) "GETLINE", /* 57425 */ + (char *) "RETURN", /* 57426 */ + (char *) "SPLIT", /* 57427 */ + (char *) "SUBSTR", /* 57428 */ + (char *) "WHILE", /* 57429 */ + (char *) "CAT", /* 57430 */ + (char *) "NOT", /* 57431 */ + (char *) "UMINUS", /* 57432 */ + (char *) "POWER", /* 57433 */ + (char *) "DECR", /* 57434 */ + (char *) "INCR", /* 57435 */ + (char *) "INDIRECT", /* 57436 */ + (char *) "LASTTOKEN", /* 57437 */ +}; + + +Cell *(*proctab[92])(Node **, int) = { + nullproc, /* FIRSTTOKEN */ + program, /* PROGRAM */ + pastat, /* PASTAT */ + dopa2, /* PASTAT2 */ + nullproc, /* XBEGIN */ + nullproc, /* XEND */ + nullproc, /* NL */ + array, /* ARRAY */ + matchop, /* MATCH */ + matchop, /* NOTMATCH */ + nullproc, /* MATCHOP */ + nullproc, /* FINAL */ + nullproc, /* DOT */ + nullproc, /* ALL */ + nullproc, /* CCL */ + nullproc, /* NCCL */ + nullproc, /* CHAR */ + nullproc, /* OR */ + nullproc, /* STAR */ + nullproc, /* QUEST */ + nullproc, /* PLUS */ + boolop, /* AND */ + boolop, /* BOR */ + nullproc, /* APPEND */ + relop, /* EQ */ + relop, /* GE */ + relop, /* GT */ + relop, /* LE */ + relop, /* LT */ + relop, /* NE */ + instat, /* IN */ + arg, /* ARG */ + bltin, /* BLTIN */ + jump, /* BREAK */ + closefile, /* CLOSE */ + jump, /* CONTINUE */ + awkdelete, /* DELETE */ + dostat, /* DO */ + jump, /* EXIT */ + forstat, /* FOR */ + nullproc, /* FUNC */ + sub, /* SUB */ + gsub, /* GSUB */ + ifstat, /* IF */ + sindex, /* INDEX */ + nullproc, /* LSUBSTR */ + matchop, /* MATCHFCN */ + jump, /* NEXT */ + jump, /* NEXTFILE */ + arith, /* ADD */ + arith, /* MINUS */ + arith, /* MULT */ + arith, /* DIVIDE */ + arith, /* MOD */ + assign, /* ASSIGN */ + nullproc, /* ASGNOP */ + assign, /* ADDEQ */ + assign, /* SUBEQ */ + assign, /* MULTEQ */ + assign, /* DIVEQ */ + assign, /* MODEQ */ + assign, /* POWEQ */ + printstat, /* PRINT */ + awkprintf, /* PRINTF */ + awksprintf, /* SPRINTF */ + nullproc, /* ELSE */ + intest, /* INTEST */ + condexpr, /* CONDEXPR */ + incrdecr, /* POSTINCR */ + incrdecr, /* PREINCR */ + incrdecr, /* POSTDECR */ + incrdecr, /* PREDECR */ + nullproc, /* VAR */ + nullproc, /* IVAR */ + getnf, /* VARNF */ + call, /* CALL */ + nullproc, /* NUMBER */ + nullproc, /* STRING */ + nullproc, /* REGEXPR */ + getline, /* GETLINE */ + jump, /* RETURN */ + split, /* SPLIT */ + substr, /* SUBSTR */ + whilestat, /* WHILE */ + cat, /* CAT */ + boolop, /* NOT */ + arith, /* UMINUS */ + arith, /* POWER */ + nullproc, /* DECR */ + nullproc, /* INCR */ + indirect, /* INDIRECT */ + nullproc, /* LASTTOKEN */ +}; + +char *tokname(int n) +{ + static char buf[100]; + + if (n < FIRSTTOKEN || n > LASTTOKEN) { + sprintf(buf, "token %d", n); + return buf; + } + return printname[n-FIRSTTOKEN]; +} diff --git a/awk/proto.h b/awk/proto.h @@ -0,0 +1,177 @@ +/**************************************************************** +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(char *); +extern int yylook(void); +extern int yyback(int *, int); +extern int yyinput(void); + +extern void *compre(char *); +extern int hexstr(char **); +extern void quoted(char **, char **, char *); +extern int match(void *, char *, char *); +extern int pmatch(void *, char *, char *); +extern int nematch(void *, char *, char *); +extern int countposn(char *, int); +extern void overflow(void); + +extern int pgetc(void); +extern char *cursource(void); + +extern Node *nodealloc(int); +extern Node *exptostat(Node *); +extern Node *node1(int, Node *); +extern Node *node2(int, Node *, Node *); +extern Node *node3(int, Node *, Node *, Node *); +extern Node *node4(int, Node *, Node *, Node *, Node *); +extern Node *stat3(int, Node *, Node *, Node *); +extern Node *op2(int, Node *, Node *); +extern Node *op1(int, Node *); +extern Node *stat1(int, Node *); +extern Node *op3(int, Node *, Node *, Node *); +extern Node *op4(int, Node *, Node *, Node *, Node *); +extern Node *stat2(int, Node *, Node *); +extern Node *stat4(int, Node *, Node *, Node *, Node *); +extern Node *celltonode(Cell *, int); +extern Node *rectonode(void); +extern Node *makearr(Node *); +extern Node *pa2stat(Node *, Node *, Node *); +extern Node *linkum(Node *, Node *); +extern void defn(Cell *, Node *, Node *); +extern int isarg(char *); +extern char *tokname(int); +extern Cell *(*proctab[])(Node **, int); +extern int ptoi(void *); +extern Node *itonp(int); + +extern void syminit(void); +extern void arginit(int, char **); +extern void envinit(char **); +extern Array *makesymtab(int); +extern void freesymtab(Cell *); +extern void freeelem(Cell *, char *); +extern Cell *setsymtab(char *, char *, double, unsigned int, Array *); +extern int hash(char *, int); +extern void rehash(Array *); +extern Cell *lookup(char *, Array *); +extern double setfval(Cell *, double); +extern void funnyvar(Cell *, char *); +extern char *setsval(Cell *, char *); +extern double getfval(Cell *); +extern char *getsval(Cell *); +extern char *tostring(char *); +extern char *qstring(char *, int); + +extern void recinit(unsigned int); +extern void initgetrec(void); +extern void makefields(int, int); +extern void growfldtab(int n); +extern int getrec(char **, int *, int); +extern void nextfile(void); +extern int readrec(char **buf, int *bufsize, FILE *inf); +extern char *getargv(int); +extern void setclvar(char *); +extern void fldbld(void); +extern void cleanfld(int, int); +extern void newfld(int); +extern int refldbld(char *, char *); +extern void recbld(void); +extern Cell *fieldadr(int); +extern void yyerror(char *); +extern void fpecatch(int); +extern void bracecheck(void); +extern void bcheck2(int, int, int); +extern void SYNTAX(char *, ...); +extern void FATAL(char *, ...); +extern void WARNING(char *, ...); +extern void error(void); +extern void eprint(void); +extern void bclass(int); +extern double errcheck(double, char *); +extern int isclvar(char *); +extern int is_number(char *); + +extern int adjbuf(char **pb, int *sz, int min, int q, char **pbp, char *what); +extern void run(Node *); +extern Cell *execute(Node *); +extern Cell *program(Node **, int); +extern Cell *call(Node **, int); +extern Cell *copycell(Cell *); +extern Cell *arg(Node **, int); +extern Cell *jump(Node **, int); +extern Cell *getline(Node **, int); +extern Cell *getnf(Node **, int); +extern Cell *array(Node **, int); +extern Cell *awkdelete(Node **, int); +extern Cell *intest(Node **, int); +extern Cell *matchop(Node **, int); +extern Cell *boolop(Node **, int); +extern Cell *relop(Node **, int); +extern void tfree(Cell *); +extern Cell *gettemp(void); +extern Cell *field(Node **, int); +extern Cell *indirect(Node **, int); +extern Cell *substr(Node **, int); +extern Cell *sindex(Node **, int); +extern int format(char **, int *, char *, Node *); +extern Cell *awksprintf(Node **, int); +extern Cell *awkprintf(Node **, int); +extern Cell *arith(Node **, int); +extern double ipow(double, int); +extern Cell *incrdecr(Node **, int); +extern Cell *assign(Node **, int); +extern Cell *cat(Node **, int); +extern Cell *pastat(Node **, int); +extern Cell *dopa2(Node **, int); +extern Cell *split(Node **, int); +extern Cell *condexpr(Node **, int); +extern Cell *ifstat(Node **, int); +extern Cell *whilestat(Node **, int); +extern Cell *dostat(Node **, int); +extern Cell *forstat(Node **, int); +extern Cell *instat(Node **, int); +extern Cell *bltin(Node **, int); +extern Cell *printstat(Node **, int); +extern Cell *nullproc(Node **, int); +extern FILE *redirect(int, Node *); +extern FILE *openfile(int, char *); +extern char *filename(FILE *); +extern Cell *closefile(Node **, int); +extern void closeall(void); +extern Cell *sub(Node **, int); +extern Cell *gsub(Node **, int); + +extern FILE *popen(const char *, const char *); +extern int pclose(FILE *); diff --git a/awk/re.c b/awk/re.c @@ -0,0 +1,325 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + + +#define DEBUG +#include <stdio.h> +#include <ctype.h> +#include <setjmp.h> +#include <math.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include "awk.h" +#include "y.tab.h" +#include "regexp.h" + + /* This file provides the interface between the main body of + * awk and the pattern matching package. It preprocesses + * patterns prior to compilation to provide awk-like semantics + * to character sequences not supported by the pattern package. + * The following conversions are performed: + * + * "()" -> "[]" + * "[-" -> "[\-" + * "[^-" -> "[^\-" + * "-]" -> "\-]" + * "[]" -> "[]*" + * "\xdddd" -> "\z" where 'z' is the UTF sequence + * for the hex value + * "\ddd" -> "\o" where 'o' is a char octal value + * "\b" -> "\B" where 'B' is backspace + * "\t" -> "\T" where 'T' is tab + * "\f" -> "\F" where 'F' is form feed + * "\n" -> "\N" where 'N' is newline + * "\r" -> "\r" where 'C' is cr + */ + +#define MAXRE 512 + +static char re[MAXRE]; /* copy buffer */ + +char *patbeg; +int patlen; /* number of chars in pattern */ + +#define NPATS 20 /* number of slots in pattern cache */ + +static struct pat_list /* dynamic pattern cache */ +{ + char *re; + int use; + Reprog *program; +} pattern[NPATS]; + +static int npats; /* cache fill level */ + + /* Compile a pattern */ +void +*compre(char *pat) +{ + int i, j, inclass; + char c, *p, *s; + Reprog *program; + + if (!compile_time) { /* search cache for dynamic pattern */ + for (i = 0; i < npats; i++) + if (!strcmp(pat, pattern[i].re)) { + pattern[i].use++; + return((void *) pattern[i].program); + } + } + /* Preprocess Pattern for compilation */ + p = re; + s = pat; + inclass = 0; + while (c = *s++) { + if (c == '\\') { + quoted(&s, &p, re+MAXRE); + continue; + } + else if (!inclass && c == '(' && *s == ')') { + if (p < re+MAXRE-2) { /* '()' -> '[]*' */ + *p++ = '['; + *p++ = ']'; + c = '*'; + s++; + } + else overflow(); + } + else if (c == '['){ /* '[-' -> '[\-' */ + inclass = 1; + if (*s == '-') { + if (p < re+MAXRE-2) { + *p++ = '['; + *p++ = '\\'; + c = *s++; + } + else overflow(); + } /* '[^-' -> '[^\-'*/ + else if (*s == '^' && s[1] == '-'){ + if (p < re+MAXRE-3) { + *p++ = '['; + *p++ = *s++; + *p++ = '\\'; + c = *s++; + } + else overflow(); + } + else if (*s == '['){ /* skip '[[' */ + if (p < re+MAXRE-1) + *p++ = c; + else overflow(); + c = *s++; + } + else if (*s == '^' && s[1] == '[') { /* skip '[^['*/ + if (p < re+MAXRE-2) { + *p++ = c; + *p++ = *s++; + c = *s++; + } + else overflow(); + } + else if (*s == ']') { /* '[]' -> '[]*' */ + if (p < re+MAXRE-2) { + *p++ = c; + *p++ = *s++; + c = '*'; + inclass = 0; + } + else overflow(); + } + } + else if (c == '-' && *s == ']') { /* '-]' -> '\-]' */ + if (p < re+MAXRE-1) + *p++ = '\\'; + else overflow(); + } + else if (c == ']') + inclass = 0; + if (p < re+MAXRE-1) + *p++ = c; + else overflow(); + } + *p = 0; + program = regcomp(re); /* compile pattern */ + if (!compile_time) { + if (npats < NPATS) /* Room in cache */ + i = npats++; + else { /* Throw out least used */ + int use = pattern[0].use; + i = 0; + for (j = 1; j < NPATS; j++) { + if (pattern[j].use < use) { + use = pattern[j].use; + i = j; + } + } + xfree(pattern[i].program); + xfree(pattern[i].re); + } + pattern[i].re = tostring(pat); + pattern[i].program = program; + pattern[i].use = 1; + } + return((void *) program); +} + + /* T/F match indication - matched string not exported */ +int +match(void *p, char *s, char *q) +{ + return regexec((Reprog *) p, (char *) s, 0, 0); +} + + /* match and delimit the matched string */ +int +pmatch(void *p, char *s, char *start) +{ + Resub m; + + m.s.sp = start; + m.e.ep = 0; + if (regexec((Reprog *) p, (char *) s, &m, 1)) { + patbeg = m.s.sp; + patlen = m.e.ep-m.s.sp; + return 1; + } + patlen = -1; + patbeg = start; + return 0; +} + + /* perform a non-empty match */ +int +nematch(void *p, char *s, char *start) +{ + if (pmatch(p, s, start) == 1 && patlen > 0) + return 1; + patlen = -1; + patbeg = start; + return 0; +} +/* in the parsing of regular expressions, metacharacters like . have */ +/* to be seen literally; \056 is not a metacharacter. */ +int +hexstr(char **pp) /* find and eval hex string at pp, return new p */ +{ + int c; + int n = 0; + int i; + + for (i = 0, c = (*pp)[i]; i < 4 && isxdigit(c); i++, c = (*pp)[i]) { + if (isdigit(c)) + n = 16 * n + c - '0'; + else if ('a' <= c && c <= 'f') + n = 16 * n + c - 'a' + 10; + else if ('A' <= c && c <= 'F') + n = 16 * n + c - 'A' + 10; + } + *pp += i; + return n; +} + + /* look for awk-specific escape sequences */ + +#define isoctdigit(c) ((c) >= '0' && (c) <= '7') /* multiple use of arg */ + +void +quoted(char **s, char **to, char *end) /* handle escaped sequence */ +{ + char *p = *s; + char *t = *to; + wchar_t c; + + switch(c = *p++) { + case 't': + c = '\t'; + break; + case 'n': + c = '\n'; + break; + case 'f': + c = '\f'; + break; + case 'r': + c = '\r'; + break; + case 'b': + c = '\b'; + break; + default: + if (t < end-1) /* all else must be escaped */ + *t++ = '\\'; + if (c == 'x') { /* hexadecimal goo follows */ + c = hexstr(&p); + if (t < end-MB_CUR_MAX) + t += wctomb(t, c); + else overflow(); + *to = t; + *s = p; + return; + } else if (isoctdigit(c)) { /* \d \dd \ddd */ + c -= '0'; + if (isoctdigit(*p)) { + c = 8 * c + *p++ - '0'; + if (isoctdigit(*p)) + c = 8 * c + *p++ - '0'; + } + } + break; + } + if (t < end-1) + *t++ = c; + *s = p; + *to = t; +} + /* count rune positions */ +int +countposn(char *s, int n) +{ + int i, j; + char *end; + + for (i = 0, end = s+n; *s && s < end; i++){ + j = mblen(s, n); + if(j <= 0) + j = 1; + s += j; + } + return(i); +} + + /* pattern package error handler */ + +void +regerror(char *s) +{ + FATAL("%s", s); +} + +void +overflow(void) +{ + FATAL("%s", "regular expression too big"); +} diff --git a/awk/run.c b/awk/run.c @@ -0,0 +1,1892 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +#define DEBUG +#include <stdio.h> +#include <ctype.h> +#include <setjmp.h> +#include <math.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include "awk.h" +#include "y.tab.h" + +#define tempfree(x) if (istemp(x)) tfree(x); else + +/* +#undef tempfree + +void tempfree(Cell *p) { + if (p->ctype == OCELL && (p->csub < CUNK || p->csub > CFREE)) { + WARNING("bad csub %d in Cell %d %s", + p->csub, p->ctype, p->sval); + } + if (istemp(p)) + tfree(p); +} +*/ + +#ifdef _NFILE +#ifndef FOPEN_MAX +#define FOPEN_MAX _NFILE +#endif +#endif + +#ifndef FOPEN_MAX +#define FOPEN_MAX 40 /* max number of open files */ +#endif + +#ifndef RAND_MAX +#define RAND_MAX 32767 /* all that ansi guarantees */ +#endif + +jmp_buf env; +extern int pairstack[]; + +Node *winner = NULL; /* root of parse tree */ +Cell *tmps; /* free temporary cells for execution */ + +static Cell truecell ={ OBOOL, BTRUE, 0, 0, 1.0, NUM }; +Cell *True = &truecell; +static Cell falsecell ={ OBOOL, BFALSE, 0, 0, 0.0, NUM }; +Cell *False = &falsecell; +static Cell breakcell ={ OJUMP, JBREAK, 0, 0, 0.0, NUM }; +Cell *jbreak = &breakcell; +static Cell contcell ={ OJUMP, JCONT, 0, 0, 0.0, NUM }; +Cell *jcont = &contcell; +static Cell nextcell ={ OJUMP, JNEXT, 0, 0, 0.0, NUM }; +Cell *jnext = &nextcell; +static Cell nextfilecell ={ OJUMP, JNEXTFILE, 0, 0, 0.0, NUM }; +Cell *jnextfile = &nextfilecell; +static Cell exitcell ={ OJUMP, JEXIT, 0, 0, 0.0, NUM }; +Cell *jexit = &exitcell; +static Cell retcell ={ OJUMP, JRET, 0, 0, 0.0, NUM }; +Cell *jret = &retcell; +static Cell tempcell ={ OCELL, CTEMP, 0, "", 0.0, NUM|STR|DONTFREE }; + +Node *curnode = NULL; /* the node being executed, for debugging */ + +/* buffer memory management */ +int adjbuf(char **pbuf, int *psiz, int minlen, int quantum, char **pbptr, + char *whatrtn) +/* pbuf: address of pointer to buffer being managed + * psiz: address of buffer size variable + * minlen: minimum length of buffer needed + * quantum: buffer size quantum + * pbptr: address of movable pointer into buffer, or 0 if none + * whatrtn: name of the calling routine if failure should cause fatal error + * + * return 0 for realloc failure, !=0 for success + */ +{ + if (minlen > *psiz) { + char *tbuf; + int rminlen = quantum ? minlen % quantum : 0; + int boff = pbptr ? *pbptr - *pbuf : 0; + /* round up to next multiple of quantum */ + if (rminlen) + minlen += quantum - rminlen; + tbuf = (char *) realloc(*pbuf, minlen); + if (tbuf == NULL) { + if (whatrtn) + FATAL("out of memory in %s", whatrtn); + return 0; + } + *pbuf = tbuf; + *psiz = minlen; + if (pbptr) + *pbptr = tbuf + boff; + } + return 1; +} + +void run(Node *a) /* execution of parse tree starts here */ +{ + extern void stdinit(void); + + stdinit(); + execute(a); + closeall(); +} + +Cell *execute(Node *u) /* execute a node of the parse tree */ +{ + 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; + Node *x; + Cell *args[NARGS], *oargs[NARGS]; /* BUG: fixed size arrays */ + Cell *y, *z, *fcn; + char *s; + + fcn = execute(a[0]); /* the function itself */ + s = fcn->nval; + if (!isfcn(fcn)) + FATAL("calling undefined function %s", s); + if (frame == NULL) { + fp = frame = (struct Frame *) calloc(nframe += 100, sizeof(struct Frame)); + if (frame == NULL) + FATAL("out of space for stack frames calling %s", s); + } + for (ncall = 0, x = a[1]; x != NULL; x = x->nnext) /* args in call */ + ncall++; + ndef = (int) fcn->fval; /* args in defn */ + dprintf( ("calling %s, %d args (%d in defn), fp=%d\n", s, ncall, ndef, (int) (fp-frame)) ); + if (ncall > ndef) + WARNING("function %s called with %d args, uses only %d", + s, ncall, ndef); + if (ncall + ndef > NARGS) + FATAL("function %s has %d arguments, limit %d", s, ncall+ndef, NARGS); + for (i = 0, x = a[1]; x != NULL; i++, x = x->nnext) { /* get call args */ + dprintf( ("evaluate args[%d], fp=%d:\n", i, (int) (fp-frame)) ); + y = execute(x); + oargs[i] = y; + dprintf( ("args[%d]: %s %f <%s>, t=%o\n", + i, y->nval, y->fval, isarr(y) ? "(array)" : y->sval, y->tval) ); + if (isfcn(y)) + FATAL("can't use function %s as argument in %s", y->nval, s); + if (isarr(y)) + args[i] = y; /* arrays by ref */ + else + args[i] = copycell(y); + tempfree(y); + } + for ( ; i < ndef; i++) { /* add null args for ones not provided */ + args[i] = gettemp(); + *args[i] = newcopycell; + } + fp++; /* now ok to up frame */ + if (fp >= frame + nframe) { + int dfp = fp - frame; /* old index */ + frame = (struct Frame *) + realloc((char *) frame, (nframe += 100) * sizeof(struct Frame)); + if (frame == NULL) + FATAL("out of space for stack frames in %s", s); + fp = frame + dfp; + } + fp->fcncell = fcn; + fp->args = args; + fp->nargs = ndef; /* number defined with (excess are locals) */ + fp->retval = gettemp(); + + dprintf( ("start exec of %s, fp=%d\n", s, (int) (fp-frame)) ); + y = execute((Node *)(fcn->sval)); /* execute body */ + dprintf( ("finished exec of %s, fp=%d\n", s, (int) (fp-frame)) ); + + for (i = 0; i < ndef; i++) { + Cell *t = fp->args[i]; + if (isarr(t)) { + if (t->csub == CCOPY) { + if (i >= ncall) { + freesymtab(t); + t->csub = CTEMP; + tempfree(t); + } else { + oargs[i]->tval = t->tval; + oargs[i]->tval &= ~(STR|NUM|DONTFREE); + oargs[i]->sval = t->sval; + tempfree(t); + } + } + } else if (t != y) { /* kludge to prevent freeing twice */ + t->csub = CTEMP; + tempfree(t); + } + } + tempfree(fcn); + if (isexit(y) || isnext(y) || isnextfile(y)) + return y; + tempfree(y); /* this can free twice! */ + z = fp->retval; /* return value */ + dprintf( ("%s returns %g |%s| %o\n", s, getfval(z), getsval(z), z->tval) ); + fp--; + return(z); +} + +Cell *copycell(Cell *x) /* make a copy of a cell in a temp */ +{ + Cell *y; + + y = gettemp(); + y->csub = CCOPY; /* prevents freeing until call is over */ + y->nval = x->nval; /* BUG? */ + y->sval = x->sval ? tostring(x->sval) : NULL; + y->fval = x->fval; + y->tval = x->tval & ~(CON|FLD|REC|DONTFREE); /* copy is not constant or field */ + /* is DONTFREE right? */ + return y; +} + +Cell *arg(Node **a, int n) /* nth argument of a function */ +{ + + n = ptoi(a[0]); /* argument number, counting from 0 */ + dprintf( ("arg(%d), fp->nargs=%d\n", n, fp->nargs) ); + if (n+1 > fp->nargs) + FATAL("argument #%d of function %s was not supplied", + n+1, fp->fcncell->nval); + return fp->args[n]; +} + +Cell *jump(Node **a, int n) /* break, continue, next, nextfile, return */ +{ + Cell *y; + + switch (n) { + case EXIT: + if (a[0] != NULL) { + y = execute(a[0]); + errorflag = (int) getfval(y); + tempfree(y); + } + longjmp(env, 1); + case RETURN: + if (a[0] != NULL) { + y = execute(a[0]); + if ((y->tval & (STR|NUM)) == (STR|NUM)) { + setsval(fp->retval, getsval(y)); + fp->retval->fval = getfval(y); + fp->retval->tval |= NUM; + } + else if (y->tval & STR) + setsval(fp->retval, getsval(y)); + else if (y->tval & NUM) + setfval(fp->retval, getfval(y)); + else /* can't happen */ + FATAL("bad type variable %d", y->tval); + tempfree(y); + } + return(jret); + case NEXT: + return(jnext); + case NEXTFILE: + nextfile(); + return(jnextfile); + case BREAK: + return(jbreak); + case CONTINUE: + return(jcont); + default: /* can't happen */ + FATAL("illegal jump type %d", n); + } + return 0; /* not reached */ +} + +Cell *getline(Node **a, int n) /* get next line from specific input */ +{ /* a[0] is variable, a[1] is operator, a[2] is filename */ + Cell *r, *x; + extern Cell **fldtab; + FILE *fp; + char *buf; + int bufsize = recsize; + int mode; + + if ((buf = (char *) malloc(bufsize)) == NULL) + FATAL("out of memory in getline"); + + fflush(stdout); /* in case someone is waiting for a prompt */ + r = gettemp(); + if (a[1] != NULL) { /* getline < file */ + x = execute(a[2]); /* filename */ + mode = ptoi(a[1]); + if (mode == '|') /* input pipe */ + mode = LE; /* arbitrary flag */ + fp = openfile(mode, getsval(x)); + tempfree(x); + if (fp == NULL) + n = -1; + else + n = readrec(&buf, &bufsize, fp); + if (n <= 0) { + ; + } else if (a[0] != NULL) { /* getline var <file */ + x = execute(a[0]); + setsval(x, buf); + tempfree(x); + } else { /* getline <file */ + setsval(fldtab[0], buf); + if (is_number(fldtab[0]->sval)) { + fldtab[0]->fval = atof(fldtab[0]->sval); + fldtab[0]->tval |= NUM; + } + } + } else { /* bare getline; use current input */ + if (a[0] == NULL) /* getline */ + n = getrec(&record, &recsize, 1); + else { /* getline var */ + n = getrec(&buf, &bufsize, 0); + x = execute(a[0]); + setsval(x, buf); + tempfree(x); + } + } + setfval(r, (Awkfloat) n); + free(buf); + return r; +} + +Cell *getnf(Node **a, int n) /* get NF */ +{ + if (donefld == 0) + fldbld(); + return (Cell *) a[0]; +} + +Cell *array(Node **a, int n) /* a[0] is symtab, a[1] is list of subscripts */ +{ + Cell *x, *y, *z; + char *s; + Node *np; + char *buf; + int bufsz = recsize; + int nsub = strlen(*SUBSEP); + + if ((buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of memory in array"); + + x = execute(a[0]); /* Cell* for symbol table */ + buf[0] = 0; + for (np = a[1]; np; np = np->nnext) { + y = execute(np); /* subscript */ + s = getsval(y); + if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, 0)) + FATAL("out of memory for %s[%s...]", x->nval, buf); + strcat(buf, s); + if (np->nnext) + strcat(buf, *SUBSEP); + tempfree(y); + } + if (!isarr(x)) { + dprintf( ("making %s into an array\n", x->nval) ); + if (freeable(x)) + xfree(x->sval); + x->tval &= ~(STR|NUM|DONTFREE); + x->tval |= ARR; + x->sval = (char *) makesymtab(NSYMTAB); + } + z = setsymtab(buf, "", 0.0, STR|NUM, (Array *) x->sval); + z->ctype = OCELL; + z->csub = CVAR; + tempfree(x); + free(buf); + return(z); +} + +Cell *awkdelete(Node **a, int n) /* a[0] is symtab, a[1] is list of subscripts */ +{ + Cell *x, *y; + Node *np; + char *s; + int nsub = strlen(*SUBSEP); + + x = execute(a[0]); /* Cell* for symbol table */ + if (!isarr(x)) + return True; + if (a[1] == 0) { /* delete the elements, not the table */ + freesymtab(x); + x->tval &= ~STR; + x->tval |= ARR; + x->sval = (char *) makesymtab(NSYMTAB); + } else { + int bufsz = recsize; + char *buf; + if ((buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of memory in adelete"); + buf[0] = 0; + for (np = a[1]; np; np = np->nnext) { + y = execute(np); /* subscript */ + s = getsval(y); + if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, 0)) + FATAL("out of memory deleting %s[%s...]", x->nval, buf); + strcat(buf, s); + if (np->nnext) + strcat(buf, *SUBSEP); + tempfree(y); + } + freeelem(x, buf); + free(buf); + } + tempfree(x); + return True; +} + +Cell *intest(Node **a, int n) /* a[0] is index (list), a[1] is symtab */ +{ + Cell *x, *ap, *k; + Node *p; + char *buf; + char *s; + int bufsz = recsize; + int nsub = strlen(*SUBSEP); + + ap = execute(a[1]); /* array name */ + if (!isarr(ap)) { + dprintf( ("making %s into an array\n", ap->nval) ); + if (freeable(ap)) + xfree(ap->sval); + ap->tval &= ~(STR|NUM|DONTFREE); + ap->tval |= ARR; + ap->sval = (char *) makesymtab(NSYMTAB); + } + if ((buf = (char *) malloc(bufsz)) == NULL) { + FATAL("out of memory in intest"); + } + buf[0] = 0; + for (p = a[0]; p; p = p->nnext) { + x = execute(p); /* expr */ + s = getsval(x); + if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, 0)) + FATAL("out of memory deleting %s[%s...]", x->nval, buf); + strcat(buf, s); + tempfree(x); + if (p->nnext) + strcat(buf, *SUBSEP); + } + k = lookup(buf, (Array *) ap->sval); + tempfree(ap); + free(buf); + if (k == NULL) + return(False); + else + return(True); +} + + +Cell *matchop(Node **a, int n) /* ~ and match() */ +{ + Cell *x, *y; + char *s, *t; + int i; + void *p; + + x = execute(a[1]); /* a[1] = target text */ + s = getsval(x); + if (a[0] == 0) /* a[1] == 0: already-compiled reg expr */ + p = (void *) a[2]; + else { + y = execute(a[2]); /* a[2] = regular expr */ + t = getsval(y); + p = compre(t); + tempfree(y); + } + if (n == MATCHFCN) + i = pmatch(p, s, s); + else + i = match(p, s, s); + tempfree(x); + if (n == MATCHFCN) { + int start = countposn(s, patbeg-s)+1; + if (patlen < 0) + start = 0; + setfval(rstartloc, (Awkfloat) start); + setfval(rlengthloc, (Awkfloat) countposn(patbeg, patlen)); + x = gettemp(); + x->tval = NUM; + x->fval = start; + return x; + } else if ((n == MATCH && i == 1) || (n == NOTMATCH && i == 0)) + return(True); + else + return(False); +} + + +Cell *boolop(Node **a, int n) /* a[0] || a[1], a[0] && a[1], !a[0] */ +{ + Cell *x, *y; + int i; + + x = execute(a[0]); + i = istrue(x); + tempfree(x); + switch (n) { + case BOR: + if (i) return(True); + y = execute(a[1]); + i = istrue(y); + tempfree(y); + if (i) return(True); + else return(False); + case AND: + if ( !i ) return(False); + y = execute(a[1]); + i = istrue(y); + tempfree(y); + if (i) return(True); + else return(False); + case NOT: + if (i) return(False); + else return(True); + default: /* can't happen */ + FATAL("unknown boolean operator %d", n); + } + return 0; /*NOTREACHED*/ +} + +Cell *relop(Node **a, int n) /* a[0 < a[1], etc. */ +{ + int i; + Cell *x, *y; + Awkfloat j; + + x = execute(a[0]); + y = execute(a[1]); + if (x->tval&NUM && y->tval&NUM) { + j = x->fval - y->fval; + i = j<0? -1: (j>0? 1: 0); + } else { + i = strcmp(getsval(x), getsval(y)); + } + tempfree(x); + tempfree(y); + switch (n) { + case LT: if (i<0) return(True); + else return(False); + case LE: if (i<=0) return(True); + else return(False); + case NE: if (i!=0) return(True); + else return(False); + case EQ: if (i == 0) return(True); + else return(False); + case GE: if (i>=0) return(True); + else return(False); + case GT: if (i>0) return(True); + else return(False); + default: /* can't happen */ + FATAL("unknown relational operator %d", n); + } + return 0; /*NOTREACHED*/ +} + +void tfree(Cell *a) /* free a tempcell */ +{ + if (freeable(a)) { + dprintf( ("freeing %s %s %o\n", a->nval, a->sval, a->tval) ); + xfree(a->sval); + } + if (a == tmps) + FATAL("tempcell list is curdled"); + a->cnext = tmps; + tmps = a; +} + +Cell *gettemp(void) /* get a tempcell */ +{ int i; + Cell *x; + + if (!tmps) { + tmps = (Cell *) calloc(100, sizeof(Cell)); + if (!tmps) + FATAL("out of space for temporaries"); + for(i = 1; i < 100; i++) + tmps[i-1].cnext = &tmps[i]; + tmps[i-1].cnext = 0; + } + x = tmps; + tmps = x->cnext; + *x = tempcell; + return(x); +} + +Cell *indirect(Node **a, int n) /* $( a[0] ) */ +{ + Cell *x; + int m; + char *s; + + x = execute(a[0]); + m = (int) getfval(x); + if (m == 0 && !is_number(s = getsval(x))) /* suspicion! */ + FATAL("illegal field $(%s), name \"%s\"", s, x->nval); + /* BUG: can x->nval ever be null??? */ + tempfree(x); + x = fieldadr(m); + x->ctype = OCELL; /* BUG? why are these needed? */ + x->csub = CFLD; + return(x); +} + +Cell *substr(Node **a, int nnn) /* substr(a[0], a[1], a[2]) */ +{ + int k, m, n; + char *s, *p; + int temp; + Cell *x, *y, *z = 0; + + x = execute(a[0]); + y = execute(a[1]); + if (a[2] != 0) + z = execute(a[2]); + s = getsval(x); + k = countposn(s, strlen(s)) + 1; + if (k <= 1) { + tempfree(x); + tempfree(y); + if (a[2] != 0) + tempfree(z); + x = gettemp(); + setsval(x, ""); + return(x); + } + m = (int) getfval(y); + if (m <= 0) + m = 1; + else if (m > k) + m = k; + tempfree(y); + if (a[2] != 0) { + n = (int) getfval(z); + tempfree(z); + } else + n = k - 1; + if (n < 0) + n = 0; + else if (n > k - m) + n = k - m; + dprintf( ("substr: m=%d, n=%d, s=%s\n", m, n, s) ); + y = gettemp(); + while (*s && --m) + s += mblen(s, k); + for (p = s; *p && n--; p += mblen(p, k)) + ; + temp = *p; /* with thanks to John Linderman */ + *p = '\0'; + setsval(y, s); + *p = temp; + tempfree(x); + return(y); +} + +Cell *sindex(Node **a, int nnn) /* index(a[0], a[1]) */ +{ + Cell *x, *y, *z; + char *s1, *s2, *p1, *p2, *q; + Awkfloat v = 0.0; + + x = execute(a[0]); + s1 = getsval(x); + y = execute(a[1]); + s2 = getsval(y); + + z = gettemp(); + for (p1 = s1; *p1 != '\0'; p1++) { + for (q=p1, p2=s2; *p2 != '\0' && *q == *p2; q++, p2++) + ; + if (*p2 == '\0') { + v = (Awkfloat) countposn(s1, p1-s1) + 1; /* origin 1 */ + break; + } + } + tempfree(x); + tempfree(y); + setfval(z, v); + return(z); +} + +#define MAXNUMSIZE 50 + +int format(char **pbuf, int *pbufsize, char *s, Node *a) /* printf-like conversions */ +{ + char *fmt; + char *p, *t, *os; + Cell *x; + int flag = 0, n; + int fmtwd; /* format width */ + int fmtsz = recsize; + char *buf = *pbuf; + int bufsize = *pbufsize; + + os = s; + p = buf; + if ((fmt = (char *) malloc(fmtsz)) == NULL) + FATAL("out of memory in format()"); + while (*s) { + adjbuf(&buf, &bufsize, MAXNUMSIZE+1+p-buf, recsize, &p, "format"); + if (*s != '%') { + *p++ = *s++; + continue; + } + if (*(s+1) == '%') { + *p++ = '%'; + s += 2; + continue; + } + /* have to be real careful in case this is a huge number, eg, %100000d */ + fmtwd = atoi(s+1); + if (fmtwd < 0) + fmtwd = -fmtwd; + adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format"); + for (t = fmt; (*t++ = *s) != '\0'; s++) { + if (!adjbuf(&fmt, &fmtsz, MAXNUMSIZE+1+t-fmt, recsize, &t, 0)) + FATAL("format item %.30s... ran format() out of memory", os); + if (isalpha(*s) && *s != 'l' && *s != 'h' && *s != 'L') + break; /* the ansi panoply */ + if (*s == '*') { + x = execute(a); + a = a->nnext; + sprintf(t-1, "%d", fmtwd=(int) getfval(x)); + if (fmtwd < 0) + fmtwd = -fmtwd; + adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format"); + t = fmt + strlen(fmt); + tempfree(x); + } + } + *t = '\0'; + if (fmtwd < 0) + fmtwd = -fmtwd; + adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format"); + + switch (*s) { + case 'f': case 'e': case 'g': case 'E': case 'G': + flag = 1; + break; + case 'd': case 'i': + flag = 2; + if(*(s-1) == 'l') break; + *(t-1) = 'l'; + *t = 'd'; + *++t = '\0'; + break; + case 'o': case 'x': case 'X': case 'u': + flag = *(s-1) == 'l' ? 2 : 3; + break; + case 's': + flag = 4; + break; + case 'c': + flag = 5; + break; + default: + WARNING("weird printf conversion %s", fmt); + flag = 0; + break; + } + if (a == NULL) + FATAL("not enough args in printf(%s)", os); + x = execute(a); + a = a->nnext; + n = MAXNUMSIZE; + if (fmtwd > n) + n = fmtwd; + adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, "format"); + switch (flag) { + case 0: sprintf(p, "%s", fmt); /* unknown, so dump it too */ + t = getsval(x); + n = strlen(t); + if (fmtwd > n) + n = fmtwd; + adjbuf(&buf, &bufsize, 1+strlen(p)+n+p-buf, recsize, &p, "format"); + p += strlen(p); + sprintf(p, "%s", t); + break; + case 1: sprintf(p, fmt, getfval(x)); break; + case 2: sprintf(p, fmt, (long) getfval(x)); break; + case 3: sprintf(p, fmt, (int) getfval(x)); break; + case 4: + t = getsval(x); + n = strlen(t); + if (fmtwd > n) + n = fmtwd; + if (!adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, 0)) + FATAL("huge string/format (%d chars) in printf %.30s... ran format() out of memory", n, t); + sprintf(p, fmt, t); + break; + case 5: + if (isnum(x)) { + if (getfval(x)) + sprintf(p, fmt, (int) getfval(x)); + else + *p++ = '\0'; + } else + sprintf(p, fmt, getsval(x)[0]); + break; + } + tempfree(x); + p += strlen(p); + s++; + } + *p = '\0'; + free(fmt); + for ( ; a; a = a->nnext) /* evaluate any remaining args */ + execute(a); + *pbuf = buf; + *pbufsize = bufsize; + return p - buf; +} + +Cell *awksprintf(Node **a, int n) /* sprintf(a[0]) */ +{ + Cell *x; + Node *y; + char *buf; + int bufsz=3*recsize; + + if ((buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of memory in awksprintf"); + y = a[0]->nnext; + x = execute(a[0]); + if (format(&buf, &bufsz, getsval(x), y) == -1) + FATAL("sprintf string %.30s... too long. can't happen.", buf); + tempfree(x); + x = gettemp(); + x->sval = buf; + x->tval = STR; + return(x); +} + +Cell *awkprintf(Node **a, int n) /* printf */ +{ /* a[0] is list of args, starting with format string */ + /* a[1] is redirection operator, a[2] is redirection file */ + FILE *fp; + Cell *x; + Node *y; + char *buf; + int len; + int bufsz=3*recsize; + + if ((buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of memory in awkprintf"); + y = a[0]->nnext; + x = execute(a[0]); + if ((len = format(&buf, &bufsz, getsval(x), y)) == -1) + FATAL("printf string %.30s... too long. can't happen.", buf); + tempfree(x); + if (a[1] == NULL) { + /* fputs(buf, stdout); */ + fwrite(buf, len, 1, stdout); + if (ferror(stdout)) + FATAL("write error on stdout"); + } else { + fp = redirect(ptoi(a[1]), a[2]); + /* fputs(buf, fp); */ + fwrite(buf, len, 1, fp); + fflush(fp); + if (ferror(fp)) + FATAL("write error on %s", filename(fp)); + } + free(buf); + return(True); +} + +Cell *arith(Node **a, int n) /* a[0] + a[1], etc. also -a[0] */ +{ + Awkfloat i, j = 0; + double v; + Cell *x, *y, *z; + + x = execute(a[0]); + i = getfval(x); + tempfree(x); + if (n != UMINUS) { + y = execute(a[1]); + j = getfval(y); + tempfree(y); + } + z = gettemp(); + switch (n) { + case ADD: + i += j; + break; + case MINUS: + i -= j; + break; + case MULT: + i *= j; + break; + case DIVIDE: + if (j == 0) + FATAL("division by zero"); + i /= j; + break; + case MOD: + if (j == 0) + FATAL("division by zero in mod"); + modf(i/j, &v); + i = i - j * v; + break; + case UMINUS: + i = -i; + break; + case POWER: + if (j >= 0 && modf(j, &v) == 0.0) /* pos integer exponent */ + i = ipow(i, (int) j); + else + i = errcheck(pow(i, j), "pow"); + break; + default: /* can't happen */ + FATAL("illegal arithmetic operator %d", n); + } + setfval(z, i); + return(z); +} + +double ipow(double x, int n) /* x**n. ought to be done by pow, but isn't always */ +{ + double v; + + if (n <= 0) + return 1; + v = ipow(x, n/2); + if (n % 2 == 0) + return v * v; + else + return x * v * v; +} + +Cell *incrdecr(Node **a, int n) /* a[0]++, etc. */ +{ + Cell *x, *z; + int k; + Awkfloat xf; + + x = execute(a[0]); + xf = getfval(x); + k = (n == PREINCR || n == POSTINCR) ? 1 : -1; + if (n == PREINCR || n == PREDECR) { + setfval(x, xf + k); + return(x); + } + z = gettemp(); + setfval(z, xf); + setfval(x, xf + k); + tempfree(x); + return(z); +} + +Cell *assign(Node **a, int n) /* a[0] = a[1], a[0] += a[1], etc. */ +{ /* this is subtle; don't muck with it. */ + Cell *x, *y; + Awkfloat xf, yf; + double v; + + y = execute(a[1]); + x = execute(a[0]); + if (n == ASSIGN) { /* ordinary assignment */ + if (x == y && !(x->tval & (FLD|REC))) /* self-assignment: */ + ; /* leave alone unless it's a field */ + else if ((y->tval & (STR|NUM)) == (STR|NUM)) { + setsval(x, getsval(y)); + x->fval = getfval(y); + x->tval |= NUM; + } + else if (isstr(y)) + setsval(x, getsval(y)); + else if (isnum(y)) + setfval(x, getfval(y)); + else + funnyvar(y, "read value of"); + tempfree(y); + return(x); + } + xf = getfval(x); + yf = getfval(y); + switch (n) { + case ADDEQ: + xf += yf; + break; + case SUBEQ: + xf -= yf; + break; + case MULTEQ: + xf *= yf; + break; + case DIVEQ: + if (yf == 0) + FATAL("division by zero in /="); + xf /= yf; + break; + case MODEQ: + if (yf == 0) + FATAL("division by zero in %%="); + modf(xf/yf, &v); + xf = xf - yf * v; + break; + case POWEQ: + if (yf >= 0 && modf(yf, &v) == 0.0) /* pos integer exponent */ + xf = ipow(xf, (int) yf); + else + xf = errcheck(pow(xf, yf), "pow"); + break; + default: + FATAL("illegal assignment operator %d", n); + break; + } + tempfree(y); + setfval(x, xf); + return(x); +} + +Cell *cat(Node **a, int q) /* a[0] cat a[1] */ +{ + Cell *x, *y, *z; + int n1, n2; + char *s; + + x = execute(a[0]); + y = execute(a[1]); + getsval(x); + getsval(y); + n1 = strlen(x->sval); + n2 = strlen(y->sval); + s = (char *) malloc(n1 + n2 + 1); + if (s == NULL) + FATAL("out of space concatenating %.15s... and %.15s...", + x->sval, y->sval); + strcpy(s, x->sval); + strcpy(s+n1, y->sval); + tempfree(y); + z = gettemp(); + z->sval = s; + z->tval = STR; + tempfree(x); + return(z); +} + +Cell *pastat(Node **a, int n) /* a[0] { a[1] } */ +{ + Cell *x; + + if (a[0] == 0) + x = execute(a[1]); + else { + x = execute(a[0]); + if (istrue(x)) { + tempfree(x); + x = execute(a[1]); + } + } + return x; +} + +Cell *dopa2(Node **a, int n) /* a[0], a[1] { a[2] } */ +{ + Cell *x; + int pair; + + pair = ptoi(a[3]); + if (pairstack[pair] == 0) { + x = execute(a[0]); + if (istrue(x)) + pairstack[pair] = 1; + tempfree(x); + } + if (pairstack[pair] == 1) { + x = execute(a[1]); + if (istrue(x)) + pairstack[pair] = 0; + tempfree(x); + x = execute(a[2]); + return(x); + } + return(False); +} + +Cell *split(Node **a, int nnn) /* split(a[0], a[1], a[2]); a[3] is type */ +{ + Cell *x = 0, *y, *ap; + char *s; + int sep; + char *t, temp, num[50], *fs = 0; + int n, arg3type; + + y = execute(a[0]); /* source string */ + s = getsval(y); + arg3type = ptoi(a[3]); + if (a[2] == 0) /* fs string */ + fs = *FS; + else if (arg3type == STRING) { /* split(str,arr,"string") */ + x = execute(a[2]); + fs = getsval(x); + } else if (arg3type == REGEXPR) + fs = "(regexpr)"; /* split(str,arr,/regexpr/) */ + else + FATAL("illegal type of split"); + sep = *fs; + ap = execute(a[1]); /* array name */ + freesymtab(ap); + dprintf( ("split: s=|%s|, a=%s, sep=|%s|\n", s, ap->nval, fs) ); + ap->tval &= ~STR; + ap->tval |= ARR; + ap->sval = (char *) makesymtab(NSYMTAB); + + n = 0; + if ((*s != '\0' && strlen(fs) > 1) || arg3type == REGEXPR) { /* reg expr */ + void *p; + if (arg3type == REGEXPR) { /* it's ready already */ + p = (void *) a[2]; + } else { + p = compre(fs); + } + t = s; + if (nematch(p,s,t)) { + do { + n++; + sprintf(num, "%d", n); + temp = *patbeg; + *patbeg = '\0'; + if (is_number(t)) + setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval); + else + setsymtab(num, t, 0.0, STR, (Array *) ap->sval); + *patbeg = temp; + t = patbeg + patlen; + if (t[-1] == 0 || *t == 0) { + n++; + sprintf(num, "%d", n); + setsymtab(num, "", 0.0, STR, (Array *) ap->sval); + goto spdone; + } + } while (nematch(p,s,t)); + } + n++; + sprintf(num, "%d", n); + if (is_number(t)) + setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval); + else + setsymtab(num, t, 0.0, STR, (Array *) ap->sval); + spdone: + p = NULL; + } else if (sep == ' ') { + for (n = 0; ; ) { + while (*s == ' ' || *s == '\t' || *s == '\n') + s++; + if (*s == 0) + break; + n++; + t = s; + do + s++; + while (*s!=' ' && *s!='\t' && *s!='\n' && *s!='\0'); + temp = *s; + *s = '\0'; + sprintf(num, "%d", n); + if (is_number(t)) + setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval); + else + setsymtab(num, t, 0.0, STR, (Array *) ap->sval); + *s = temp; + if (*s != 0) + s++; + } + } else if (sep == 0) { /* new: split(s, a, "") => 1 char/elem */ + for (n = 0; *s != 0; s++) { + char buf[2]; + n++; + sprintf(num, "%d", n); + buf[0] = *s; + buf[1] = 0; + if (isdigit(buf[0])) + setsymtab(num, buf, atof(buf), STR|NUM, (Array *) ap->sval); + else + setsymtab(num, buf, 0.0, STR, (Array *) ap->sval); + } + } else if (*s != 0) { + for (;;) { + n++; + t = s; + while (*s != sep && *s != '\n' && *s != '\0') + s++; + temp = *s; + *s = '\0'; + sprintf(num, "%d", n); + if (is_number(t)) + setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval); + else + setsymtab(num, t, 0.0, STR, (Array *) ap->sval); + *s = temp; + if (*s++ == 0) + break; + } + } + tempfree(ap); + tempfree(y); + if (a[2] != 0 && arg3type == STRING) + tempfree(x); + x = gettemp(); + x->tval = NUM; + x->fval = n; + return(x); +} + +Cell *condexpr(Node **a, int n) /* a[0] ? a[1] : a[2] */ +{ + Cell *x; + + x = execute(a[0]); + if (istrue(x)) { + tempfree(x); + x = execute(a[1]); + } else { + tempfree(x); + x = execute(a[2]); + } + return(x); +} + +Cell *ifstat(Node **a, int n) /* if (a[0]) a[1]; else a[2] */ +{ + Cell *x; + + x = execute(a[0]); + if (istrue(x)) { + tempfree(x); + x = execute(a[1]); + } else if (a[2] != 0) { + tempfree(x); + x = execute(a[2]); + } + return(x); +} + +Cell *whilestat(Node **a, int n) /* while (a[0]) a[1] */ +{ + Cell *x; + + for (;;) { + x = execute(a[0]); + if (!istrue(x)) + return(x); + tempfree(x); + x = execute(a[1]); + if (isbreak(x)) { + x = True; + return(x); + } + if (isnext(x) || isexit(x) || isret(x)) + return(x); + tempfree(x); + } +} + +Cell *dostat(Node **a, int n) /* do a[0]; while(a[1]) */ +{ + Cell *x; + + for (;;) { + x = execute(a[0]); + if (isbreak(x)) + return True; + if (isnext(x) || isnextfile(x) || isexit(x) || isret(x)) + return(x); + tempfree(x); + x = execute(a[1]); + if (!istrue(x)) + return(x); + tempfree(x); + } +} + +Cell *forstat(Node **a, int n) /* for (a[0]; a[1]; a[2]) a[3] */ +{ + Cell *x; + + x = execute(a[0]); + tempfree(x); + for (;;) { + if (a[1]!=0) { + x = execute(a[1]); + if (!istrue(x)) return(x); + else tempfree(x); + } + x = execute(a[3]); + if (isbreak(x)) /* turn off break */ + return True; + if (isnext(x) || isexit(x) || isret(x)) + return(x); + tempfree(x); + x = execute(a[2]); + tempfree(x); + } +} + +Cell *instat(Node **a, int n) /* for (a[0] in a[1]) a[2] */ +{ + Cell *x, *vp, *arrayp, *cp, *ncp; + Array *tp; + int i; + + vp = execute(a[0]); + arrayp = execute(a[1]); + if (!isarr(arrayp)) { + return True; + } + tp = (Array *) arrayp->sval; + tempfree(arrayp); + for (i = 0; i < tp->size; i++) { /* this routine knows too much */ + for (cp = tp->tab[i]; cp != NULL; cp = ncp) { + setsval(vp, cp->nval); + ncp = cp->cnext; + x = execute(a[2]); + if (isbreak(x)) { + tempfree(vp); + return True; + } + if (isnext(x) || isexit(x) || isret(x)) { + tempfree(vp); + return(x); + } + tempfree(x); + } + } + return True; +} + +Cell *bltin(Node **a, int n) /* builtin functions. a[0] is type, a[1] is arg list */ +{ + Cell *x, *y; + Awkfloat u; + int t; + wchar_t wc; + char *p, *buf; + char mbc[50]; + Node *nextarg; + FILE *fp; + + t = ptoi(a[0]); + x = execute(a[1]); + nextarg = a[1]->nnext; + switch (t) { + case FLENGTH: + p = getsval(x); + u = (Awkfloat) countposn(p, strlen(p)); break; + case FLOG: + u = errcheck(log(getfval(x)), "log"); break; + case FINT: + modf(getfval(x), &u); break; + case FEXP: + u = errcheck(exp(getfval(x)), "exp"); break; + case FSQRT: + u = errcheck(sqrt(getfval(x)), "sqrt"); break; + case FSIN: + u = sin(getfval(x)); break; + case FCOS: + u = cos(getfval(x)); break; + case FATAN: + if (nextarg == 0) { + WARNING("atan2 requires two arguments; returning 1.0"); + u = 1.0; + } else { + y = execute(a[1]->nnext); + u = atan2(getfval(x), getfval(y)); + tempfree(y); + nextarg = nextarg->nnext; + } + break; + case FSYSTEM: + fflush(stdout); /* in case something is buffered already */ + u = (Awkfloat) system(getsval(x)) / 256; /* 256 is unix-dep */ + break; + case FRAND: + /* in principle, rand() returns something in 0..RAND_MAX */ + u = (Awkfloat) (rand() % RAND_MAX) / RAND_MAX; + break; + case FSRAND: + if (isrec(x)) /* no argument provided */ + u = time((time_t *)0); + else + u = getfval(x); + srand((unsigned int) u); + break; + case FTOUPPER: + case FTOLOWER: + buf = tostring(getsval(x)); + if (t == FTOUPPER) { + for (p = buf; *p; p++) + if (islower(*p)) + *p = toupper(*p); + } else { + for (p = buf; *p; p++) + if (isupper(*p)) + *p = tolower(*p); + } + tempfree(x); + x = gettemp(); + setsval(x, buf); + free(buf); + return x; + case FFLUSH: + if ((fp = openfile(FFLUSH, getsval(x))) == NULL) + u = EOF; + else + u = fflush(fp); + break; + case FUTF: + wc = (int)getfval(x); + mbc[wctomb(mbc, wc)] = 0; + tempfree(x); + x = gettemp(); + setsval(x, mbc); + return x; + default: /* can't happen */ + FATAL("illegal function type %d", t); + break; + } + tempfree(x); + x = gettemp(); + setfval(x, u); + if (nextarg != 0) { + WARNING("warning: function has too many arguments"); + for ( ; nextarg; nextarg = nextarg->nnext) + execute(nextarg); + } + return(x); +} + +Cell *printstat(Node **a, int n) /* print a[0] */ +{ + Node *x; + Cell *y; + FILE *fp; + + if (a[1] == 0) /* a[1] is redirection operator, a[2] is file */ + fp = stdout; + else + fp = redirect(ptoi(a[1]), a[2]); + for (x = a[0]; x != NULL; x = x->nnext) { + y = execute(x); + fputs(getsval(y), fp); + tempfree(y); + if (x->nnext == NULL) + 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; + char *fname; + int mode; /* '|', 'a', 'w' => LE/LT, GT */ +} files[FOPEN_MAX] ={ + { NULL, "/dev/stdin", LT }, /* watch out: don't free this! */ + { NULL, "/dev/stdout", GT }, + { NULL, "/dev/stderr", GT } +}; + +void stdinit(void) /* in case stdin, etc., are not constants */ +{ + files[0].fp = stdin; + files[1].fp = stdout; + files[2].fp = stderr; +} + +FILE *openfile(int a, char *us) +{ + char *s = us; + int i, m; + FILE *fp = 0; + + if (*s == '\0') + FATAL("null file name in print or getline"); + for (i=0; i < FOPEN_MAX; i++) + if (files[i].fname && strcmp(s, files[i].fname) == 0) { + if (a == files[i].mode || (a==APPEND && files[i].mode==GT)) + return files[i].fp; + if (a == FFLUSH) + return files[i].fp; + } + if (a == FFLUSH) /* didn't find it, so don't create it! */ + return NULL; + + for (i=0; i < FOPEN_MAX; i++) + if (files[i].fp == 0) + break; + if (i >= FOPEN_MAX) + FATAL("%s makes too many open files", s); + fflush(stdout); /* force a semblance of order */ + m = a; + if (a == GT) { + fp = fopen(s, "w"); + } else if (a == APPEND) { + fp = fopen(s, "a"); + m = GT; /* so can mix > and >> */ + } else if (a == '|') { /* output pipe */ + fp = popen(s, "w"); + } else if (a == LE) { /* input pipe */ + fp = popen(s, "r"); + } else if (a == LT) { /* getline <file */ + fp = strcmp(s, "-") == 0 ? stdin : fopen(s, "r"); /* "-" is stdin */ + } else /* can't happen */ + FATAL("illegal redirection %d", a); + if (fp != NULL) { + files[i].fname = tostring(s); + files[i].fp = fp; + files[i].mode = m; + } + return fp; +} + +char *filename(FILE *fp) +{ + int i; + + for (i = 0; i < FOPEN_MAX; i++) + if (fp == files[i].fp) + return files[i].fname; + return "???"; +} + +Cell *closefile(Node **a, int n) +{ + Cell *x; + int i, stat; + + n = n; + x = execute(a[0]); + getsval(x); + for (i = 0; i < FOPEN_MAX; i++) + if (files[i].fname && strcmp(x->sval, files[i].fname) == 0) { + if (ferror(files[i].fp)) + WARNING( "i/o error occurred on %s", files[i].fname ); + if (files[i].mode == '|' || files[i].mode == LE) + stat = pclose(files[i].fp); + else + stat = fclose(files[i].fp); + if (stat == EOF) + WARNING( "i/o error occurred closing %s", files[i].fname ); + if (i > 2) /* don't do /dev/std... */ + xfree(files[i].fname); + files[i].fname = NULL; /* watch out for ref thru this */ + files[i].fp = NULL; + } + tempfree(x); + return(True); +} + +void closeall(void) +{ + int i, stat; + + for (i = 0; i < FOPEN_MAX; i++) + if (files[i].fp) { + if (ferror(files[i].fp)) + WARNING( "i/o error occurred on %s", files[i].fname ); + if (files[i].mode == '|' || files[i].mode == LE) + stat = pclose(files[i].fp); + else + stat = fclose(files[i].fp); + if (stat == EOF) + WARNING( "i/o error occurred while closing %s", files[i].fname ); + } +} + +void backsub(char **pb_ptr, char **sptr_ptr); + +Cell *sub(Node **a, int nnn) /* substitute command */ +{ + char *sptr, *pb, *q; + Cell *x, *y, *result; + char *t, *buf; + void *p; + int bufsz = recsize; + + if ((buf = (char *) malloc(bufsz)) == NULL) + FATAL("out of memory in sub"); + x = execute(a[3]); /* target string */ + t = getsval(x); + if (a[0] == 0) /* 0 => a[1] is already-compiled regexpr */ + p = (void *) a[1]; /* regular expression */ + else { + y = execute(a[1]); + p = compre(getsval(y)); + tempfree(y); + } + y = execute(a[2]); /* replacement string */ + result = False; + if (pmatch(p, t, t)) { + sptr = t; + adjbuf(&buf, &bufsz, 1+patbeg-sptr, recsize, 0, "sub"); + pb = buf; + while (sptr < patbeg) + *pb++ = *sptr++; + sptr = getsval(y); + while (*sptr != 0) { + adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "sub"); + if (*sptr == '\\') { + backsub(&pb, &sptr); + } else if (*sptr == '&') { + sptr++; + adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "sub"); + for (q = patbeg; q < patbeg+patlen; ) + *pb++ = *q++; + } else + *pb++ = *sptr++; + } + *pb = '\0'; + if (pb > buf + bufsz) + FATAL("sub result1 %.30s too big; can't happen", buf); + sptr = patbeg + patlen; + if ((patlen == 0 && *patbeg) || (patlen && *(sptr-1))) { + adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "sub"); + while ((*pb++ = *sptr++) != 0) + ; + } + if (pb > buf + bufsz) + FATAL("sub result2 %.30s too big; can't happen", buf); + setsval(x, buf); /* BUG: should be able to avoid copy */ + result = True;; + } + tempfree(x); + tempfree(y); + free(buf); + return result; +} + +Cell *gsub(Node **a, int nnn) /* global substitute */ +{ + Cell *x, *y; + char *rptr, *sptr, *t, *pb, *c; + char *buf; + void *p; + int mflag, num; + int bufsz = recsize; + + if ((buf = (char *)malloc(bufsz)) == NULL) + FATAL("out of memory in gsub"); + mflag = 0; /* if mflag == 0, can replace empty string */ + num = 0; + x = execute(a[3]); /* target string */ + c = t = getsval(x); + if (a[0] == 0) /* 0 => a[1] is already-compiled regexpr */ + p = (void *) a[1]; /* regular expression */ + else { + y = execute(a[1]); + p = compre(getsval(y)); + tempfree(y); + } + y = execute(a[2]); /* replacement string */ + if (pmatch(p, t, c)) { + pb = buf; + rptr = getsval(y); + do { + if (patlen == 0 && *patbeg != 0) { /* matched empty string */ + if (mflag == 0) { /* can replace empty */ + num++; + sptr = rptr; + while (*sptr != 0) { + adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gsub"); + if (*sptr == '\\') { + backsub(&pb, &sptr); + } else if (*sptr == '&') { + char *q; + sptr++; + adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gsub"); + for (q = patbeg; q < patbeg+patlen; ) + *pb++ = *q++; + } else + *pb++ = *sptr++; + } + } + if (*c == 0) /* at end */ + goto done; + adjbuf(&buf, &bufsz, 2+pb-buf, recsize, &pb, "gsub"); + *pb++ = *c++; + if (pb > buf + bufsz) /* BUG: not sure of this test */ + FATAL("gsub result0 %.30s too big; can't happen", buf); + mflag = 0; + } + else { /* matched nonempty string */ + num++; + sptr = c; + adjbuf(&buf, &bufsz, 1+(patbeg-sptr)+pb-buf, recsize, &pb, "gsub"); + while (sptr < patbeg) + *pb++ = *sptr++; + sptr = rptr; + while (*sptr != 0) { + adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gsub"); + if (*sptr == '\\') { + backsub(&pb, &sptr); + } else if (*sptr == '&') { + char *q; + sptr++; + adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gsub"); + for (q = patbeg; q < patbeg+patlen; ) + *pb++ = *q++; + } else + *pb++ = *sptr++; + } + c = patbeg + patlen; + if ((c[-1] == 0) || (*c == 0)) + goto done; + if (pb > buf + bufsz) + FATAL("gsub result1 %.30s too big; can't happen", buf); + mflag = 1; + } + } while (pmatch(p, t, c)); + sptr = c; + adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "gsub"); + while ((*pb++ = *sptr++) != 0) + ; + done: if (pb > buf + bufsz) + FATAL("gsub result2 %.30s too big; can't happen", buf); + *pb = '\0'; + setsval(x, buf); /* BUG: should be able to avoid copy + free */ + } + tempfree(x); + tempfree(y); + x = gettemp(); + x->tval = NUM; + x->fval = num; + free(buf); + return(x); +} + +void backsub(char **pb_ptr, char **sptr_ptr) /* handle \\& variations */ +{ /* sptr[0] == '\\' */ + char *pb = *pb_ptr, *sptr = *sptr_ptr; + + if (sptr[1] == '\\') { + if (sptr[2] == '\\' && sptr[3] == '&') { /* \\\& -> \& */ + *pb++ = '\\'; + *pb++ = '&'; + sptr += 4; + } else if (sptr[2] == '&') { /* \\& -> \ + matched */ + *pb++ = '\\'; + sptr += 2; + } else { /* \\x -> \\x */ + *pb++ = *sptr++; + *pb++ = *sptr++; + } + } else if (sptr[1] == '&') { /* literal & */ + sptr++; + *pb++ = *sptr++; + } else /* literal \ */ + *pb++ = *sptr++; + + *pb_ptr = pb; + *sptr_ptr = sptr; +} diff --git a/awk/tran.c b/awk/tran.c @@ -0,0 +1,434 @@ +/**************************************************************** +Copyright (C) Lucent Technologies 1997 +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name Lucent Technologies or any of +its entities not be used in advertising or publicity pertaining +to distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +#define DEBUG +#include <stdio.h> +#include <math.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include "awk.h" +#include "y.tab.h" + +#define FULLTAB 2 /* rehash when table gets this x full */ +#define GROWTAB 4 /* grow table by this factor */ + +Array *symtab; /* main symbol table */ + +char **FS; /* initial field sep */ +char **RS; /* initial record sep */ +char **OFS; /* output field sep */ +char **ORS; /* output record sep */ +char **OFMT; /* output format for numbers */ +char **CONVFMT; /* format for conversions in getsval */ +Awkfloat *NF; /* number of fields in current record */ +Awkfloat *NR; /* number of current record */ +Awkfloat *FNR; /* number of current record in current file */ +char **FILENAME; /* current filename argument */ +Awkfloat *ARGC; /* number of arguments from command line */ +char **SUBSEP; /* subscript separator for a[i,j,k]; default \034 */ +Awkfloat *RSTART; /* start of re matched with ~; origin 1 (!) */ +Awkfloat *RLENGTH; /* length of same */ + +Cell *nrloc; /* NR */ +Cell *nfloc; /* NF */ +Cell *fnrloc; /* FNR */ +Array *ARGVtab; /* symbol table containing ARGV[...] */ +Array *ENVtab; /* symbol table containing ENVIRON[...] */ +Cell *rstartloc; /* RSTART */ +Cell *rlengthloc; /* RLENGTH */ +Cell *symtabloc; /* SYMTAB */ + +Cell *nullloc; /* a guaranteed empty cell */ +Node *nullnode; /* zero&null, converted into a node for comparisons */ +Cell *literal0; + +extern Cell **fldtab; + +void syminit(void) /* initialize symbol table with builtin vars */ +{ + literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab); + /* this is used for if(x)... tests: */ + nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab); + nullnode = celltonode(nullloc, CCON); + + FS = &setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab)->sval; + RS = &setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab)->sval; + OFS = &setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab)->sval; + ORS = &setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab)->sval; + OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval; + CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval; + FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval; + nfloc = setsymtab("NF", "", 0.0, NUM, symtab); + NF = &nfloc->fval; + nrloc = setsymtab("NR", "", 0.0, NUM, symtab); + NR = &nrloc->fval; + fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab); + FNR = &fnrloc->fval; + SUBSEP = &setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab)->sval; + rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab); + RSTART = &rstartloc->fval; + rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab); + RLENGTH = &rlengthloc->fval; + symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab); + symtabloc->sval = (char *) symtab; +} + +void arginit(int ac, char **av) /* set up ARGV and ARGC */ +{ + Cell *cp; + int i; + char temp[50]; + + ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval; + cp = setsymtab("ARGV", "", 0.0, ARR, symtab); + ARGVtab = makesymtab(NSYMTAB); /* could be (int) ARGC as well */ + cp->sval = (char *) ARGVtab; + for (i = 0; i < ac; i++) { + sprintf(temp, "%d", i); + if (is_number(*av)) + setsymtab(temp, *av, atof(*av), STR|NUM, ARGVtab); + else + setsymtab(temp, *av, 0.0, STR, ARGVtab); + av++; + } +} + +void envinit(char **envp) /* set up ENVIRON variable */ +{ + Cell *cp; + char *p; + + cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab); + ENVtab = makesymtab(NSYMTAB); + cp->sval = (char *) ENVtab; + for ( ; *envp; envp++) { + if ((p = strchr(*envp, '=')) == NULL) + continue; + *p++ = 0; /* split into two strings at = */ + if (is_number(p)) + setsymtab(*envp, p, atof(p), STR|NUM, ENVtab); + else + setsymtab(*envp, p, 0.0, STR, ENVtab); + p[-1] = '='; /* restore in case env is passed down to a shell */ + } +} + +Array *makesymtab(int n) /* make a new symbol table */ +{ + Array *ap; + Cell **tp; + + ap = (Array *) malloc(sizeof(Array)); + tp = (Cell **) calloc(n, sizeof(Cell *)); + if (ap == NULL || tp == NULL) + FATAL("out of space in makesymtab"); + ap->nelem = 0; + ap->size = n; + ap->tab = tp; + return(ap); +} + +void freesymtab(Cell *ap) /* free a symbol table */ +{ + Cell *cp, *temp; + Array *tp; + int i; + + if (!isarr(ap)) + return; + tp = (Array *) ap->sval; + if (tp == NULL) + return; + for (i = 0; i < tp->size; i++) { + for (cp = tp->tab[i]; cp != NULL; cp = temp) { + xfree(cp->nval); + if (freeable(cp)) + xfree(cp->sval); + temp = cp->cnext; /* avoids freeing then using */ + free(cp); + } + tp->tab[i] = 0; + } + free(tp->tab); + free(tp); +} + +void freeelem(Cell *ap, char *s) /* free elem s from ap (i.e., ap["s"] */ +{ + Array *tp; + Cell *p, *prev = NULL; + int h; + + tp = (Array *) ap->sval; + h = hash(s, tp->size); + for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext) + if (strcmp(s, p->nval) == 0) { + if (prev == NULL) /* 1st one */ + tp->tab[h] = p->cnext; + else /* middle somewhere */ + prev->cnext = p->cnext; + if (freeable(p)) + xfree(p->sval); + free(p->nval); + free(p); + tp->nelem--; + return; + } +} + +Cell *setsymtab(char *n, char *s, Awkfloat f, unsigned t, Array *tp) +{ + int h; + Cell *p; + + if (n != NULL && (p = lookup(n, tp)) != NULL) { + dprintf( ("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n", + p, p->nval, p->sval, p->fval, p->tval) ); + return(p); + } + p = (Cell *) malloc(sizeof(Cell)); + if (p == NULL) + FATAL("out of space for symbol table at %s", n); + p->nval = tostring(n); + p->sval = s ? tostring(s) : tostring(""); + p->fval = f; + p->tval = t; + p->csub = CUNK; + p->ctype = OCELL; + tp->nelem++; + if (tp->nelem > FULLTAB * tp->size) + rehash(tp); + h = hash(n, tp->size); + p->cnext = tp->tab[h]; + tp->tab[h] = p; + dprintf( ("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n", + p, p->nval, p->sval, p->fval, p->tval) ); + return(p); +} + +int hash(char *s, int n) /* form hash value for string s */ +{ + unsigned hashval; + + for (hashval = 0; *s != '\0'; s++) + hashval = (*s + 31 * hashval); + return hashval % n; +} + +void rehash(Array *tp) /* rehash items in small table into big one */ +{ + int i, nh, nsz; + Cell *cp, *op, **np; + + nsz = GROWTAB * tp->size; + np = (Cell **) calloc(nsz, sizeof(Cell *)); + if (np == NULL) /* can't do it, but can keep running. */ + return; /* someone else will run out later. */ + for (i = 0; i < tp->size; i++) { + for (cp = tp->tab[i]; cp; cp = op) { + op = cp->cnext; + nh = hash(cp->nval, nsz); + cp->cnext = np[nh]; + np[nh] = cp; + } + } + free(tp->tab); + tp->tab = np; + tp->size = nsz; +} + +Cell *lookup(char *s, Array *tp) /* look for s in tp */ +{ + Cell *p; + int h; + + h = hash(s, tp->size); + for (p = tp->tab[h]; p != NULL; p = p->cnext) + if (strcmp(s, p->nval) == 0) + return(p); /* found it */ + return(NULL); /* not found */ +} + +Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */ +{ + int fldno; + + if ((vp->tval & (NUM | STR)) == 0) + funnyvar(vp, "assign to"); + if (isfld(vp)) { + donerec = 0; /* mark $0 invalid */ + fldno = atoi(vp->nval); + if (fldno > *NF) + newfld(fldno); + dprintf( ("setting field %d to %g\n", fldno, f) ); + } else if (isrec(vp)) { + donefld = 0; /* mark $1... invalid */ + donerec = 1; + } + if (freeable(vp)) + xfree(vp->sval); /* free any previous string */ + vp->tval &= ~STR; /* mark string invalid */ + vp->tval |= NUM; /* mark number ok */ + dprintf( ("setfval %p: %s = %g, t=%o\n", vp, vp->nval, f, vp->tval) ); + return vp->fval = f; +} + +void funnyvar(Cell *vp, char *rw) +{ + if (isarr(vp)) + FATAL("can't %s %s; it's an array name.", rw, vp->nval); + if (vp->tval & FCN) + FATAL("can't %s %s; it's a function.", rw, vp->nval); + WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o", + vp, vp->nval, vp->sval, vp->fval, vp->tval); +} + +char *setsval(Cell *vp, char *s) /* set string val of a Cell */ +{ + char *t; + int fldno; + + dprintf( ("starting setsval %p: %s = \"%s\", t=%o\n", vp, vp->nval, s, vp->tval) ); + if ((vp->tval & (NUM | STR)) == 0) + funnyvar(vp, "assign to"); + if (isfld(vp)) { + donerec = 0; /* mark $0 invalid */ + fldno = atoi(vp->nval); + if (fldno > *NF) + newfld(fldno); + dprintf( ("setting field %d to %s (%p)\n", fldno, s, s) ); + } else if (isrec(vp)) { + donefld = 0; /* mark $1... invalid */ + donerec = 1; + } + t = tostring(s); /* in case it's self-assign */ + vp->tval &= ~NUM; + vp->tval |= STR; + if (freeable(vp)) + xfree(vp->sval); + vp->tval &= ~DONTFREE; + dprintf( ("setsval %p: %s = \"%s (%p)\", t=%o\n", vp, vp->nval, t,t, vp->tval) ); + return(vp->sval = t); +} + +Awkfloat getfval(Cell *vp) /* get float val of a Cell */ +{ + if ((vp->tval & (NUM | STR)) == 0) + funnyvar(vp, "read value of"); + if (isfld(vp) && donefld == 0) + fldbld(); + else if (isrec(vp) && donerec == 0) + recbld(); + if (!isnum(vp)) { /* not a number */ + vp->fval = atof(vp->sval); /* best guess */ + if (is_number(vp->sval) && !(vp->tval&CON)) + vp->tval |= NUM; /* make NUM only sparingly */ + } + dprintf( ("getfval %p: %s = %g, t=%o\n", vp, vp->nval, vp->fval, vp->tval) ); + return(vp->fval); +} + +char *getsval(Cell *vp) /* get string val of a Cell */ +{ + char s[100]; /* BUG: unchecked */ + double dtemp; + + if ((vp->tval & (NUM | STR)) == 0) + funnyvar(vp, "read value of"); + if (isfld(vp) && donefld == 0) + fldbld(); + else if (isrec(vp) && donerec == 0) + recbld(); + if (isstr(vp) == 0) { + if (freeable(vp)) + xfree(vp->sval); + if (modf(vp->fval, &dtemp) == 0) /* it's integral */ + sprintf(s, "%.30g", vp->fval); + else + sprintf(s, *CONVFMT, vp->fval); + vp->sval = tostring(s); + vp->tval &= ~DONTFREE; + vp->tval |= STR; + } + dprintf( ("getsval %p: %s = \"%s (%p)\", t=%o\n", vp, vp->nval, vp->sval, vp->sval, vp->tval) ); + return(vp->sval); +} + +char *tostring(char *s) /* make a copy of string s */ +{ + char *p; + + p = (char *) malloc(strlen(s)+1); + if (p == NULL) + FATAL("out of space in tostring on %s", s); + strcpy(p, s); + return(p); +} + +char *qstring(char *s, int delim) /* collect string up to next delim */ +{ + char *os = s; + int c, n; + char *buf, *bp; + + if ((buf = (char *) malloc(strlen(s)+3)) == NULL) + FATAL( "out of space in qstring(%s)", s); + for (bp = buf; (c = *s) != delim; s++) { + if (c == '\n') + SYNTAX( "newline in string %.20s...", os ); + else if (c != '\\') + *bp++ = c; + else { /* \something */ + c = *++s; + if (c == 0) { /* \ at end */ + *bp++ = '\\'; + break; /* for loop */ + } + switch (c) { + case '\\': *bp++ = '\\'; break; + case 'n': *bp++ = '\n'; break; + case 't': *bp++ = '\t'; break; + case 'b': *bp++ = '\b'; break; + case 'f': *bp++ = '\f'; break; + case 'r': *bp++ = '\r'; break; + default: + if (!isdigit(c)) { + *bp++ = c; + break; + } + n = c - '0'; + if (isdigit(s[1])) { + n = 8 * n + *++s - '0'; + if (isdigit(s[1])) + n = 8 * n + *++s - '0'; + } + *bp++ = n; + break; + } + } + } + *bp++ = 0; + return buf; +} diff --git a/basename/Makefile b/basename/Makefile @@ -0,0 +1,36 @@ +# basename - basename unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = basename + +OFILES = basename.o + +MANFILES = basename.1 + +all: ${TARG} + @echo built ${TARG} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 diff --git a/basename/basename.1 b/basename/basename.1 @@ -0,0 +1,35 @@ +.TH BASENAME 1 +.SH NAME +basename \- strip file name affixes +.SH SYNOPSIS +.B basename +[ +.B -d +] +.I string +[ +.I suffix +] +.SH DESCRIPTION +.PP +.I Basename +deletes any prefix ending in slash +.RB ( / ) +and the +.IR suffix , +if present in +.IR string , +from +.IR string , +and prints the result on the standard output. +.PP +The +.B -d +option instead prints the directory component, +that is, +.I string +up to but not including the final slash. +If the string contains no slash, +a period and newline are printed. +.SH SOURCE +.B \*9/src/cmd/basename.c diff --git a/basename/basename.c b/basename/basename.c @@ -0,0 +1,41 @@ +#include <u.h> +#include <libc.h> + +void +main(int argc, char *argv[]) +{ + char *pr; + int n, dflag; + + dflag = 0; + if(argc>1 && strcmp(argv[1], "-d") == 0){ + --argc; + ++argv; + dflag = 1; + } + if(argc < 2 || argc > 3){ + fprint(2, "usage: basename [-d] string [suffix]\n"); + exits("usage"); + } + pr = utfrrune(argv[1], '/'); + if(dflag){ + if(pr){ + *pr = 0; + print("%s\n", argv[1]); + exits(0); + } + print(".\n"); + exits(0); + } + if(pr) + pr++; + else + pr = argv[1]; + if(argc==3){ + n = strlen(pr)-strlen(argv[2]); + if(n >= 0 && !strcmp(pr+n, argv[2])) + pr[n] = 0; + } + print("%s\n", pr); + exits(0); +} diff --git a/bc/Makefile b/bc/Makefile @@ -0,0 +1,46 @@ +# bc - bc unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = bc + +OFILES = y.tab.o + +YFILES = bc.y + +MANFILES = bc.1 + +all: + @if [ ! -f y.tab.c ]; then \ + ${MAKE} -f Makefile depend;\ + fi + @${MAKE} -f Makefile ${TARG} + @echo built ${TARG} + +depend: + @echo YACC ${YFILES} + @${YACC} -d ${YFILES} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} y.tab.c y.tab.h + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -lm -L${PREFIX}/lib -L../lib9 -l9 diff --git a/bc/bc.1 b/bc/bc.1 @@ -0,0 +1,292 @@ +.TH BC 1 +.SH NAME +bc \- arbitrary-precision arithmetic language +.SH SYNOPSIS +.B bc +[ +.B -c +] +[ +.B -l +] +[ +.B -s +] +[ +.I file ... +] +.SH DESCRIPTION +.I Bc +is an interactive processor for a language that resembles +C but provides arithmetic on numbers of arbitrary length with up +to 100 digits right of the decimal point. +It takes input from any files given, then reads +the standard input. +The +.B -l +argument stands for the name +of an arbitrary precision math library. +The +.B -s +argument suppresses the automatic display +of calculation results; all output is via the +.B print +command. +.PP +The following syntax for +.I bc +programs is like that of C; +.I L +means letter +.BR a - z , +.I E +means expression, +.I S +means statement. +.TF length(E) +.TP +Lexical +.RS +.HP +comments are enclosed in +.B /* */ +.HP +newlines end statements +.RE +.TP +Names +.IP +simple variables: +.I L +.br +array elements: +.IB L [ E ] +.br +The words +.BR ibase , +.BR obase , +and +.B scale +.TP +Other operands +.IP +arbitrarily long numbers with optional sign and decimal point. +.RS +.TP +.BI ( E ) +.TP +.BI sqrt( E ) +.TP +.BI length( E ) +number of significant decimal digits +.TP +.BI scale( E ) +number of digits right of decimal point +.TP +.IB L ( E , ... ,\fIE\fP) +function call +.RE +.TP +Operators +.RS +.HP +.B "+ - * / % ^\ " +.RB ( % +is remainder; +.B ^ +is power) +.HP +.B "++ --\ " +.TP +.B "== <= >= != < >" +.TP +.B "= += -= *= /= %= ^=" +.RE +.TP +Statements +.RS +.br +.I E +.br +.B { +.I S +.B ; +\&... +.B ; +.I S +.B } +.br +.B "print" +.I E +.br +.B "if (" +.I E +.B ) +.I S +.br +.B "while (" +.I E +.B ) +.I S +.br +.B "for (" +.I E +.B ; +.I E +.B ; +.I E +.B ")" +.I S +.br +null statement +.br +.B break +.br +.B quit +.br +\fL"\fRtext\fL"\fR +.RE +.TP +Function definitions +.RS +.br +.B define +.I L +.B ( +.I L +.B , +\&... +.B , +.I L +.B ){ +.PD0 +.br +.B auto +.I L +.B , +\&... +.B , +.I L +.br +.I S +.B ; +\&... +.B ; +.I S +.br +.B return +.I E +.LP +.B } +.RE +.TP +Functions in +.B -l +math library +.RS +.TP +.BI s( x ) +sine +.TP +.BI c( x ) +cosine +.TP +.BI e( x ) +exponential +.TP +.BI l( x ) +log +.TP +.BI a( x ) +arctangent +.TP +.BI j( "n, x" ) +Bessel function +.RE +.PP +.DT +All function arguments are passed by value. +.PD +.PP +The value of an expression at the top level is printed +unless the main operator is an assignment or the +.B -s +command line argument is given. +Text in quotes, which may include newlines, is always printed. +Either semicolons or newlines may separate statements. +Assignment to +.B scale +influences the number of digits to be retained on arithmetic +operations in the manner of +.IR dc (1). +Assignments to +.B ibase +or +.B obase +set the input and output number radix respectively. +.PP +The same letter may be used as an array, a function, +and a simple variable simultaneously. +All variables are global to the program. +Automatic variables are pushed down during function calls. +In a declaration of an array as a function argument +or automatic variable +empty square brackets must follow the array name. +.PP +.I Bc +is actually a preprocessor for +.IR dc (1), +which it invokes automatically, unless the +.B -c +(compile only) +option is present. +In this case the +.I dc +input is sent to the standard output instead. +.SH EXAMPLE +Define a function to compute an approximate value of +the exponential. +Use it to print 10 values. +(The exponential function in the library gives better answers.) +.PP +.EX +scale = 20 +define e(x) { + auto a, b, c, i, s + a = 1 + b = 1 + s = 1 + for(i=1; 1; i++) { + a *= x + b *= i + c = a/b + if(c == 0) return s + s += c + } +} +for(i=1; i<=10; i++) print e(i) +.EE +.SH FILES +.B \*9/lib/bclib +mathematical library +.SH SOURCE +.B \*9/src/cmd/bc.y +.SH "SEE ALSO" +.IR dc (1), +.IR hoc (1) +.SH BUGS +No +.LR && , +.LR || , +or +.L ! +operators. +.PP +A +.L for +statement must have all three +.LR E s. +.PP +A +.L quit +is interpreted when read, not when executed. diff --git a/bc/bc.y b/bc/bc.y @@ -0,0 +1,985 @@ +%{ + #include <u.h> + #include <libc.h> + #include <bio.h> + + #define bsp_max 5000 + + Biobuf *in; + #define stdin bstdin + #define stdout bstdout + Biobuf stdin; + Biobuf stdout; + char cary[1000]; + char* cp = { cary }; + char string[1000]; + char* str = { string }; + int crs = 128; + int rcrs = 128; /* reset crs */ + int bindx = 0; + int lev = 0; + int ln; + int* ttp; + char* ss = ""; + int bstack[10] = { 0 }; + char* numb[15] = + { + " 0", " 1", " 2", " 3", " 4", " 5", + " 6", " 7", " 8", " 9", " 10", " 11", + " 12", " 13", " 14" + }; + int* pre; + int* post; + + long peekc = -1; + int sargc; + int ifile; + char** sargv; + + char *funtab[] = + { + "<1>","<2>","<3>","<4>","<5>", + "<6>","<7>","<8>","<9>","<10>", + "<11>","<12>","<13>","<14>","<15>", + "<16>","<17>","<18>","<19>","<20>", + "<21>","<22>","<23>","<24>","<25>", + "<26>" + }; + char *atab[] = + { + "<221>","<222>","<223>","<224>","<225>", + "<226>","<227>","<228>","<229>","<230>", + "<231>","<232>","<233>","<234>","<235>", + "<236>","<237>","<238>","<239>","<240>", + "<241>","<242>","<243>","<244>","<245>", + "<246>" + }; + char* letr[26] = + { + "a","b","c","d","e","f","g","h","i","j", + "k","l","m","n","o","p","q","r","s","t", + "u","v","w","x","y","z" + }; + char* dot = { "." }; + int bspace[bsp_max]; + int* bsp_nxt = { bspace }; + int bdebug = 0; + int lflag; + int cflag; + int sflag; + + int* bundle(int, ...); + void conout(int*, char*); + int cpeek(int, int, int); + int getch(void); + int* geta(char*); + int* getf(char*); + void getout(void); + void output(int*); + void pp(char*); + void routput(int*); + void tp(char*); + void yyerror(char*, ...); + int yyparse(void); + + typedef void* pointer; +/* #pragma varargck type "lx" pointer */ + +%} +%union +{ + int* iptr; + char* cptr; + int cc; +} + +%type <iptr> pstat stat stat1 def slist dlets e ase nase +%type <iptr> slist re fprefix cargs eora cons constant lora +%type <cptr> crs + +%token <cptr> LETTER EQOP _AUTO DOT +%token <cc> DIGIT SQRT LENGTH _IF FFF EQ +%token <cc> _PRINT _WHILE _FOR NE LE GE INCR DECR +%token <cc> _RETURN _BREAK _DEFINE BASE OBASE SCALE +%token <cc> QSTR ERROR + +%right '=' EQOP +%left '+' '-' +%left '*' '/' '%' +%right '^' +%left UMINUS + +%% +start: + start stuff +| stuff + +stuff: + pstat tail + { + output($1); + } +| def dargs ')' '{' dlist slist '}' + { + ttp = bundle(6, pre, $6, post , "0", numb[lev], "Q"); + conout(ttp, (char*)$1); + rcrs = crs; + output((int*)""); /* this is horse puk!! */ + lev = bindx = 0; + } + +dlist: + tail +| dlist _AUTO dlets tail + +stat: + stat1 +| nase + { + if(sflag) + bundle(2, $1, "s."); + } + +pstat: + stat1 + { + if(sflag) + bundle(2, $1, "0"); + } +| nase + { + if(!sflag) + bundle(2, $1, "ps."); + } + +stat1: + { + bundle(1, ""); + } +| ase + { + bundle(2, $1, "s."); + } +| SCALE '=' e + { + bundle(2, $3, "k"); + } +| SCALE EQOP e + { + bundle(4, "K", $3, $2, "k"); + } +| BASE '=' e + { + bundle(2, $3, "i"); + } +| BASE EQOP e + { + bundle(4, "I", $3, $2, "i"); + } +| OBASE '=' e + { + bundle(2, $3, "o"); + } +| OBASE EQOP e + { + bundle(4, "O", $3, $2, "o"); + } +| QSTR + { + bundle(3, "[", $1, "]P"); + } +| _BREAK + { + bundle(2, numb[lev-bstack[bindx-1]], "Q"); + } +| _PRINT e + { + bundle(2, $2, "ps."); + } +| _RETURN e + { + bundle(4, $2, post, numb[lev], "Q"); + } +| _RETURN + { + bundle(4, "0", post, numb[lev], "Q"); + } +| '{' slist '}' + { + $$ = $2; + } +| FFF + { + bundle(1, "fY"); + } +| _IF crs BLEV '(' re ')' stat + { + conout($7, $2); + bundle(3, $5, $2, " "); + } +| _WHILE crs '(' re ')' stat BLEV + { + bundle(3, $6, $4, $2); + conout($$, $2); + bundle(3, $4, $2, " "); + } +| fprefix crs re ';' e ')' stat BLEV + { + bundle(5, $7, $5, "s.", $3, $2); + conout($$, $2); + bundle(5, $1, "s.", $3, $2, " "); + } +| '~' LETTER '=' e + { + bundle(3, $4, "S", $2); + } + +fprefix: + _FOR '(' e ';' + { + $$ = $3; + } + +BLEV: + = + { + --bindx; + } + +slist: + stat +| slist tail stat + { + bundle(2, $1, $3); + } + +tail: + '\n' + { + ln++; + } +| ';' + +re: + e EQ e + { + $$ = bundle(3, $1, $3, "="); + } +| e '<' e + { + bundle(3, $1, $3, ">"); + } +| e '>' e + { + bundle(3, $1, $3, "<"); + } +| e NE e + { + bundle(3, $1, $3, "!="); + } +| e GE e + { + bundle(3, $1, $3, "!>"); + } +| e LE e + { + bundle(3, $1, $3, "!<"); + } +| e + { + bundle(2, $1, " 0!="); + } + +nase: + '(' e ')' + { + $$ = $2; + } +| cons + { + bundle(3, " ", $1, " "); + } +| DOT cons + { + bundle(3, " .", $2, " "); + } +| cons DOT cons + { + bundle(5, " ", $1, ".", $3, " "); + } +| cons DOT + { + bundle(4, " ", $1, ".", " "); + } +| DOT + { + $<cptr>$ = "l."; + } +| LETTER '[' e ']' + { + bundle(3, $3, ";", geta($1)); + } +| LETTER INCR + { + bundle(4, "l", $1, "d1+s", $1); + } +| INCR LETTER + { + bundle(4, "l", $2, "1+ds", $2); + } +| DECR LETTER + { + bundle(4, "l", $2, "1-ds", $2); + } +| LETTER DECR + { + bundle(4, "l", $1, "d1-s", $1); + } +| LETTER '[' e ']' INCR + { + bundle(7, $3, ";", geta($1), "d1+" ,$3, ":" ,geta($1)); + } +| INCR LETTER '[' e ']' + { + bundle(7, $4, ";", geta($2), "1+d", $4, ":", geta($2)); + } +| LETTER '[' e ']' DECR + { + bundle(7, $3, ";", geta($1), "d1-", $3, ":", geta($1)); + } +| DECR LETTER '[' e ']' + { + bundle(7, $4, ";", geta($2), "1-d", $4, ":" ,geta($2)); + } +| SCALE INCR + { + bundle(1, "Kd1+k"); + } +| INCR SCALE + { + bundle(1, "K1+dk"); + } +| SCALE DECR + { + bundle(1, "Kd1-k"); + } +| DECR SCALE + { + bundle(1, "K1-dk"); + } +| BASE INCR + { + bundle(1, "Id1+i"); + } +| INCR BASE + { + bundle(1, "I1+di"); + } +| BASE DECR + { + bundle(1, "Id1-i"); + } +| DECR BASE + { + bundle(1, "I1-di"); + } +| OBASE INCR + { + bundle(1, "Od1+o"); + } +| INCR OBASE + { + bundle(1, "O1+do"); + } +| OBASE DECR + { + bundle(1, "Od1-o"); + } +| DECR OBASE + { + bundle(1, "O1-do"); + } +| LETTER '(' cargs ')' + { + bundle(4, $3, "l", getf($1), "x"); + } +| LETTER '(' ')' + { + bundle(3, "l", getf($1), "x"); + } +| LETTER = { + bundle(2, "l", $1); + } +| LENGTH '(' e ')' + { + bundle(2, $3, "Z"); + } +| SCALE '(' e ')' + { + bundle(2, $3, "X"); + } +| '?' + { + bundle(1, "?"); + } +| SQRT '(' e ')' + { + bundle(2, $3, "v"); + } +| '~' LETTER + { + bundle(2, "L", $2); + } +| SCALE + { + bundle(1, "K"); + } +| BASE + { + bundle(1, "I"); + } +| OBASE + { + bundle(1, "O"); + } +| '-' e + { + bundle(3, " 0", $2, "-"); + } +| e '+' e + { + bundle(3, $1, $3, "+"); + } +| e '-' e + { + bundle(3, $1, $3, "-"); + } +| e '*' e + { + bundle(3, $1, $3, "*"); + } +| e '/' e + { + bundle(3, $1, $3, "/"); + } +| e '%' e + { + bundle(3, $1, $3, "%%"); + } +| e '^' e + { + bundle(3, $1, $3, "^"); + } + +ase: + LETTER '=' e + { + bundle(3, $3, "ds", $1); + } +| LETTER '[' e ']' '=' e + { + bundle(5, $6, "d", $3, ":", geta($1)); + } +| LETTER EQOP e + { + bundle(6, "l", $1, $3, $2, "ds", $1); + } +| LETTER '[' e ']' EQOP e + { + bundle(9, $3, ";", geta($1), $6, $5, "d", $3, ":", geta($1)); + } + +e: + ase +| nase + +cargs: + eora +| cargs ',' eora + { + bundle(2, $1, $3); + } + +eora: + e +| LETTER '[' ']' + { + bundle(2, "l", geta($1)); + } + +cons: + constant + { + *cp++ = 0; + } + +constant: + '_' + { + $<cptr>$ = cp; + *cp++ = '_'; + } +| DIGIT + { + $<cptr>$ = cp; + *cp++ = $1; + } +| constant DIGIT + { + *cp++ = $2; + } + +crs: + = + { + $$ = cp; + *cp++ = '<'; + *cp++ = crs/100+'0'; + *cp++ = (crs%100)/10+'0'; + *cp++ = crs%10+'0'; + *cp++ = '>'; + *cp++ = '\0'; + if(crs++ >= 220) { + yyerror("program too big"); + getout(); + } + bstack[bindx++] = lev++; + } + +def: + _DEFINE LETTER '(' + { + $$ = getf($2); + pre = (int*)""; + post = (int*)""; + lev = 1; + bindx = 0; + bstack[bindx] = 0; + } + +dargs: +| lora + { + pp((char*)$1); + } +| dargs ',' lora + { + pp((char*)$3); + } + +dlets: + lora + { + tp((char*)$1); + } +| dlets ',' lora + { + tp((char*)$3); + } + +lora: + LETTER + { + $<cptr>$=$1; + } +| LETTER '[' ']' + { + $$ = geta($1); + } + +%% + +int +yylex(void) +{ + int c, ch; + +restart: + c = getch(); + peekc = -1; + while(c == ' ' || c == '\t') + c = getch(); + if(c == '\\') { + getch(); + goto restart; + } + if(c >= 'a' && c <= 'z') { + /* look ahead to look for reserved words */ + peekc = getch(); + if(peekc >= 'a' && peekc <= 'z') { /* must be reserved word */ + if(c=='p' && peekc=='r') { + c = _PRINT; + goto skip; + } + if(c=='i' && peekc=='f') { + c = _IF; + goto skip; + } + if(c=='w' && peekc=='h') { + c = _WHILE; + goto skip; + } + if(c=='f' && peekc=='o') { + c = _FOR; + goto skip; + } + if(c=='s' && peekc=='q') { + c = SQRT; + goto skip; + } + if(c=='r' && peekc=='e') { + c = _RETURN; + goto skip; + } + if(c=='b' && peekc=='r') { + c = _BREAK; + goto skip; + } + if(c=='d' && peekc=='e') { + c = _DEFINE; + goto skip; + } + if(c=='s' && peekc=='c') { + c = SCALE; + goto skip; + } + if(c=='b' && peekc=='a') { + c = BASE; + goto skip; + } + if(c=='i' && peekc=='b') { + c = BASE; + goto skip; + } + if(c=='o' && peekc=='b') { + c = OBASE; + goto skip; + } + if(c=='d' && peekc=='i') { + c = FFF; + goto skip; + } + if(c=='a' && peekc=='u') { + c = _AUTO; + goto skip; + } + if(c=='l' && peekc=='e') { + c = LENGTH; + goto skip; + } + if(c=='q' && peekc=='u') + getout(); + /* could not be found */ + return ERROR; + + skip: /* skip over rest of word */ + peekc = -1; + for(;;) { + ch = getch(); + if(ch < 'a' || ch > 'z') + break; + } + peekc = ch; + return c; + } + + /* usual case; just one single letter */ + yylval.cptr = letr[c-'a']; + return LETTER; + } + if((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) { + yylval.cc = c; + return DIGIT; + } + switch(c) { + case '.': + return DOT; + case '*': + yylval.cptr = "*"; + return cpeek('=', EQOP, c); + case '%': + yylval.cptr = "%%"; + return cpeek('=', EQOP, c); + case '^': + yylval.cptr = "^"; + return cpeek('=', EQOP, c); + case '+': + ch = cpeek('=', EQOP, c); + if(ch == EQOP) { + yylval.cptr = "+"; + return ch; + } + return cpeek('+', INCR, c); + case '-': + ch = cpeek('=', EQOP, c); + if(ch == EQOP) { + yylval.cptr = "-"; + return ch; + } + return cpeek('-', DECR, c); + case '=': + return cpeek('=', EQ, '='); + case '<': + return cpeek('=', LE, '<'); + case '>': + return cpeek('=', GE, '>'); + case '!': + return cpeek('=', NE, '!'); + case '/': + ch = cpeek('=', EQOP, c); + if(ch == EQOP) { + yylval.cptr = "/"; + return ch; + } + if(peekc == '*') { + peekc = -1; + for(;;) { + ch = getch(); + if(ch == '*') { + peekc = getch(); + if(peekc == '/') { + peekc = -1; + goto restart; + } + } + } + } + return c; + case '"': + yylval.cptr = str; + while((c=getch()) != '"'){ + *str++ = c; + if(str >= &string[999]){ + yyerror("string space exceeded"); + getout(); + } + } + *str++ = 0; + return QSTR; + default: + return c; + } +} + +int +cpeek(int c, int yes, int no) +{ + + peekc = getch(); + if(peekc == c) { + peekc = -1; + return yes; + } + return no; +} + +int +getch(void) +{ + long ch; + +loop: + ch = peekc; + if(ch < 0){ + if(in == 0) + ch = -1; + else + ch = Bgetc(in); + } + peekc = -1; + if(ch >= 0) + return ch; + ifile++; + if(ifile > sargc) { + if(ifile >= sargc+2) + getout(); + in = &stdin; + Binit(in, 0, OREAD); + ln = 0; + goto loop; + } + Bterm(in); + if((in = Bopen(sargv[ifile], OREAD)) != 0){ + ln = 0; + ss = sargv[ifile]; + goto loop; + } + yyerror("cannot open input file"); + return 0; /* shut up ken */ +} + +int* +bundle(int a, ...) +{ + int i, *p, *q; + + p = &a; + i = *p++; + q = bsp_nxt; + if(bdebug) + fprint(2, "bundle %d elements at %lx\n", i, q); + while(i-- > 0) { + if(bsp_nxt >= &bspace[bsp_max]) + yyerror("bundling space exceeded"); + *bsp_nxt++ = *p++; + } + *bsp_nxt++ = 0; + yyval.iptr = q; + return q; +} + +void +routput(int *p) +{ + if(bdebug) + fprint(2, "routput(%lx)\n", p); + if(p >= &bspace[0] && p < &bspace[bsp_max]) { + /* part of a bundle */ + while(*p != 0) + routput((int*)(*p++)); + } else + Bprint(&stdout, (char*)p); /* character string */ +} + +void +output(int *p) +{ + routput(p); + bsp_nxt = &bspace[0]; + Bprint(&stdout, "\n"); + Bflush(&stdout); + cp = cary; + crs = rcrs; +} + +void +conout(int *p, char *s) +{ + Bprint(&stdout, "["); + routput(p); + Bprint(&stdout, "]s%s\n", s); + Bflush(&stdout); + lev--; +} + +void +yyerror(char *s, ...) +{ + if(ifile > sargc) + ss = "teletype"; + Bprint(&stdout, "c[%s on line %d, %s]pc\n", s, ln+1, ss); + Bflush(&stdout); + cp = cary; + crs = rcrs; + bindx = 0; + lev = 0; + bsp_nxt = &bspace[0]; +} + +void +pp(char *s) +{ + /* puts the relevant stuff on pre and post for the letter s */ + bundle(3, "S", s, pre); + pre = yyval.iptr; + bundle(4, post, "L", s, "s."); + post = yyval.iptr; +} + +void +tp(char *s) +{ + /* same as pp, but for temps */ + bundle(3, "0S", s, pre); + pre = yyval.iptr; + bundle(4, post, "L", s, "s."); + post = yyval.iptr; +} + +void +yyinit(int argc, char **argv) +{ + Binit(&stdout, 1, OWRITE); + sargv = argv; + sargc = argc - 1; + if(sargc == 0) { + in = &stdin; + Binit(in, 0, OREAD); + } else if((in = Bopen(sargv[1], OREAD)) == 0) + yyerror("cannot open input file"); + ifile = 1; + ln = 0; + ss = sargv[1]; +} + +void +getout(void) +{ + Bprint(&stdout, "q"); + Bflush(&stdout); + exits(0); +} + +int* +getf(char *p) +{ + return (int*)funtab[*p - 'a']; +} + +int* +geta(char *p) +{ + return (int*)atab[*p - 'a']; +} + +void +main(int argc, char **argv) +{ + int p[2]; + + while(argc > 1 && *argv[1] == '-') { + switch(argv[1][1]) { + case 'd': + bdebug++; + break; + case 'c': + cflag++; + break; + case 'l': + lflag++; + break; + case 's': + sflag++; + break; + default: + fprint(2, "Usage: bc [-l] [-c] [file ...]\n"); + exits("usage"); + } + argc--; + argv++; + } + if(lflag) { + argv--; + argc++; + argv[1] = unsharp("#9/lib/bclib"); + } + if(cflag) { + yyinit(argc, argv); + for(;;) + yyparse(); + /* exits(0); */ + } + pipe(p); + if(fork() == 0) { + dup(p[1], 1); + close(p[0]); + close(p[1]); + yyinit(argc, argv); + for(;;) + yyparse(); + } + dup(p[0], 0); + close(p[0]); + close(p[1]); + execlp("dc", "dc", (char*)0); +} diff --git a/cat/Makefile b/cat/Makefile @@ -0,0 +1,36 @@ +# cat - cat unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = cat + +OFILES = cat.o + +MANFILES = cat.1 + +all: ${TARG} + @echo built ${TARG} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 diff --git a/cat/cat.1 b/cat/cat.1 @@ -0,0 +1,108 @@ +.TH CAT 1 +.SH NAME +cat, read, nobs \- catenate files +.SH SYNOPSIS +.B cat +[ +.I file ... +] +.br +.B read +[ +.B -m +] [ +.B -n +.I nline +] [ +.I file ... +] +.br +.B nobs +[ +.I file ... +] +.SH DESCRIPTION +.I Cat +reads each +.I file +in sequence and writes it on the standard output. +Thus +.IP +.L +cat file +.LP +prints a file and +.IP +.L +cat file1 file2 >file3 +.LP +concatenates the first two files and places the result +on the third. +.PP +If no +.I file +is given, +.I cat +reads from the standard input. +Output is buffered in blocks matching the input. +.PP +.I Read +copies to standard output exactly one line from the named +.IR file , +default standard input. +It is useful in interactive +.IR rc (1) +scripts. +.PP +The +.B -m +flag causes it to continue reading and writing multiple lines until end of file; +.B -n +causes it to read no more than +.I nline +lines. +.PP +.I Read +always executes a single +.B write +for each line of input, which can be helpful when +preparing input to programs that expect line-at-a-time data. +It never reads any more data from the input than it prints to the output. +.PP +.I Nobs +copies the named files to +standard output except that it removes all backspace +characters and the characters that precede them. +It is useful to use as +.B $PAGER +with the Unix version of +.IR man (1) +when run inside a +.I win +(see +.IR acme (1)) +window. +.SH SOURCE +.B \*9/src/cmd/cat.c +.br +.B \*9/src/cmd/read.c +.br +.B \*9/bin/nobs +.SH SEE ALSO +.IR cp (1) +.SH DIAGNOSTICS +.I Read +exits with status +.B eof +on end of file or, in the +.B -n +case, if it doesn't read +.I nlines +lines. +.SH BUGS +Beware of +.L "cat a b >a" +and +.LR "cat a b >b" , +which +destroy input files before reading them. diff --git a/cat/cat.c b/cat/cat.c @@ -0,0 +1,36 @@ +#include <u.h> +#include <libc.h> + +void +cat(int f, char *s) +{ + char buf[8192]; + long n; + + while((n=read(f, buf, (long)sizeof buf))>0) + if(write(1, buf, n)!=n) + sysfatal("write error copying %s: %r", s); + if(n < 0) + sysfatal("error reading %s: %r", s); +} + +void +main(int argc, char *argv[]) +{ + int f, i; + + argv0 = "cat"; + if(argc == 1) + cat(0, "<stdin>"); + else for(i=1; i<argc; i++){ + f = open(argv[i], OREAD); + if(f < 0) + sysfatal("can't open %s: %r", argv[i]); + else{ + cat(f, argv[i]); + close(f); + } + } + exits(0); +} + diff --git a/cleanname/Makefile b/cleanname/Makefile @@ -0,0 +1,36 @@ +# cleanname - cleanname unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = cleanname + +OFILES = cleanname.o + +MANFILES = cleanname.1 + +all: ${TARG} + @echo built ${TARG} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 diff --git a/cleanname/cleanname.1 b/cleanname/cleanname.1 @@ -0,0 +1,32 @@ +.TH CLEANNAME 1 +.SH NAME +cleanname \- clean a path name +.SH SYNOPSIS +.B cleanname +[ +.B -d +.I pwd +] +.I names ... +.SH DESCRIPTION +For each file name argument, +.IR cleanname , +by lexical processing only, +prints the shortest equivalent string that names the same +(possibly hypothetical) file. +It eliminates multiple and trailing slashes, and it lexically +interprets +.B . +and +.B .. +directory components in the name. +If the +.B -d +option is present, +unrooted names are prefixed with +.IB pwd / +before processing. +.SH SOURCE +.B \*9/src/cmd/cleanname.c +.SH SEE ALSO +.IR cleanname (3). diff --git a/cleanname/cleanname.c b/cleanname/cleanname.c @@ -0,0 +1,44 @@ +#include <u.h> +#include <libc.h> + +void +main(int argc, char **argv) +{ + char *dir; + char *name; + int i; + + dir = nil; + ARGBEGIN{ + case 'd': + if((dir=ARGF()) == nil) + goto Usage; + break; + default: + goto Usage; + }ARGEND; + + if(argc < 1) { + Usage: + fprint(2, "usage: cleanname [-d pwd] name...\n"); + exits("usage"); + } + + for(i=0; i<argc; i++) { + if(dir == nil || argv[i][0] == '/') { + cleanname(argv[i]); + print("%s\n", argv[i]); + } else { + name = malloc(strlen(argv[i])+1+strlen(dir)+1); + if(name == nil) { + fprint(2, "cleanname: out of memory\n"); + exits("out of memory"); + } + sprint(name, "%s/%s", dir, argv[i]); + cleanname(name); + print("%s\n", name); + free(name); + } + } + exits(0); +} diff --git a/config.mk b/config.mk @@ -0,0 +1,15 @@ +# Customize to fit your system + +# paths +PREFIX = /usr/local/9 +MANPREFIX = ${PREFIX}/share/man + +# flags +VERSION = 20051114 +CFLAGS = -Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -Os -c -I. -DPREFIX="\"${PREFIX}\"" +LDFLAGS = -static + +# compiler +AR = ar rc +CC = cc +YACC = ../yacc/9yacc diff --git a/date/Makefile b/date/Makefile @@ -0,0 +1,36 @@ +# date - date unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = date + +OFILES = date.o + +MANFILES = date.1 + +all: ${TARG} + @echo built ${TARG} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 diff --git a/date/date.1 b/date/date.1 @@ -0,0 +1,58 @@ +.TH DATE 1 +.SH NAME +date \- date and time +.SH SYNOPSIS +.B date +[ +.I option +] [ +.I seconds +] +.\" .br +.\" .B clock +.SH DESCRIPTION +Print the date, in the format +.PP +.B + Tue Aug 16 17:03:52 CDT 1977 +.PP +The options are +.TP +.B -u +Report Greenwich Mean Time (GMT) rather than local time. +.TP +.B -n +Report the date as the number of seconds since the +epoch, 00:00:00 GMT, January 1, 1970. +.PP +The conversion from Greenwich Mean Time to local time depends on the +.B $timezone +environment variable; see +.IR ctime (3). +.PP +If the optional argument +.I seconds +is present, it is used as the time to convert rather than +the real time. +.\" .SH FILES +.\" .TF /adm/timezone/local +.\" .TP +.\" .B /env/timezone +.\" Current timezone name and adjustments. +.\" .TP +.\" .B /adm/timezone +.\" A directory containing timezone tables. +.\" .TP +.\" .B /adm/timezone/local +.\" Default timezone file, copied by +.\" .IR init (8) +.\" into +.\" .BR /env/timezone . +.\" .PD +.\" .PP +.\" .I Clock +.\" draws a simple analog clock in its window. +.SH SOURCE +.B \*9/src/cmd/date.c +.\" .br +.\" .B \*9/src/cmd/draw/clock.c diff --git a/date/date.c b/date/date.c @@ -0,0 +1,30 @@ +#include <u.h> +#include <libc.h> + +int uflg, nflg; + +void +main(int argc, char *argv[]) +{ + ulong now; + + ARGBEGIN{ + case 'n': nflg = 1; break; + case 'u': uflg = 1; break; + default: fprint(2, "usage: date [-un] [seconds]\n"); exits("usage"); + }ARGEND + + if(argc == 1) + now = strtoul(*argv, 0, 0); + else + now = time(0); + + if(nflg) + print("%ld\n", now); + else if(uflg) + print("%s", asctime(gmtime(now))); + else + print("%s", ctime(now)); + + exits(0); +} diff --git a/echo/Makefile b/echo/Makefile @@ -0,0 +1,36 @@ +# echo - echo unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = echo + +OFILES = echo.o + +MANFILES = echo.1 + +all: ${TARG} + @echo built ${TARG} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 diff --git a/echo/echo.1 b/echo/echo.1 @@ -0,0 +1,26 @@ +.TH ECHO 1 +.SH NAME +echo \- print arguments +.SH SYNOPSIS +.B echo +[ +.B -n +] +[ +.I arg ... +] +.SH DESCRIPTION +.I Echo +writes its arguments separated by blanks and terminated by +a newline on the standard output. +Option +.B -n +suppresses the newline. +.SH SOURCE +.B \*9/src/cmd/echo.c +.SH DIAGNOSTICS +If +.I echo +draws an error while writing to standard output, the exit status is +.LR "write error" . +Otherwise the exit status is empty. diff --git a/echo/echo.c b/echo/echo.c @@ -0,0 +1,38 @@ +#include <u.h> +#include <libc.h> + +void +main(int argc, char *argv[]) +{ + int nflag; + int i, len; + char *buf, *p; + + nflag = 0; + if(argc > 1 && strcmp(argv[1], "-n") == 0) + nflag = 1; + + len = 1; + for(i = 1+nflag; i < argc; i++) + len += strlen(argv[i])+1; + + buf = malloc(len); + if(buf == 0) + exits("no memory"); + + p = buf; + for(i = 1+nflag; i < argc; i++){ + strcpy(p, argv[i]); + p += strlen(p); + if(i < argc-1) + *p++ = ' '; + } + + if(!nflag) + *p++ = '\n'; + + if(write(1, buf, p-buf) < 0) + fprint(2, "echo: write error: %r\n"); + + exits((char *)0); +} diff --git a/grep/Makefile b/grep/Makefile @@ -0,0 +1,46 @@ +# grep - grep unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = grep + +OFILES = y.tab.o main.o comp.o sub.o + +YFILES = grep.y + +MANFILES = grep.1 + +all: + @if [ ! -f y.tab.c ]; then \ + ${MAKE} -f Makefile depend;\ + fi + @${MAKE} -f Makefile ${TARG} + @echo built ${TARG} + +depend: + @echo YACC ${YFILES} + @${YACC} -d ${YFILES} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} y.tab.c y.tab.h + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 diff --git a/grep/comp.c b/grep/comp.c @@ -0,0 +1,277 @@ +#include "grep.h" + +/* + * incremental compiler. + * add the branch c to the + * state s. + */ +void +increment(State *s, int c) +{ + int i; + State *t, **tt; + Re *re1, *re2; + + nfollow = 0; + gen++; + matched = 0; + for(i=0; i<s->count; i++) + fol1(s->re[i], c); + qsort(follow, nfollow, sizeof(*follow), fcmp); + for(tt=&state0; t = *tt;) { + if(t->count > nfollow) { + tt = &t->linkleft; + goto cont; + } + if(t->count < nfollow) { + tt = &t->linkright; + goto cont; + } + for(i=0; i<nfollow; i++) { + re1 = t->re[i]; + re2 = follow[i]; + if(re1 > re2) { + tt = &t->linkleft; + goto cont; + } + if(re1 < re2) { + tt = &t->linkright; + goto cont; + } + } + if(!!matched && !t->match) { + tt = &t->linkleft; + goto cont; + } + if(!matched && !!t->match) { + tt = &t->linkright; + goto cont; + } + s->next[c] = t; + return; + cont:; + } + + t = sal(nfollow); + *tt = t; + for(i=0; i<nfollow; i++) { + re1 = follow[i]; + t->re[i] = re1; + } + s->next[c] = t; + t->match = matched; +} + +int +fcmp(const void *va, const void *vb) +{ + Re **aa, **bb; + Re *a, *b; + + aa = (Re**)va; + bb = (Re**)vb; + a = *aa; + b = *bb; + if(a > b) + return 1; + if(a < b) + return -1; + return 0; +} + +void +fol1(Re *r, int c) +{ + Re *r1; + +loop: + if(r->gen == gen) + return; + if(nfollow >= maxfollow) + error("nfollow"); + r->gen = gen; + switch(r->type) { + default: + error("fol1"); + + case Tcase: + if(c >= 0 && c < 256) + if(r1 = r->u.cases[c]) + follow[nfollow++] = r1; + if(r = r->next) + goto loop; + break; + + case Talt: + case Tor: + fol1(r->u.alt, c); + r = r->next; + goto loop; + + case Tbegin: + if(c == '\n' || c == Cbegin) + follow[nfollow++] = r->next; + break; + + case Tend: + if(c == '\n') + matched = 1; + break; + + case Tclass: + if(c >= r->u.x.lo && c <= r->u.x.hi) + follow[nfollow++] = r->next; + break; + } +} + +Rune tab1[] = +{ + 0x007f, + 0x07ff, +}; +Rune tab2[] = +{ + 0x003f, + 0x0fff, +}; + +Re2 +rclass(Rune p0, Rune p1) +{ + char xc0[6], xc1[6]; + int i, n, m; + Re2 x; + + if(p0 > p1) + return re2char(0xff, 0xff); // no match + + /* + * bust range into same length + * character sequences + */ + for(i=0; i<nelem(tab1); i++) { + m = tab1[i]; + if(p0 <= m && p1 > m) + return re2or(rclass(p0, m), rclass(m+1, p1)); + } + + /* + * bust range into part of a single page + * or into full pages + */ + for(i=0; i<nelem(tab2); i++) { + m = tab2[i]; + if((p0 & ~m) != (p1 & ~m)) { + if((p0 & m) != 0) + return re2or(rclass(p0, p0|m), rclass((p0|m)+1, p1)); + if((p1 & m) != m) + return re2or(rclass(p0, (p1&~m)-1), rclass(p1&~m, p1)); + } + } + + n = runetochar(xc0, &p0); + i = runetochar(xc1, &p1); + if(i != n) + error("length"); + + x = re2char(xc0[0], xc1[0]); + for(i=1; i<n; i++) + x = re2cat(x, re2char(xc0[i], xc1[i])); + return x; +} + +int +pcmp(const void *va, const void *vb) +{ + int n; + Rune *a, *b; + + a = (Rune*)va; + b = (Rune*)vb; + + n = a[0] - b[0]; + if(n) + return n; + return a[1] - b[1]; +} + +/* + * convert character chass into + * run-pair ranges of matches. + * this is 10646/utf specific and + * needs to be changed for some + * other input character set. + * this is the key to a fast + * regular search of characters + * by looking at sequential bytes. + */ +Re2 +re2class(char *s) +{ + Rune pairs[200], *p, *q, ov; + int nc; + Re2 x; + + nc = 0; + if(*s == '^') { + nc = 1; + s++; + } + + p = pairs; + s += chartorune(p, s); + for(;;) { + if(*p == '\\') + s += chartorune(p, s); + if(*p == 0) + break; + p[1] = *p; + p += 2; + s += chartorune(p, s); + if(*p != '-') + continue; + s += chartorune(p, s); + if(*p == '\\') + s += chartorune(p, s); + if(*p == 0) + break; + p[-1] = *p; + s += chartorune(p, s); + } + *p = 0; + qsort(pairs, (p-pairs)/2, 2*sizeof(*pairs), pcmp); + + q = pairs; + for(p=pairs+2; *p; p+=2) { + if(p[0] > p[1]) + continue; + if(p[0] > q[1] || p[1] < q[0]) { + q[2] = p[0]; + q[3] = p[1]; + q += 2; + continue; + } + if(p[0] < q[0]) + q[0] = p[0]; + if(p[1] > q[1]) + q[1] = p[1]; + } + q[2] = 0; + + p = pairs; + if(nc) { + x = rclass(0, p[0]-1); + ov = p[1]+1; + for(p+=2; *p; p+=2) { + x = re2or(x, rclass(ov, p[0]-1)); + ov = p[1]+1; + } + x = re2or(x, rclass(ov, 0xffff)); + } else { + x = rclass(p[0], p[1]); + for(p+=2; *p; p+=2) + x = re2or(x, rclass(p[0], p[1])); + } + return x; +} diff --git a/grep/grep.1 b/grep/grep.1 @@ -0,0 +1,124 @@ +.TH GREP 1 +.SH NAME +grep, g \- search a file for a pattern +.SH SYNOPSIS +.B grep +[ +.I option ... +] +.I pattern +[ +.I file ... +] +.PP +.B g +[ +.I option ... +] +.I pattern +[ +.I file ... +] +.SH DESCRIPTION +.I Grep\^ +searches the input +.I files\^ +(standard input default) +for lines that match the +.IR pattern , +a regular expression as defined in +.IR regexp (7) +with the addition of a newline character as an alternative +(substitute for +.BR | ) +with lowest precedence. +Normally, each line matching the pattern is `selected', +and each selected line is copied to the standard output. +The options are +.TP +.B -c +Print only a count of matching lines. +.PD 0 +.TP +.B -h +Do not print file name tags (headers) with output lines. +.TP +.B -e +The following argument is taken as a +.IR pattern . +This option makes it easy to specify patterns that +might confuse argument parsing, such as +.BR -n . +.TP +.B -i +Ignore alphabetic case distinctions. The implementation +folds into lower case all letters in the pattern and input before +interpretation. Matched lines are printed in their original form. +.TP +.B -l +(ell) Print the names of files with selected lines; don't print the lines. +.TP +.B -L +Print the names of files with no selected lines; +the converse of +.BR -l . +.TP +.B -n +Mark each printed line with its line number counted in its file. +.TP +.B -s +Produce no output, but return status. +.TP +.B -v +Reverse: print lines that do not match the pattern. +.TP +.B -f +The pattern argument is the name of a file containing regular +expressions one per line. +.TP +.B -b +Don't buffer the output: write each output line as soon as it is discovered. +.PD +.PP +Output lines are tagged by file name when there is more than one +input file. +(To force this tagging, include +.B /dev/null +as a file name argument.) +.PP +Care should be taken when +using the shell metacharacters +.B $*[^|()=\e +and newline +in +.IR pattern ; +it is safest to enclose the +entire expression +in single quotes +.BR \&\|' \|.\|.\|.\| ' . +An expression starting with '*' +will treat the rest of the expression +as literal characters. +.PP +.I G +invokes grep with +.B -n +and forces tagging of output lines by file name. +If no files are listed, it searches all files matching +.IP +.EX +*.C *.b *.c *.h *.m *.cc *.java *.cgi *.pl *.py *.tex *.ms +.EE +.SH SOURCE +.B \*9/src/cmd/grep +.br +.B \*9/bin/g +.SH SEE ALSO +.IR ed (1), +.IR awk (1), +.IR sed (1), +.IR sam (1), +.IR regexp (7) +.SH DIAGNOSTICS +Exit status is null if any lines are selected, +or non-null when no lines are selected or an error occurs. diff --git a/grep/grep.h b/grep/grep.h @@ -0,0 +1,125 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +#ifndef EXTERN +#define EXTERN extern +#endif + +typedef struct Re Re; +typedef struct Re2 Re2; +typedef struct State State; + +struct State +{ + int count; + int match; + Re** re; + State* linkleft; + State* linkright; + State* next[256]; +}; +struct Re2 +{ + Re* beg; + Re* end; +}; +struct Re +{ + uchar type; + ushort gen; + union + { + Re* alt; /* Talt */ + Re** cases; /* case */ + struct /* class */ + { + Rune lo; + Rune hi; + } x; + Rune val; /* char */ + } u; + Re* next; +}; + +enum +{ + Talt = 1, + Tbegin, + Tcase, + Tclass, + Tend, + Tor, + + Caselim = 7, + Nhunk = 1<<16, + Cbegin = 0x10000, + Flshcnt = (1<<9)-1, + + Cflag = 1<<0, + Hflag = 1<<1, + Iflag = 1<<2, + Llflag = 1<<3, + LLflag = 1<<4, + Nflag = 1<<5, + Sflag = 1<<6, + Vflag = 1<<7, + Bflag = 1<<8 +}; + +EXTERN union +{ + char string[16*1024]; + struct + { + /* + * if a line requires multiple reads, we keep shifting + * buf down into pre and then do another read into + * buf. so you'll get the last 16-32k of the matching line. + * if h were smaller than buf you'd get a suffix of the + * line with a hole cut out. + */ + uchar pre[16*1024]; /* to save to previous '\n' */ + uchar buf[16*1024]; /* input buffer */ + } u; +} u; + +EXTERN char *filename; +EXTERN Biobuf bout; +EXTERN char flags[256]; +EXTERN Re** follow; +EXTERN ushort gen; +EXTERN char* input; +EXTERN long lineno; +EXTERN int literal; +EXTERN int matched; +EXTERN long maxfollow; +EXTERN long nfollow; +EXTERN int peekc; +EXTERN Biobuf* rein; +EXTERN State* state0; +EXTERN Re2 topre; + +extern Re* addcase(Re*); +extern void appendnext(Re*, Re*); +extern void error(char*); +extern int fcmp(const void*, const void*); /* (Re**, Re**) */ +extern void fol1(Re*, int); +extern int getrec(void); +extern void increment(State*, int); +#define initstate grepinitstate +extern State* initstate(Re*); +extern void* mal(int); +extern void patchnext(Re*, Re*); +extern Re* ral(int); +extern Re2 re2cat(Re2, Re2); +extern Re2 re2class(char*); +extern Re2 re2or(Re2, Re2); +extern Re2 re2char(int, int); +extern Re2 re2star(Re2); +extern State* sal(int); +extern int search(char*, int); +extern void str2top(char*); +extern int yyparse(void); +extern void reprint(char*, Re*); +extern void yyerror(char*, ...); diff --git a/grep/grep.y b/grep/grep.y @@ -0,0 +1,226 @@ +%{ +#include "grep.h" +%} + +%union +{ + int val; + char* str; + Re2 re; +} + +%type <re> expr prog +%type <re> expr0 expr1 expr2 expr3 expr4 +%token <str> LCLASS +%token <val> LCHAR +%token LLPAREN LRPAREN LALT LSTAR LPLUS LQUES +%token LBEGIN LEND LDOT LBAD LNEWLINE +%% + +prog: + expr newlines + { + $$.beg = ral(Tend); + $$.end = $$.beg; + $$ = re2cat(re2star(re2or(re2char(0x00, '\n'-1), re2char('\n'+1, 0xff))), $$); + $$ = re2cat($1, $$); + $$ = re2cat(re2star(re2char(0x00, 0xff)), $$); + topre = $$; + } + +expr: + expr0 +| expr newlines expr0 + { + $$ = re2or($1, $3); + } + +expr0: + expr1 +| LSTAR { literal = 1; } expr1 + { + $$ = $3; + } + +expr1: + expr2 +| expr1 LALT expr2 + { + $$ = re2or($1, $3); + } + +expr2: + expr3 +| expr2 expr3 + { + $$ = re2cat($1, $2); + } + +expr3: + expr4 +| expr3 LSTAR + { + $$ = re2star($1); + } +| expr3 LPLUS + { + $$.beg = ral(Talt); + patchnext($1.end, $$.beg); + $$.beg->u.alt = $1.beg; + $$.end = $$.beg; + $$.beg = $1.beg; + } +| expr3 LQUES + { + $$.beg = ral(Talt); + $$.beg->u.alt = $1.beg; + $$.end = $1.end; + appendnext($$.end, $$.beg); + } + +expr4: + LCHAR + { + $$.beg = ral(Tclass); + $$.beg->u.x.lo = $1; + $$.beg->u.x.hi = $1; + $$.end = $$.beg; + } +| LBEGIN + { + $$.beg = ral(Tbegin); + $$.end = $$.beg; + } +| LEND + { + $$.beg = ral(Tend); + $$.end = $$.beg; + } +| LDOT + { + $$ = re2class("^\n"); + } +| LCLASS + { + $$ = re2class($1); + } +| LLPAREN expr1 LRPAREN + { + $$ = $2; + } + +newlines: + LNEWLINE +| newlines LNEWLINE +%% + +void +yyerror(char *e, ...) +{ + if(filename) + fprint(2, "grep: %s:%ld: %s\n", filename, lineno, e); + else + fprint(2, "grep: %s\n", e); + exits("syntax"); +} + +int +yylex(void) +{ + char *q, *eq; + int c, s; + + if(peekc) { + s = peekc; + peekc = 0; + return s; + } + c = getrec(); + if(literal) { + if(c != 0 && c != '\n') { + yylval.val = c; + return LCHAR; + } + literal = 0; + } + switch(c) { + default: + yylval.val = c; + s = LCHAR; + break; + case '\\': + c = getrec(); + yylval.val = c; + s = LCHAR; + if(c == '\n') + s = LNEWLINE; + break; + case '[': + goto getclass; + case '(': + s = LLPAREN; + break; + case ')': + s = LRPAREN; + break; + case '|': + s = LALT; + break; + case '*': + s = LSTAR; + break; + case '+': + s = LPLUS; + break; + case '?': + s = LQUES; + break; + case '^': + s = LBEGIN; + break; + case '$': + s = LEND; + break; + case '.': + s = LDOT; + break; + case 0: + peekc = -1; + case '\n': + s = LNEWLINE; + break; + } + return s; + +getclass: + q = u.string; + eq = q + nelem(u.string) - 5; + c = getrec(); + if(c == '^') { + q[0] = '^'; + q[1] = '\n'; + q[2] = '-'; + q[3] = '\n'; + q += 4; + c = getrec(); + } + for(;;) { + if(q >= eq) + error("class too long"); + if(c == ']' || c == 0) + break; + if(c == '\\') { + *q++ = c; + c = getrec(); + if(c == 0) + break; + } + *q++ = c; + c = getrec(); + } + *q = 0; + if(c == 0) + return LBAD; + yylval.str = u.string; + return LCLASS; +} diff --git a/grep/main.c b/grep/main.c @@ -0,0 +1,263 @@ +#define EXTERN +#include "grep.h" + +char *validflags = "bchiLlnsv"; +void +usage(void) +{ + fprint(2, "usage: grep [-%s] [-f file] [-e expr] [file ...]\n", validflags); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + int i, status; + + ARGBEGIN { + default: + if(utfrune(validflags, ARGC()) == nil) + usage(); + flags[ARGC()]++; + break; + + case 'E': /* ignore, turns gnu grep into egrep */ + break; + + case 'e': + flags['e']++; + lineno = 0; + str2top(ARGF()); + break; + + case 'f': + flags['f']++; + filename = ARGF(); + rein = Bopen(filename, OREAD); + if(rein == 0) { + fprint(2, "grep: can't open %s: %r\n", filename); + exits("open"); + } + lineno = 1; + str2top(filename); + break; + } ARGEND + + if(flags['f'] == 0 && flags['e'] == 0) { + if(argc <= 0) + usage(); + str2top(argv[0]); + argc--; + argv++; + } + + follow = mal(maxfollow*sizeof(*follow)); + state0 = initstate(topre.beg); + + Binit(&bout, 1, OWRITE); + switch(argc) { + case 0: + status = search(0, 0); + break; + case 1: + status = search(argv[0], 0); + break; + default: + status = 0; + for(i=0; i<argc; i++) + status |= search(argv[i], Hflag); + break; + } + if(status) + exits(0); + exits("no matches"); +} + +int +search(char *file, int flag) +{ + State *s, *ns; + int c, fid, eof, nl, empty; + long count, lineno, n; + uchar *elp, *lp, *bol; + + if(file == 0) { + file = "stdin"; + fid = 0; + flag |= Bflag; + } else + fid = open(file, OREAD); + + if(fid < 0) { + fprint(2, "grep: can't open %s: %r\n", file); + return 0; + } + + if(flags['b']) + flag ^= Bflag; /* dont buffer output */ + if(flags['c']) + flag |= Cflag; /* count */ + if(flags['h']) + flag &= ~Hflag; /* do not print file name in output */ + if(flags['i']) + flag |= Iflag; /* fold upper-lower */ + if(flags['l']) + flag |= Llflag; /* print only name of file if any match */ + if(flags['L']) + flag |= LLflag; /* print only name of file if any non match */ + if(flags['n']) + flag |= Nflag; /* count only */ + if(flags['s']) + flag |= Sflag; /* status only */ + if(flags['v']) + flag |= Vflag; /* inverse match */ + + s = state0; + lineno = 0; + count = 0; + eof = 0; + empty = 1; + nl = 0; + lp = u.u.buf; + bol = lp; + +loop0: + n = lp-bol; + if(n > sizeof(u.u.pre)) + n = sizeof(u.u.pre); + memmove(u.u.buf-n, bol, n); + bol = u.u.buf-n; + n = read(fid, u.u.buf, sizeof(u.u.buf)); + /* if file has no final newline, simulate one to emit matches to last line */ + if(n > 0) { + empty = 0; + nl = u.u.buf[n-1]=='\n'; + } else { + if(n < 0){ + fprint(2, "grep: read error on %s: %r\n", file); + return count != 0; + } + if(!eof && !nl && !empty) { + u.u.buf[0] = '\n'; + n = 1; + eof = 1; + } + } + if(n <= 0) { + close(fid); + if(flag & Cflag) { + if(flag & Hflag) + Bprint(&bout, "%s:", file); + Bprint(&bout, "%ld\n", count); + } + if(((flag&Llflag) && count != 0) || ((flag&LLflag) && count == 0)) + Bprint(&bout, "%s\n", file); + Bflush(&bout); + return count != 0; + } + lp = u.u.buf; + elp = lp+n; + if(flag & Iflag) + goto loopi; + +/* + * normal character loop + */ +loop: + c = *lp; + ns = s->next[c]; + if(ns == 0) { + increment(s, c); + goto loop; + } +// if(flags['2']) +// if(s->match) +// print("%d: %.2x**\n", s, c); +// else +// print("%d: %.2x\n", s, c); + lp++; + s = ns; + if(c == '\n') { + lineno++; + if(!!s->match == !(flag&Vflag)) { + count++; + if(flag & (Cflag|Sflag|Llflag|LLflag)) + goto cont; + if(flag & Hflag) + Bprint(&bout, "%s:", file); + if(flag & Nflag) + Bprint(&bout, "%ld: ", lineno); + /* suppress extra newline at EOF unless we are labeling matches with file name */ + Bwrite(&bout, bol, lp-bol-(eof && !(flag&Hflag))); + if(flag & Bflag) + Bflush(&bout); + } + if((lineno & Flshcnt) == 0) + Bflush(&bout); + cont: + bol = lp; + } + if(lp != elp) + goto loop; + goto loop0; + +/* + * character loop for -i flag + * for speed + */ +loopi: + c = *lp; + if(c >= 'A' && c <= 'Z') + c += 'a'-'A'; + ns = s->next[c]; + if(ns == 0) { + increment(s, c); + goto loopi; + } + lp++; + s = ns; + if(c == '\n') { + lineno++; + if(!!s->match == !(flag&Vflag)) { + count++; + if(flag & (Cflag|Sflag|Llflag|LLflag)) + goto conti; + if(flag & Hflag) + Bprint(&bout, "%s:", file); + if(flag & Nflag) + Bprint(&bout, "%ld: ", lineno); + /* suppress extra newline at EOF unless we are labeling matches with file name */ + Bwrite(&bout, bol, lp-bol-(eof && !(flag&Hflag))); + if(flag & Bflag) + Bflush(&bout); + } + if((lineno & Flshcnt) == 0) + Bflush(&bout); + conti: + bol = lp; + } + if(lp != elp) + goto loopi; + goto loop0; +} + +State* +initstate(Re *r) +{ + State *s; + int i; + + addcase(r); + if(flags['1']) + reprint("r", r); + nfollow = 0; + gen++; + fol1(r, Cbegin); + follow[nfollow++] = r; + qsort(follow, nfollow, sizeof(*follow), fcmp); + + s = sal(nfollow); + for(i=0; i<nfollow; i++) + s->re[i] = follow[i]; + return s; +} diff --git a/grep/sub.c b/grep/sub.c @@ -0,0 +1,317 @@ +#include "grep.h" + +void* +mal(int n) +{ + static char *s; + static int m = 0; + void *v; + + n = (n+3) & ~3; + if(m < n) { + if(n > Nhunk) { + v = sbrk(n); + memset(v, 0, n); + return v; + } + s = sbrk(Nhunk); + m = Nhunk; + } + v = s; + s += n; + m -= n; + memset(v, 0, n); + return v; +} + +State* +sal(int n) +{ + State *s; + + s = mal(sizeof(*s)); +// s->next = mal(256*sizeof(*s->next)); + s->count = n; + s->re = mal(n*sizeof(*state0->re)); + return s; +} + +Re* +ral(int type) +{ + Re *r; + + r = mal(sizeof(*r)); + r->type = type; + maxfollow++; + return r; +} + +void +error(char *s) +{ + fprint(2, "grep: internal error: %s\n", s); + exits(s); +} + +int +countor(Re *r) +{ + int n; + + n = 0; +loop: + switch(r->type) { + case Tor: + n += countor(r->u.alt); + r = r->next; + goto loop; + case Tclass: + return n + r->u.x.hi - r->u.x.lo + 1; + } + return n; +} + +Re* +oralloc(int t, Re *r, Re *b) +{ + Re *a; + + if(b == 0) + return r; + a = ral(t); + a->u.alt = r; + a->next = b; + return a; +} + +void +case1(Re *c, Re *r) +{ + int n; + +loop: + switch(r->type) { + case Tor: + case1(c, r->u.alt); + r = r->next; + goto loop; + + case Tclass: /* add to character */ + for(n=r->u.x.lo; n<=r->u.x.hi; n++) + c->u.cases[n] = oralloc(Tor, r->next, c->u.cases[n]); + break; + + default: /* add everything unknown to next */ + c->next = oralloc(Talt, r, c->next); + break; + } +} + +Re* +addcase(Re *r) +{ + int i, n; + Re *a; + + if(r->gen == gen) + return r; + r->gen = gen; + switch(r->type) { + default: + error("addcase"); + + case Tor: + n = countor(r); + if(n >= Caselim) { + a = ral(Tcase); + a->u.cases = mal(256*sizeof(*a->u.cases)); + case1(a, r); + for(i=0; i<256; i++) + if(a->u.cases[i]) { + r = a->u.cases[i]; + if(countor(r) < n) + a->u.cases[i] = addcase(r); + } + return a; + } + return r; + + case Talt: + r->next = addcase(r->next); + r->u.alt = addcase(r->u.alt); + return r; + + case Tbegin: + case Tend: + case Tclass: + return r; + } +} + +void +str2top(char *p) +{ + Re2 oldtop; + + oldtop = topre; + input = p; + topre.beg = 0; + topre.end = 0; + yyparse(); + gen++; + if(topre.beg == 0) + yyerror("syntax"); + if(oldtop.beg) + topre = re2or(oldtop, topre); +} + +void +appendnext(Re *a, Re *b) +{ + Re *n; + + while(n = a->next) + a = n; + a->next = b; +} + +void +patchnext(Re *a, Re *b) +{ + Re *n; + + while(a) { + n = a->next; + a->next = b; + a = n; + } +} + +int +getrec(void) +{ + int c; + + if(flags['f']) { + c = Bgetc(rein); + if(c <= 0) + return 0; + } else + c = *input++ & 0xff; + if(flags['i'] && c >= 'A' && c <= 'Z') + c += 'a'-'A'; + if(c == '\n') + lineno++; + return c; +} + +Re2 +re2cat(Re2 a, Re2 b) +{ + Re2 c; + + c.beg = a.beg; + c.end = b.end; + patchnext(a.end, b.beg); + return c; +} + +Re2 +re2star(Re2 a) +{ + Re2 c; + + c.beg = ral(Talt); + c.beg->u.alt = a.beg; + patchnext(a.end, c.beg); + c.end = c.beg; + return c; +} + +Re2 +re2or(Re2 a, Re2 b) +{ + Re2 c; + + c.beg = ral(Tor); + c.beg->u.alt = b.beg; + c.beg->next = a.beg; + c.end = b.end; + appendnext(c.end, a.end); + return c; +} + +Re2 +re2char(int c0, int c1) +{ + Re2 c; + + c.beg = ral(Tclass); + c.beg->u.x.lo = c0 & 0xff; + c.beg->u.x.hi = c1 & 0xff; + c.end = c.beg; + return c; +} + +void +reprint1(Re *a) +{ + int i, j; + +loop: + if(a == 0) + return; + if(a->gen == gen) + return; + a->gen = gen; + print("%p: ", a); + switch(a->type) { + default: + print("type %d\n", a->type); + error("print1 type"); + + case Tcase: + print("case ->%p\n", a->next); + for(i=0; i<256; i++) + if(a->u.cases[i]) { + for(j=i+1; j<256; j++) + if(a->u.cases[i] != a->u.cases[j]) + break; + print(" [%.2x-%.2x] ->%p\n", i, j-1, a->u.cases[i]); + i = j-1; + } + for(i=0; i<256; i++) + reprint1(a->u.cases[i]); + break; + + case Tbegin: + print("^ ->%p\n", a->next); + break; + + case Tend: + print("$ ->%p\n", a->next); + break; + + case Tclass: + print("[%.2x-%.2x] ->%p\n", a->u.x.lo, a->u.x.hi, a->next); + break; + + case Tor: + case Talt: + print("| %p ->%p\n", a->u.alt, a->next); + reprint1(a->u.alt); + break; + } + a = a->next; + goto loop; +} + +void +reprint(char *s, Re *r) +{ + print("%s:\n", s); + gen++; + reprint1(r); + print("\n\n"); +} diff --git a/lib9/LICENSE b/lib9/LICENSE @@ -0,0 +1,19 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. +*/ + +This is a Unix port of the Plan 9 formatted I/O package. + +Please send comments about the packaging +to Russ Cox <rsc@post.harvard.edu>. + diff --git a/lib9/Makefile b/lib9/Makefile @@ -0,0 +1,208 @@ +# lib9 - unix port from plan9 lib9 + +# this works in gnu make +SYSNAME:=${shell uname} +OBJTYPE:=${shell uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g'} + +# this works in bsd make +SYSNAME!=uname +OBJTYPE!=uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g' + +# the gnu rules will mess up bsd but not vice versa, +# hence the gnu rules come first. + +include ../config.mk + +LIB=lib9.a +TARG=lib9 + +# following objects are not compiled for several reasons +# crypt.o +# netcrypt.o +OFILES=\ + fmt/dofmt.o\ + fmt/fltfmt.o\ + fmt/fmt.o\ + fmt/fmtfd.o\ + fmt/fmtfdflush.o\ + fmt/fmtlock.o\ + fmt/fmtprint.o\ + fmt/fmtquote.o\ + fmt/fmtrune.o\ + fmt/fmtstr.o\ + fmt/fmtvprint.o\ + fmt/fprint.o\ + fmt/nan64.o\ + fmt/print.o\ + fmt/runefmtstr.o\ + fmt/runeseprint.o\ + fmt/runesmprint.o\ + fmt/runesnprint.o\ + fmt/runesprint.o\ + fmt/runevseprint.o\ + fmt/runevsmprint.o\ + fmt/runevsnprint.o\ + fmt/seprint.o\ + fmt/smprint.o\ + fmt/snprint.o\ + fmt/sprint.o\ + fmt/strtod.o\ + fmt/vfprint.o\ + fmt/vseprint.o\ + fmt/vsmprint.o\ + fmt/vsnprint.o\ + fmt/charstod.o\ + fmt/pow10.o\ + utf/rune.o\ + utf/runestrcat.o\ + utf/runestrchr.o\ + utf/runestrcmp.o\ + utf/runestrcpy.o\ + utf/runestrdup.o\ + utf/runestrlen.o\ + utf/runestrecpy.o\ + utf/runestrncat.o\ + utf/runestrncmp.o\ + utf/runestrncpy.o\ + utf/runestrrchr.o\ + utf/runestrstr.o\ + utf/runetype.o\ + utf/utfecpy.o\ + utf/utflen.o\ + utf/utfnlen.o\ + utf/utfrrune.o\ + utf/utfrune.o\ + utf/utfutf.o\ + bio/bbuffered.o\ + bio/bfildes.o\ + bio/bflush.o\ + bio/bgetc.o\ + bio/bgetd.o\ + bio/bgetrune.o\ + bio/binit.o\ + bio/boffset.o\ + bio/bprint.o\ + bio/bputc.o\ + bio/bputrune.o\ + bio/brdline.o\ + bio/brdstr.o\ + bio/bread.o\ + bio/bseek.o\ + bio/bvprint.o\ + bio/bwrite.o\ + regex/regcomp.o\ + regex/regerror.o\ + regex/regexec.o\ + regex/regsub.o\ + regex/regaux.o\ + regex/rregexec.o\ + regex/rregsub.o\ + _exits.o\ + _p9dialparse.o\ + _p9dir.o\ + announce.o\ + argv0.o\ + atexit.o\ + atoi.o\ + atol.o\ + atoll.o\ + atnotify.o\ + await.o\ + cistrcmp.o\ + cistrncmp.o\ + cistrstr.o\ + cleanname.o\ + convD2M.o\ + convM2D.o\ + convM2S.o\ + convS2M.o\ + create.o\ + ctime.o\ + date.o\ + dial.o\ + dirfstat.o\ + dirfwstat.o\ + dirmodefmt.o\ + dirread.o\ + dirstat.o\ + dirwstat.o\ + dup.o\ + encodefmt.o\ + errstr.o\ + exec.o\ + execl.o\ + fcallfmt.o\ + get9root.o\ + getcallerpc-$(OBJTYPE).o\ + getenv.o\ + getfields.o\ + getnetconn.o\ + getns.o\ + getuser.o\ + getwd.o\ + jmp.o\ + lrand.o\ + lnrand.o\ + main.o\ + malloc.o\ + malloctag.o\ + mallocz.o\ + nan.o\ + needsrcquote.o\ + needstack.o\ + netmkaddr.o\ + notify.o\ + nrand.o\ + nulldir.o\ + open.o\ + opentemp.o\ + pipe.o\ + post9p.o\ + postnote.o\ + qlock.o\ + quote.o\ + rand.o\ + read9pmsg.o\ + readcons.o\ + readn.o\ + rfork.o\ + searchpath.o\ + seek.o\ + sendfd.o\ + sleep.o\ + strdup.o\ + strecpy.o\ + sysfatal.o\ + syslog.o\ + sysname.o\ + time.o\ + tokenize.o\ + truerand.o\ + u16.o\ + u32.o\ + u64.o\ + unsharp.o\ + wait.o\ + waitpid.o\ + +all: ${LIB} + @echo built lib9 + +install: + @mkdir -p ${DESTDIR}${MANPREFIX}/man7 + @cp -f regexp.7 ${DESTDIR}${MANPREFIX}/man7 + @chmod 444 ${DESTDIR}${MANPREFIX}/man7/regexp.7 + +uninstall: + rm -f ${DESTDIR}${MANPREFIX}/man7/regexp.7 + +${LIB}: ${OFILES} + @echo AR ${TARG} + @${AR} ${LIB} ${OFILES} + +.c.o: + @echo CC $*.c + @${CC} -o $*.o ${CFLAGS} -I${PREFIX}/include $*.c + +clean: + rm -f ${OFILES} ${LIB} diff --git a/lib9/_exits.c b/lib9/_exits.c @@ -0,0 +1,10 @@ +#include <u.h> +#include <libc.h> + +void +_exits(char *s) +{ + if(s && *s) + _exit(1); + _exit(0); +} diff --git a/lib9/_p9dialparse.c b/lib9/_p9dialparse.c @@ -0,0 +1,185 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> + +#include <sys/types.h> +#include <netdb.h> +#include <sys/un.h> +#include <netinet/in.h> + +static char *nets[] = { "tcp", "udp", nil }; +#define CLASS(p) ((*(uchar*)(p))>>6) + +static struct { + char *net; + char *service; + int port; +} porttbl[] = { + "tcp", "9fs", 564, + "tcp", "whoami", 565, + "tcp", "guard", 566, + "tcp", "ticket", 567, + "tcp", "exportfs", 17007, + "tcp", "rexexec", 17009, + "tcp", "ncpu", 17010, + "tcp", "cpu", 17013, + "tcp", "venti", 17034, + "tcp", "wiki", 17035, + "tcp", "secstore", 5356, +}; + +static int +parseip(char *host, u32int *pip) +{ + uchar addr[4]; + int x, i; + char *p; + + p = host; + for(i=0; i<4 && *p; i++){ + x = strtoul(p, &p, 0); + if(x < 0 || x >= 256) + return -1; + if(*p != '.' && *p != 0) + return -1; + if(*p == '.') + p++; + addr[i] = x; + } + + switch(CLASS(addr)){ + case 0: + case 1: + if(i == 3){ + addr[3] = addr[2]; + addr[2] = addr[1]; + addr[1] = 0; + }else if(i == 2){ + addr[3] = addr[1]; + addr[2] = 0; + addr[1] = 0; + }else if(i != 4) + return -1; + break; + case 2: + if(i == 3){ + addr[3] = addr[2]; + addr[2] = 0; + }else if(i != 4) + return -1; + break; + } + *pip = *(u32int*)addr; + return 0; +} + +int +p9dialparse(char *addr, char **pnet, char **punix, u32int *phost, int *pport) +{ + char *net, *host, *port, *e; + int i; + struct servent *se; + struct hostent *he; + struct sockaddr_un *sockun; + + if(strncmp(addr, "/net/", 5) == 0) + addr += 5; + + *punix = nil; + net = addr; + if((host = strchr(net, '!')) == nil){ + werrstr("malformed address"); + return -1; + } + *host++ = 0; + if((port = strchr(host, '!')) == nil){ + if(strcmp(net, "unix")==0 || strcmp(net, "net")==0){ + Unix: + if(strlen(host)+1 > sizeof sockun->sun_path){ + werrstr("unix socket name too long"); + return -1; + } + *punix = host; + *pnet = "unix"; + *phost = 0; + *pport = 0; + return 0; + } + werrstr("malformed address"); + return -1; + } + *port++ = 0; + + if(*host == 0){ + werrstr("malformed address (empty host)"); + return -1; + } + if(*port == 0){ + werrstr("malformed address (empty port)"); + return -1; + } + + if(strcmp(net, "unix") == 0) + goto Unix; + + if(strcmp(net, "tcp")!=0 && strcmp(net, "udp")!=0 && strcmp(net, "net") != 0){ + werrstr("bad network %s!%s!%s", net, host, port); + return -1; + } + + /* translate host */ + if(strcmp(host, "*") == 0) + *phost = 0; + else if(parseip(host, phost) == 0) + {} + else if((he = gethostbyname(host)) != nil) + *phost = *(u32int*)(he->h_addr); + else{ + werrstr("unknown host %s", host); + return -1; + } + + /* translate network and port; should return list rather than first */ + if(strcmp(net, "net") == 0){ + for(i=0; nets[i]; i++){ + if((se = getservbyname(port, nets[i])) != nil){ + *pnet = nets[i]; + *pport = ntohs(se->s_port); + return 0; + } + } + } + + for(i=0; i<nelem(porttbl); i++){ + if(strcmp(net, "net") == 0 || strcmp(porttbl[i].net, net) == 0) + if(strcmp(porttbl[i].service, port) == 0){ + *pnet = porttbl[i].net; + *pport = porttbl[i].port; + return 0; + } + } + + if(strcmp(net, "net") == 0){ + werrstr("unknown service net!*!%s", port); + return -1; + } + + if(strcmp(net, "tcp") != 0 && strcmp(net, "udp") != 0){ + werrstr("unknown network %s", net); + return -1; + } + + *pnet = net; + i = strtol(port, &e, 0); + if(*e == 0){ + *pport = i; + return 0; + } + + if((se = getservbyname(port, net)) != nil){ + *pport = ntohs(se->s_port); + return 0; + } + werrstr("unknown service %s!*!%s", net, port); + return -1; +} diff --git a/lib9/_p9dir.c b/lib9/_p9dir.c @@ -0,0 +1,231 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <pwd.h> +#include <grp.h> + +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#include <sys/disklabel.h> +#include <sys/ioctl.h> +static int diskdev[] = { + 151, /* aacd */ + 116, /* ad */ + 157, /* ar */ + 118, /* afd */ + 133, /* amrd */ + 13, /* da */ + 102, /* fla */ + 109, /* idad */ + 95, /* md */ + 131, /* mlxd */ + 168, /* pst */ + 147, /* twed */ + 43, /* vn */ + 3, /* wd */ + 87, /* wfd */ +}; +static int +isdisk(struct stat *st) +{ + int i, dev; + + if(!S_ISCHR(st->st_mode)) + return 0; + dev = major(st->st_rdev); + for(i=0; i<nelem(diskdev); i++) + if(diskdev[i] == dev) + return 1; + return 0; +} +#define _HAVEDISKLABEL +#endif + +#if defined(__linux__) +#include <linux/hdreg.h> +#include <linux/fs.h> +#include <sys/ioctl.h> +#undef major +#define major(dev) ((int)(((dev) >> 8) & 0xff)) +static vlong +disksize(int fd, int dev) +{ + u64int u64; + long l; + struct hd_geometry geo; + + memset(&geo, 0, sizeof geo); + l = 0; + u64 = 0; +#ifdef BLKGETSIZE64 + if(ioctl(fd, BLKGETSIZE64, &u64) >= 0) + return u64; +#endif + if(ioctl(fd, BLKGETSIZE, &l) >= 0) + return (vlong)l*512; + if(ioctl(fd, HDIO_GETGEO, &geo) >= 0) + return (vlong)geo.heads*geo.sectors*geo.cylinders*512; + return 0; +} +#define _HAVEDISKSIZE +#endif + +#if !defined(__linux__) && !defined(__sun__) +#define _HAVESTGEN +#endif + +/* + * Caching the last group and passwd looked up is + * a significant win (stupidly enough) on most systems. + * It's not safe for threaded programs, but neither is using + * getpwnam in the first place, so I'm not too worried. + */ +int +_p9dir(struct stat *lst, struct stat *st, char *name, Dir *d, char **str, char *estr) +{ + char *s; + char tmp[20]; + static struct group *g; + static struct passwd *p; + static int gid, uid; + int sz, fd; + + fd = -1; + USED(fd); + sz = 0; + if(d) + memset(d, 0, sizeof *d); + + /* name */ + s = strrchr(name, '/'); + if(s) + s++; + if(!s || !*s) + s = name; + if(*s == '/') + s++; + if(*s == 0) + s = "/"; + if(d){ + if(*str + strlen(s)+1 > estr) + d->name = "oops"; + else{ + strcpy(*str, s); + d->name = *str; + *str += strlen(*str)+1; + } + } + sz += strlen(s)+1; + + /* user */ + if(p && st->st_uid == uid && p->pw_uid == uid) + ; + else{ + p = getpwuid(st->st_uid); + uid = st->st_uid; + } + if(p == nil){ + snprint(tmp, sizeof tmp, "%d", (int)st->st_uid); + s = tmp; + }else + s = p->pw_name; + sz += strlen(s)+1; + if(d){ + if(*str+strlen(s)+1 > estr) + d->uid = "oops"; + else{ + strcpy(*str, s); + d->uid = *str; + *str += strlen(*str)+1; + } + } + + /* group */ + if(g && st->st_gid == gid && g->gr_gid == gid) + ; + else{ + g = getgrgid(st->st_gid); + gid = st->st_gid; + } + if(g == nil){ + snprint(tmp, sizeof tmp, "%d", (int)st->st_gid); + s = tmp; + }else + s = g->gr_name; + sz += strlen(s)+1; + if(d){ + if(*str + strlen(s)+1 > estr) + d->gid = "oops"; + else{ + strcpy(*str, s); + d->gid = *str; + *str += strlen(*str)+1; + } + } + + if(d){ + d->type = 'M'; + + d->muid = ""; + d->qid.path = ((uvlong)st->st_dev<<32) | st->st_ino; +#ifdef _HAVESTGEN + d->qid.vers = st->st_gen; +#endif + d->mode = st->st_mode&0777; + d->atime = st->st_atime; + d->mtime = st->st_mtime; + d->length = st->st_size; + + if(S_ISDIR(st->st_mode)){ + d->length = 0; + d->mode |= DMDIR; + d->qid.type = QTDIR; + } + if(S_ISLNK(lst->st_mode)) /* yes, lst not st */ + d->mode |= DMSYMLINK; + if(S_ISFIFO(st->st_mode)) + d->mode |= DMNAMEDPIPE; + if(S_ISSOCK(st->st_mode)) + d->mode |= DMSOCKET; + if(S_ISBLK(st->st_mode)){ + d->mode |= DMDEVICE; + d->qid.path = ('b'<<16)|st->st_rdev; + } + if(S_ISCHR(st->st_mode)){ + d->mode |= DMDEVICE; + d->qid.path = ('c'<<16)|st->st_rdev; + } + /* fetch real size for disks */ +#ifdef _HAVEDISKSIZE + if(S_ISBLK(st->st_mode) && (fd = open(name, O_RDONLY)) >= 0){ + d->length = disksize(fd, major(st->st_dev)); + close(fd); + } +#endif +#ifdef _HAVEDISKLABEL + if(isdisk(st)){ + int fd, n; + struct disklabel lab; + + if((fd = open(name, O_RDONLY)) < 0) + goto nosize; + if(ioctl(fd, DIOCGDINFO, &lab) < 0) + goto nosize; + n = minor(st->st_rdev)&7; + if(n >= lab.d_npartitions) + goto nosize; + + d->length = (vlong)(lab.d_partitions[n].p_size) * lab.d_secsize; + + nosize: + if(fd >= 0) + close(fd); + } +#endif + } + + return sz; +} + diff --git a/lib9/_p9translate.c b/lib9/_p9translate.c @@ -0,0 +1,46 @@ +#include <u.h> +#include <libc.h> + +/* + * I don't want too many of these, + * but the ones we have are just too useful. + */ +static struct { + char *old; + char *new; +} replace[] = { + "#9", nil, /* must be first */ + "#d", "/dev/fd", +}; + +char* +plan9translate(char *old) +{ + char *new; + int i, olen, nlen, len; + + if(replace[0].new == nil){ + replace[0].new = getenv("PLAN9"); + if(replace[0].new == nil) + replace[0].new = "/usr/local/plan9"; + } + + for(i=0; i<nelem(replace); i++){ + if(!replace[i].new) + continue; + olen = strlen(replace[i].old); + if(strncmp(old, replace[i].old, olen) != 0 + || (old[olen] != '\0' && old[olen] != '/')) + continue; + nlen = strlen(replace[i].new); + len = strlen(old)+nlen-olen; + new = malloc(len+1); + if(new == nil) + return "<out of memory>"; + strcpy(new, replace[i].new); + strcpy(new+nlen, old+olen); + assert(strlen(new) == len); + return new; + } + return old; +} diff --git a/lib9/announce.c b/lib9/announce.c @@ -0,0 +1,152 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/un.h> +#include <errno.h> + +#undef sun +#define sun sockun + +int +_p9netfd(char *dir) +{ + int fd; + + if(strncmp(dir, "/dev/fd/", 8) != 0) + return -1; + fd = strtol(dir+8, &dir, 0); + if(*dir != 0) + return -1; + return fd; +} + +static void +putfd(char *dir, int fd) +{ + snprint(dir, NETPATHLEN, "/dev/fd/%d", fd); +} + +#undef unix +#define unix sockunix + +int +p9announce(char *addr, char *dir) +{ + int proto; + char *buf, *unix; + char *net; + u32int host; + int port, s; + int n; + socklen_t sn; + struct sockaddr_in sa; + struct sockaddr_un sun; + + buf = strdup(addr); + if(buf == nil) + return -1; + + if(p9dialparse(buf, &net, &unix, &host, &port) < 0){ + free(buf); + return -1; + } + if(strcmp(net, "tcp") == 0) + proto = SOCK_STREAM; + else if(strcmp(net, "udp") == 0) + proto = SOCK_DGRAM; + else if(strcmp(net, "unix") == 0) + goto Unix; + else{ + werrstr("can only handle tcp, udp, and unix: not %s", net); + free(buf); + return -1; + } + free(buf); + + memset(&sa, 0, sizeof sa); + memmove(&sa.sin_addr, &host, 4); + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + if((s = socket(AF_INET, proto, 0)) < 0) + return -1; + sn = sizeof n; + if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0 + && n == SOCK_STREAM){ + n = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n); + } + if(bind(s, (struct sockaddr*)&sa, sizeof sa) < 0){ + close(s); + return -1; + } + if(proto == SOCK_STREAM){ + listen(s, 8); + putfd(dir, s); + } + return s; + +Unix: + memset(&sun, 0, sizeof sun); + sun.sun_family = AF_UNIX; + strcpy(sun.sun_path, unix); + if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + return -1; + sn = sizeof sun; + if(bind(s, (struct sockaddr*)&sun, sizeof sun) < 0){ + if(errno == EADDRINUSE + && connect(s, (struct sockaddr*)&sun, sizeof sun) < 0 + && errno == ECONNREFUSED){ + /* dead socket, so remove it */ + remove(unix); + close(s); + if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + return -1; + if(bind(s, (struct sockaddr*)&sun, sizeof sun) >= 0) + goto Success; + } + close(s); + return -1; + } +Success: + listen(s, 8); + putfd(dir, s); + return s; +} + +int +p9listen(char *dir, char *newdir) +{ + int fd, one; + + if((fd = _p9netfd(dir)) < 0){ + werrstr("bad 'directory' in listen: %s", dir); + return -1; + } + + if((fd = accept(fd, nil, nil)) < 0) + return -1; + + one = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one); + + putfd(newdir, fd); + return fd; +} + +int +p9accept(int cfd, char *dir) +{ + int fd; + + if((fd = _p9netfd(dir)) < 0){ + werrstr("bad 'directory' in accept"); + return -1; + } + /* need to dup because the listen fd will be closed */ + return dup(fd); +} + diff --git a/lib9/argv0.c b/lib9/argv0.c @@ -0,0 +1,9 @@ +#include <lib9.h> + +char *argv0; + +/* + * Mac OS can't deal with files that only declare data. + * ARGBEGIN mentions this function so that this file gets pulled in. + */ +void __fixargv0(void) { } diff --git a/lib9/atexit.c b/lib9/atexit.c @@ -0,0 +1,54 @@ +#include <u.h> +#include <libc.h> + +#define NEXIT 33 + +static Lock onexlock; +static struct +{ + void (*f)(void); + int pid; +}onex[NEXIT]; + +int +atexit(void (*f)(void)) +{ + int i; + + lock(&onexlock); + for(i=0; i<NEXIT; i++) + if(onex[i].f == 0) { + onex[i].pid = getpid(); + onex[i].f = f; + unlock(&onexlock); + return 1; + } + unlock(&onexlock); + return 0; +} + +void +atexitdont(void (*f)(void)) +{ + int i, pid; + + pid = getpid(); + for(i=0; i<NEXIT; i++) + if(onex[i].f == f && onex[i].pid == pid) + onex[i].f = 0; +} + +void +exits(char *s) +{ + int i, pid; + void (*f)(void); + + pid = getpid(); + for(i = NEXIT-1; i >= 0; i--) + if((f = onex[i].f) && pid == onex[i].pid) { + onex[i].f = 0; + (*f)(); + } + exit(s && *s ? 1 : 0); +} diff --git a/lib9/atnotify.c b/lib9/atnotify.c @@ -0,0 +1,58 @@ +#include <u.h> +#include <libc.h> + +#define NFN 33 +static int (*onnot[NFN])(void*, char*); +static Lock onnotlock; + +static +void +notifier(void *v, char *s) +{ + int i; + + for(i=0; i<NFN; i++) + if(onnot[i] && ((*onnot[i])(v, s))){ + noted(NCONT); + return; + } + noted(NDFLT); +} + +int +atnotify(int (*f)(void*, char*), int in) +{ + int i, n, ret; + static int init; + + if(!init){ + notify(notifier); + init = 1; /* assign = */ + } + ret = 0; + lock(&onnotlock); + if(in){ + for(i=0; i<NFN; i++) + if(onnot[i] == 0) { + onnot[i] = f; + ret = 1; + break; + } + }else{ + n = 0; + for(i=0; i<NFN; i++) + if(onnot[i]){ + if(ret==0 && onnot[i]==f){ + onnot[i] = 0; + ret = 1; + }else + n++; + } + if(n == 0){ + init = 0; + notify(0); + } + } + unlock(&onnotlock); + return ret; +} diff --git a/lib9/atoi.c b/lib9/atoi.c @@ -0,0 +1,9 @@ +#include <u.h> +#include <libc.h> + +int +atoi(char *s) +{ + return strtol(s, 0, 0); +} + diff --git a/lib9/atol.c b/lib9/atol.c @@ -0,0 +1,9 @@ +#include <u.h> +#include <libc.h> + +long +atol(char *s) +{ + return strtol(s, 0, 0); +} + diff --git a/lib9/atoll.c b/lib9/atoll.c @@ -0,0 +1,9 @@ +#include <u.h> +#include <libc.h> + +vlong +atoll(char *s) +{ + return strtoll(s, 0, 0); +} + diff --git a/lib9/await.c b/lib9/await.c @@ -0,0 +1,137 @@ +#define NOPLAN9DEFINES +#include <u.h> +#include <libc.h> + +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/resource.h> + +#ifndef WCOREDUMP /* not on Mac OS X Tiger */ +#define WCOREDUMP(status) 0 +#endif + +static struct { + int sig; + char *str; +} tab[] = { + SIGHUP, "hangup", + SIGINT, "interrupt", + SIGQUIT, "quit", + SIGILL, "sys: illegal instruction", + SIGTRAP, "sys: breakpoint", + SIGABRT, "sys: abort", +#ifdef SIGEMT + SIGEMT, "sys: emulate instruction executed", +#endif + SIGFPE, "sys: fp: trap", + SIGKILL, "sys: kill", + SIGBUS, "sys: bus error", + SIGSEGV, "sys: segmentation violation", + SIGALRM, "alarm", + SIGTERM, "kill", + SIGURG, "sys: urgent condition on socket", + SIGSTOP, "sys: stop", + SIGTSTP, "sys: tstp", + SIGCONT, "sys: cont", + SIGCHLD, "sys: child", + SIGTTIN, "sys: ttin", + SIGTTOU, "sys: ttou", +#ifdef SIGIO /* not on Mac OS X Tiger */ + SIGIO, "sys: i/o possible on fd", +#endif + SIGXCPU, "sys: cpu time limit exceeded", + SIGXFSZ, "sys: file size limit exceeded", + SIGVTALRM, "sys: virtual time alarm", + SIGPROF, "sys: profiling timer alarm", +#ifdef SIGWINCH /* not on Mac OS X Tiger */ + SIGWINCH, "sys: window size change", +#endif +#ifdef SIGINFO + SIGINFO, "sys: status request", +#endif + SIGUSR1, "sys: usr1", + SIGUSR2, "sys: usr2", + SIGPIPE, "sys: write on closed pipe", +}; + +char* +_p9sigstr(int sig, char *tmp) +{ + int i; + + for(i=0; i<nelem(tab); i++) + if(tab[i].sig == sig) + return tab[i].str; + if(tmp == nil) + return nil; + sprint(tmp, "sys: signal %d", sig); + return tmp; +} + +int +_p9strsig(char *s) +{ + int i; + + for(i=0; i<nelem(tab); i++) + if(strcmp(s, tab[i].str) == 0) + return tab[i].sig; + return 0; +} + +static int +_await(int pid4, char *str, int n, int opt) +{ + int pid, status, cd; + struct rusage ru; + char buf[128], tmp[64]; + ulong u, s; + + for(;;){ + /* On Linux, pid==-1 means anyone; on SunOS, it's pid==0. */ + if(pid4 == -1) + pid = wait3(&status, opt, &ru); + else + pid = wait4(pid4, &status, opt, &ru); + if(pid <= 0) + return -1; + u = ru.ru_utime.tv_sec*1000+((ru.ru_utime.tv_usec+500)/1000); + s = ru.ru_stime.tv_sec*1000+((ru.ru_stime.tv_usec+500)/1000); + if(WIFEXITED(status)){ + status = WEXITSTATUS(status); + if(status) + snprint(buf, sizeof buf, "%d %lud %lud %lud %d", pid, u, s, u+s, status); + else + snprint(buf, sizeof buf, "%d %lud %lud %lud ''", pid, u, s, u+s, status); + strecpy(str, str+n, buf); + return strlen(str); + } + if(WIFSIGNALED(status)){ + cd = WCOREDUMP(status); + snprint(buf, sizeof buf, "%d %lud %lud %lud 'signal: %s%s'", pid, u, s, u+s, _p9sigstr(WTERMSIG(status), tmp), cd ? " (core dumped)" : ""); + strecpy(str, str+n, buf); + return strlen(str); + } + } +} + +int +await(char *str, int n) +{ + return _await(-1, str, n, 0); +} + +int +awaitnohang(char *str, int n) +{ + return _await(-1, str, n, WNOHANG); +} + +int +awaitfor(int pid, char *str, int n) +{ + return _await(pid, str, n, 0); +} + diff --git a/lib9/bio.h b/lib9/bio.h @@ -0,0 +1,106 @@ +#ifndef _BIO_H_ +#define _BIO_H_ 1 +#if defined(__cplusplus) +extern "C" { +#endif + +#ifdef AUTOLIB +AUTOLIB(bio) +#endif + +#include <sys/types.h> /* for off_t */ +#include <stdarg.h> +#include <fcntl.h> /* for O_RDONLY, O_WRONLY */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ +#define ODIRECT 128 /* or'ed in, direct access */ +#define ONONBLOCK 256 /* or'ed in, non-blocking call */ +#define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ +#define OLOCK 0x2000 /* or'ed in, lock after opening */ +#define OAPPEND 0x4000 /* or'ed in, append only */ + +typedef struct Biobuf Biobuf; + +enum +{ + Bsize = 8*1024, + Bungetsize = 4, /* space for ungetc */ + Bmagic = 0x314159, + Beof = -1, + Bbad = -2, + + Binactive = 0, /* states */ + Bractive, + Bwactive, + Bracteof, + + Bend +}; + +struct Biobuf +{ + int icount; /* neg num of bytes at eob */ + int ocount; /* num of bytes at bob */ + int rdline; /* num of bytes after rdline */ + int runesize; /* num of bytes of last getrune */ + int state; /* r/w/inactive */ + int fid; /* open file */ + int flag; /* magic if malloc'ed */ + off_t offset; /* offset of buffer in file */ + int bsize; /* size of buffer */ + unsigned char* bbuf; /* pointer to beginning of buffer */ + unsigned char* ebuf; /* pointer to end of buffer */ + unsigned char* gbuf; /* pointer to good data in buf */ + unsigned char b[Bungetsize+Bsize]; +}; + +#define BGETC(bp)\ + ((bp)->icount?(bp)->bbuf[(bp)->bsize+(bp)->icount++]:Bgetc((bp))) +#define BPUTC(bp,c)\ + ((bp)->ocount?(bp)->bbuf[(bp)->bsize+(bp)->ocount++]=(c),0:Bputc((bp),(c))) +#define BOFFSET(bp)\ + (((bp)->state==Bractive)?\ + (bp)->offset + (bp)->icount:\ + (((bp)->state==Bwactive)?\ + (bp)->offset + ((bp)->bsize + (bp)->ocount):\ + -1)) +#define BLINELEN(bp)\ + (bp)->rdline +#define BFILDES(bp)\ + (bp)->fid + +int Bbuffered(Biobuf*); +Biobuf* Bfdopen(int, int); +int Bfildes(Biobuf*); +int Bflush(Biobuf*); +int Bgetc(Biobuf*); +int Bgetd(Biobuf*, double*); +long Bgetrune(Biobuf*); +int Binit(Biobuf*, int, int); +int Binits(Biobuf*, int, int, unsigned char*, int); +int Blinelen(Biobuf*); +off_t Boffset(Biobuf*); +Biobuf* Bopen(char*, int); +int Bprint(Biobuf*, char*, ...); +int Bputc(Biobuf*, int); +int Bputrune(Biobuf*, long); +void* Brdline(Biobuf*, int); +char* Brdstr(Biobuf*, int, int); +long Bread(Biobuf*, void*, long); +off_t Bseek(Biobuf*, off_t, int); +int Bterm(Biobuf*); +int Bungetc(Biobuf*); +int Bungetrune(Biobuf*); +long Bwrite(Biobuf*, void*, long); +int Bvprint(Biobuf*, char*, va_list); + +#if defined(__cplusplus) +} +#endif +#endif diff --git a/lib9/bio/_lib9.h b/lib9/bio/_lib9.h @@ -0,0 +1,12 @@ +#include <fmt.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +#define OREAD O_RDONLY +#define OWRITE O_WRONLY + +#include <utf.h> + +#define nil ((void*)0) diff --git a/lib9/bio/bbuffered.c b/lib9/bio/bbuffered.c @@ -0,0 +1,21 @@ +#include "lib9.h" +#include <bio.h> +#include <fmt.h> + +int +Bbuffered(Biobuf *bp) +{ + switch(bp->state) { + case Bracteof: + case Bractive: + return -bp->icount; + + case Bwactive: + return bp->bsize + bp->ocount; + + case Binactive: + return 0; + } + fprint(2, "Bbuffered: unknown state %d\n", bp->state); + return 0; +} diff --git a/lib9/bio/bcat.c b/lib9/bio/bcat.c @@ -0,0 +1,46 @@ +#include <fmt.h> +#include "bio.h" + +Biobuf bout; + +void +bcat(Biobuf *b, char *name) +{ + char buf[1000]; + int n; + + while((n = Bread(b, buf, sizeof buf)) > 0){ + if(Bwrite(&bout, buf, n) < 0) + fprint(2, "writing during %s: %r\n", name); + } + if(n < 0) + fprint(2, "reading %s: %r\n", name); +} + +int +main(int argc, char **argv) +{ + int i; + Biobuf b, *bp; + Fmt fmt; + + Binit(&bout, 1, O_WRONLY); + Bfmtinit(&fmt, &bout); + fmtprint(&fmt, "hello, world\n"); + Bfmtflush(&fmt); + + if(argc == 1){ + Binit(&b, 0, O_RDONLY); + bcat(&b, "<stdin>"); + }else{ + for(i=1; i<argc; i++){ + if((bp = Bopen(argv[i], O_RDONLY)) == 0){ + fprint(2, "Bopen %s: %r\n", argv[i]); + continue; + } + bcat(bp, argv[i]); + Bterm(bp); + } + } + exit(0); +} diff --git a/lib9/bio/bfildes.c b/lib9/bio/bfildes.c @@ -0,0 +1,9 @@ +#include "lib9.h" +#include <bio.h> + +int +Bfildes(Biobuf *bp) +{ + + return bp->fid; +} diff --git a/lib9/bio/bflush.c b/lib9/bio/bflush.c @@ -0,0 +1,34 @@ +#include "lib9.h" +#include <bio.h> +#include <unistd.h> + +int +Bflush(Biobuf *bp) +{ + int n, c; + + switch(bp->state) { + case Bwactive: + n = bp->bsize+bp->ocount; + if(n == 0) + return 0; + c = write(bp->fid, bp->bbuf, n); + if(n == c) { + bp->offset += n; + bp->ocount = -bp->bsize; + return 0; + } + bp->state = Binactive; + bp->ocount = 0; + break; + + case Bracteof: + bp->state = Bractive; + + case Bractive: + bp->icount = 0; + bp->gbuf = bp->ebuf; + return 0; + } + return Beof; +} diff --git a/lib9/bio/bgetc.c b/lib9/bio/bgetc.c @@ -0,0 +1,54 @@ +#include "lib9.h" +#include <bio.h> +#include <unistd.h> + +int +Bgetc(Biobuf *bp) +{ + int i; + +loop: + i = bp->icount; + if(i != 0) { + bp->icount = i+1; + return bp->ebuf[i]; + } + if(bp->state != Bractive) { + if(bp->state == Bracteof) + bp->state = Bractive; + return Beof; + } + /* + * get next buffer, try to keep Bungetsize + * characters pre-catenated from the previous + * buffer to allow that many ungets. + */ + memmove(bp->bbuf-Bungetsize, bp->ebuf-Bungetsize, Bungetsize); + i = read(bp->fid, bp->bbuf, bp->bsize); + bp->gbuf = bp->bbuf; + if(i <= 0) { + bp->state = Bracteof; + if(i < 0) + bp->state = Binactive; + return Beof; + } + if(i < bp->bsize) { + memmove(bp->ebuf-i-Bungetsize, bp->bbuf-Bungetsize, i+Bungetsize); + bp->gbuf = bp->ebuf-i; + } + bp->icount = -i; + bp->offset += i; + goto loop; +} + +int +Bungetc(Biobuf *bp) +{ + + if(bp->state == Bracteof) + bp->state = Bractive; + if(bp->state != Bractive) + return Beof; + bp->icount--; + return 1; +} diff --git a/lib9/bio/bgetd.c b/lib9/bio/bgetd.c @@ -0,0 +1,37 @@ +#include "lib9.h" +#include <bio.h> +#include <fmt.h> + +struct bgetd +{ + Biobuf* b; + int eof; +}; + +static int +Bgetdf(void *vp) +{ + int c; + struct bgetd *bg = vp; + + c = Bgetc(bg->b); + if(c == Beof) + bg->eof = 1; + return c; +} + +int +Bgetd(Biobuf *bp, double *dp) +{ + double d; + struct bgetd b; + + b.b = bp; + b.eof = 0; + d = fmtcharstod(Bgetdf, &b); + if(b.eof) + return -1; + Bungetc(bp); + *dp = d; + return 1; +} diff --git a/lib9/bio/bgetrune.c b/lib9/bio/bgetrune.c @@ -0,0 +1,47 @@ +#include "lib9.h" +#include <bio.h> +#include <utf.h> + +long +Bgetrune(Biobuf *bp) +{ + int c, i; + Rune rune; + char str[4]; + + c = Bgetc(bp); + if(c < Runeself) { /* one char */ + bp->runesize = 1; + return c; + } + str[0] = c; + + for(i=1;;) { + c = Bgetc(bp); + if(c < 0) + return c; + str[i++] = c; + + if(fullrune(str, i)) { + bp->runesize = chartorune(&rune, str); + while(i > bp->runesize) { + Bungetc(bp); + i--; + } + return rune; + } + } +} + +int +Bungetrune(Biobuf *bp) +{ + + if(bp->state == Bracteof) + bp->state = Bractive; + if(bp->state != Bractive) + return Beof; + bp->icount -= bp->runesize; + bp->runesize = 0; + return 1; +} diff --git a/lib9/bio/binit.c b/lib9/bio/binit.c @@ -0,0 +1,156 @@ +#include "lib9.h" +#include <bio.h> +#include <fmt.h> +#include <stdlib.h> +#include <unistd.h> + +enum +{ + MAXBUFS = 20 +}; + +static Biobuf* wbufs[MAXBUFS]; +static int atexitflag; + +static +void +batexit(void) +{ + Biobuf *bp; + int i; + + for(i=0; i<MAXBUFS; i++) { + bp = wbufs[i]; + if(bp != 0) { + wbufs[i] = 0; + Bflush(bp); + } + } +} + +static +void +deinstall(Biobuf *bp) +{ + int i; + + for(i=0; i<MAXBUFS; i++) + if(wbufs[i] == bp) + wbufs[i] = 0; +} + +static +void +install(Biobuf *bp) +{ + int i; + + deinstall(bp); + for(i=0; i<MAXBUFS; i++) + if(wbufs[i] == 0) { + wbufs[i] = bp; + break; + } + if(atexitflag == 0) { + atexitflag = 1; + atexit(batexit); + } +} + +int +Binits(Biobuf *bp, int f, int mode, unsigned char *p, int size) +{ + + p += Bungetsize; /* make room for Bungets */ + size -= Bungetsize; + + switch(mode&~(OCEXEC|ORCLOSE|OTRUNC)) { + default: + fprint(2, "Bopen: unknown mode %d\n", mode); + return Beof; + + case OREAD: + bp->state = Bractive; + bp->ocount = 0; + break; + + case OWRITE: + install(bp); + bp->state = Bwactive; + bp->ocount = -size; + break; + } + bp->bbuf = p; + bp->ebuf = p+size; + bp->bsize = size; + bp->icount = 0; + bp->gbuf = bp->ebuf; + bp->fid = f; + bp->flag = 0; + bp->rdline = 0; + bp->offset = 0; + bp->runesize = 0; + return 0; +} + + +int +Binit(Biobuf *bp, int f, int mode) +{ + return Binits(bp, f, mode, bp->b, sizeof(bp->b)); +} + +Biobuf* +Bfdopen(int f, int mode) +{ + Biobuf *bp; + + bp = malloc(sizeof(Biobuf)); + if(bp == 0) + return 0; + Binits(bp, f, mode, bp->b, sizeof(bp->b)); + bp->flag = Bmagic; + return bp; +} + +Biobuf* +Bopen(char *name, int mode) +{ + Biobuf *bp; + int f; + + switch(mode&~(OCEXEC|ORCLOSE|OTRUNC)) { + default: + fprint(2, "Bopen: unknown mode %d\n", mode); + return 0; + + case OREAD: + f = open(name, OREAD); + if(f < 0) + return 0; + break; + + case OWRITE: + f = creat(name, 0666); + if(f < 0) + return 0; + } + bp = Bfdopen(f, mode); + if(bp == 0) + close(f); + return bp; +} + +int +Bterm(Biobuf *bp) +{ + + deinstall(bp); + Bflush(bp); + if(bp->flag == Bmagic) { + bp->flag = 0; + close(bp->fid); + free(bp); + } + return 0; +} diff --git a/lib9/bio/boffset.c b/lib9/bio/boffset.c @@ -0,0 +1,26 @@ +#include "lib9.h" +#include <bio.h> +#include <fmt.h> + +off_t +Boffset(Biobuf *bp) +{ + off_t n; + + switch(bp->state) { + default: + fprint(2, "Boffset: unknown state %d\n", bp->state); + n = Beof; + break; + + case Bracteof: + case Bractive: + n = bp->offset + bp->icount; + break; + + case Bwactive: + n = bp->offset + (bp->bsize + bp->ocount); + break; + } + return n; +} diff --git a/lib9/bio/bprint.c b/lib9/bio/bprint.c @@ -0,0 +1,14 @@ +#include "lib9.h" +#include <bio.h> + +int +Bprint(Biobuf *bp, char *fmt, ...) +{ + int n; + va_list arg; + + va_start(arg, fmt); + n = Bvprint(bp, fmt, arg); + va_end(arg); + return n; +} diff --git a/lib9/bio/bputc.c b/lib9/bio/bputc.c @@ -0,0 +1,20 @@ +#include "lib9.h" +#include <bio.h> + +int +Bputc(Biobuf *bp, int c) +{ + int i; + + for(;;) { + i = bp->ocount; + if(i) { + bp->ebuf[i++] = c; + bp->ocount = i; + return 0; + } + if(Bflush(bp) == Beof) + break; + } + return Beof; +} diff --git a/lib9/bio/bputrune.c b/lib9/bio/bputrune.c @@ -0,0 +1,23 @@ +#include "lib9.h" +#include <bio.h> +#include <utf.h> + +int +Bputrune(Biobuf *bp, long c) +{ + Rune rune; + char str[4]; + int n; + + rune = c; + if(rune < Runeself) { + Bputc(bp, rune); + return 1; + } + n = runetochar(str, &rune); + if(n == 0) + return Bbad; + if(Bwrite(bp, str, n) != n) + return Beof; + return n; +} diff --git a/lib9/bio/brdline.c b/lib9/bio/brdline.c @@ -0,0 +1,95 @@ +#include "lib9.h" +#include <bio.h> +#include <unistd.h> + +void* +Brdline(Biobuf *bp, int delim) +{ + char *ip, *ep; + int i, j; + + i = -bp->icount; + if(i == 0) { + /* + * eof or other error + */ + if(bp->state != Bractive) { + if(bp->state == Bracteof) + bp->state = Bractive; + bp->rdline = 0; + bp->gbuf = bp->ebuf; + return 0; + } + } + + /* + * first try in remainder of buffer (gbuf doesn't change) + */ + ip = (char*)bp->ebuf - i; + ep = memchr(ip, delim, i); + if(ep) { + j = (ep - ip) + 1; + bp->rdline = j; + bp->icount += j; + return ip; + } + + /* + * copy data to beginning of buffer + */ + if(i < bp->bsize) + memmove(bp->bbuf, ip, i); + bp->gbuf = bp->bbuf; + + /* + * append to buffer looking for the delim + */ + ip = (char*)bp->bbuf + i; + while(i < bp->bsize) { + j = read(bp->fid, ip, bp->bsize-i); + if(j <= 0) { + /* + * end of file with no delim + */ + memmove(bp->ebuf-i, bp->bbuf, i); + bp->rdline = i; + bp->icount = -i; + bp->gbuf = bp->ebuf-i; + return 0; + } + bp->offset += j; + i += j; + ep = memchr(ip, delim, j); + if(ep) { + /* + * found in new piece + * copy back up and reset everything + */ + ip = (char*)bp->ebuf - i; + if(i < bp->bsize){ + memmove(ip, bp->bbuf, i); + bp->gbuf = (unsigned char*)ip; + } + j = (ep - (char*)bp->bbuf) + 1; + bp->rdline = j; + bp->icount = j - i; + return ip; + } + ip += j; + } + + /* + * full buffer without finding + */ + bp->rdline = bp->bsize; + bp->icount = -bp->bsize; + bp->gbuf = bp->bbuf; + return 0; +} + +int +Blinelen(Biobuf *bp) +{ + + return bp->rdline; +} diff --git a/lib9/bio/brdstr.c b/lib9/bio/brdstr.c @@ -0,0 +1,113 @@ +#include "lib9.h" +#include <bio.h> +#include <stdlib.h> +#include <unistd.h> + +static char* +badd(char *p, int *np, char *data, int ndata, int delim, int nulldelim) +{ + int n; + + n = *np; + p = realloc(p, n+ndata+1); + if(p){ + memmove(p+n, data, ndata); + n += ndata; + if(n>0 && nulldelim && p[n-1]==delim) + p[--n] = '\0'; + else + p[n] = '\0'; + *np = n; + } + return p; +} + +char* +Brdstr(Biobuf *bp, int delim, int nulldelim) +{ + char *ip, *ep, *p; + int i, j; + + i = -bp->icount; + bp->rdline = 0; + if(i == 0) { + /* + * eof or other error + */ + if(bp->state != Bractive) { + if(bp->state == Bracteof) + bp->state = Bractive; + bp->gbuf = bp->ebuf; + return nil; + } + } + + /* + * first try in remainder of buffer (gbuf doesn't change) + */ + ip = (char*)bp->ebuf - i; + ep = memchr(ip, delim, i); + if(ep) { + j = (ep - ip) + 1; + bp->icount += j; + return badd(nil, &bp->rdline, ip, j, delim, nulldelim); + } + + /* + * copy data to beginning of buffer + */ + if(i < bp->bsize) + memmove(bp->bbuf, ip, i); + bp->gbuf = bp->bbuf; + + /* + * append to buffer looking for the delim + */ + p = nil; + for(;;){ + ip = (char*)bp->bbuf + i; + while(i < bp->bsize) { + j = read(bp->fid, ip, bp->bsize-i); + if(j <= 0 && i == 0) + return p; + if(j <= 0 && i > 0){ + /* + * end of file but no delim. pretend we got a delim + * by making the delim \0 and smashing it with nulldelim. + */ + j = 1; + ep = ip; + delim = '\0'; + nulldelim = 1; + *ep = delim; /* there will be room for this */ + }else{ + bp->offset += j; + ep = memchr(ip, delim, j); + } + i += j; + if(ep) { + /* + * found in new piece + * copy back up and reset everything + */ + ip = (char*)bp->ebuf - i; + if(i < bp->bsize){ + memmove(ip, bp->bbuf, i); + bp->gbuf = (unsigned char*)ip; + } + j = (ep - (char*)bp->bbuf) + 1; + bp->icount = j - i; + return badd(p, &bp->rdline, ip, j, delim, nulldelim); + } + ip += j; + } + + /* + * full buffer without finding; add to user string and continue + */ + p = badd(p, &bp->rdline, (char*)bp->bbuf, bp->bsize, 0, 0); + i = 0; + bp->icount = 0; + bp->gbuf = bp->ebuf; + } +} diff --git a/lib9/bio/bread.c b/lib9/bio/bread.c @@ -0,0 +1,46 @@ +#include "lib9.h" +#include <bio.h> +#include <unistd.h> + +long +Bread(Biobuf *bp, void *ap, long count) +{ + long c; + unsigned char *p; + int i, n, ic; + + p = ap; + c = count; + ic = bp->icount; + + while(c > 0) { + n = -ic; + if(n > c) + n = c; + if(n == 0) { + if(bp->state != Bractive) + break; + i = read(bp->fid, bp->bbuf, bp->bsize); + if(i <= 0) { + bp->state = Bracteof; + if(i < 0) + bp->state = Binactive; + break; + } + bp->gbuf = bp->bbuf; + bp->offset += i; + if(i < bp->bsize) { + memmove(bp->ebuf-i, bp->bbuf, i); + bp->gbuf = bp->ebuf-i; + } + ic = -i; + continue; + } + memmove(p, bp->ebuf+ic, n); + c -= n; + ic += n; + p += n; + } + bp->icount = ic; + return count-c; +} diff --git a/lib9/bio/bseek.c b/lib9/bio/bseek.c @@ -0,0 +1,63 @@ +#include "lib9.h" +#include <bio.h> +#include <fmt.h> +#include <sys/types.h> +#include <unistd.h> + +off_t +Bseek(Biobuf *bp, off_t offset, int base) +{ + long long n, d; + int bufsz; + + switch(bp->state) { + default: + fprint(2, "Bseek: unknown state %d\n", bp->state); + return Beof; + + case Bracteof: + bp->state = Bractive; + bp->icount = 0; + bp->gbuf = bp->ebuf; + + case Bractive: + n = offset; + if(base == 1) { + n += Boffset(bp); + base = 0; + } + + /* + * try to seek within buffer + */ + if(base == 0) { + d = n - Boffset(bp); + bufsz = bp->ebuf - bp->gbuf; + if(-bufsz <= d && d <= bufsz){ + bp->icount += d; + if(d >= 0) { + if(bp->icount <= 0) + return n; + } else { + if(bp->ebuf - bp->gbuf >= -bp->icount) + return n; + } + } + } + + /* + * reset the buffer + */ + n = lseek(bp->fid, n, base); + bp->icount = 0; + bp->gbuf = bp->ebuf; + break; + + case Bwactive: + Bflush(bp); + n = lseek(bp->fid, offset, base); + break; + } + bp->offset = n; + return n; +} diff --git a/lib9/bio/bvprint.c b/lib9/bio/bvprint.c @@ -0,0 +1,39 @@ +#include "lib9.h" +#include <bio.h> +#include <fmt.h> + +static int +fmtBflush(Fmt *f) +{ + Biobuf *bp; + + bp = f->farg; + bp->ocount = (char*)f->to - (char*)f->stop; + if(Bflush(bp) < 0) + return 0; + f->stop = bp->ebuf; + f->to = (char*)f->stop + bp->ocount; + f->start = f->to; + return 1; +} + +int +Bvprint(Biobuf *bp, char *fmt, va_list arg) +{ + int n; + Fmt f; + + f.runes = 0; + f.stop = bp->ebuf; + f.start = (char*)f.stop + bp->ocount; + f.to = f.start; + f.flush = fmtBflush; + f.farg = bp; + f.nfmt = 0; + n = fmtvprint(&f, fmt, arg); + bp->ocount = (char*)f.to - (char*)f.stop; + return n; +} + + + diff --git a/lib9/bio/bwrite.c b/lib9/bio/bwrite.c @@ -0,0 +1,39 @@ +#include "lib9.h" +#include <bio.h> +#include <unistd.h> + +long +Bwrite(Biobuf *bp, void *ap, long count) +{ + long c; + unsigned char *p; + int i, n, oc; + + p = ap; + c = count; + oc = bp->ocount; + + while(c > 0) { + n = -oc; + if(n > c) + n = c; + if(n == 0) { + if(bp->state != Bwactive) + return Beof; + i = write(bp->fid, bp->bbuf, bp->bsize); + if(i != bp->bsize) { + bp->state = Binactive; + return Beof; + } + bp->offset += i; + oc = -bp->bsize; + continue; + } + memmove(bp->ebuf+oc, p, n); + oc += n; + c -= n; + p += n; + } + bp->ocount = oc; + return count-c; +} diff --git a/lib9/bio/lib9.std.h b/lib9/bio/lib9.std.h @@ -0,0 +1,20 @@ +#include <utf.h> +#include <fmt.h> + +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +#define OREAD O_RDONLY +#define OWRITE O_WRONLY + +#define OCEXEC 0 +#define ORCLOSE 0 +#define OTRUNC 0 + + +#define nil ((void*)0) + +typedef long long vlong; +typedef unsigned long long uvlong; diff --git a/lib9/cistrcmp.c b/lib9/cistrcmp.c @@ -0,0 +1,26 @@ +#include <u.h> +#include <libc.h> + +int +cistrcmp(char *s1, char *s2) +{ + int c1, c2; + + while(*s1){ + c1 = *(uchar*)s1++; + c2 = *(uchar*)s2++; + + if(c1 == c2) + continue; + + if(c1 >= 'A' && c1 <= 'Z') + c1 -= 'A' - 'a'; + + if(c2 >= 'A' && c2 <= 'Z') + c2 -= 'A' - 'a'; + + if(c1 != c2) + return c1 - c2; + } + return -*s2; +} diff --git a/lib9/cistrncmp.c b/lib9/cistrncmp.c @@ -0,0 +1,28 @@ +#include <u.h> +#include <libc.h> + +int +cistrncmp(char *s1, char *s2, int n) +{ + int c1, c2; + + while(*s1 && n-- > 0){ + c1 = *(uchar*)s1++; + c2 = *(uchar*)s2++; + + if(c1 == c2) + continue; + + if(c1 >= 'A' && c1 <= 'Z') + c1 -= 'A' - 'a'; + + if(c2 >= 'A' && c2 <= 'Z') + c2 -= 'A' - 'a'; + + if(c1 != c2) + return c1 - c2; + } + if(n <= 0) + return 0; + return -*s2; +} diff --git a/lib9/cistrstr.c b/lib9/cistrstr.c @@ -0,0 +1,23 @@ +#include <u.h> +#include <libc.h> + +char* +cistrstr(char *s, char *sub) +{ + int c, csub, n; + + csub = *sub; + if(csub == '\0') + return s; + if(csub >= 'A' && csub <= 'Z') + csub -= 'A' - 'a'; + sub++; + n = strlen(sub); + for(; c = *s; s++){ + if(c >= 'A' && c <= 'Z') + c -= 'A' - 'a'; + if(c == csub && cistrncmp(s+1, sub, n) == 0) + return s; + } + return nil; +} diff --git a/lib9/cleanname.c b/lib9/cleanname.c @@ -0,0 +1,52 @@ +#include <u.h> +#include <libc.h> + +/* + * In place, rewrite name to compress multiple /, eliminate ., and process .. + */ +#define SEP(x) ((x)=='/' || (x) == 0) +char* +cleanname(char *name) +{ + char *p, *q, *dotdot; + int rooted; + + rooted = name[0] == '/'; + + /* + * invariants: + * p points at beginning of path element we're considering. + * q points just past the last path element we wrote (no slash). + * dotdot points just past the point where .. cannot backtrack + * any further (no slash). + */ + p = q = dotdot = name+rooted; + while(*p) { + if(p[0] == '/') /* null element */ + p++; + else if(p[0] == '.' && SEP(p[1])) + p += 1; /* don't count the separator in case it is nul */ + else if(p[0] == '.' && p[1] == '.' && SEP(p[2])) { + p += 2; + if(q > dotdot) { /* can backtrack */ + while(--q > dotdot && *q != '/') + ; + } else if(!rooted) { /* /.. is / but ./../ is .. */ + if(q != name) + *q++ = '/'; + *q++ = '.'; + *q++ = '.'; + dotdot = q; + } + } else { /* real path element */ + if(q != name+rooted) + *q++ = '/'; + while((*q = *p) != '/' && *q != 0) + p++, q++; + } + } + if(q == name) /* empty string is really ``.'' */ + *q++ = '.'; + *q = '\0'; + return name; +} diff --git a/lib9/convD2M.c b/lib9/convD2M.c @@ -0,0 +1,95 @@ +#include <u.h> +#include <libc.h> +#include <fcall.h> + +uint +sizeD2M(Dir *d) +{ + char *sv[4]; + int i, ns; + + sv[0] = d->name; + sv[1] = d->uid; + sv[2] = d->gid; + sv[3] = d->muid; + + ns = 0; + for(i = 0; i < 4; i++) + if(sv[i]) + ns += strlen(sv[i]); + + return STATFIXLEN + ns; +} + +uint +convD2M(Dir *d, uchar *buf, uint nbuf) +{ + uchar *p, *ebuf; + char *sv[4]; + int i, ns, nsv[4], ss; + + if(nbuf < BIT16SZ) + return 0; + + p = buf; + ebuf = buf + nbuf; + + sv[0] = d->name; + sv[1] = d->uid; + sv[2] = d->gid; + sv[3] = d->muid; + + ns = 0; + for(i = 0; i < 4; i++){ + if(sv[i]) + nsv[i] = strlen(sv[i]); + else + nsv[i] = 0; + ns += nsv[i]; + } + + ss = STATFIXLEN + ns; + + /* set size befor erroring, so user can know how much is needed */ + /* note that length excludes count field itself */ + PBIT16(p, ss-BIT16SZ); + p += BIT16SZ; + + if(ss > nbuf) + return BIT16SZ; + + PBIT16(p, d->type); + p += BIT16SZ; + PBIT32(p, d->dev); + p += BIT32SZ; + PBIT8(p, d->qid.type); + p += BIT8SZ; + PBIT32(p, d->qid.vers); + p += BIT32SZ; + PBIT64(p, d->qid.path); + p += BIT64SZ; + PBIT32(p, d->mode); + p += BIT32SZ; + PBIT32(p, d->atime); + p += BIT32SZ; + PBIT32(p, d->mtime); + p += BIT32SZ; + PBIT64(p, d->length); + p += BIT64SZ; + + for(i = 0; i < 4; i++){ + ns = nsv[i]; + if(p + ns + BIT16SZ > ebuf) + return 0; + PBIT16(p, ns); + p += BIT16SZ; + if(ns) + memmove(p, sv[i], ns); + p += ns; + } + + if(ss != p - buf) + return 0; + + return p - buf; +} diff --git a/lib9/convM2D.c b/lib9/convM2D.c @@ -0,0 +1,94 @@ +#include <u.h> +#include <libc.h> +#include <fcall.h> + +int +statcheck(uchar *buf, uint nbuf) +{ + uchar *ebuf; + int i; + + ebuf = buf + nbuf; + + if(nbuf < STATFIXLEN || nbuf != BIT16SZ + GBIT16(buf)) + return -1; + + buf += STATFIXLEN - 4 * BIT16SZ; + + for(i = 0; i < 4; i++){ + if(buf + BIT16SZ > ebuf) + return -1; + buf += BIT16SZ + GBIT16(buf); + } + + if(buf != ebuf) + return -1; + + return 0; +} + +static char nullstring[] = ""; + +uint +convM2D(uchar *buf, uint nbuf, Dir *d, char *strs) +{ + uchar *p, *ebuf; + char *sv[4]; + int i, ns; + + if(nbuf < STATFIXLEN) + return 0; + + p = buf; + ebuf = buf + nbuf; + + p += BIT16SZ; /* ignore size */ + d->type = GBIT16(p); + p += BIT16SZ; + d->dev = GBIT32(p); + p += BIT32SZ; + d->qid.type = GBIT8(p); + p += BIT8SZ; + d->qid.vers = GBIT32(p); + p += BIT32SZ; + d->qid.path = GBIT64(p); + p += BIT64SZ; + d->mode = GBIT32(p); + p += BIT32SZ; + d->atime = GBIT32(p); + p += BIT32SZ; + d->mtime = GBIT32(p); + p += BIT32SZ; + d->length = GBIT64(p); + p += BIT64SZ; + + for(i = 0; i < 4; i++){ + if(p + BIT16SZ > ebuf) + return 0; + ns = GBIT16(p); + p += BIT16SZ; + if(p + ns > ebuf) + return 0; + if(strs){ + sv[i] = strs; + memmove(strs, p, ns); + strs += ns; + *strs++ = '\0'; + } + p += ns; + } + + if(strs){ + d->name = sv[0]; + d->uid = sv[1]; + d->gid = sv[2]; + d->muid = sv[3]; + }else{ + d->name = nullstring; + d->uid = nullstring; + d->gid = nullstring; + d->muid = nullstring; + } + + return p - buf; +} diff --git a/lib9/convM2S.c b/lib9/convM2S.c @@ -0,0 +1,323 @@ +#include <u.h> +#include <libc.h> +#include <fcall.h> + +static +uchar* +gstring(uchar *p, uchar *ep, char **s) +{ + uint n; + + if(p+BIT16SZ > ep) + return nil; + n = GBIT16(p); + p += BIT16SZ - 1; + if(p+n+1 > ep) + return nil; + /* move it down, on top of count, to make room for '\0' */ + memmove(p, p + 1, n); + p[n] = '\0'; + *s = (char*)p; + p += n+1; + return p; +} + +static +uchar* +gqid(uchar *p, uchar *ep, Qid *q) +{ + if(p+QIDSZ > ep) + return nil; + q->type = GBIT8(p); + p += BIT8SZ; + q->vers = GBIT32(p); + p += BIT32SZ; + q->path = GBIT64(p); + p += BIT64SZ; + return p; +} + +/* + * no syntactic checks. + * three causes for error: + * 1. message size field is incorrect + * 2. input buffer too short for its own data (counts too long, etc.) + * 3. too many names or qids + * gqid() and gstring() return nil if they would reach beyond buffer. + * main switch statement checks range and also can fall through + * to test at end of routine. + */ +uint +convM2S(uchar *ap, uint nap, Fcall *f) +{ + uchar *p, *ep; + uint i, size; + + p = ap; + ep = p + nap; + + if(p+BIT32SZ+BIT8SZ+BIT16SZ > ep) + return 0; + size = GBIT32(p); + p += BIT32SZ; + + if(size < BIT32SZ+BIT8SZ+BIT16SZ) + return 0; + + f->type = GBIT8(p); + p += BIT8SZ; + f->tag = GBIT16(p); + p += BIT16SZ; + + switch(f->type) + { + default: + return 0; + + case Tversion: + if(p+BIT32SZ > ep) + return 0; + f->msize = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->version); + break; + + case Tflush: + if(p+BIT16SZ > ep) + return 0; + f->oldtag = GBIT16(p); + p += BIT16SZ; + break; + + case Tauth: + if(p+BIT32SZ > ep) + return 0; + f->afid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->uname); + if(p == nil) + break; + p = gstring(p, ep, &f->aname); + if(p == nil) + break; + break; + + case Tattach: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + if(p+BIT32SZ > ep) + return 0; + f->afid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->uname); + if(p == nil) + break; + p = gstring(p, ep, &f->aname); + if(p == nil) + break; + break; + + case Twalk: + if(p+BIT32SZ+BIT32SZ+BIT16SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->newfid = GBIT32(p); + p += BIT32SZ; + f->nwname = GBIT16(p); + p += BIT16SZ; + if(f->nwname > MAXWELEM) + return 0; + for(i=0; i<f->nwname; i++){ + p = gstring(p, ep, &f->wname[i]); + if(p == nil) + break; + } + break; + + case Topen: + case Topenfd: + if(p+BIT32SZ+BIT8SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + + case Tcreate: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->name); + if(p == nil) + break; + if(p+BIT32SZ+BIT8SZ > ep) + return 0; + f->perm = GBIT32(p); + p += BIT32SZ; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + + case Tread: + if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + break; + + case Twrite: + if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + if(p+f->count > ep) + return 0; + f->data = (char*)p; + p += f->count; + break; + + case Tclunk: + case Tremove: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + + case Tstat: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + + case Twstat: + if(p+BIT32SZ+BIT16SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->nstat = GBIT16(p); + p += BIT16SZ; + if(p+f->nstat > ep) + return 0; + f->stat = p; + p += f->nstat; + break; + +/* + */ + case Rversion: + if(p+BIT32SZ > ep) + return 0; + f->msize = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->version); + break; + + case Rerror: + p = gstring(p, ep, &f->ename); + break; + + case Rflush: + break; + + case Rauth: + p = gqid(p, ep, &f->aqid); + if(p == nil) + break; + break; + + case Rattach: + p = gqid(p, ep, &f->qid); + if(p == nil) + break; + break; + + case Rwalk: + if(p+BIT16SZ > ep) + return 0; + f->nwqid = GBIT16(p); + p += BIT16SZ; + if(f->nwqid > MAXWELEM) + return 0; + for(i=0; i<f->nwqid; i++){ + p = gqid(p, ep, &f->wqid[i]); + if(p == nil) + break; + } + break; + + case Ropen: + case Ropenfd: + case Rcreate: + p = gqid(p, ep, &f->qid); + if(p == nil) + break; + if(p+BIT32SZ > ep) + return 0; + f->iounit = GBIT32(p); + p += BIT32SZ; + if(f->type == Ropenfd){ + if(p+BIT32SZ > ep) + return 0; + f->unixfd = GBIT32(p); + p += BIT32SZ; + } + break; + + case Rread: + if(p+BIT32SZ > ep) + return 0; + f->count = GBIT32(p); + p += BIT32SZ; + if(p+f->count > ep) + return 0; + f->data = (char*)p; + p += f->count; + break; + + case Rwrite: + if(p+BIT32SZ > ep) + return 0; + f->count = GBIT32(p); + p += BIT32SZ; + break; + + case Rclunk: + case Rremove: + break; + + case Rstat: + if(p+BIT16SZ > ep) + return 0; + f->nstat = GBIT16(p); + p += BIT16SZ; + if(p+f->nstat > ep) + return 0; + f->stat = p; + p += f->nstat; + break; + + case Rwstat: + break; + } + + if(p==nil || p>ep) + return 0; + if(ap+size == p) + return size; + return 0; +} diff --git a/lib9/convS2M.c b/lib9/convS2M.c @@ -0,0 +1,399 @@ +#include <u.h> +#include <libc.h> +#include <fcall.h> + +static +uchar* +pstring(uchar *p, char *s) +{ + uint n; + + if(s == nil){ + PBIT16(p, 0); + p += BIT16SZ; + return p; + } + + n = strlen(s); + PBIT16(p, n); + p += BIT16SZ; + memmove(p, s, n); + p += n; + return p; +} + +static +uchar* +pqid(uchar *p, Qid *q) +{ + PBIT8(p, q->type); + p += BIT8SZ; + PBIT32(p, q->vers); + p += BIT32SZ; + PBIT64(p, q->path); + p += BIT64SZ; + return p; +} + +static +uint +stringsz(char *s) +{ + if(s == nil) + return BIT16SZ; + + return BIT16SZ+strlen(s); +} + +uint +sizeS2M(Fcall *f) +{ + uint n; + int i; + + n = 0; + n += BIT32SZ; /* size */ + n += BIT8SZ; /* type */ + n += BIT16SZ; /* tag */ + + switch(f->type) + { + default: + return 0; + + case Tversion: + n += BIT32SZ; + n += stringsz(f->version); + break; + + case Tflush: + n += BIT16SZ; + break; + + case Tauth: + n += BIT32SZ; + n += stringsz(f->uname); + n += stringsz(f->aname); + break; + + case Tattach: + n += BIT32SZ; + n += BIT32SZ; + n += stringsz(f->uname); + n += stringsz(f->aname); + break; + + case Twalk: + n += BIT32SZ; + n += BIT32SZ; + n += BIT16SZ; + for(i=0; i<f->nwname; i++) + n += stringsz(f->wname[i]); + break; + + case Topen: + case Topenfd: + n += BIT32SZ; + n += BIT8SZ; + break; + + case Tcreate: + n += BIT32SZ; + n += stringsz(f->name); + n += BIT32SZ; + n += BIT8SZ; + break; + + case Tread: + n += BIT32SZ; + n += BIT64SZ; + n += BIT32SZ; + break; + + case Twrite: + n += BIT32SZ; + n += BIT64SZ; + n += BIT32SZ; + n += f->count; + break; + + case Tclunk: + case Tremove: + n += BIT32SZ; + break; + + case Tstat: + n += BIT32SZ; + break; + + case Twstat: + n += BIT32SZ; + n += BIT16SZ; + n += f->nstat; + break; +/* + */ + + case Rversion: + n += BIT32SZ; + n += stringsz(f->version); + break; + + case Rerror: + n += stringsz(f->ename); + break; + + case Rflush: + break; + + case Rauth: + n += QIDSZ; + break; + + case Rattach: + n += QIDSZ; + break; + + case Rwalk: + n += BIT16SZ; + n += f->nwqid*QIDSZ; + break; + + case Ropen: + case Rcreate: + n += QIDSZ; + n += BIT32SZ; + break; + + case Ropenfd: + n += QIDSZ; + n += BIT32SZ; + n += BIT32SZ; + break; + + case Rread: + n += BIT32SZ; + n += f->count; + break; + + case Rwrite: + n += BIT32SZ; + break; + + case Rclunk: + break; + + case Rremove: + break; + + case Rstat: + n += BIT16SZ; + n += f->nstat; + break; + + case Rwstat: + break; + } + return n; +} + +uint +convS2M(Fcall *f, uchar *ap, uint nap) +{ + uchar *p; + uint i, size; + + size = sizeS2M(f); + if(size == 0) + return 0; + if(size > nap) + return 0; + + p = (uchar*)ap; + + PBIT32(p, size); + p += BIT32SZ; + PBIT8(p, f->type); + p += BIT8SZ; + PBIT16(p, f->tag); + p += BIT16SZ; + + switch(f->type) + { + default: + return 0; + + case Tversion: + PBIT32(p, f->msize); + p += BIT32SZ; + p = pstring(p, f->version); + break; + + case Tflush: + PBIT16(p, f->oldtag); + p += BIT16SZ; + break; + + case Tauth: + PBIT32(p, f->afid); + p += BIT32SZ; + p = pstring(p, f->uname); + p = pstring(p, f->aname); + break; + + case Tattach: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT32(p, f->afid); + p += BIT32SZ; + p = pstring(p, f->uname); + p = pstring(p, f->aname); + break; + + case Twalk: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT32(p, f->newfid); + p += BIT32SZ; + PBIT16(p, f->nwname); + p += BIT16SZ; + if(f->nwname > MAXWELEM) + return 0; + for(i=0; i<f->nwname; i++) + p = pstring(p, f->wname[i]); + break; + + case Topen: + case Topenfd: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT8(p, f->mode); + p += BIT8SZ; + break; + + case Tcreate: + PBIT32(p, f->fid); + p += BIT32SZ; + p = pstring(p, f->name); + PBIT32(p, f->perm); + p += BIT32SZ; + PBIT8(p, f->mode); + p += BIT8SZ; + break; + + case Tread: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT32(p, f->count); + p += BIT32SZ; + break; + + case Twrite: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT32(p, f->count); + p += BIT32SZ; + memmove(p, f->data, f->count); + p += f->count; + break; + + case Tclunk: + case Tremove: + PBIT32(p, f->fid); + p += BIT32SZ; + break; + + case Tstat: + PBIT32(p, f->fid); + p += BIT32SZ; + break; + + case Twstat: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT16(p, f->nstat); + p += BIT16SZ; + memmove(p, f->stat, f->nstat); + p += f->nstat; + break; +/* + */ + + case Rversion: + PBIT32(p, f->msize); + p += BIT32SZ; + p = pstring(p, f->version); + break; + + case Rerror: + p = pstring(p, f->ename); + break; + + case Rflush: + break; + + case Rauth: + p = pqid(p, &f->aqid); + break; + + case Rattach: + p = pqid(p, &f->qid); + break; + + case Rwalk: + PBIT16(p, f->nwqid); + p += BIT16SZ; + if(f->nwqid > MAXWELEM) + return 0; + for(i=0; i<f->nwqid; i++) + p = pqid(p, &f->wqid[i]); + break; + + case Ropen: + case Rcreate: + case Ropenfd: + p = pqid(p, &f->qid); + PBIT32(p, f->iounit); + p += BIT32SZ; + if(f->type == Ropenfd){ + PBIT32(p, f->unixfd); + p += BIT32SZ; + } + break; + + case Rread: + PBIT32(p, f->count); + p += BIT32SZ; + memmove(p, f->data, f->count); + p += f->count; + break; + + case Rwrite: + PBIT32(p, f->count); + p += BIT32SZ; + break; + + case Rclunk: + break; + + case Rremove: + break; + + case Rstat: + PBIT16(p, f->nstat); + p += BIT16SZ; + memmove(p, f->stat, f->nstat); + p += f->nstat; + break; + + case Rwstat: + break; + } + if(size != p-ap) + return 0; + return size; +} diff --git a/lib9/create.c b/lib9/create.c @@ -0,0 +1,75 @@ +#define _GNU_SOURCE /* for Linux O_DIRECT */ +#include <u.h> +#define NOPLAN9DEFINES +#include <sys/file.h> +#include <unistd.h> +#include <fcntl.h> +#include <libc.h> +#include <sys/stat.h> +#ifndef O_DIRECT +#define O_DIRECT 0 +#endif + +int +p9create(char *path, int mode, ulong perm) +{ + int fd, cexec, umode, rclose, lock, rdwr; + struct flock fl; + + rdwr = mode&3; + lock = mode&OLOCK; + cexec = mode&OCEXEC; + rclose = mode&ORCLOSE; + mode &= ~(ORCLOSE|OCEXEC|OLOCK); + + /* XXX should get mode mask right? */ + fd = -1; + if(perm&DMDIR){ + if(mode != OREAD){ + werrstr("bad mode in directory create"); + goto out; + } + if(mkdir(path, perm&0777) < 0) + goto out; + fd = open(path, O_RDONLY); + }else{ + umode = (mode&3)|O_CREAT|O_TRUNC; + mode &= ~(3|OTRUNC); + if(mode&ODIRECT){ + umode |= O_DIRECT; + mode &= ~ODIRECT; + } + if(mode&OEXCL){ + umode |= O_EXCL; + mode &= ~OEXCL; + } + if(mode&OAPPEND){ + umode |= O_APPEND; + mode &= ~OAPPEND; + } + if(mode){ + werrstr("unsupported mode in create"); + goto out; + } + fd = open(path, umode, perm); + } +out: + if(fd >= 0){ + if(lock){ + fl.l_type = (rdwr==OREAD) ? F_RDLCK : F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + if(fcntl(fd, F_SETLK, &fl) < 0){ + close(fd); + werrstr("lock: %r"); + return -1; + } + } + if(cexec) + fcntl(fd, F_SETFL, FD_CLOEXEC); + if(rclose) + remove(path); + } + return fd; +} diff --git a/lib9/ctime.c b/lib9/ctime.c @@ -0,0 +1,58 @@ +#include <u.h> +#include <libc.h> + +static +void +ct_numb(char *cp, int n) +{ + + cp[0] = ' '; + if(n >= 10) + cp[0] = (n/10)%10 + '0'; + cp[1] = n%10 + '0'; +} + +char* +asctime(Tm *t) +{ + int i; + char *ncp; + static char cbuf[30]; + + strcpy(cbuf, "Thu Jan 01 00:00:00 GMT 1970\n"); + ncp = &"SunMonTueWedThuFriSat"[t->wday*3]; + cbuf[0] = *ncp++; + cbuf[1] = *ncp++; + cbuf[2] = *ncp; + ncp = &"JanFebMarAprMayJunJulAugSepOctNovDec"[t->mon*3]; + cbuf[4] = *ncp++; + cbuf[5] = *ncp++; + cbuf[6] = *ncp; + ct_numb(cbuf+8, t->mday); + ct_numb(cbuf+11, t->hour+100); + ct_numb(cbuf+14, t->min+100); + ct_numb(cbuf+17, t->sec+100); + ncp = t->zone; + for(i=0; i<3; i++) + if(ncp[i] == 0) + break; + for(; i<3; i++) + ncp[i] = '?'; + ncp = t->zone; + cbuf[20] = *ncp++; + cbuf[21] = *ncp++; + cbuf[22] = *ncp; + if(t->year >= 100) { + cbuf[24] = '2'; + cbuf[25] = '0'; + } + ct_numb(cbuf+26, t->year+100); + return cbuf; +} + +char* +ctime(long t) +{ + return asctime(localtime(t)); +} + diff --git a/lib9/date.c b/lib9/date.c @@ -0,0 +1,125 @@ +#include <u.h> +#include <stdlib.h> /* setenv etc. */ +#define NOPLAN9DEFINES +#include <libc.h> +#include <time.h> + +#define _HAVETIMEGM 1 +#define _HAVETMZONE 1 +#define _HAVETMTZOFF 1 + +#if defined(__linux__) +# undef _HAVETMZONE +# undef _HAVETMTZOFF + +#elif defined(__sun__) +# undef _HAVETIMEGM +# undef _HAVETMZONE +# undef _HAVETMTZOFF + +#endif + +static Tm bigtm; + +static void +tm2Tm(struct tm *tm, Tm *bigtm) +{ + char *s; + + memset(bigtm, 0, sizeof *bigtm); + bigtm->sec = tm->tm_sec; + bigtm->min = tm->tm_min; + bigtm->hour = tm->tm_hour; + bigtm->mday = tm->tm_mday; + bigtm->mon = tm->tm_mon; + bigtm->year = tm->tm_year; + bigtm->wday = tm->tm_wday; + strftime(bigtm->zone, sizeof bigtm->zone, "%Z", tm); +#ifdef _HAVETZOFF + bigtm->tzoff = tm->tm_gmtoff; +#endif + + if(bigtm->zone[0] == 0){ + s = getenv("TIMEZONE"); + if(s){ + strecpy(bigtm->zone, bigtm->zone+4, s); + free(s); + } + } +} + +static void +Tm2tm(Tm *bigtm, struct tm *tm) +{ + memset(tm, 0, sizeof *tm); + tm->tm_sec = bigtm->sec; + tm->tm_min = bigtm->min; + tm->tm_hour = bigtm->hour; + tm->tm_mday = bigtm->mday; + tm->tm_mon = bigtm->mon; + tm->tm_year = bigtm->year; + tm->tm_wday = bigtm->wday; +#ifdef _HAVETMZONE + tm->tm_zone = bigtm->zone; +#endif +#ifdef _HAVETZOFF + tm->tm_gmtoff = bigtm->tzoff; +#endif +} + +Tm* +p9gmtime(long x) +{ + time_t t; + struct tm tm; + + t = (time_t)x; + tm = *gmtime(&t); + tm2Tm(&tm, &bigtm); + return &bigtm; +} + +Tm* +p9localtime(long x) +{ + time_t t; + struct tm tm; + + t = (time_t)x; + tm = *localtime(&t); + tm2Tm(&tm, &bigtm); + return &bigtm; +} + +#if !defined(_HAVETIMEGM) +static time_t +timegm(struct tm *tm) +{ + time_t ret; + char *tz; + char *s; + + tz = getenv("TZ"); + putenv("TZ="); + tzset(); + ret = mktime(tm); + if(tz){ + s = smprint("TZ=%s", tz); + if(s) + putenv(s); + } + return ret; +} +#endif + +long +p9tm2sec(Tm *bigtm) +{ + struct tm tm; + + Tm2tm(bigtm, &tm); + if(strcmp(bigtm->zone, "GMT") == 0 || strcmp(bigtm->zone, "UCT") == 0) + return timegm(&tm); + return mktime(&tm); /* local time zone */ +} + diff --git a/lib9/debugmalloc.c b/lib9/debugmalloc.c @@ -0,0 +1,166 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> + +/* + * The Unix libc routines cannot be trusted to do their own locking. + * Sad but apparently true. + */ +static Lock malloclock; +static int mallocpid; + +/* + * The Unix mallocs don't do nearly enough error checking + * for my tastes. We'll waste another 24 bytes per guy so that + * we can. This is severely antisocial, since now free and p9free + * are not interchangeable. + */ +int debugmalloc; + +#define Overhead (debugmalloc ? (6*sizeof(ulong)) : 0) +#define MallocMagic 0xA110C09 +#define ReallocMagic 0xB110C09 +#define CallocMagic 0xC110C09 +#define FreeMagic 0xF533F533 +#define CheckMagic 0 +#define END "\x7F\x2E\x55\x23" + +static void +whoops(void *v) +{ + fprint(2, "bad malloc block %p\n", v); + abort(); +} + +static void* +mark(void *v, ulong pc, ulong n, ulong magic) +{ + ulong *u; + char *p; + + if(!debugmalloc) + return v; + + if(v == nil) + return nil; + + if(magic == FreeMagic || magic == CheckMagic){ + u = (ulong*)((char*)v-4*sizeof(ulong)); + if(u[0] != MallocMagic && u[0] != ReallocMagic && u[0] != CallocMagic) + whoops(v); + n = u[1]; + p = (char*)v+n; + if(memcmp(p, END, 4) != 0) + whoops(v); + if(magic != CheckMagic){ + u[0] = FreeMagic; + u[1] = u[2] = u[3] = pc; + if(n > 16){ + u[4] = u[5] = u[6] = u[7] = pc; + memset((char*)v+16, 0xFB, n-16); + } + } + return u; + }else{ + u = v; + u[0] = magic; + u[1] = n; + u[2] = 0; + u[3] = 0; + if(magic == ReallocMagic) + u[3] = pc; + else + u[2] = pc; + p = (char*)(u+4)+n; + memmove(p, END, 4); + return u+4; + } +} + +void +setmalloctag(void *v, ulong t) +{ + ulong *u; + + if(!debugmalloc) + return; + + if(v == nil) + return; + u = mark(v, 0, 0, 0); + u[2] = t; +} + +void +setrealloctag(void *v, ulong t) +{ + ulong *u; + + if(!debugmalloc) + return; + + if(v == nil) + return; + u = mark(v, 0, 0, 0); + u[3] = t; +} + +void* +p9malloc(ulong n) +{ + void *v; + if(n == 0) + n++; +//fprint(2, "%s %d malloc\n", argv0, getpid()); + lock(&malloclock); + mallocpid = getpid(); + v = malloc(n+Overhead); + v = mark(v, getcallerpc(&n), n, MallocMagic); + unlock(&malloclock); +//fprint(2, "%s %d donemalloc\n", argv0, getpid()); + return v; +} + +void +p9free(void *v) +{ + if(v == nil) + return; + +//fprint(2, "%s %d free\n", argv0, getpid()); + lock(&malloclock); + mallocpid = getpid(); + v = mark(v, getcallerpc(&v), 0, FreeMagic); + free(v); + unlock(&malloclock); +//fprint(2, "%s %d donefree\n", argv0, getpid()); +} + +void* +p9calloc(ulong a, ulong b) +{ + void *v; + +//fprint(2, "%s %d calloc\n", argv0, getpid()); + lock(&malloclock); + mallocpid = getpid(); + v = calloc(a*b+Overhead, 1); + v = mark(v, getcallerpc(&a), a*b, CallocMagic); + unlock(&malloclock); +//fprint(2, "%s %d donecalloc\n", argv0, getpid()); + return v; +} + +void* +p9realloc(void *v, ulong n) +{ +//fprint(2, "%s %d realloc\n", argv0, getpid()); + lock(&malloclock); + mallocpid = getpid(); + v = mark(v, getcallerpc(&v), 0, CheckMagic); + v = realloc(v, n+Overhead); + v = mark(v, getcallerpc(&v), n, ReallocMagic); + unlock(&malloclock); +//fprint(2, "%s %d donerealloc\n", argv0, getpid()); + return v; +} diff --git a/lib9/dial.c b/lib9/dial.c @@ -0,0 +1,137 @@ +#include <u.h> +#include <libc.h> + +#undef accept +#undef announce +#undef dial +#undef setnetmtpt +#undef hangup +#undef listen +#undef netmkaddr +#undef reject + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/un.h> +#include <netdb.h> + +#undef unix +#define unix xunix + +int +p9dial(char *addr, char *local, char *dummy2, int *dummy3) +{ + char *buf; + char *net, *unix; + u32int host; + int port; + int proto; + socklen_t sn; + int n; + struct sockaddr_in sa, sal; + struct sockaddr_un su; + int s; + + if(dummy2 || dummy3){ + werrstr("cannot handle extra arguments in dial"); + return -1; + } + + buf = strdup(addr); + if(buf == nil) + return -1; + + if(p9dialparse(buf, &net, &unix, &host, &port) < 0){ + free(buf); + return -1; + } + + if(strcmp(net, "tcp") == 0) + proto = SOCK_STREAM; + else if(strcmp(net, "udp") == 0) + proto = SOCK_DGRAM; + else if(strcmp(net, "unix") == 0) + goto Unix; + else{ + werrstr("can only handle tcp, udp, and unix: not %s", net); + free(buf); + return -1; + } + free(buf); + + memset(&sa, 0, sizeof sa); + memmove(&sa.sin_addr, &host, 4); + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + if((s = socket(AF_INET, proto, 0)) < 0) + return -1; + + if(local){ + buf = strdup(local); + if(buf == nil){ + close(s); + return -1; + } + if(p9dialparse(buf, &net, &unix, &host, &port) < 0){ + badlocal: + free(buf); + close(s); + return -1; + } + if(unix){ + werrstr("bad local address %s for dial %s", local, addr); + goto badlocal; + } + memset(&sal, 0, sizeof sal); + memmove(&sal.sin_addr, &local, 4); + sal.sin_family = AF_INET; + sal.sin_port = htons(port); + sn = sizeof n; + if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0 + && n == SOCK_STREAM){ + n = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n); + } + if(bind(s, (struct sockaddr*)&sal, sizeof sal) < 0) + goto badlocal; + free(buf); + } + + if(connect(s, (struct sockaddr*)&sa, sizeof sa) < 0){ + close(s); + return -1; + } + if(proto == SOCK_STREAM){ + int one = 1; + setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one); + } + return s; + +Unix: + if(local){ + werrstr("local address not supported on unix network"); + free(buf); + return -1; + } + memset(&su, 0, sizeof su); + su.sun_family = AF_UNIX; + if(strlen(unix)+1 > sizeof su.sun_path){ + werrstr("unix socket name too long"); + free(buf); + return -1; + } + strcpy(su.sun_path, unix); + free(buf); + if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){ + werrstr("socket: %r"); + return -1; + } + if(connect(s, (struct sockaddr*)&su, sizeof su) < 0){ + werrstr("connect %s: %r", su.sun_path); + close(s); + return -1; + } + return s; +} + diff --git a/lib9/dirfstat.c b/lib9/dirfstat.c @@ -0,0 +1,29 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> + +#include <sys/stat.h> + +extern int _p9dir(struct stat*, struct stat*, char*, Dir*, char**, char*); + +Dir* +dirfstat(int fd) +{ + struct stat st; + int nstr; + Dir *d; + char *str, tmp[100]; + + if(fstat(fd, &st) < 0) + return nil; + + snprint(tmp, sizeof tmp, "/dev/fd/%d", fd); + nstr = _p9dir(&st, &st, tmp, nil, nil, nil); + d = mallocz(sizeof(Dir)+nstr, 1); + if(d == nil) + return nil; + str = (char*)&d[1]; + _p9dir(&st, &st, tmp, d, &str, str+nstr); + return d; +} + diff --git a/lib9/dirfwstat.c b/lib9/dirfwstat.c @@ -0,0 +1,53 @@ +#define NOPLAN9DEFINES +#include <u.h> +#include <libc.h> +#include <sys/time.h> +#include <sys/stat.h> + +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) +/* do nothing -- futimes exists and is fine */ + +#elif defined(__SunOS5_9__) +/* use futimesat */ +static int +futimes(int fd, struct timeval *tv) +{ + return futimesat(fd, 0, tv); +} + +#else +/* provide dummy */ +/* rename just in case -- linux provides an unusable one */ +#undef futimes +#define futimes myfutimes +static int +futimes(int fd, struct timeval *tv) +{ + werrstr("futimes not available"); + return -1; +} + +#endif + +int +dirfwstat(int fd, Dir *dir) +{ + int ret; + struct timeval tv[2]; + + ret = 0; + if(~dir->mode != 0){ + if(fchmod(fd, dir->mode) < 0) + ret = -1; + } + if(~dir->mtime != 0){ + tv[0].tv_sec = dir->mtime; + tv[0].tv_usec = 0; + tv[1].tv_sec = dir->mtime; + tv[1].tv_usec = 0; + if(futimes(fd, tv) < 0) + ret = -1; + } + return ret; +} + diff --git a/lib9/dirmodefmt.c b/lib9/dirmodefmt.c @@ -0,0 +1,62 @@ +#include <u.h> +#include <libc.h> + +static char *modes[] = +{ + "---", + "--x", + "-w-", + "-wx", + "r--", + "r-x", + "rw-", + "rwx", +}; + +static void +rwx(long m, char *s) +{ + strncpy(s, modes[m], 3); +} + +int +dirmodefmt(Fmt *f) +{ + static char buf[16]; + ulong m; + + m = va_arg(f->args, ulong); + + if(m & DMDIR) + buf[0]='d'; + else if(m & DMAPPEND) + buf[0]='a'; + else if(m & DMAUTH) + buf[0]='A'; + else if(m & DMDEVICE) + buf[0] = 'D'; + else if(m & DMSOCKET) + buf[0] = 'S'; + else if(m & DMNAMEDPIPE) + buf[0] = 'P'; + else + buf[0]='-'; + + /* + * It's a little weird to have DMSYMLINK conflict with DMEXCL + * here, but since you can have symlinks to any of the above + * things, this is a better display. Especially since we don't do + * DMEXCL on any of the supported systems. + */ + if(m & DMEXCL) + buf[1]='l'; + else if(m & DMSYMLINK) + buf[1] = 'L'; + else + buf[1]='-'; + rwx((m>>6)&7, buf+2); + rwx((m>>3)&7, buf+5); + rwx((m>>0)&7, buf+8); + buf[11] = 0; + return fmtstrcpy(f, buf); +} diff --git a/lib9/dirread.c b/lib9/dirread.c @@ -0,0 +1,188 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> +#include <sys/stat.h> +#include <dirent.h> + +extern int _p9dir(struct stat*, struct stat*, char*, Dir*, char**, char*); + +#if defined(__linux__) +static int +mygetdents(int fd, struct dirent *buf, int n) +{ + off_t off; + int nn; + + /* This doesn't match the man page, but it works in Debian with a 2.2 kernel */ + off = p9seek(fd, 0, 1); + nn = getdirentries(fd, (void*)buf, n, &off); + return nn; +} +#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +static int +mygetdents(int fd, struct dirent *buf, int n) +{ + long off; + return getdirentries(fd, (void*)buf, n, &off); +} +#elif defined(__sun__) +static int +mygetdents(int fd, struct dirent *buf, int n) +{ + return getdents(fd, (void*)buf, n); +} +#endif + +static int +countde(char *p, int n) +{ + char *e; + int m; + struct dirent *de; + + e = p+n; + m = 0; + while(p < e){ + de = (struct dirent*)p; + if(de->d_reclen <= 4+2+2+1 || p+de->d_reclen > e) + break; + if(de->d_name[0]=='.' && de->d_name[1]==0) + de->d_name[0] = 0; + else if(de->d_name[0]=='.' && de->d_name[1]=='.' && de->d_name[2]==0) + de->d_name[0] = 0; + m++; + p += de->d_reclen; + } + return m; +} + +static int +dirpackage(int fd, char *buf, int n, Dir **dp) +{ + int oldwd; + char *p, *str, *estr; + int i, nstr, m; + struct dirent *de; + struct stat st, lst; + Dir *d; + + n = countde(buf, n); + if(n <= 0) + return n; + + if((oldwd = open(".", O_RDONLY)) < 0) + return -1; + if(fchdir(fd) < 0) + return -1; + + p = buf; + nstr = 0; + + for(i=0; i<n; i++){ + de = (struct dirent*)p; + memset(&lst, 0, sizeof lst); + if(de->d_name[0] == 0) + /* nothing */ {} + else if(lstat(de->d_name, &lst) < 0) + de->d_name[0] = 0; + else{ + st = lst; + if(S_ISLNK(lst.st_mode)) + stat(de->d_name, &st); + nstr += _p9dir(&lst, &st, de->d_name, nil, nil, nil); + } + p += de->d_reclen; + } + + d = malloc(sizeof(Dir)*n+nstr); + if(d == nil){ + fchdir(oldwd); + close(oldwd); + return -1; + } + str = (char*)&d[n]; + estr = str+nstr; + + p = buf; + m = 0; + for(i=0; i<n; i++){ + de = (struct dirent*)p; + if(de->d_name[0] != 0 && lstat(de->d_name, &lst) >= 0){ + st = lst; + if((lst.st_mode&S_IFMT) == S_IFLNK) + stat(de->d_name, &st); + _p9dir(&lst, &st, de->d_name, &d[m++], &str, estr); + } + p += de->d_reclen; + } + + fchdir(oldwd); + close(oldwd); + *dp = d; + return m; +} + +long +dirread(int fd, Dir **dp) +{ + char *buf; + struct stat st; + int n; + + *dp = 0; + + if(fstat(fd, &st) < 0) + return -1; + + if(st.st_blksize < 8192) + st.st_blksize = 8192; + + buf = malloc(st.st_blksize); + if(buf == nil) + return -1; + + n = mygetdents(fd, (void*)buf, st.st_blksize); + if(n < 0){ + free(buf); + return -1; + } + n = dirpackage(fd, buf, n, dp); + free(buf); + return n; +} + + +long +dirreadall(int fd, Dir **d) +{ + uchar *buf, *nbuf; + long n, ts; + struct stat st; + + if(fstat(fd, &st) < 0) + return -1; + + if(st.st_blksize < 8192) + st.st_blksize = 8192; + + buf = nil; + ts = 0; + for(;;){ + nbuf = realloc(buf, ts+st.st_blksize); + if(nbuf == nil){ + free(buf); + return -1; + } + buf = nbuf; + n = mygetdents(fd, (void*)(buf+ts), st.st_blksize); + if(n <= 0) + break; + ts += n; + } + if(ts >= 0) + ts = dirpackage(fd, (char*)buf, ts, d); + free(buf); + if(ts == 0 && n < 0) + return -1; + return ts; +} diff --git a/lib9/dirstat.c b/lib9/dirstat.c @@ -0,0 +1,32 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> + +#include <sys/stat.h> + +extern int _p9dir(struct stat*, struct stat*, char*, Dir*, char**, char*); + +Dir* +dirstat(char *file) +{ + struct stat lst; + struct stat st; + int nstr; + Dir *d; + char *str; + + if(lstat(file, &lst) < 0) + return nil; + st = lst; + if((lst.st_mode&S_IFMT) == S_IFLNK) + stat(file, &st); + + nstr = _p9dir(&lst, &st, file, nil, nil, nil); + d = mallocz(sizeof(Dir)+nstr, 1); + if(d == nil) + return nil; + str = (char*)&d[1]; + _p9dir(&lst, &st, file, d, &str, str+nstr); + return d; +} + diff --git a/lib9/dirwstat.c b/lib9/dirwstat.c @@ -0,0 +1,19 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> +#include <sys/time.h> +#include <utime.h> + +int +dirwstat(char *file, Dir *dir) +{ + struct utimbuf ub; + + /* BUG handle more */ + if(~dir->mtime == 0) + return 0; + + ub.actime = dir->mtime; + ub.modtime = dir->mtime; + return utime(file, &ub); +} diff --git a/lib9/dup.c b/lib9/dup.c @@ -0,0 +1,12 @@ +#include <u.h> +#include <libc.h> + +#undef dup + +int +p9dup(int old, int new) +{ + if(new == -1) + return dup(old); + return dup2(old, new); +} diff --git a/lib9/encodefmt.c b/lib9/encodefmt.c @@ -0,0 +1,83 @@ +#include <lib9.h> +#include <ctype.h> +#include <stdlib.h> +#include "fmt.h" + +extern int enc64(char*, int, uchar*, int); +extern int enc32(char*, int, uchar*, int); +extern int enc16(char*, int, uchar*, int); + +int +encodefmt(Fmt *f) +{ + char *out; + char *buf, *p; + int len; + int ilen; + int rv; + uchar *b; + char obuf[64]; // rsc optimization + + b = va_arg(f->args, uchar*); + if(b == 0) + return fmtstrcpy(f, "<nil>"); + + ilen = f->prec; + f->prec = 0; + + if(!(f->flags&FmtPrec) || ilen < 0) + goto error; + + f->flags &= ~FmtPrec; + + switch(f->r){ + case '<': + len = (8*ilen+4)/5 + 3; + break; + case '[': + len = (8*ilen+5)/6 + 4; + break; + case 'H': + len = 2*ilen + 1; + break; + default: + goto error; + } + + if(len > sizeof(obuf)){ + buf = malloc(len); + if(buf == nil) + goto error; + } else + buf = obuf; + + // convert + out = buf; + switch(f->r){ + case '<': + rv = enc32(out, len, b, ilen); + break; + case '[': + rv = enc64(out, len, b, ilen); + break; + case 'H': + rv = enc16(out, len, b, ilen); + if(rv >= 0 && (f->flags & FmtLong)) + for(p = buf; *p; p++) + *p = tolower((uchar)*p); + break; + default: + rv = -1; + break; + } + if(rv < 0) + goto error; + + fmtstrcpy(f, buf); + if(buf != obuf) + free(buf); + return 0; + +error: + return fmtstrcpy(f, "<encodefmt>"); +} diff --git a/lib9/errstr.c b/lib9/errstr.c @@ -0,0 +1,81 @@ +/* + * We assume there's only one error buffer for the whole system. + * If you use ffork, you need to provide a _syserrstr. Since most + * people will use libthread (which provides a _syserrstr), this is + * okay. + */ + +#include <u.h> +#include <errno.h> +#include <string.h> +#include <libc.h> + +enum +{ + EPLAN9 = 0x19283745, +}; + +char *(*_syserrstr)(void); +static char xsyserr[ERRMAX]; +static char* +getsyserr(void) +{ + char *s; + + s = nil; + if(_syserrstr) + s = (*_syserrstr)(); + if(s == nil) + s = xsyserr; + return s; +} + +int +errstr(char *err, uint n) +{ + char tmp[ERRMAX]; + char *syserr; + + strecpy(tmp, tmp+ERRMAX, err); + rerrstr(err, n); + syserr = getsyserr(); + strecpy(syserr, syserr+ERRMAX, tmp); + errno = EPLAN9; + return 0; +} + +void +rerrstr(char *err, uint n) +{ + char *syserr; + + syserr = getsyserr(); + if(errno == EINTR) + strcpy(syserr, "interrupted"); + else if(errno != EPLAN9) + strcpy(syserr, strerror(errno)); + strecpy(err, err+n, syserr); +} + +/* replaces __errfmt in libfmt */ + +int +__errfmt(Fmt *f) +{ + if(errno == EPLAN9) + return fmtstrcpy(f, getsyserr()); + return fmtstrcpy(f, strerror(errno)); +} + +void +werrstr(char *fmt, ...) +{ + va_list arg; + char buf[ERRMAX]; + + va_start(arg, fmt); + vseprint(buf, buf+ERRMAX, fmt, arg); + va_end(arg); + errstr(buf, ERRMAX); +} + diff --git a/lib9/exec.c b/lib9/exec.c @@ -0,0 +1,9 @@ +#include <u.h> +#include <libc.h> + +int +exec(char *prog, char *argv[]) +{ + /* to mimic plan 9 should be just exec, but execvp is a better fit for unix */ + return execvp(prog, argv); +} diff --git a/lib9/execl.c b/lib9/execl.c @@ -0,0 +1,29 @@ +#include <u.h> +#include <libc.h> + +int +execl(char *prog, ...) +{ + int i; + va_list arg; + char **argv; + + va_start(arg, prog); + for(i=0; va_arg(arg, char*) != nil; i++) + ; + va_end(arg); + + argv = malloc((i+1)*sizeof(char*)); + if(argv == nil) + return -1; + + va_start(arg, prog); + for(i=0; (argv[i] = va_arg(arg, char*)) != nil; i++) + ; + va_end(arg); + + exec(prog, argv); + free(argv); + return -1; +} + diff --git a/lib9/fcall.h b/lib9/fcall.h @@ -0,0 +1,133 @@ +#ifndef _FCALL_H_ +#define _FCALL_H_ 1 +#ifdef __cplusplus +extern "C" { +#endif +/* +#pragma src "/sys/src/libc/9sys" +#pragma lib "libc.a" +*/ + +#define VERSION9P "9P2000" +#define MAXWELEM 16 + +typedef +struct Fcall +{ + uchar type; + u32int fid; + ushort tag; + u32int msize; /* Tversion, Rversion */ + char *version; /* Tversion, Rversion */ + ushort oldtag; /* Tflush */ + char *ename; /* Rerror */ + Qid qid; /* Rattach, Ropen, Rcreate */ + u32int iounit; /* Ropen, Rcreate */ + Qid aqid; /* Rauth */ + u32int afid; /* Tauth, Tattach */ + char *uname; /* Tauth, Tattach */ + char *aname; /* Tauth, Tattach */ + u32int perm; /* Tcreate */ + char *name; /* Tcreate */ + uchar mode; /* Tcreate, Topen */ + u32int newfid; /* Twalk */ + ushort nwname; /* Twalk */ + char *wname[MAXWELEM]; /* Twalk */ + ushort nwqid; /* Rwalk */ + Qid wqid[MAXWELEM]; /* Rwalk */ + vlong offset; /* Tread, Twrite */ + u32int count; /* Tread, Twrite, Rread */ + char *data; /* Twrite, Rread */ + ushort nstat; /* Twstat, Rstat */ + uchar *stat; /* Twstat, Rstat */ + int unixfd; /* Ropenfd */ +} Fcall; + + +#define GBIT8(p) ((p)[0]) +#define GBIT16(p) ((p)[0]|((p)[1]<<8)) +#define GBIT32(p) ((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24)) +#define GBIT64(p) ((vlong)((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24)) |\ + ((vlong)((p)[4]|((p)[5]<<8)|((p)[6]<<16)|((p)[7]<<24)) << 32)) + +#define PBIT8(p,v) (p)[0]=(v) +#define PBIT16(p,v) (p)[0]=(v);(p)[1]=(v)>>8 +#define PBIT32(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24 +#define PBIT64(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24;\ + (p)[4]=(v)>>32;(p)[5]=(v)>>40;(p)[6]=(v)>>48;(p)[7]=(v)>>56 + +#define BIT8SZ 1 +#define BIT16SZ 2 +#define BIT32SZ 4 +#define BIT64SZ 8 +#define QIDSZ (BIT8SZ+BIT32SZ+BIT64SZ) + +/* STATFIXLEN includes leading 16-bit count */ +/* The count, however, excludes itself; total size is BIT16SZ+count */ +#define STATFIXLEN (BIT16SZ+QIDSZ+5*BIT16SZ+4*BIT32SZ+1*BIT64SZ) /* amount of fixed length data in a stat buffer */ + +#define NOTAG (ushort)~0U /* Dummy tag */ +#define NOFID (u32int)~0U /* Dummy fid */ +#define IOHDRSZ 24 /* ample room for Twrite/Rread header (iounit) */ + +enum +{ + Tversion = 100, + Rversion, + Tauth = 102, + Rauth, + Tattach = 104, + Rattach, + Terror = 106, /* illegal */ + Rerror, + Tflush = 108, + Rflush, + Twalk = 110, + Rwalk, + Topen = 112, + Ropen, + Tcreate = 114, + Rcreate, + Tread = 116, + Rread, + Twrite = 118, + Rwrite, + Tclunk = 120, + Rclunk, + Tremove = 122, + Rremove, + Tstat = 124, + Rstat, + Twstat = 126, + Rwstat, + Tmax, + + Topenfd = 98, + Ropenfd, +}; + +uint convM2S(uchar*, uint, Fcall*); +uint convS2M(Fcall*, uchar*, uint); +uint sizeS2M(Fcall*); + +int statcheck(uchar *abuf, uint nbuf); +uint convM2D(uchar*, uint, Dir*, char*); +uint convD2M(Dir*, uchar*, uint); +uint sizeD2M(Dir*); + +int fcallfmt(Fmt*); +int dirfmt(Fmt*); +int dirmodefmt(Fmt*); + +int read9pmsg(int, void*, uint); + +/* +#pragma varargck type "F" Fcall* +#pragma varargck type "M" ulong +#pragma varargck type "D" Dir* +*/ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib9/fcallfmt.c b/lib9/fcallfmt.c @@ -0,0 +1,253 @@ +#include <u.h> +#include <libc.h> +#include <fcall.h> + +static uint dumpsome(char*, char*, char*, long); +static void fdirconv(char*, char*, Dir*); +static char *qidtype(char*, uchar); + +#define QIDFMT "(%.16llux %lud %s)" + +int +fcallfmt(Fmt *fmt) +{ + Fcall *f; + int fid, type, tag, i; + char buf[512], tmp[200]; + char *p, *e; + Dir *d; + Qid *q; + + e = buf+sizeof(buf); + f = va_arg(fmt->args, Fcall*); + type = f->type; + fid = f->fid; + tag = f->tag; + switch(type){ + case Tversion: /* 100 */ + seprint(buf, e, "Tversion tag %ud msize %ud version '%s'", tag, f->msize, f->version); + break; + case Rversion: + seprint(buf, e, "Rversion tag %ud msize %ud version '%s'", tag, f->msize, f->version); + break; + case Tauth: /* 102 */ + seprint(buf, e, "Tauth tag %ud afid %d uname %s aname %s", tag, + f->afid, f->uname, f->aname); + break; + case Rauth: + seprint(buf, e, "Rauth tag %ud qid " QIDFMT, tag, + f->aqid.path, f->aqid.vers, qidtype(tmp, f->aqid.type)); + break; + case Tattach: /* 104 */ + seprint(buf, e, "Tattach tag %ud fid %d afid %d uname %s aname %s", tag, + fid, f->afid, f->uname, f->aname); + break; + case Rattach: + seprint(buf, e, "Rattach tag %ud qid " QIDFMT, tag, + f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type)); + break; + case Rerror: /* 107; 106 (Terror) illegal */ + seprint(buf, e, "Rerror tag %ud ename %s", tag, f->ename); + break; + case Tflush: /* 108 */ + seprint(buf, e, "Tflush tag %ud oldtag %ud", tag, f->oldtag); + break; + case Rflush: + seprint(buf, e, "Rflush tag %ud", tag); + break; + case Twalk: /* 110 */ + p = seprint(buf, e, "Twalk tag %ud fid %d newfid %d nwname %d ", tag, fid, f->newfid, f->nwname); + if(f->nwname <= MAXWELEM) + for(i=0; i<f->nwname; i++) + p = seprint(p, e, "%d:%s ", i, f->wname[i]); + break; + case Rwalk: + p = seprint(buf, e, "Rwalk tag %ud nwqid %ud ", tag, f->nwqid); + if(f->nwqid <= MAXWELEM) + for(i=0; i<f->nwqid; i++){ + q = &f->wqid[i]; + p = seprint(p, e, "%d:" QIDFMT " ", i, + q->path, q->vers, qidtype(tmp, q->type)); + } + break; + case Topen: /* 112 */ + seprint(buf, e, "Topen tag %ud fid %ud mode %d", tag, fid, f->mode); + break; + case Ropen: + seprint(buf, e, "Ropen tag %ud qid " QIDFMT " iounit %ud", tag, + f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit); + break; + case Topenfd: /* 98 */ + seprint(buf, e, "Topenfd tag %ud fid %ud mode %d", tag, fid, f->mode); + break; + case Ropenfd: + seprint(buf, e, "Ropenfd tag %ud qid " QIDFMT " iounit %ud unixfd %d", tag, + f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit, f->unixfd); + break; + case Tcreate: /* 114 */ + seprint(buf, e, "Tcreate tag %ud fid %ud name %s perm %M mode %d", tag, fid, f->name, (ulong)f->perm, f->mode); + break; + case Rcreate: + seprint(buf, e, "Rcreate tag %ud qid " QIDFMT " iounit %ud ", tag, + f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit); + break; + case Tread: /* 116 */ + seprint(buf, e, "Tread tag %ud fid %d offset %lld count %ud", + tag, fid, f->offset, f->count); + break; + case Rread: + p = seprint(buf, e, "Rread tag %ud count %ud ", tag, f->count); + dumpsome(p, e, f->data, f->count); + break; + case Twrite: /* 118 */ + p = seprint(buf, e, "Twrite tag %ud fid %d offset %lld count %ud ", + tag, fid, f->offset, f->count); + dumpsome(p, e, f->data, f->count); + break; + case Rwrite: + seprint(buf, e, "Rwrite tag %ud count %ud", tag, f->count); + break; + case Tclunk: /* 120 */ + seprint(buf, e, "Tclunk tag %ud fid %ud", tag, fid); + break; + case Rclunk: + seprint(buf, e, "Rclunk tag %ud", tag); + break; + case Tremove: /* 122 */ + seprint(buf, e, "Tremove tag %ud fid %ud", tag, fid); + break; + case Rremove: + seprint(buf, e, "Rremove tag %ud", tag); + break; + case Tstat: /* 124 */ + seprint(buf, e, "Tstat tag %ud fid %ud", tag, fid); + break; + case Rstat: + p = seprint(buf, e, "Rstat tag %ud ", tag); + if(f->nstat > sizeof tmp) + seprint(p, e, " stat(%d bytes)", f->nstat); + else{ + d = (Dir*)tmp; + convM2D(f->stat, f->nstat, d, (char*)(d+1)); + seprint(p, e, " stat "); + fdirconv(p+6, e, d); + } + break; + case Twstat: /* 126 */ + p = seprint(buf, e, "Twstat tag %ud fid %ud", tag, fid); + if(f->nstat > sizeof tmp) + seprint(p, e, " stat(%d bytes)", f->nstat); + else{ + d = (Dir*)tmp; + convM2D(f->stat, f->nstat, d, (char*)(d+1)); + seprint(p, e, " stat "); + fdirconv(p+6, e, d); + } + break; + case Rwstat: + seprint(buf, e, "Rwstat tag %ud", tag); + break; + default: + seprint(buf, e, "unknown type %d", type); + } + return fmtstrcpy(fmt, buf); +} + +static char* +qidtype(char *s, uchar t) +{ + char *p; + + p = s; + if(t & QTDIR) + *p++ = 'd'; + if(t & QTAPPEND) + *p++ = 'a'; + if(t & QTEXCL) + *p++ = 'l'; + if(t & QTAUTH) + *p++ = 'A'; + *p = '\0'; + return s; +} + +int +dirfmt(Fmt *fmt) +{ + char buf[160]; + + fdirconv(buf, buf+sizeof buf, va_arg(fmt->args, Dir*)); + return fmtstrcpy(fmt, buf); +} + +static void +fdirconv(char *buf, char *e, Dir *d) +{ + char tmp[16]; + + seprint(buf, e, "'%s' '%s' '%s' '%s' " + "q " QIDFMT " m %#luo " + "at %ld mt %ld l %lld " + "t %d d %d", + d->name, d->uid, d->gid, d->muid, + d->qid.path, d->qid.vers, qidtype(tmp, d->qid.type), d->mode, + d->atime, d->mtime, d->length, + d->type, d->dev); +} + +/* + * dump out count (or DUMPL, if count is bigger) bytes from + * buf to ans, as a string if they are all printable, + * else as a series of hex bytes + */ +#define DUMPL 64 + +static uint +dumpsome(char *ans, char *e, char *buf, long count) +{ + int i, printable; + char *p; + + if(buf == nil){ + seprint(ans, e, "<no data>"); + return strlen(ans); + } + printable = 1; + if(count > DUMPL) + count = DUMPL; + for(i=0; i<count && printable; i++) + if((buf[i]!=0 && buf[i]<32) || (uchar)buf[i]>127) + printable = 0; + p = ans; + *p++ = '\''; + if(printable){ + if(2*count > e-p-2) + count = (e-p-2)/2; + for(i=0; i<count; i++){ + if(buf[i] == 0){ + *p++ = '\\'; + *p++ = '0'; + }else if(buf[i] == '\t'){ + *p++ = '\\'; + *p++ = 't'; + }else if(buf[i] == '\n'){ + *p++ = '\\'; + *p++ = 'n'; + }else + *p++ = buf[i]; + } + }else{ + if(2*count > e-p-2) + count = (e-p-2)/2; + for(i=0; i<count; i++){ + if(i>0 && i%4==0) + *p++ = ' '; + sprint(p, "%2.2ux", (uchar)buf[i]); + p += 2; + } + } + *p++ = '\''; + *p = 0; + assert(p < e); + return p - ans; +} diff --git a/lib9/fmt.h b/lib9/fmt.h @@ -0,0 +1,101 @@ +#ifndef _FMT_H_ +#define _FMT_H_ 1 +#if defined(__cplusplus) +extern "C" { +#endif +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + +#include <stdarg.h> +#include <utf.h> + +typedef struct Fmt Fmt; +struct Fmt{ + unsigned char runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + unsigned long flags; +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + FmtLDouble = FmtByte << 1, + + FmtFlag = FmtLDouble << 1 +}; + +extern int (*fmtdoquote)(int); + +/* Edit .+1,/^$/ | cfn $PLAN9/src/lib9/fmt/?*.c | grep -v static |grep -v __ */ +int dofmt(Fmt *f, char *fmt); +int dorfmt(Fmt *f, const Rune *fmt); +double fmtcharstod(int(*f)(void*), void *vp); +int fmtfdflush(Fmt *f); +int fmtfdinit(Fmt *f, int fd, char *buf, int size); +int fmtinstall(int c, int (*f)(Fmt*)); +int fmtprint(Fmt *f, char *fmt, ...); +int fmtrune(Fmt *f, int r); +int fmtrunestrcpy(Fmt *f, Rune *s); +int fmtstrcpy(Fmt *f, char *s); +char* fmtstrflush(Fmt *f); +int fmtstrinit(Fmt *f); +double fmtstrtod(const char *as, char **aas); +int fmtvprint(Fmt *f, char *fmt, va_list args); +int fprint(int fd, char *fmt, ...); +int print(char *fmt, ...); +void quotefmtinstall(void); +int quoterunestrfmt(Fmt *f); +int quotestrfmt(Fmt *f); +Rune* runefmtstrflush(Fmt *f); +int runefmtstrinit(Fmt *f); +Rune* runeseprint(Rune *buf, Rune *e, char *fmt, ...); +Rune* runesmprint(char *fmt, ...); +int runesnprint(Rune *buf, int len, char *fmt, ...); +int runesprint(Rune *buf, char *fmt, ...); +Rune* runevseprint(Rune *buf, Rune *e, char *fmt, va_list args); +Rune* runevsmprint(char *fmt, va_list args); +int runevsnprint(Rune *buf, int len, char *fmt, va_list args); +char* seprint(char *buf, char *e, char *fmt, ...); +char* smprint(char *fmt, ...); +int snprint(char *buf, int len, char *fmt, ...); +int sprint(char *buf, char *fmt, ...); +int vfprint(int fd, char *fmt, va_list args); +char* vseprint(char *buf, char *e, char *fmt, va_list args); +char* vsmprint(char *fmt, va_list args); +int vsnprint(char *buf, int len, char *fmt, va_list args); + +#if defined(__cplusplus) +} +#endif +#endif diff --git a/lib9/fmt/LICENSE b/lib9/fmt/LICENSE @@ -0,0 +1,19 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. +*/ + +This is a Unix port of the Plan 9 formatted I/O package. + +Please send comments about the packaging +to Russ Cox <rsc@post.harvard.edu>. + diff --git a/lib9/fmt/charstod.c b/lib9/fmt/charstod.c @@ -0,0 +1,85 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +/* + * Reads a floating-point number by interpreting successive characters + * returned by (*f)(vp). The last call it makes to f terminates the + * scan, so is not a character in the number. It may therefore be + * necessary to back up the input stream up one byte after calling charstod. + */ + +double +fmtcharstod(int(*f)(void*), void *vp) +{ + double num, dem; + int neg, eneg, dig, exp, c; + + num = 0; + neg = 0; + dig = 0; + exp = 0; + eneg = 0; + + c = (*f)(vp); + while(c == ' ' || c == '\t') + c = (*f)(vp); + if(c == '-' || c == '+'){ + if(c == '-') + neg = 1; + c = (*f)(vp); + } + while(c >= '0' && c <= '9'){ + num = num*10 + c-'0'; + c = (*f)(vp); + } + if(c == '.') + c = (*f)(vp); + while(c >= '0' && c <= '9'){ + num = num*10 + c-'0'; + dig++; + c = (*f)(vp); + } + if(c == 'e' || c == 'E'){ + c = (*f)(vp); + if(c == '-' || c == '+'){ + if(c == '-'){ + dig = -dig; + eneg = 1; + } + c = (*f)(vp); + } + while(c >= '0' && c <= '9'){ + exp = exp*10 + c-'0'; + c = (*f)(vp); + } + } + exp -= dig; + if(exp < 0){ + exp = -exp; + eneg = !eneg; + } + dem = __fmtpow10(exp); + if(eneg) + num /= dem; + else + num *= dem; + if(neg) + return -num; + return num; +} diff --git a/lib9/fmt/dofmt.c b/lib9/fmt/dofmt.c @@ -0,0 +1,552 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +/* format the output into f->to and return the number of characters fmted */ +int +dofmt(Fmt *f, char *fmt) +{ + Rune rune, *rt, *rs; + int r; + char *t, *s; + int n, nfmt; + + nfmt = f->nfmt; + for(;;){ + if(f->runes){ + rt = (Rune*)f->to; + rs = (Rune*)f->stop; + while((r = *(uchar*)fmt) && r != '%'){ + if(r < Runeself) + fmt++; + else{ + fmt += chartorune(&rune, fmt); + r = rune; + } + FMTRCHAR(f, rt, rs, r); + } + fmt++; + f->nfmt += rt - (Rune *)f->to; + f->to = rt; + if(!r) + return f->nfmt - nfmt; + f->stop = rs; + }else{ + t = (char*)f->to; + s = (char*)f->stop; + while((r = *(uchar*)fmt) && r != '%'){ + if(r < Runeself){ + FMTCHAR(f, t, s, r); + fmt++; + }else{ + n = chartorune(&rune, fmt); + if(t + n > s){ + t = (char*)__fmtflush(f, t, n); + if(t != nil) + s = (char*)f->stop; + else + return -1; + } + while(n--) + *t++ = *fmt++; + } + } + fmt++; + f->nfmt += t - (char *)f->to; + f->to = t; + if(!r) + return f->nfmt - nfmt; + f->stop = s; + } + + fmt = (char*)__fmtdispatch(f, fmt, 0); + if(fmt == nil) + return -1; + } +} + +void * +__fmtflush(Fmt *f, void *t, int len) +{ + if(f->runes) + f->nfmt += (Rune*)t - (Rune*)f->to; + else + f->nfmt += (char*)t - (char *)f->to; + f->to = t; + if(f->flush == 0 || (*f->flush)(f) == 0 || (char*)f->to + len > (char*)f->stop){ + f->stop = f->to; + return nil; + } + return f->to; +} + +/* + * put a formatted block of memory sz bytes long of n runes into the output buffer, + * left/right justified in a field of at least f->width charactes + */ +int +__fmtpad(Fmt *f, int n) +{ + char *t, *s; + int i; + + t = (char*)f->to; + s = (char*)f->stop; + for(i = 0; i < n; i++) + FMTCHAR(f, t, s, ' '); + f->nfmt += t - (char *)f->to; + f->to = t; + return 0; +} + +int +__rfmtpad(Fmt *f, int n) +{ + Rune *t, *s; + int i; + + t = (Rune*)f->to; + s = (Rune*)f->stop; + for(i = 0; i < n; i++) + FMTRCHAR(f, t, s, ' '); + f->nfmt += t - (Rune *)f->to; + f->to = t; + return 0; +} + +int +__fmtcpy(Fmt *f, const void *vm, int n, int sz) +{ + Rune *rt, *rs, r; + char *t, *s, *m, *me; + ulong fl; + int nc, w; + + m = (char*)vm; + me = m + sz; + w = f->width; + fl = f->flags; + if((fl & FmtPrec) && n > f->prec) + n = f->prec; + if(f->runes){ + if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0) + return -1; + rt = (Rune*)f->to; + rs = (Rune*)f->stop; + for(nc = n; nc > 0; nc--){ + r = *(uchar*)m; + if(r < Runeself) + m++; + else if((me - m) >= UTFmax || fullrune(m, me-m)) + m += chartorune(&r, m); + else + break; + FMTRCHAR(f, rt, rs, r); + } + f->nfmt += rt - (Rune *)f->to; + f->to = rt; + if(fl & FmtLeft && __rfmtpad(f, w - n) < 0) + return -1; + }else{ + if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0) + return -1; + t = (char*)f->to; + s = (char*)f->stop; + for(nc = n; nc > 0; nc--){ + r = *(uchar*)m; + if(r < Runeself) + m++; + else if((me - m) >= UTFmax || fullrune(m, me-m)) + m += chartorune(&r, m); + else + break; + FMTRUNE(f, t, s, r); + } + f->nfmt += t - (char *)f->to; + f->to = t; + if(fl & FmtLeft && __fmtpad(f, w - n) < 0) + return -1; + } + return 0; +} + +int +__fmtrcpy(Fmt *f, const void *vm, int n) +{ + Rune r, *m, *me, *rt, *rs; + char *t, *s; + ulong fl; + int w; + + m = (Rune*)vm; + w = f->width; + fl = f->flags; + if((fl & FmtPrec) && n > f->prec) + n = f->prec; + if(f->runes){ + if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0) + return -1; + rt = (Rune*)f->to; + rs = (Rune*)f->stop; + for(me = m + n; m < me; m++) + FMTRCHAR(f, rt, rs, *m); + f->nfmt += rt - (Rune *)f->to; + f->to = rt; + if(fl & FmtLeft && __rfmtpad(f, w - n) < 0) + return -1; + }else{ + if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0) + return -1; + t = (char*)f->to; + s = (char*)f->stop; + for(me = m + n; m < me; m++){ + r = *m; + FMTRUNE(f, t, s, r); + } + f->nfmt += t - (char *)f->to; + f->to = t; + if(fl & FmtLeft && __fmtpad(f, w - n) < 0) + return -1; + } + return 0; +} + +/* fmt out one character */ +int +__charfmt(Fmt *f) +{ + char x[1]; + + x[0] = va_arg(f->args, int); + f->prec = 1; + return __fmtcpy(f, (const char*)x, 1, 1); +} + +/* fmt out one rune */ +int +__runefmt(Fmt *f) +{ + Rune x[1]; + + x[0] = va_arg(f->args, int); + return __fmtrcpy(f, (const void*)x, 1); +} + +/* public helper routine: fmt out a null terminated string already in hand */ +int +fmtstrcpy(Fmt *f, char *s) +{ + int i, j; + Rune r; + + if(!s) + return __fmtcpy(f, "<nil>", 5, 5); + /* if precision is specified, make sure we don't wander off the end */ + if(f->flags & FmtPrec){ + i = 0; + for(j=0; j<f->prec && s[i]; j++) + i += chartorune(&r, s+i); + return __fmtcpy(f, s, j, i); + } + return __fmtcpy(f, s, utflen(s), strlen(s)); +} + +/* fmt out a null terminated utf string */ +int +__strfmt(Fmt *f) +{ + char *s; + + s = va_arg(f->args, char *); + return fmtstrcpy(f, s); +} + +/* public helper routine: fmt out a null terminated rune string already in hand */ +int +fmtrunestrcpy(Fmt *f, Rune *s) +{ + Rune *e; + int n, p; + + if(!s) + return __fmtcpy(f, "<nil>", 5, 5); + /* if precision is specified, make sure we don't wander off the end */ + if(f->flags & FmtPrec){ + p = f->prec; + for(n = 0; n < p; n++) + if(s[n] == 0) + break; + }else{ + for(e = s; *e; e++) + ; + n = e - s; + } + return __fmtrcpy(f, s, n); +} + +/* fmt out a null terminated rune string */ +int +__runesfmt(Fmt *f) +{ + Rune *s; + + s = va_arg(f->args, Rune *); + return fmtrunestrcpy(f, s); +} + +/* fmt a % */ +int +__percentfmt(Fmt *f) +{ + Rune x[1]; + + x[0] = f->r; + f->prec = 1; + return __fmtrcpy(f, (const void*)x, 1); +} + +/* fmt an integer */ +int +__ifmt(Fmt *f) +{ + char buf[70], *p, *conv; + uvlong vu; + ulong u; + int neg, base, i, n, fl, w, isv; + + neg = 0; + fl = f->flags; + isv = 0; + vu = 0; + u = 0; +#ifndef PLAN9PORT + /* + * Unsigned verbs for ANSI C + */ + switch(f->r){ + case 'x': + case 'X': + case 'o': + case 'u': + case 'p': + fl |= FmtUnsigned; + fl &= ~(FmtSign|FmtSpace); + break; + } +#endif + if(f->r == 'p'){ + u = (ulong)va_arg(f->args, void*); + f->r = 'x'; + fl |= FmtUnsigned; + }else if(fl & FmtVLong){ + isv = 1; + if(fl & FmtUnsigned) + vu = va_arg(f->args, uvlong); + else + vu = va_arg(f->args, vlong); + }else if(fl & FmtLong){ + if(fl & FmtUnsigned) + u = va_arg(f->args, ulong); + else + u = va_arg(f->args, long); + }else if(fl & FmtByte){ + if(fl & FmtUnsigned) + u = (uchar)va_arg(f->args, int); + else + u = (char)va_arg(f->args, int); + }else if(fl & FmtShort){ + if(fl & FmtUnsigned) + u = (ushort)va_arg(f->args, int); + else + u = (short)va_arg(f->args, int); + }else{ + if(fl & FmtUnsigned) + u = va_arg(f->args, uint); + else + u = va_arg(f->args, int); + } + conv = "0123456789abcdef"; + switch(f->r){ + case 'd': + case 'i': + case 'u': + base = 10; + break; + case 'x': + base = 16; + break; + case 'X': + base = 16; + conv = "0123456789ABCDEF"; + break; + case 'b': + base = 2; + break; + case 'o': + base = 8; + break; + default: + return -1; + } + if(!(fl & FmtUnsigned)){ + if(isv && (vlong)vu < 0){ + vu = -(vlong)vu; + neg = 1; + }else if(!isv && (long)u < 0){ + u = -(long)u; + neg = 1; + } + } + p = buf + sizeof buf - 1; + n = 0; + if(isv){ + while(vu){ + i = vu % base; + vu /= base; + if((fl & FmtComma) && n % 4 == 3){ + *p-- = ','; + n++; + } + *p-- = conv[i]; + n++; + } + }else{ + while(u){ + i = u % base; + u /= base; + if((fl & FmtComma) && n % 4 == 3){ + *p-- = ','; + n++; + } + *p-- = conv[i]; + n++; + } + } + if(n == 0){ + *p-- = '0'; + n = 1; + } + for(w = f->prec; n < w && p > buf+3; n++) + *p-- = '0'; + if(neg || (fl & (FmtSign|FmtSpace))) + n++; + if(fl & FmtSharp){ + if(base == 16) + n += 2; + else if(base == 8){ + if(p[1] == '0') + fl &= ~FmtSharp; + else + n++; + } + } + if((fl & FmtZero) && !(fl & (FmtLeft|FmtPrec))){ + for(w = f->width; n < w && p > buf+3; n++) + *p-- = '0'; + f->width = 0; + } + if(fl & FmtSharp){ + if(base == 16) + *p-- = f->r; + if(base == 16 || base == 8) + *p-- = '0'; + } + if(neg) + *p-- = '-'; + else if(fl & FmtSign) + *p-- = '+'; + else if(fl & FmtSpace) + *p-- = ' '; + f->flags &= ~FmtPrec; + return __fmtcpy(f, p + 1, n, n); +} + +int +__countfmt(Fmt *f) +{ + void *p; + ulong fl; + + fl = f->flags; + p = va_arg(f->args, void*); + if(fl & FmtVLong){ + *(vlong*)p = f->nfmt; + }else if(fl & FmtLong){ + *(long*)p = f->nfmt; + }else if(fl & FmtByte){ + *(char*)p = f->nfmt; + }else if(fl & FmtShort){ + *(short*)p = f->nfmt; + }else{ + *(int*)p = f->nfmt; + } + return 0; +} + +int +__flagfmt(Fmt *f) +{ + switch(f->r){ + case ',': + f->flags |= FmtComma; + break; + case '-': + f->flags |= FmtLeft; + break; + case '+': + f->flags |= FmtSign; + break; + case '#': + f->flags |= FmtSharp; + break; + case ' ': + f->flags |= FmtSpace; + break; + case 'u': + f->flags |= FmtUnsigned; + break; + case 'h': + if(f->flags & FmtShort) + f->flags |= FmtByte; + f->flags |= FmtShort; + break; + case 'L': + f->flags |= FmtLDouble; + break; + case 'l': + if(f->flags & FmtLong) + f->flags |= FmtVLong; + f->flags |= FmtLong; + break; + } + return 1; +} + +/* default error format */ +int +__badfmt(Fmt *f) +{ + char x[3]; + + x[0] = '%'; + x[1] = f->r; + x[2] = '%'; + f->prec = 3; + __fmtcpy(f, (const void*)x, 3, 3); + return 0; +} diff --git a/lib9/fmt/dorfmt.c b/lib9/fmt/dorfmt.c @@ -0,0 +1,61 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +/* format the output into f->to and return the number of characters fmted */ + +int +dorfmt(Fmt *f, const Rune *fmt) +{ + Rune *rt, *rs; + int r; + char *t, *s; + int nfmt; + + nfmt = f->nfmt; + for(;;){ + if(f->runes){ + rt = f->to; + rs = f->stop; + while((r = *fmt++) && r != '%'){ + FMTRCHAR(f, rt, rs, r); + } + f->nfmt += rt - (Rune *)f->to; + f->to = rt; + if(!r) + return f->nfmt - nfmt; + f->stop = rs; + }else{ + t = f->to; + s = f->stop; + while((r = *fmt++) && r != '%'){ + FMTRUNE(f, t, f->stop, r); + } + f->nfmt += t - (char *)f->to; + f->to = t; + if(!r) + return f->nfmt - nfmt; + f->stop = s; + } + + fmt = __fmtdispatch(f, (Rune*)fmt, 1); + if(fmt == nil) + return -1; + } + return 0; /* not reached */ +} diff --git a/lib9/fmt/errfmt.c b/lib9/fmt/errfmt.c @@ -0,0 +1,28 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +int +__errfmt(Fmt *f) +{ + char *s; + + s = strerror(errno); + return fmtstrcpy(f, s); +} diff --git a/lib9/fmt/fltfmt.c b/lib9/fmt/fltfmt.c @@ -0,0 +1,394 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdio.h> +#include <math.h> +#include <float.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <stdarg.h> +#include <ctype.h> +#include <fmt.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +enum +{ + FDIGIT = 30, + FDEFLT = 6, + NSIGNIF = 17 +}; + +/* + * first few powers of 10, enough for about 1/2 of the + * total space for doubles. + */ +static double pows10[] = +{ + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, + 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, + 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, + 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, + 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, + 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, + 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89, + 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99, + 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, + 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, + 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, + 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, + 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, + 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, +}; + +#define pow10(x) fmtpow10(x) + +static double +pow10(int n) +{ + double d; + int neg; + + neg = 0; + if(n < 0){ + if(n < DBL_MIN_10_EXP){ + return 0.; + } + neg = 1; + n = -n; + }else if(n > DBL_MAX_10_EXP){ + return HUGE_VAL; + } + if(n < (int)(sizeof(pows10)/sizeof(pows10[0]))) + d = pows10[n]; + else{ + d = pows10[sizeof(pows10)/sizeof(pows10[0]) - 1]; + for(;;){ + n -= sizeof(pows10)/sizeof(pows10[0]) - 1; + if(n < (int)(sizeof(pows10)/sizeof(pows10[0]))){ + d *= pows10[n]; + break; + } + d *= pows10[sizeof(pows10)/sizeof(pows10[0]) - 1]; + } + } + if(neg){ + return 1./d; + } + return d; +} + +static int +xadd(char *a, int n, int v) +{ + char *b; + int c; + + if(n < 0 || n >= NSIGNIF) + return 0; + for(b = a+n; b >= a; b--) { + c = *b + v; + if(c <= '9') { + *b = c; + return 0; + } + *b = '0'; + v = 1; + } + *a = '1'; /* overflow adding */ + return 1; +} + +static int +xsub(char *a, int n, int v) +{ + char *b; + int c; + + for(b = a+n; b >= a; b--) { + c = *b - v; + if(c >= '0') { + *b = c; + return 0; + } + *b = '9'; + v = 1; + } + *a = '9'; /* underflow subtracting */ + return 1; +} + +static void +xdtoa(Fmt *fmt, char *s2, double f) +{ + char s1[NSIGNIF+10]; + double g, h; + int e, d, i, n; + int c1, c2, c3, c4, ucase, sign, chr, prec; + + prec = FDEFLT; + if(fmt->flags & FmtPrec) + prec = fmt->prec; + if(prec > FDIGIT) + prec = FDIGIT; + if(__isNaN(f)) { + strcpy(s2, "NaN"); + return; + } + if(__isInf(f, 1)) { + strcpy(s2, "+Inf"); + return; + } + if(__isInf(f, -1)) { + strcpy(s2, "-Inf"); + return; + } + sign = 0; + if(f < 0) { + f = -f; + sign++; + } + ucase = 0; + chr = fmt->r; + if(isupper(chr)) { + ucase = 1; + chr = tolower(chr); + } + + e = 0; + g = f; + if(g != 0) { + frexp(f, &e); + e = e * .301029995664; + if(e >= -150 && e <= +150) { + d = 0; + h = f; + } else { + d = e/2; + h = f * pow10(-d); + } + g = h * pow10(d-e); + while(g < 1) { + e--; + g = h * pow10(d-e); + } + while(g >= 10) { + e++; + g = h * pow10(d-e); + } + } + + /* + * convert NSIGNIF digits and convert + * back to get accuracy. + */ + for(i=0; i<NSIGNIF; i++) { + d = g; + s1[i] = d + '0'; + g = (g - d) * 10; + } + s1[i] = 0; + + /* + * try decimal rounding to eliminate 9s + */ + c2 = prec + 1; + if(chr == 'f') + c2 += e; + if(c2 >= NSIGNIF-2) { + strcpy(s2, s1); + d = e; + s1[NSIGNIF-2] = '0'; + s1[NSIGNIF-1] = '0'; + sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1); + g = strtod(s1, nil); + if(g == f) + goto found; + if(xadd(s1, NSIGNIF-3, 1)) { + e++; + sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1); + } + g = strtod(s1, nil); + if(g == f) + goto found; + strcpy(s1, s2); + e = d; + } + + /* + * convert back so s1 gets exact answer + */ + for(;;) { + sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1); + g = strtod(s1, nil); + if(f > g) { + if(xadd(s1, NSIGNIF-1, 1)) + e--; + continue; + } + if(f < g) { + if(xsub(s1, NSIGNIF-1, 1)) + e++; + continue; + } + break; + } + +found: + /* + * sign + */ + d = 0; + i = 0; + if(sign) + s2[d++] = '-'; + else if(fmt->flags & FmtSign) + s2[d++] = '+'; + else if(fmt->flags & FmtSpace) + s2[d++] = ' '; + + /* + * copy into final place + * c1 digits of leading '0' + * c2 digits from conversion + * c3 digits of trailing '0' + * c4 digits after '.' + */ + c1 = 0; + c2 = prec + 1; + c3 = 0; + c4 = prec; + switch(chr) { + default: + if(xadd(s1, c2, 5)) + e++; + break; + case 'g': + /* + * decide on 'e' of 'f' style convers + */ + if(xadd(s1, c2, 5)) + e++; + if(e >= -5 && e <= prec) { + c1 = -e - 1; + c4 = prec - e; + chr = 'h'; // flag for 'f' style + } + break; + case 'f': + if(xadd(s1, c2+e, 5)) + e++; + c1 = -e; + if(c1 > prec) + c1 = c2; + c2 += e; + break; + } + + /* + * clean up c1 c2 and c3 + */ + if(c1 < 0) + c1 = 0; + if(c2 < 0) + c2 = 0; + if(c2 > NSIGNIF) { + c3 = c2-NSIGNIF; + c2 = NSIGNIF; + } + + /* + * copy digits + */ + while(c1 > 0) { + if(c1+c2+c3 == c4) + s2[d++] = '.'; + s2[d++] = '0'; + c1--; + } + while(c2 > 0) { + if(c2+c3 == c4) + s2[d++] = '.'; + s2[d++] = s1[i++]; + c2--; + } + while(c3 > 0) { + if(c3 == c4) + s2[d++] = '.'; + s2[d++] = '0'; + c3--; + } + + /* + * strip trailing '0' on g conv + */ + if(fmt->flags & FmtSharp) { + if(0 == c4) + s2[d++] = '.'; + } else + if(chr == 'g' || chr == 'h') { + for(n=d-1; n>=0; n--) + if(s2[n] != '0') + break; + for(i=n; i>=0; i--) + if(s2[i] == '.') { + d = n; + if(i != n) + d++; + break; + } + } + if(chr == 'e' || chr == 'g') { + if(ucase) + s2[d++] = 'E'; + else + s2[d++] = 'e'; + c1 = e; + if(c1 < 0) { + s2[d++] = '-'; + c1 = -c1; + } else + s2[d++] = '+'; + if(c1 >= 100) { + s2[d++] = c1/100 + '0'; + c1 = c1%100; + } + s2[d++] = c1/10 + '0'; + s2[d++] = c1%10 + '0'; + } + s2[d] = 0; +} + +static int +floatfmt(Fmt *fmt, double f) +{ + char s[FDIGIT+10]; + + xdtoa(fmt, s, f); + fmt->flags &= FmtWidth|FmtLeft; + __fmtcpy(fmt, s, strlen(s), strlen(s)); + return 0; +} + +int +__efgfmt(Fmt *f) +{ + double d; + + d = va_arg(f->args, double); + return floatfmt(f, d); +} diff --git a/lib9/fmt/fmt.c b/lib9/fmt/fmt.c @@ -0,0 +1,231 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +enum +{ + Maxfmt = 64 +}; + +typedef struct Convfmt Convfmt; +struct Convfmt +{ + int c; + volatile Fmts fmt; /* for spin lock in fmtfmt; avoids race due to write order */ +}; + +struct +{ + /* lock by calling __fmtlock, __fmtunlock */ + int nfmt; + Convfmt fmt[Maxfmt]; +} fmtalloc; + +static Convfmt knownfmt[] = { + ' ', __flagfmt, + '#', __flagfmt, + '%', __percentfmt, + '+', __flagfmt, + ',', __flagfmt, + '-', __flagfmt, + 'C', __runefmt, /* Plan 9 addition */ + 'E', __efgfmt, +#ifndef PLAN9PORT + 'F', __efgfmt, /* ANSI only */ +#endif + 'G', __efgfmt, +#ifndef PLAN9PORT + 'L', __flagfmt, /* ANSI only */ +#endif + 'S', __runesfmt, /* Plan 9 addition */ + 'X', __ifmt, + 'b', __ifmt, /* Plan 9 addition */ + 'c', __charfmt, + 'd', __ifmt, + 'e', __efgfmt, + 'f', __efgfmt, + 'g', __efgfmt, + 'h', __flagfmt, +#ifndef PLAN9PORT + 'i', __ifmt, /* ANSI only */ +#endif + 'l', __flagfmt, + 'n', __countfmt, + 'o', __ifmt, + 'p', __ifmt, + 'r', __errfmt, + 's', __strfmt, +#ifdef PLAN9PORT + 'u', __flagfmt, +#else + 'u', __ifmt, +#endif + 'x', __ifmt, + 0, nil, +}; + + +int (*fmtdoquote)(int); + +/* + * __fmtlock() must be set + */ +static int +__fmtinstall(int c, Fmts f) +{ + Convfmt *p, *ep; + + if(c<=0 || c>=65536) + return -1; + if(!f) + f = __badfmt; + + ep = &fmtalloc.fmt[fmtalloc.nfmt]; + for(p=fmtalloc.fmt; p<ep; p++) + if(p->c == c) + break; + + if(p == &fmtalloc.fmt[Maxfmt]) + return -1; + + p->fmt = f; + if(p == ep){ /* installing a new format character */ + fmtalloc.nfmt++; + p->c = c; + } + + return 0; +} + +int +fmtinstall(int c, int (*f)(Fmt*)) +{ + int ret; + + __fmtlock(); + ret = __fmtinstall(c, f); + __fmtunlock(); + return ret; +} + +static Fmts +fmtfmt(int c) +{ + Convfmt *p, *ep; + + ep = &fmtalloc.fmt[fmtalloc.nfmt]; + for(p=fmtalloc.fmt; p<ep; p++) + if(p->c == c){ + while(p->fmt == nil) /* loop until value is updated */ + ; + return p->fmt; + } + + /* is this a predefined format char? */ + __fmtlock(); + for(p=knownfmt; p->c; p++) + if(p->c == c){ + __fmtinstall(p->c, p->fmt); + __fmtunlock(); + return p->fmt; + } + __fmtunlock(); + + return __badfmt; +} + +void* +__fmtdispatch(Fmt *f, void *fmt, int isrunes) +{ + Rune rune, r; + int i, n; + + f->flags = 0; + f->width = f->prec = 0; + + for(;;){ + if(isrunes){ + r = *(Rune*)fmt; + fmt = (Rune*)fmt + 1; + }else{ + fmt = (char*)fmt + chartorune(&rune, (char*)fmt); + r = rune; + } + f->r = r; + switch(r){ + case '\0': + return nil; + case '.': + f->flags |= FmtWidth|FmtPrec; + continue; + case '0': + if(!(f->flags & FmtWidth)){ + f->flags |= FmtZero; + continue; + } + /* fall through */ + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + i = 0; + while(r >= '0' && r <= '9'){ + i = i * 10 + r - '0'; + if(isrunes){ + r = *(Rune*)fmt; + fmt = (Rune*)fmt + 1; + }else{ + r = *(char*)fmt; + fmt = (char*)fmt + 1; + } + } + if(isrunes) + fmt = (Rune*)fmt - 1; + else + fmt = (char*)fmt - 1; + numflag: + if(f->flags & FmtWidth){ + f->flags |= FmtPrec; + f->prec = i; + }else{ + f->flags |= FmtWidth; + f->width = i; + } + continue; + case '*': + i = va_arg(f->args, int); + if(i < 0){ + /* + * negative precision => + * ignore the precision. + */ + if(f->flags & FmtPrec){ + f->flags &= ~FmtPrec; + f->prec = 0; + continue; + } + i = -i; + f->flags |= FmtLeft; + } + goto numflag; + } + n = (*fmtfmt(r))(f); + if(n < 0) + return nil; + if(n == 0) + return fmt; + } +} diff --git a/lib9/fmt/fmtdef.h b/lib9/fmt/fmtdef.h @@ -0,0 +1,116 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + +/* + * dofmt -- format to a buffer + * the number of characters formatted is returned, + * or -1 if there was an error. + * if the buffer is ever filled, flush is called. + * it should reset the buffer and return whether formatting should continue. + */ + +typedef int (*Fmts)(Fmt*); + +typedef struct Quoteinfo Quoteinfo; +struct Quoteinfo +{ + int quoted; /* if set, string must be quoted */ + int nrunesin; /* number of input runes that can be accepted */ + int nbytesin; /* number of input bytes that can be accepted */ + int nrunesout; /* number of runes that will be generated */ + int nbytesout; /* number of bytes that will be generated */ +}; + +/* Edit .+1,/^$/ |cfn |grep -v static | grep __ */ +double __Inf(int sign); +double __NaN(void); +int __badfmt(Fmt *f); +int __charfmt(Fmt *f); +int __countfmt(Fmt *f); +int __efgfmt(Fmt *fmt); +int __errfmt(Fmt *f); +int __flagfmt(Fmt *f); +int __fmtFdFlush(Fmt *f); +int __fmtcpy(Fmt *f, const void *vm, int n, int sz); +void* __fmtdispatch(Fmt *f, void *fmt, int isrunes); +void * __fmtflush(Fmt *f, void *t, int len); +void __fmtlock(void); +int __fmtpad(Fmt *f, int n); +double __fmtpow10(int n); +int __fmtrcpy(Fmt *f, const void *vm, int n); +void __fmtunlock(void); +int __ifmt(Fmt *f); +int __isInf(double d, int sign); +int __isNaN(double d); +int __needsquotes(char *s, int *quotelenp); +int __percentfmt(Fmt *f); +void __quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q, int sharp, int runesout); +int __quotestrfmt(int runesin, Fmt *f); +int __rfmtpad(Fmt *f, int n); +int __runefmt(Fmt *f); +int __runeneedsquotes(Rune *r, int *quotelenp); +int __runesfmt(Fmt *f); +int __strfmt(Fmt *f); + +#define FMTCHAR(f, t, s, c)\ + do{\ + if(t + 1 > (char*)s){\ + t = __fmtflush(f, t, 1);\ + if(t != nil)\ + s = f->stop;\ + else\ + return -1;\ + }\ + *t++ = c;\ + }while(0) + +#define FMTRCHAR(f, t, s, c)\ + do{\ + if(t + 1 > (Rune*)s){\ + t = __fmtflush(f, t, sizeof(Rune));\ + if(t != nil)\ + s = f->stop;\ + else\ + return -1;\ + }\ + *t++ = c;\ + }while(0) + +#define FMTRUNE(f, t, s, r)\ + do{\ + Rune _rune;\ + int _runelen;\ + if(t + UTFmax > (char*)s && t + (_runelen = runelen(r)) > (char*)s){\ + t = __fmtflush(f, t, _runelen);\ + if(t != nil)\ + s = f->stop;\ + else\ + return -1;\ + }\ + if(r < Runeself)\ + *t++ = r;\ + else{\ + _rune = r;\ + t += runetochar(t, &_rune);\ + }\ + }while(0) + +#ifdef va_copy +# define VA_COPY(a,b) va_copy(a,b) +# define VA_END(a) va_end(a) +#else +# define VA_COPY(a,b) (a) = (b) +# define VA_END(a) +#endif + diff --git a/lib9/fmt/fmtfd.c b/lib9/fmt/fmtfd.c @@ -0,0 +1,46 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +/* + * public routine for final flush of a formatting buffer + * to a file descriptor; returns total char count. + */ +int +fmtfdflush(Fmt *f) +{ + if(__fmtFdFlush(f) <= 0) + return -1; + return f->nfmt; +} + +/* + * initialize an output buffer for buffered printing + */ +int +fmtfdinit(Fmt *f, int fd, char *buf, int size) +{ + f->runes = 0; + f->start = buf; + f->to = buf; + f->stop = buf + size; + f->flush = __fmtFdFlush; + f->farg = (void*)fd; + f->nfmt = 0; + return 0; +} diff --git a/lib9/fmt/fmtfdflush.c b/lib9/fmt/fmtfdflush.c @@ -0,0 +1,34 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <unistd.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +/* + * generic routine for flushing a formatting buffer + * to a file descriptor + */ +int +__fmtFdFlush(Fmt *f) +{ + int n; + + n = (char*)f->to - (char*)f->start; + if(n && write((int)f->farg, f->start, n) != n) + return 0; + f->to = f->start; + return 1; +} diff --git a/lib9/fmt/fmtlock.c b/lib9/fmt/fmtlock.c @@ -0,0 +1,27 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +void +__fmtlock(void) +{ +} + +void +__fmtunlock(void) +{ +} diff --git a/lib9/fmt/fmtprint.c b/lib9/fmt/fmtprint.c @@ -0,0 +1,48 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +/* + * format a string into the output buffer + * designed for formats which themselves call fmt, + * but ignore any width flags + */ +int +fmtprint(Fmt *f, char *fmt, ...) +{ + va_list va; + int n; + + f->flags = 0; + f->width = 0; + f->prec = 0; + VA_COPY(va, f->args); + VA_END(f->args); + va_start(f->args, fmt); + n = dofmt(f, fmt); + va_end(f->args); + f->flags = 0; + f->width = 0; + f->prec = 0; + VA_COPY(f->args,va); + VA_END(va); + if(n >= 0) + return 0; + return n; +} + diff --git a/lib9/fmt/fmtquote.c b/lib9/fmt/fmtquote.c @@ -0,0 +1,264 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +/* + * How many bytes of output UTF will be produced by quoting (if necessary) this string? + * How many runes? How much of the input will be consumed? + * The parameter q is filled in by __quotesetup. + * The string may be UTF or Runes (s or r). + * Return count does not include NUL. + * Terminate the scan at the first of: + * NUL in input + * count exceeded in input + * count exceeded on output + * *ninp is set to number of input bytes accepted. + * nin may be <0 initially, to avoid checking input by count. + */ +void +__quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q, int sharp, int runesout) +{ + int w; + Rune c; + + q->quoted = 0; + q->nbytesout = 0; + q->nrunesout = 0; + q->nbytesin = 0; + q->nrunesin = 0; + if(sharp || nin==0 || (s && *s=='\0') || (r && *r=='\0')){ + if(nout < 2) + return; + q->quoted = 1; + q->nbytesout = 2; + q->nrunesout = 2; + } + for(; nin!=0; nin--){ + if(s) + w = chartorune(&c, s); + else{ + c = *r; + w = runelen(c); + } + + if(c == '\0') + break; + if(runesout){ + if(q->nrunesout+1 > nout) + break; + }else{ + if(q->nbytesout+w > nout) + break; + } + + if((c <= L' ') || (c == L'\'') || (fmtdoquote!=nil && fmtdoquote(c))){ + if(!q->quoted){ + if(runesout){ + if(1+q->nrunesout+1+1 > nout) /* no room for quotes */ + break; + }else{ + if(1+q->nbytesout+w+1 > nout) /* no room for quotes */ + break; + } + q->nrunesout += 2; /* include quotes */ + q->nbytesout += 2; /* include quotes */ + q->quoted = 1; + } + if(c == '\'') { + if(runesout){ + if(1+q->nrunesout+1 > nout) /* no room for quotes */ + break; + }else{ + if(1+q->nbytesout+w > nout) /* no room for quotes */ + break; + } + q->nbytesout++; + q->nrunesout++; /* quotes reproduce as two characters */ + } + } + + /* advance input */ + if(s) + s += w; + else + r++; + q->nbytesin += w; + q->nrunesin++; + + /* advance output */ + q->nbytesout += w; + q->nrunesout++; + } +} + +static int +qstrfmt(char *sin, Rune *rin, Quoteinfo *q, Fmt *f) +{ + Rune r, *rm, *rme; + char *t, *s, *m, *me; + Rune *rt, *rs; + ulong fl; + int nc, w; + + m = sin; + me = m + q->nbytesin; + rm = rin; + rme = rm + q->nrunesin; + + w = f->width; + fl = f->flags; + if(f->runes){ + if(!(fl & FmtLeft) && __rfmtpad(f, w - q->nrunesout) < 0) + return -1; + }else{ + if(!(fl & FmtLeft) && __fmtpad(f, w - q->nbytesout) < 0) + return -1; + } + t = (char*)f->to; + s = (char*)f->stop; + rt = (Rune*)f->to; + rs = (Rune*)f->stop; + if(f->runes) + FMTRCHAR(f, rt, rs, '\''); + else + FMTRUNE(f, t, s, '\''); + for(nc = q->nrunesin; nc > 0; nc--){ + if(sin){ + r = *(uchar*)m; + if(r < Runeself) + m++; + else if((me - m) >= UTFmax || fullrune(m, me-m)) + m += chartorune(&r, m); + else + break; + }else{ + if(rm >= rme) + break; + r = *(uchar*)rm++; + } + if(f->runes){ + FMTRCHAR(f, rt, rs, r); + if(r == '\'') + FMTRCHAR(f, rt, rs, r); + }else{ + FMTRUNE(f, t, s, r); + if(r == '\'') + FMTRUNE(f, t, s, r); + } + } + + if(f->runes){ + FMTRCHAR(f, rt, rs, '\''); + USED(rs); + f->nfmt += rt - (Rune *)f->to; + f->to = rt; + if(fl & FmtLeft && __rfmtpad(f, w - q->nrunesout) < 0) + return -1; + }else{ + FMTRUNE(f, t, s, '\''); + USED(s); + f->nfmt += t - (char *)f->to; + f->to = t; + if(fl & FmtLeft && __fmtpad(f, w - q->nbytesout) < 0) + return -1; + } + return 0; +} + +int +__quotestrfmt(int runesin, Fmt *f) +{ + int nin, outlen; + Rune *r; + char *s; + Quoteinfo q; + + nin = -1; + if(f->flags&FmtPrec) + nin = f->prec; + if(runesin){ + r = va_arg(f->args, Rune *); + s = nil; + }else{ + s = va_arg(f->args, char *); + r = nil; + } + if(!s && !r) + return __fmtcpy(f, (void*)"<nil>", 5, 5); + + if(f->flush) + outlen = 0x7FFFFFFF; /* if we can flush, no output limit */ + else if(f->runes) + outlen = (Rune*)f->stop - (Rune*)f->to; + else + outlen = (char*)f->stop - (char*)f->to; + + __quotesetup(s, r, nin, outlen, &q, f->flags&FmtSharp, f->runes); +//print("bytes in %d bytes out %d runes in %d runesout %d\n", q.nbytesin, q.nbytesout, q.nrunesin, q.nrunesout); + + if(runesin){ + if(!q.quoted) + return __fmtrcpy(f, r, q.nrunesin); + return qstrfmt(nil, r, &q, f); + } + + if(!q.quoted) + return __fmtcpy(f, s, q.nrunesin, q.nbytesin); + return qstrfmt(s, nil, &q, f); +} + +int +quotestrfmt(Fmt *f) +{ + return __quotestrfmt(0, f); +} + +int +quoterunestrfmt(Fmt *f) +{ + return __quotestrfmt(1, f); +} + +void +quotefmtinstall(void) +{ + fmtinstall('q', quotestrfmt); + fmtinstall('Q', quoterunestrfmt); +} + +int +__needsquotes(char *s, int *quotelenp) +{ + Quoteinfo q; + + __quotesetup(s, nil, -1, 0x7FFFFFFF, &q, 0, 0); + *quotelenp = q.nbytesout; + + return q.quoted; +} + +int +__runeneedsquotes(Rune *r, int *quotelenp) +{ + Quoteinfo q; + + __quotesetup(nil, r, -1, 0x7FFFFFFF, &q, 0, 0); + *quotelenp = q.nrunesout; + + return q.quoted; +} diff --git a/lib9/fmt/fmtrune.c b/lib9/fmt/fmtrune.c @@ -0,0 +1,40 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +int +fmtrune(Fmt *f, int r) +{ + Rune *rt; + char *t; + int n; + + if(f->runes){ + rt = (Rune*)f->to; + FMTRCHAR(f, rt, f->stop, r); + f->to = rt; + n = 1; + }else{ + t = (char*)f->to; + FMTRUNE(f, t, f->stop, r); + n = t - (char*)f->to; + f->to = t; + } + f->nfmt += n; + return 0; +} diff --git a/lib9/fmt/fmtstr.c b/lib9/fmt/fmtstr.c @@ -0,0 +1,27 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdlib.h> +#include <stdarg.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +char* +fmtstrflush(Fmt *f) +{ + if(f->start == nil) + return nil; + *(char*)f->to = '\0'; + return (char*)f->start; +} diff --git a/lib9/fmt/fmtvprint.c b/lib9/fmt/fmtvprint.c @@ -0,0 +1,49 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + + +/* + * format a string into the output buffer + * designed for formats which themselves call fmt, + * but ignore any width flags + */ +int +fmtvprint(Fmt *f, char *fmt, va_list args) +{ + va_list va; + int n; + + f->flags = 0; + f->width = 0; + f->prec = 0; + VA_COPY(va,f->args); + VA_END(f->args); + VA_COPY(f->args,args); + n = dofmt(f, fmt); + f->flags = 0; + f->width = 0; + f->prec = 0; + VA_END(f->args); + VA_COPY(f->args,va); + VA_END(va); + if(n >= 0) + return 0; + return n; +} + diff --git a/lib9/fmt/fprint.c b/lib9/fmt/fprint.c @@ -0,0 +1,29 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +int +fprint(int fd, char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = vfprint(fd, fmt, args); + va_end(args); + return n; +} diff --git a/lib9/fmt/nan64.c b/lib9/fmt/nan64.c @@ -0,0 +1,67 @@ +/* + * 64-bit IEEE not-a-number routines. + * This is big/little-endian portable assuming that + * the 64-bit doubles and 64-bit integers have the + * same byte ordering. + */ + +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +#if defined (__APPLE__) || (__powerpc__) +#define _NEEDLL +#endif + +static uvlong uvnan = ((uvlong)0x7FF00000<<32)|0x00000001; +static uvlong uvinf = ((uvlong)0x7FF00000<<32)|0x00000000; +static uvlong uvneginf = ((uvlong)0xFFF00000<<32)|0x00000000; + +double +__NaN(void) +{ + uvlong *p; + + /* gcc complains about "return *(double*)&uvnan;" */ + p = &uvnan; + return *(double*)p; +} + +int +__isNaN(double d) +{ + uvlong x; + double *p; + + p = &d; + x = *(uvlong*)p; + return (ulong)(x>>32)==0x7FF00000 && !__isInf(d, 0); +} + +double +__Inf(int sign) +{ + uvlong *p; + + if(sign < 0) + p = &uvinf; + else + p = &uvneginf; + return *(double*)p; +} + +int +__isInf(double d, int sign) +{ + uvlong x; + double *p; + + p = &d; + x = *(uvlong*)p; + if(sign == 0) + return x==uvinf || x==uvneginf; + else if(sign > 0) + return x==uvinf; + else + return x==uvneginf; +} diff --git a/lib9/fmt/plan9.h b/lib9/fmt/plan9.h @@ -0,0 +1,33 @@ +/* + * compiler directive on Plan 9 + */ +#ifndef USED +#define USED(x) if(x);else +#endif + +/* + * easiest way to make sure these are defined + */ +#define uchar _fmtuchar +#define ushort _fmtushort +#define uint _fmtuint +#define ulong _fmtulong +#define vlong _fmtvlong +#define uvlong _fmtuvlong +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned long long uvlong; +typedef long long vlong; + +/* + * nil cannot be ((void*)0) on ANSI C, + * because it is used for function pointers + */ +#undef nil +#define nil 0 + +#undef nelem +#define nelem(x) (sizeof (x)/sizeof (x)[0]) + diff --git a/lib9/fmt/portdate b/lib9/fmt/portdate @@ -0,0 +1,30 @@ +dofmt.c 2004/1225 +dorfmt.c 2004/1225 +errfmt.c 2004/1225 +fltfmt.c 2004/1225 +fmt.c 2004/1225 +fmtfd.c 2004/1225 +fmtlock.c 2004/1225 +fmtprint.c 2004/1225 +fmtquote.c 2004/1225 +fmtrune.c 2004/1225 +fmtstr.c 2004/1225 +fmtvprint.c 2004/1225 +fprint.c 2004/1225 +print.c 2004/1225 +runefmtstr.c 2004/1225 +runeseprint.c 2004/1225 +runesmprint.c 2004/1225 +runesnprint.c 2004/1225 +runesprint.c 2004/1225 +runevseprint.c 2004/1225 +runevsmprint.c 2004/1225 +runevsnprint.c 2004/1225 +seprint.c 2004/1225 +smprint.c 2004/1225 +snprint.c 2004/1225 +sprint.c 2004/1225 +vfprint.c 2004/1225 +vseprint.c 2004/1225 +vsmprint.c 2004/1225 +vsnprint.c 2004/1225 diff --git a/lib9/fmt/pow10.c b/lib9/fmt/pow10.c @@ -0,0 +1,57 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +/* + * this table might overflow 127-bit exponent representations. + * in that case, truncate it after 1.0e38. + * it is important to get all one can from this + * routine since it is used in atof to scale numbers. + * the presumption is that C converts fp numbers better + * than multipication of lower powers of 10. + */ + +static +double tab[] = +{ + 1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, + 1.0e10,1.0e11,1.0e12,1.0e13,1.0e14,1.0e15,1.0e16,1.0e17,1.0e18,1.0e19, + 1.0e20,1.0e21,1.0e22,1.0e23,1.0e24,1.0e25,1.0e26,1.0e27,1.0e28,1.0e29, + 1.0e30,1.0e31,1.0e32,1.0e33,1.0e34,1.0e35,1.0e36,1.0e37,1.0e38,1.0e39, + 1.0e40,1.0e41,1.0e42,1.0e43,1.0e44,1.0e45,1.0e46,1.0e47,1.0e48,1.0e49, + 1.0e50,1.0e51,1.0e52,1.0e53,1.0e54,1.0e55,1.0e56,1.0e57,1.0e58,1.0e59, + 1.0e60,1.0e61,1.0e62,1.0e63,1.0e64,1.0e65,1.0e66,1.0e67,1.0e68,1.0e69, +}; + +double +__fmtpow10(int n) +{ + int m; + + if(n < 0) { + n = -n; + if(n < (int)(sizeof(tab)/sizeof(tab[0]))) + return 1/tab[n]; + m = n/2; + return __fmtpow10(-m) * __fmtpow10(m-n); + } + if(n < (int)(sizeof(tab)/sizeof(tab[0]))) + return tab[n]; + m = n/2; + return __fmtpow10(m) * __fmtpow10(n-m); +} diff --git a/lib9/fmt/print.c b/lib9/fmt/print.c @@ -0,0 +1,29 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +int +print(char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = vfprint(1, fmt, args); + va_end(args); + return n; +} diff --git a/lib9/fmt/runefmtstr.c b/lib9/fmt/runefmtstr.c @@ -0,0 +1,27 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <stdlib.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +Rune* +runefmtstrflush(Fmt *f) +{ + if(f->start == nil) + return nil; + *(Rune*)f->to = '\0'; + return f->start; +} diff --git a/lib9/fmt/runeseprint.c b/lib9/fmt/runeseprint.c @@ -0,0 +1,30 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +Rune* +runeseprint(Rune *buf, Rune *e, char *fmt, ...) +{ + Rune *p; + va_list args; + + va_start(args, fmt); + p = runevseprint(buf, e, fmt, args); + va_end(args); + return p; +} diff --git a/lib9/fmt/runesmprint.c b/lib9/fmt/runesmprint.c @@ -0,0 +1,30 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +Rune* +runesmprint(char *fmt, ...) +{ + va_list args; + Rune *p; + + va_start(args, fmt); + p = runevsmprint(fmt, args); + va_end(args); + return p; +} diff --git a/lib9/fmt/runesnprint.c b/lib9/fmt/runesnprint.c @@ -0,0 +1,31 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +int +runesnprint(Rune *buf, int len, char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = runevsnprint(buf, len, fmt, args); + va_end(args); + return n; +} + diff --git a/lib9/fmt/runesprint.c b/lib9/fmt/runesprint.c @@ -0,0 +1,30 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +int +runesprint(Rune *buf, char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = runevsnprint(buf, 256, fmt, args); + va_end(args); + return n; +} diff --git a/lib9/fmt/runevseprint.c b/lib9/fmt/runevseprint.c @@ -0,0 +1,40 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +Rune* +runevseprint(Rune *buf, Rune *e, char *fmt, va_list args) +{ + Fmt f; + + if(e <= buf) + return nil; + f.runes = 1; + f.start = buf; + f.to = buf; + f.stop = e - 1; + f.flush = nil; + f.farg = nil; + f.nfmt = 0; + VA_COPY(f.args,args); + dofmt(&f, fmt); + VA_END(f.args); + *(Rune*)f.to = '\0'; + return (Rune*)f.to; +} + diff --git a/lib9/fmt/runevsmprint.c b/lib9/fmt/runevsmprint.c @@ -0,0 +1,97 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +/* + * Plan 9 port version must include libc.h in order to + * get Plan 9 debugging malloc, which sometimes returns + * different pointers than the standard malloc. + */ +#ifdef PLAN9PORT +#include <u.h> +#include <libc.h> +#include "fmtdef.h" +#else +#include <stdlib.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" +#endif + +static int +runeFmtStrFlush(Fmt *f) +{ + Rune *s; + int n; + + if(f->start == nil) + return 0; + n = (int)f->farg; + n *= 2; + s = (Rune*)f->start; + f->start = realloc(s, sizeof(Rune)*n); + if(f->start == nil){ + f->farg = nil; + f->to = nil; + f->stop = nil; + free(s); + return 0; + } + f->farg = (void*)n; + f->to = (Rune*)f->start + ((Rune*)f->to - s); + f->stop = (Rune*)f->start + n - 1; + return 1; +} + +int +runefmtstrinit(Fmt *f) +{ + int n; + + memset(f, 0, sizeof *f); + f->runes = 1; + n = 32; + f->start = malloc(sizeof(Rune)*n); + if(f->start == nil) + return -1; + f->to = f->start; + f->stop = (Rune*)f->start + n - 1; + f->flush = runeFmtStrFlush; + f->farg = (void*)n; + f->nfmt = 0; + return 0; +} + +/* + * print into an allocated string buffer + */ +Rune* +runevsmprint(char *fmt, va_list args) +{ + Fmt f; + int n; + + if(runefmtstrinit(&f) < 0) + return nil; + VA_COPY(f.args,args); + n = dofmt(&f, fmt); + VA_END(f.args); + if(f.start == nil) + return nil; + if(n < 0){ + free(f.start); + return nil; + } + *(Rune*)f.to = '\0'; + return (Rune*)f.start; +} diff --git a/lib9/fmt/runevsnprint.c b/lib9/fmt/runevsnprint.c @@ -0,0 +1,39 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +int +runevsnprint(Rune *buf, int len, char *fmt, va_list args) +{ + Fmt f; + + if(len <= 0) + return -1; + f.runes = 1; + f.start = buf; + f.to = buf; + f.stop = buf + len - 1; + f.flush = nil; + f.farg = nil; + f.nfmt = 0; + VA_COPY(f.args,args); + dofmt(&f, fmt); + VA_END(f.args); + *(Rune*)f.to = '\0'; + return (Rune*)f.to - buf; +} diff --git a/lib9/fmt/seprint.c b/lib9/fmt/seprint.c @@ -0,0 +1,29 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +char* +seprint(char *buf, char *e, char *fmt, ...) +{ + char *p; + va_list args; + + va_start(args, fmt); + p = vseprint(buf, e, fmt, args); + va_end(args); + return p; +} diff --git a/lib9/fmt/smprint.c b/lib9/fmt/smprint.c @@ -0,0 +1,29 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +char* +smprint(char *fmt, ...) +{ + va_list args; + char *p; + + va_start(args, fmt); + p = vsmprint(fmt, args); + va_end(args); + return p; +} diff --git a/lib9/fmt/snprint.c b/lib9/fmt/snprint.c @@ -0,0 +1,30 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +int +snprint(char *buf, int len, char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = vsnprint(buf, len, fmt, args); + va_end(args); + return n; +} + diff --git a/lib9/fmt/sprint.c b/lib9/fmt/sprint.c @@ -0,0 +1,39 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <fmt.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +int +sprint(char *buf, char *fmt, ...) +{ + int n; + uint len; + va_list args; + + len = 1<<30; /* big number, but sprint is deprecated anyway */ + /* + * on PowerPC, the stack is near the top of memory, so + * we must be sure not to overflow a 32-bit pointer. + */ + if(buf+len < buf) + len = -(uint)buf-1; + + va_start(args, fmt); + n = vsnprint(buf, len, fmt, args); + va_end(args); + return n; +} diff --git a/lib9/fmt/strtod.c b/lib9/fmt/strtod.c @@ -0,0 +1,532 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdlib.h> +#include <math.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +static ulong +umuldiv(ulong a, ulong b, ulong c) +{ + double d; + + d = ((double)a * (double)b) / (double)c; + if(d >= 4294967295.) + d = 4294967295.; + return (ulong)d; +} + +/* + * This routine will convert to arbitrary precision + * floating point entirely in multi-precision fixed. + * The answer is the closest floating point number to + * the given decimal number. Exactly half way are + * rounded ala ieee rules. + * Method is to scale input decimal between .500 and .999... + * with external power of 2, then binary search for the + * closest mantissa to this decimal number. + * Nmant is is the required precision. (53 for ieee dp) + * Nbits is the max number of bits/word. (must be <= 28) + * Prec is calculated - the number of words of fixed mantissa. + */ +enum +{ + Nbits = 28, /* bits safely represented in a ulong */ + Nmant = 53, /* bits of precision required */ + Prec = (Nmant+Nbits+1)/Nbits, /* words of Nbits each to represent mantissa */ + Sigbit = 1<<(Prec*Nbits-Nmant), /* first significant bit of Prec-th word */ + Ndig = 1500, + One = (ulong)(1<<Nbits), + Half = (ulong)(One>>1), + Maxe = 310, + + Fsign = 1<<0, /* found - */ + Fesign = 1<<1, /* found e- */ + Fdpoint = 1<<2, /* found . */ + + S0 = 0, /* _ _S0 +S1 #S2 .S3 */ + S1, /* _+ #S2 .S3 */ + S2, /* _+# #S2 .S4 eS5 */ + S3, /* _+. #S4 */ + S4, /* _+#.# #S4 eS5 */ + S5, /* _+#.#e +S6 #S7 */ + S6, /* _+#.#e+ #S7 */ + S7, /* _+#.#e+# #S7 */ +}; + +static int xcmp(char*, char*); +static int fpcmp(char*, ulong*); +static void frnorm(ulong*); +static void divascii(char*, int*, int*, int*); +static void mulascii(char*, int*, int*, int*); + +typedef struct Tab Tab; +struct Tab +{ + int bp; + int siz; + char* cmp; +}; + +double +fmtstrtod(const char *as, char **aas) +{ + int na, ex, dp, bp, c, i, flag, state; + ulong low[Prec], hig[Prec], mid[Prec]; + double d; + char *s, a[Ndig]; + + flag = 0; /* Fsign, Fesign, Fdpoint */ + na = 0; /* number of digits of a[] */ + dp = 0; /* na of decimal point */ + ex = 0; /* exonent */ + + state = S0; + for(s=(char*)as;; s++) { + c = *s; + if(c >= '0' && c <= '9') { + switch(state) { + case S0: + case S1: + case S2: + state = S2; + break; + case S3: + case S4: + state = S4; + break; + + case S5: + case S6: + case S7: + state = S7; + ex = ex*10 + (c-'0'); + continue; + } + if(na == 0 && c == '0') { + dp--; + continue; + } + if(na < Ndig-50) + a[na++] = c; + continue; + } + switch(c) { + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + if(state == S0) + continue; + break; + case '-': + if(state == S0) + flag |= Fsign; + else + flag |= Fesign; + case '+': + if(state == S0) + state = S1; + else + if(state == S5) + state = S6; + else + break; /* syntax */ + continue; + case '.': + flag |= Fdpoint; + dp = na; + if(state == S0 || state == S1) { + state = S3; + continue; + } + if(state == S2) { + state = S4; + continue; + } + break; + case 'e': + case 'E': + if(state == S2 || state == S4) { + state = S5; + continue; + } + break; + } + break; + } + + /* + * clean up return char-pointer + */ + switch(state) { + case S0: + if(xcmp(s, "nan") == 0) { + if(aas != nil) + *aas = s+3; + goto retnan; + } + case S1: + if(xcmp(s, "infinity") == 0) { + if(aas != nil) + *aas = s+8; + goto retinf; + } + if(xcmp(s, "inf") == 0) { + if(aas != nil) + *aas = s+3; + goto retinf; + } + case S3: + if(aas != nil) + *aas = (char*)as; + goto ret0; /* no digits found */ + case S6: + s--; /* back over +- */ + case S5: + s--; /* back over e */ + break; + } + if(aas != nil) + *aas = s; + + if(flag & Fdpoint) + while(na > 0 && a[na-1] == '0') + na--; + if(na == 0) + goto ret0; /* zero */ + a[na] = 0; + if(!(flag & Fdpoint)) + dp = na; + if(flag & Fesign) + ex = -ex; + dp += ex; + if(dp < -Maxe){ + errno = ERANGE; + goto ret0; /* underflow by exp */ + } else + if(dp > +Maxe) + goto retinf; /* overflow by exp */ + + /* + * normalize the decimal ascii number + * to range .[5-9][0-9]* e0 + */ + bp = 0; /* binary exponent */ + while(dp > 0) + divascii(a, &na, &dp, &bp); + while(dp < 0 || a[0] < '5') + mulascii(a, &na, &dp, &bp); + + /* close approx by naive conversion */ + mid[0] = 0; + mid[1] = 1; + for(i=0; c=a[i]; i++) { + mid[0] = mid[0]*10 + (c-'0'); + mid[1] = mid[1]*10; + if(i >= 8) + break; + } + low[0] = umuldiv(mid[0], One, mid[1]); + hig[0] = umuldiv(mid[0]+1, One, mid[1]); + for(i=1; i<Prec; i++) { + low[i] = 0; + hig[i] = One-1; + } + + /* binary search for closest mantissa */ + for(;;) { + /* mid = (hig + low) / 2 */ + c = 0; + for(i=0; i<Prec; i++) { + mid[i] = hig[i] + low[i]; + if(c) + mid[i] += One; + c = mid[i] & 1; + mid[i] >>= 1; + } + frnorm(mid); + + /* compare */ + c = fpcmp(a, mid); + if(c > 0) { + c = 1; + for(i=0; i<Prec; i++) + if(low[i] != mid[i]) { + c = 0; + low[i] = mid[i]; + } + if(c) + break; /* between mid and hig */ + continue; + } + if(c < 0) { + for(i=0; i<Prec; i++) + hig[i] = mid[i]; + continue; + } + + /* only hard part is if even/odd roundings wants to go up */ + c = mid[Prec-1] & (Sigbit-1); + if(c == Sigbit/2 && (mid[Prec-1]&Sigbit) == 0) + mid[Prec-1] -= c; + break; /* exactly mid */ + } + + /* normal rounding applies */ + c = mid[Prec-1] & (Sigbit-1); + mid[Prec-1] -= c; + if(c >= Sigbit/2) { + mid[Prec-1] += Sigbit; + frnorm(mid); + } + goto out; + +ret0: + return 0; + +retnan: + return __NaN(); + +retinf: + /* + * Unix strtod requires these. Plan 9 would return Inf(0) or Inf(-1). */ + errno = ERANGE; + if(flag & Fsign) + return -HUGE_VAL; + return HUGE_VAL; + +out: + d = 0; + for(i=0; i<Prec; i++) + d = d*One + mid[i]; + if(flag & Fsign) + d = -d; + d = ldexp(d, bp - Prec*Nbits); + if(d == 0){ /* underflow */ + errno = ERANGE; + } + return d; +} + +static void +frnorm(ulong *f) +{ + int i, c; + + c = 0; + for(i=Prec-1; i>0; i--) { + f[i] += c; + c = f[i] >> Nbits; + f[i] &= One-1; + } + f[0] += c; +} + +static int +fpcmp(char *a, ulong* f) +{ + ulong tf[Prec]; + int i, d, c; + + for(i=0; i<Prec; i++) + tf[i] = f[i]; + + for(;;) { + /* tf *= 10 */ + for(i=0; i<Prec; i++) + tf[i] = tf[i]*10; + frnorm(tf); + d = (tf[0] >> Nbits) + '0'; + tf[0] &= One-1; + + /* compare next digit */ + c = *a; + if(c == 0) { + if('0' < d) + return -1; + if(tf[0] != 0) + goto cont; + for(i=1; i<Prec; i++) + if(tf[i] != 0) + goto cont; + return 0; + } + if(c > d) + return +1; + if(c < d) + return -1; + a++; + cont:; + } +} + +static void +divby(char *a, int *na, int b) +{ + int n, c; + char *p; + + p = a; + n = 0; + while(n>>b == 0) { + c = *a++; + if(c == 0) { + while(n) { + c = n*10; + if(c>>b) + break; + n = c; + } + goto xx; + } + n = n*10 + c-'0'; + (*na)--; + } + for(;;) { + c = n>>b; + n -= c<<b; + *p++ = c + '0'; + c = *a++; + if(c == 0) + break; + n = n*10 + c-'0'; + } + (*na)++; +xx: + while(n) { + n = n*10; + c = n>>b; + n -= c<<b; + *p++ = c + '0'; + (*na)++; + } + *p = 0; +} + +static Tab tab1[] = +{ + 1, 0, "", + 3, 1, "7", + 6, 2, "63", + 9, 3, "511", + 13, 4, "8191", + 16, 5, "65535", + 19, 6, "524287", + 23, 7, "8388607", + 26, 8, "67108863", + 27, 9, "134217727", +}; + +static void +divascii(char *a, int *na, int *dp, int *bp) +{ + int b, d; + Tab *t; + + d = *dp; + if(d >= (int)(nelem(tab1))) + d = (int)(nelem(tab1))-1; + t = tab1 + d; + b = t->bp; + if(memcmp(a, t->cmp, t->siz) > 0) + d--; + *dp -= d; + *bp += b; + divby(a, na, b); +} + +static void +mulby(char *a, char *p, char *q, int b) +{ + int n, c; + + n = 0; + *p = 0; + for(;;) { + q--; + if(q < a) + break; + c = *q - '0'; + c = (c<<b) + n; + n = c/10; + c -= n*10; + p--; + *p = c + '0'; + } + while(n) { + c = n; + n = c/10; + c -= n*10; + p--; + *p = c + '0'; + } +} + +static Tab tab2[] = +{ + 1, 1, "", /* dp = 0-0 */ + 3, 3, "125", + 6, 5, "15625", + 9, 7, "1953125", + 13, 10, "1220703125", + 16, 12, "152587890625", + 19, 14, "19073486328125", + 23, 17, "11920928955078125", + 26, 19, "1490116119384765625", + 27, 19, "7450580596923828125", /* dp 8-9 */ +}; + +static void +mulascii(char *a, int *na, int *dp, int *bp) +{ + char *p; + int d, b; + Tab *t; + + d = -*dp; + if(d >= (int)(nelem(tab2))) + d = (int)(nelem(tab2))-1; + t = tab2 + d; + b = t->bp; + if(memcmp(a, t->cmp, t->siz) < 0) + d--; + p = a + *na; + *bp -= b; + *dp += d; + *na += d; + mulby(a, p+d, p, b); +} + +static int +xcmp(char *a, char *b) +{ + int c1, c2; + + while(c1 = *b++) { + c2 = *a++; + if(isupper(c2)) + c2 = tolower(c2); + if(c1 != c2) + return 1; + } + return 0; +} diff --git a/lib9/fmt/strtod.h b/lib9/fmt/strtod.h @@ -0,0 +1,4 @@ +extern double __NaN(void); +extern double __Inf(int); +extern double __isNaN(double); +extern double __isInf(double, int); diff --git a/lib9/fmt/test.c b/lib9/fmt/test.c @@ -0,0 +1,44 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdio.h> +#include <stdarg.h> +#include <utf.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +int +main(int argc, char *argv[]) +{ + quotefmtinstall(); + print("hello world\n"); + print("x: %x\n", 0x87654321); + print("u: %u\n", 0x87654321); + print("d: %d\n", 0x87654321); + print("s: %s\n", "hi there"); + print("q: %q\n", "hi i'm here"); + print("c: %c\n", '!'); + print("g: %g %g %g\n", 3.14159, 3.14159e10, 3.14159e-10); + print("e: %e %e %e\n", 3.14159, 3.14159e10, 3.14159e-10); + print("f: %f %f %f\n", 3.14159, 3.14159e10, 3.14159e-10); + print("smiley: %C\n", (Rune)0x263a); + print("%g %.18g\n", 2e25, 2e25); + print("%2.18g\n", 1.0); + print("%2.18f\n", 1.0); + print("%f\n", 3.1415927/4); + print("%d\n", 23); + print("%i\n", 23); + print("%0.10d\n", 12345); + return 0; +} diff --git a/lib9/fmt/test2.c b/lib9/fmt/test2.c @@ -0,0 +1,9 @@ +#include <stdarg.h> +#include <utf.h> +#include <fmt.h> + +int +main(int argc, char **argv) +{ + print("%020.10d\n", 100); +} diff --git a/lib9/fmt/test3.c b/lib9/fmt/test3.c @@ -0,0 +1,52 @@ +#include <u.h> +#include <libc.h> +#include <stdio.h> + +void +test(char *fmt, ...) +{ + va_list arg; + char fmtbuf[100], stdbuf[100]; + + va_start(arg, fmt); + vsnprint(fmtbuf, sizeof fmtbuf, fmt, arg); + va_end(arg); + + va_start(arg, fmt); + vsnprint(stdbuf, sizeof stdbuf, fmt, arg); + va_end(arg); + + if(strcmp(fmtbuf, stdbuf) != 0) + print("fmt %s: fmt=\"%s\" std=\"%s\"\n", fmt, fmtbuf, stdbuf); + + print("fmt %s: %s\n", fmt, fmtbuf); +} + + +int +main(int argc, char *argv[]) +{ + test("%f", 3.14159); + test("%f", 3.14159e10); + test("%f", 3.14159e-10); + + test("%e", 3.14159); + test("%e", 3.14159e10); + test("%e", 3.14159e-10); + + test("%g", 3.14159); + test("%g", 3.14159e10); + test("%g", 3.14159e-10); + + test("%g", 2e25); + test("%.18g", 2e25); + + test("%2.18g", 1.0); + test("%2.18f", 1.0); + test("%f", 3.1415927/4); + + test("%20.10d", 12345); + test("%0.10d", 12345); + + return 0; +} diff --git a/lib9/fmt/vfprint.c b/lib9/fmt/vfprint.c @@ -0,0 +1,33 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +int +vfprint(int fd, char *fmt, va_list args) +{ + Fmt f; + char buf[256]; + int n; + + fmtfdinit(&f, fd, buf, sizeof(buf)); + VA_COPY(f.args,args); + n = dofmt(&f, fmt); + VA_END(f.args); + if(n > 0 && __fmtFdFlush(&f) == 0) + return -1; + return n; +} diff --git a/lib9/fmt/vseprint.c b/lib9/fmt/vseprint.c @@ -0,0 +1,39 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +char* +vseprint(char *buf, char *e, char *fmt, va_list args) +{ + Fmt f; + + if(e <= buf) + return nil; + f.runes = 0; + f.start = buf; + f.to = buf; + f.stop = e - 1; + f.flush = 0; + f.farg = nil; + f.nfmt = 0; + VA_COPY(f.args,args); + dofmt(&f, fmt); + VA_END(f.args); + *(char*)f.to = '\0'; + return (char*)f.to; +} + diff --git a/lib9/fmt/vsmprint.c b/lib9/fmt/vsmprint.c @@ -0,0 +1,94 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +/* + * Plan 9 port version must include libc.h in order to + * get Plan 9 debugging malloc, which sometimes returns + * different pointers than the standard malloc. + */ +#ifdef PLAN9PORT +#include <u.h> +#include <libc.h> +#include "fmtdef.h" +#else +#include <stdlib.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" +#endif + +static int +fmtStrFlush(Fmt *f) +{ + char *s; + int n; + + if(f->start == nil) + return 0; + n = (int)f->farg; + n *= 2; + s = (char*)f->start; + f->start = realloc(s, n); + if(f->start == nil){ + f->farg = nil; + f->to = nil; + f->stop = nil; + free(s); + return 0; + } + f->farg = (void*)n; + f->to = (char*)f->start + ((char*)f->to - s); + f->stop = (char*)f->start + n - 1; + return 1; +} + +int +fmtstrinit(Fmt *f) +{ + int n; + + memset(f, 0, sizeof *f); + f->runes = 0; + n = 32; + f->start = malloc(n); + if(f->start == nil) + return -1; + f->to = f->start; + f->stop = (char*)f->start + n - 1; + f->flush = fmtStrFlush; + f->farg = (void*)n; + f->nfmt = 0; + return 0; +} + +/* + * print into an allocated string buffer + */ +char* +vsmprint(char *fmt, va_list args) +{ + Fmt f; + int n; + + if(fmtstrinit(&f) < 0) + return nil; + VA_COPY(f.args,args); + n = dofmt(&f, fmt); + VA_END(f.args); + if(n < 0){ + free(f.start); + return nil; + } + return fmtstrflush(&f); +} diff --git a/lib9/fmt/vsnprint.c b/lib9/fmt/vsnprint.c @@ -0,0 +1,39 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdlib.h> +#include <stdarg.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +int +vsnprint(char *buf, int len, char *fmt, va_list args) +{ + Fmt f; + + if(len <= 0) + return -1; + f.runes = 0; + f.start = buf; + f.to = buf; + f.stop = buf + len - 1; + f.flush = 0; + f.farg = nil; + f.nfmt = 0; + VA_COPY(f.args,args); + dofmt(&f, fmt); + VA_END(f.args); + *(char*)f.to = '\0'; + return (char*)f.to - buf; +} diff --git a/lib9/fmtlock2.c b/lib9/fmtlock2.c @@ -0,0 +1,16 @@ +#include <u.h> +#include <libc.h> + +static Lock fmtlock; + +void +__fmtlock(void) +{ + lock(&fmtlock); +} + +void +__fmtunlock(void) +{ + unlock(&fmtlock); +} diff --git a/lib9/fork.c b/lib9/fork.c @@ -0,0 +1,22 @@ +#include <u.h> +#include <signal.h> +#include <libc.h> +#include "9proc.h" +#undef fork + +int +p9fork(void) +{ + int pid; + sigset_t all, old; + + sigfillset(&all); + sigprocmask(SIG_SETMASK, &all, &old); + pid = fork(); + if(pid == 0){ + _clearuproc(); + _p9uproc(0); + } + sigprocmask(SIG_SETMASK, &old, nil); + return pid; +} diff --git a/lib9/get9root.c b/lib9/get9root.c @@ -0,0 +1,18 @@ +#include <u.h> +#include <libc.h> + +char* +get9root(void) +{ + static char *s; + + if(s) + return s; + + if((s = getenv("PLAN9")) != 0) + return s; + /* could do better - search $PATH */ + s = "/usr/local/plan9"; + return s; +} + diff --git a/lib9/getcallerpc-386.c b/lib9/getcallerpc-386.c @@ -0,0 +1,7 @@ +#include <lib9.h> + +ulong +getcallerpc(void *x) +{ + return (((ulong*)(x))[-1]); +} diff --git a/lib9/getcallerpc-PowerMacintosh.c b/lib9/getcallerpc-PowerMacintosh.c @@ -0,0 +1,7 @@ +#include <lib9.h> + +ulong +getcallerpc(void *x) +{ + return (((ulong*)(x))[-4]); +} diff --git a/lib9/getcallerpc-power.c b/lib9/getcallerpc-power.c @@ -0,0 +1,11 @@ +#include <lib9.h> + +ulong +getcallerpc(void *x) +{ + ulong *lp; + + lp = x; + + return lp[-1]; +} diff --git a/lib9/getcallerpc-ppc.c b/lib9/getcallerpc-ppc.c @@ -0,0 +1,7 @@ +#include <lib9.h> + +ulong +getcallerpc(void *x) +{ + return (((ulong*)(x))[-4]); +} diff --git a/lib9/getcallerpc-sun4u.s b/lib9/getcallerpc-sun4u.s @@ -0,0 +1,5 @@ +.text +.globl getcallerpc +getcallerpc: + retl + or %o7, %r0, %o0 diff --git a/lib9/getcallerpc-x86_64.c b/lib9/getcallerpc-x86_64.c @@ -0,0 +1,7 @@ +#include <lib9.h> + +ulong +getcallerpc(void *x) +{ + return (((ulong*)(x))[-1]); +} diff --git a/lib9/getenv.c b/lib9/getenv.c @@ -0,0 +1,26 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> + +char* +p9getenv(char *s) +{ + char *t; + + t = getenv(s); + if(t == 0) + return 0; + return strdup(t); +} + +int +p9putenv(char *s, char *v) +{ + char *t; + + t = smprint("%s=%s", s, v); + if(t == nil) + return -1; + putenv(t); + return 0; +} diff --git a/lib9/getfields.c b/lib9/getfields.c @@ -0,0 +1,36 @@ +#include <lib9.h> + +int +getfields(char *str, char **args, int max, int mflag, char *set) +{ + Rune r; + int nr, intok, narg; + + if(max <= 0) + return 0; + + narg = 0; + args[narg] = str; + if(!mflag) + narg++; + intok = 0; + for(;; str += nr) { + nr = chartorune(&r, str); + if(r == 0) + break; + if(utfrune(set, r)) { + if(narg >= max) + break; + *str = 0; + intok = 0; + args[narg] = str + nr; + if(!mflag) + narg++; + } else { + if(!intok && mflag) + narg++; + intok = 1; + } + } + return narg; +} diff --git a/lib9/getnetconn.c b/lib9/getnetconn.c @@ -0,0 +1,134 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/un.h> +#include <errno.h> + +#undef sun +#define sun sockun + +extern int _p9netfd(char*); + +static char *unknown = "unknown"; + +static int +convert(int s, struct sockaddr *sa, char **lsys, char **lserv, char **laddr) +{ + struct sockaddr_un *sun; + struct sockaddr_in *sin; + uchar *ip; + u32int ipl; + socklen_t sn; + int n; + char *net; + + switch(sa->sa_family){ + case AF_INET: + sin = (void*)sa; + ip = (uchar*)&sin->sin_addr; + ipl = *(u32int*)ip; + if(ipl == 0) + *lsys = strdup("*"); + else + *lsys = smprint("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + *lserv = smprint("%d", ntohs(sin->sin_port)); + sn = sizeof n; + if(getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) < 0) + return -1; + if(n == SOCK_STREAM) + net = "tcp"; + else if(n == SOCK_DGRAM) + net = "udp"; + else{ + werrstr("unknown network type"); + return -1; + } + *laddr = smprint("%s!%s!%s", net, *lsys, *lserv); + if(*lsys == nil || *lserv == nil || *laddr == nil) + return -1; + return 0; + case AF_UNIX: + sun = (void*)sa; + *lsys = unknown; + *lserv = unknown; + *laddr = smprint("unix!%s", sun->sun_path); + if(*laddr == nil) + return -1; + return 0; + default: + werrstr("unknown socket family"); + return -1; + } +} + +NetConnInfo* +getnetconninfo(char *dir, int fd) +{ + socklen_t sn; + union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_un sun; + } u; + NetConnInfo *nci; + + if(dir){ + if((fd = _p9netfd(dir)) < 0){ + werrstr("no such network connection %s", dir); + return nil; + } + } + + nci = mallocz(sizeof *nci, 1); + if(nci == nil) + goto err; + nci->dir = smprint("/dev/fd/%d", fd); + nci->root = strdup("/net"); + nci->spec = unknown; + if(nci->dir == nil || nci->root == nil) + goto err; + sn = sizeof sn; + if(getsockname(fd, &u.sa, &sn) < 0) + goto err; + if(convert(fd, &u.sa, &nci->lsys, &nci->lserv, &nci->laddr) < 0) + goto err; + sn = sizeof sn; + if(getpeername(fd, &u.sa, &sn) < 0) + goto err; + if(convert(fd, &u.sa, &nci->rsys, &nci->rserv, &nci->raddr) < 0) + goto err; + return nci; + +err: + freenetconninfo(nci); + return nil; +} + +static void +xfree(void *v) +{ + if(v != nil && v != unknown) + free(v); +} + +void +freenetconninfo(NetConnInfo *nci) +{ + if(nci == nil) + return; + xfree(nci->dir); + xfree(nci->root); + xfree(nci->spec); + xfree(nci->lsys); + xfree(nci->lserv); + xfree(nci->rsys); + xfree(nci->rserv); + xfree(nci->laddr); + xfree(nci->raddr); + free(nci); +} + diff --git a/lib9/getns.c b/lib9/getns.c @@ -0,0 +1,74 @@ +#include <u.h> +#include <libc.h> +#include <ctype.h> + +/* + * Absent other hints, it works reasonably well to use + * the X11 display name as the name space identifier. + * This is how sam's B has worked since the early days. + * Since most programs using name spaces are also using X, + * this still seems reasonable. Terminal-only sessions + * can set $NAMESPACE. + */ +static char* +nsfromdisplay(void) +{ + int fd; + Dir *d; + char *disp, *p; + + if((disp = getenv("DISPLAY")) == nil){ + werrstr("$DISPLAY not set"); + return nil; + } + + /* canonicalize: xxx:0.0 => xxx:0 */ + p = strrchr(disp, ':'); + if(p){ + p++; + while(isdigit((uchar)*p)) + p++; + if(strcmp(p, ".0") == 0) + *p = 0; + } + + p = smprint("/tmp/ns.%s.%s", getuser(), disp); + free(disp); + if(p == nil){ + werrstr("out of memory"); + return p; + } + if((fd=create(p, OREAD, DMDIR|0700)) >= 0){ + close(fd); + return p; + } + if((d = dirstat(p)) == nil){ + free(d); + werrstr("stat %s: %r", p); + free(p); + return nil; + } + if((d->mode&0777) != 0700 || strcmp(d->uid, getuser()) != 0){ + werrstr("bad name space dir %s", p); + free(p); + free(d); + return nil; + } + free(d); + return p; +} + +char* +getns(void) +{ + char *ns; + + ns = getenv("NAMESPACE"); + if(ns == nil) + ns = nsfromdisplay(); + if(ns == nil){ + werrstr("$NAMESPACE not set, %r"); + return nil; + } + return ns; +} diff --git a/lib9/getuser.c b/lib9/getuser.c @@ -0,0 +1,16 @@ +#include <u.h> +#include <pwd.h> +#include <libc.h> + +char* +getuser(void) +{ + static char user[64]; + struct passwd *pw; + + pw = getpwuid(getuid()); + if(pw == nil) + return "none"; + strecpy(user, user+sizeof user, pw->pw_name); + return user; +} diff --git a/lib9/getwd.c b/lib9/getwd.c @@ -0,0 +1,10 @@ +#include <u.h> +#include <libc.h> + +#undef getwd + +char* +p9getwd(char *s, int ns) +{ + return getcwd(s, ns); +} diff --git a/lib9/jmp-FreeBSD.s b/lib9/jmp-FreeBSD.s @@ -0,0 +1,3 @@ +.globl sigsetjmp, p9setjmp +p9setjmp: + jmp sigsetjmp diff --git a/lib9/jmp.c b/lib9/jmp.c @@ -0,0 +1,17 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> + +void +p9longjmp(p9jmp_buf buf, int val) +{ + siglongjmp((void*)buf, val); +} + +void +p9notejmp(void *x, p9jmp_buf buf, int val) +{ + USED(x); + siglongjmp((void*)buf, val); +} + diff --git a/lib9/lib9.h b/lib9/lib9.h @@ -0,0 +1,17 @@ +#include <string.h> +#include "utf.h" + +#define nil ((void*)0) + +#define uchar _fmtuchar +#define ushort _fmtushort +#define uint _fmtuint +#define ulong _fmtulong +#define vlong _fmtvlong +#define uvlong _fmtuvlong + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + diff --git a/lib9/libc.h b/lib9/libc.h @@ -0,0 +1,871 @@ +/* + * Lib9 is miscellany from the Plan 9 C library that doesn't + * fit into libutf or into libfmt, but is still missing from traditional + * Unix C libraries. + */ +#ifndef _LIBC_H_ +#define _LIBC_H_ 1 +#if defined(__cplusplus) +extern "C" { +#endif + +/* + * Begin usual libc.h + */ + +#ifndef nil +#define nil ((void*)0) +#endif +#define nelem(x) (sizeof(x)/sizeof((x)[0])) + +#ifndef offsetof +#define offsetof(s, m) (ulong)(&(((s*)0)->m)) +#endif + +/* + * mem routines (provided by system <string.h>) + * +extern void* memccpy(void*, void*, int, ulong); +extern void* memset(void*, int, ulong); +extern int memcmp(void*, void*, ulong); +extern void* memcpy(void*, void*, ulong); +extern void* memmove(void*, void*, ulong); +extern void* memchr(void*, int, ulong); + */ + +/* + * string routines (provided by system <string.h>) + * +extern char* strcat(char*, char*); +extern char* strchr(char*, int); +extern int strcmp(char*, char*); +extern char* strcpy(char*, char*); + */ +extern char* strecpy(char*, char*, char*); +extern char* p9strdup(char*); +/* +extern char* strncat(char*, char*, long); +extern char* strncpy(char*, char*, long); +extern int strncmp(char*, char*, long); +extern char* strpbrk(char*, char*); +extern char* strrchr(char*, int); +extern char* strtok(char*, char*); +extern long strlen(char*); +extern long strspn(char*, char*); +extern long strcspn(char*, char*); +extern char* strstr(char*, char*); + */ +extern int cistrncmp(char*, char*, int); +extern int cistrcmp(char*, char*); +extern char* cistrstr(char*, char*); +extern int tokenize(char*, char**, int); + +/* +enum +{ + UTFmax = 3, + Runesync = 0x80, + Runeself = 0x80, + Runeerror = 0x80, +}; +*/ + +/* + * rune routines (provided by <utf.h> + * +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern int runelen(long); +extern int runenlen(Rune*, int); +extern int fullrune(char*, int); +extern int utflen(char*); +extern int utfnlen(char*, long); +extern char* utfrune(char*, long); +extern char* utfrrune(char*, long); +extern char* utfutf(char*, char*); +extern char* utfecpy(char*, char*, char*); + +extern Rune* runestrcat(Rune*, Rune*); +extern Rune* runestrchr(Rune*, Rune); +extern int runestrcmp(Rune*, Rune*); +extern Rune* runestrcpy(Rune*, Rune*); +extern Rune* runestrncpy(Rune*, Rune*, long); +extern Rune* runestrecpy(Rune*, Rune*, Rune*); +extern Rune* runestrdup(Rune*); +extern Rune* runestrncat(Rune*, Rune*, long); +extern int runestrncmp(Rune*, Rune*, long); +extern Rune* runestrrchr(Rune*, Rune); +extern long runestrlen(Rune*); +extern Rune* runestrstr(Rune*, Rune*); + +extern Rune tolowerrune(Rune); +extern Rune totitlerune(Rune); +extern Rune toupperrune(Rune); +extern int isalpharune(Rune); +extern int islowerrune(Rune); +extern int isspacerune(Rune); +extern int istitlerune(Rune); +extern int isupperrune(Rune); + */ + +/* + * malloc (provied by system <stdlib.h>) + * +extern void* malloc(ulong); + */ +extern void* p9malloc(ulong); +extern void* mallocz(ulong, int); +extern void p9free(void*); +extern void* p9calloc(ulong, ulong); +extern void* p9realloc(void*, ulong); +extern void setmalloctag(void*, ulong); +extern void setrealloctag(void*, ulong); +extern ulong getmalloctag(void*); +extern ulong getrealloctag(void*); +/* +extern void* malloctopoolblock(void*); +*/ +#ifndef NOPLAN9DEFINES +#define malloc p9malloc +#define realloc p9realloc +#define calloc p9calloc +#define free p9free +#undef strdup +#define strdup p9strdup +#endif + +/* + * print routines (provided by <fmt.h>) + * +typedef struct Fmt Fmt; +struct Fmt{ + uchar runes; + void *start; + void *to; + void *stop; + int (*flush)(Fmt *); + void *farg; + int nfmt; + va_list args; + int r; + int width; + int prec; + ulong flags; +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + + FmtFlag = FmtByte << 1 +}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int sprint(char*, char*, ...); +extern int fprint(int, char*, ...); +extern int vfprint(int, char*, va_list); + +extern int runesprint(Rune*, char*, ...); +extern int runesnprint(Rune*, int, char*, ...); +extern int runevsnprint(Rune*, int, char*, va_list); +extern Rune* runeseprint(Rune*, Rune*, char*, ...); +extern Rune* runevseprint(Rune*, Rune*, char*, va_list); +extern Rune* runesmprint(char*, ...); +extern Rune* runevsmprint(char*, va_list); + +extern int fmtfdinit(Fmt*, int, char*, int); +extern int fmtfdflush(Fmt*); +extern int fmtstrinit(Fmt*); +extern char* fmtstrflush(Fmt*); +extern int runefmtstrinit(Fmt*); +extern Rune* runefmtstrflush(Fmt*); + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int dorfmt(Fmt*, Rune*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); +extern int fmtrunestrcpy(Fmt*, Rune*); + */ + +/* + * error string for %r + * supplied on per os basis, not part of fmt library + * + * (provided by lib9, but declared in fmt.h) + * +extern int errfmt(Fmt *f); + */ + +/* + * quoted strings + */ +extern char *unquotestrdup(char*); +extern Rune *unquoterunestrdup(Rune*); +extern char *quotestrdup(char*); +extern Rune *quoterunestrdup(Rune*); +/* + * in fmt.h + * +extern void quotefmtinstall(void); +extern int quotestrfmt(Fmt*); +extern int quoterunestrfmt(Fmt*); + */ +#ifndef NOPLAN9DEFINES +#define doquote fmtdoquote +#endif +extern int needsrcquote(int); + +/* + * random number + */ +extern void p9srand(long); +extern int p9rand(void); + +extern int p9nrand(int); +extern long p9lrand(void); +extern long p9lnrand(long); +extern double p9frand(void); +extern ulong truerand(void); /* uses /dev/random */ +extern ulong ntruerand(ulong); /* uses /dev/random */ + +#ifndef NOPLAN9DEFINES +#define srand p9srand +#define rand p9rand +#define nrand p9nrand +#define lrand p9lrand +#define lnrand p9lnrand +#define frand p9frand +#endif + +/* + * math + */ +extern ulong getfcr(void); +extern void setfsr(ulong); +extern ulong getfsr(void); +extern void setfcr(ulong); +extern double NaN(void); +extern double Inf(int); +extern int isNaN(double); +extern int isInf(double, int); +extern ulong umuldiv(ulong, ulong, ulong); +extern long muldiv(long, long, long); + +/* + * provided by math.h + * +extern double pow(double, double); +extern double atan2(double, double); +extern double fabs(double); +extern double atan(double); +extern double log(double); +extern double log10(double); +extern double exp(double); +extern double floor(double); +extern double ceil(double); +extern double hypot(double, double); +extern double sin(double); +extern double cos(double); +extern double tan(double); +extern double asin(double); +extern double acos(double); +extern double sinh(double); +extern double cosh(double); +extern double tanh(double); +extern double sqrt(double); +extern double fmod(double, double); +#define HUGE 3.4028234e38 +#define PIO2 1.570796326794896619231e0 +#define PI (PIO2+PIO2) + */ +#define PI M_PI +#define PIO2 M_PI_2 + +/* + * Time-of-day + */ + +typedef +struct Tm +{ + int sec; + int min; + int hour; + int mday; + int mon; + int year; + int wday; + int yday; + char zone[4]; + int tzoff; +} Tm; + +extern Tm* p9gmtime(long); +extern Tm* p9localtime(long); +extern char* p9asctime(Tm*); +extern char* p9ctime(long); +extern double p9cputime(void); +extern long p9times(long*); +extern long p9tm2sec(Tm*); +extern vlong p9nsec(void); + +#ifndef NOPLAN9DEFINES +#define gmtime p9gmtime +#define localtime p9localtime +#define asctime p9asctime +#define ctime p9ctime +#define cputime p9cputime +#define times p9times +#define tm2sec p9tm2sec +#define nsec p9nsec +#endif + +/* + * one-of-a-kind + */ +enum +{ + PNPROC = 1, + PNGROUP = 2, +}; + +/* extern int abs(int); <stdlib.h> */ +extern int p9atexit(void(*)(void)); +extern void p9atexitdont(void(*)(void)); +extern int atnotify(int(*)(void*, char*), int); +/* + * <stdlib.h> +extern double atof(char*); <stdlib.h> + */ +extern int p9atoi(char*); +extern long p9atol(char*); +extern vlong p9atoll(char*); +extern double fmtcharstod(int(*)(void*), void*); +extern char* cleanname(char*); +extern int p9decrypt(void*, void*, int); +extern int p9encrypt(void*, void*, int); +extern int netcrypt(void*, void*); +extern int dec64(uchar*, int, char*, int); +extern int enc64(char*, int, uchar*, int); +extern int dec32(uchar*, int, char*, int); +extern int enc32(char*, int, uchar*, int); +extern int dec16(uchar*, int, char*, int); +extern int enc16(char*, int, uchar*, int); +extern int encodefmt(Fmt*); +extern int dirmodefmt(Fmt*); +extern void exits(char*); +extern double frexp(double, int*); +extern ulong getcallerpc(void*); +extern char* p9getenv(char*); +extern int p9putenv(char*, char*); +extern int getfields(char*, char**, int, int, char*); +extern int gettokens(char *, char **, int, char *); +extern char* getuser(void); +extern char* p9getwd(char*, int); +extern int iounit(int); +/* extern long labs(long); <math.h> */ +/* extern double ldexp(double, int); <math.h> */ +extern void p9longjmp(p9jmp_buf, int); +extern char* mktemp(char*); +extern int opentemp(char*); +/* extern double modf(double, double*); <math.h> */ +extern void p9notejmp(void*, p9jmp_buf, int); +extern void perror(const char*); +extern int postnote(int, int, char *); +extern double p9pow10(int); +/* extern int putenv(char*, char*); <stdlib.h. */ +/* extern void qsort(void*, long, long, int (*)(void*, void*)); <stdlib.h> */ +extern char* searchpath(char*); +/* extern int p9setjmp(p9jmp_buf); */ +#define p9setjmp(b) sigsetjmp((void*)(b), 1) +/* + * <stdlib.h> +extern long strtol(char*, char**, int); +extern ulong strtoul(char*, char**, int); +extern vlong strtoll(char*, char**, int); +extern uvlong strtoull(char*, char**, int); + */ +extern void sysfatal(char*, ...); +extern void p9syslog(int, char*, char*, ...); +extern long p9time(long*); +/* extern int tolower(int); <ctype.h> */ +/* extern int toupper(int); <ctype.h> */ +extern void needstack(int); +extern char* readcons(char*, char*, int); + +#ifndef NOPLAN9DEFINES +#define atexit p9atexit +#define atexitdont p9atexitdont +#define atoi p9atoi +#define atol p9atol +#define atoll p9atoll +#define encrypt p9encrypt +#define decrypt p9decrypt +#define getenv p9getenv +#define getwd p9getwd +#define longjmp p9longjmp +#undef setjmp +#define setjmp p9setjmp +#define putenv p9putenv +#define notejmp p9notejmp +#define jmp_buf p9jmp_buf +#define time p9time +#define pow10 p9pow10 +#define strtod fmtstrtod +#define charstod fmtcharstod +#define syslog p9syslog +#endif + +/* + * just enough information so that libc can be + * properly locked without dragging in all of libthread + */ +typedef struct _Thread _Thread; +typedef struct _Threadlist _Threadlist; +struct _Threadlist +{ + _Thread *head; + _Thread *tail; +}; + +extern _Thread *(*threadnow)(void); + +/* + * synchronization + */ +typedef struct Lock Lock; +struct Lock +{ +#ifdef PLAN9PORT_USING_PTHREADS + int init; + pthread_mutex_t mutex; +#endif + int held; +}; + +extern void lock(Lock*); +extern void unlock(Lock*); +extern int canlock(Lock*); +extern int (*_lock)(Lock*, int, ulong); +extern void (*_unlock)(Lock*, ulong); + +typedef struct QLock QLock; +struct QLock +{ + Lock l; + _Thread *owner; + _Threadlist waiting; +}; + +extern void qlock(QLock*); +extern void qunlock(QLock*); +extern int canqlock(QLock*); +extern int (*_qlock)(QLock*, int, ulong); /* do not use */ +extern void (*_qunlock)(QLock*, ulong); + +typedef struct Rendez Rendez; +struct Rendez +{ + QLock *l; + _Threadlist waiting; +}; + +extern void rsleep(Rendez*); /* unlocks r->l, sleeps, locks r->l again */ +extern int rwakeup(Rendez*); +extern int rwakeupall(Rendez*); +extern void (*_rsleep)(Rendez*, ulong); /* do not use */ +extern int (*_rwakeup)(Rendez*, int, ulong); + +typedef struct RWLock RWLock; +struct RWLock +{ + Lock l; + int readers; + _Thread *writer; + _Threadlist rwaiting; + _Threadlist wwaiting; +}; + +extern void rlock(RWLock*); +extern void runlock(RWLock*); +extern int canrlock(RWLock*); +extern void wlock(RWLock*); +extern void wunlock(RWLock*); +extern int canwlock(RWLock*); +extern int (*_rlock)(RWLock*, int, ulong); /* do not use */ +extern int (*_wlock)(RWLock*, int, ulong); +extern void (*_runlock)(RWLock*, ulong); +extern void (*_wunlock)(RWLock*, ulong); + +/* + * per-process private data + */ +extern void** privalloc(void); +extern void privfree(void**); + +/* + * network dialing + */ +#define NETPATHLEN 40 +extern int p9accept(int, char*); +extern int p9announce(char*, char*); +extern int p9dial(char*, char*, char*, int*); +extern int p9dialparse(char *ds, char **net, char **unixa, u32int *ip, int *port); +extern void p9setnetmtpt(char*, int, char*); +extern int p9listen(char*, char*); +extern char* p9netmkaddr(char*, char*, char*); +extern int p9reject(int, char*, char*); + +#ifndef NOPLAN9DEFINES +#define accept p9accept +#define announce p9announce +#define dial p9dial +#define setnetmtpt p9setnetmtpt +#define listen p9listen +#define netmkaddr p9netmkaddr +#define reject p9reject +#endif + +/* + * encryption + */ +extern int pushssl(int, char*, char*, char*, int*); +extern int pushtls(int, char*, char*, int, char*, char*); + +/* + * network services + */ +typedef struct NetConnInfo NetConnInfo; +struct NetConnInfo +{ + char *dir; /* connection directory */ + char *root; /* network root */ + char *spec; /* binding spec */ + char *lsys; /* local system */ + char *lserv; /* local service */ + char *rsys; /* remote system */ + char *rserv; /* remote service */ + char *laddr; + char *raddr; +}; +extern NetConnInfo* getnetconninfo(char*, int); +extern void freenetconninfo(NetConnInfo*); + +/* + * system calls + * + */ +#define STATMAX 65535U /* max length of machine-independent stat structure */ +#define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */ +#define ERRMAX 128 /* max length of error string */ + +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MCACHE 0x0010 /* cache some data */ +#define MMASK 0x0017 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ +#define ODIRECT 128 /* or'ed in, direct access */ +#define ONONBLOCK 256 /* or'ed in, non-blocking call */ +#define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ +#define OLOCK 0x2000 /* or'ed in, lock after opening */ +#define OAPPEND 0x4000 /* or'ed in, append only */ + +#define AEXIST 0 /* accessible: exists */ +#define AEXEC 1 /* execute access */ +#define AWRITE 2 /* write access */ +#define AREAD 4 /* read access */ + +/* Segattch */ +#define SG_RONLY 0040 /* read only */ +#define SG_CEXEC 0100 /* detach on exec */ + +#define NCONT 0 /* continue after note */ +#define NDFLT 1 /* terminate after note */ +#define NSAVE 2 /* clear note but hold state */ +#define NRSTR 3 /* restore saved state */ + +/* bits in Qid.type */ +#define QTDIR 0x80 /* type bit for directories */ +#define QTAPPEND 0x40 /* type bit for append only files */ +#define QTEXCL 0x20 /* type bit for exclusive use files */ +#define QTMOUNT 0x10 /* type bit for mounted channel */ +#define QTAUTH 0x08 /* type bit for authentication file */ +#define QTLINK 0x04 /* symbolic link */ +#define QTFILE 0x00 /* plain file */ + +/* bits in Dir.mode */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMAUTH 0x08000000 /* mode bit for authentication file */ +#define DMDEVICE 0x00800000 /* mode bit for device files (Unix) */ +#define DMSYMLINK 0x00400000 /* mode bit for symbolic links (Unix) */ +#define DMNAMEDPIPE 0x00200000 /* mode bit for named pipes (Unix) */ +#define DMSOCKET 0x00100000 /* mode bit for sockets (Unix) */ + +#define DMREAD 0x4 /* mode bit for read permission */ +#define DMWRITE 0x2 /* mode bit for write permission */ +#define DMEXEC 0x1 /* mode bit for execute permission */ + +#ifdef RFMEM /* FreeBSD, OpenBSD */ +#undef RFFDG +#undef RFNOTEG +#undef RFPROC +#undef RFMEM +#undef RFNOWAIT +#undef RFCFDG +#undef RFNAMEG +#undef RFENVG +#undef RFCENVG +#undef RFCFDG +#undef RFCNAMEG +#endif + +enum +{ + RFNAMEG = (1<<0), + RFENVG = (1<<1), + RFFDG = (1<<2), + RFNOTEG = (1<<3), + RFPROC = (1<<4), + RFMEM = (1<<5), + RFNOWAIT = (1<<6), + RFCNAMEG = (1<<10), + RFCENVG = (1<<11), + RFCFDG = (1<<12), +/* RFREND = (1<<13), */ +/* RFNOMNT = (1<<14) */ +}; + +typedef +struct Qid +{ + uvlong path; + ulong vers; + uchar type; +} Qid; + +typedef +struct Dir { + /* system-modified data */ + ushort type; /* server type */ + uint dev; /* server subtype */ + /* file data */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + ulong atime; /* last read time */ + ulong mtime; /* last write time */ + vlong length; /* file length */ + char *name; /* last element of path */ + char *uid; /* owner name */ + char *gid; /* group name */ + char *muid; /* last modifier name */ +} Dir; + +/* keep /sys/src/ape/lib/ap/plan9/sys9.h in sync with this -rsc */ +typedef +struct Waitmsg +{ + int pid; /* of loved one */ + ulong time[3]; /* of loved one & descendants */ + char *msg; +} Waitmsg; + +typedef +struct IOchunk +{ + void *addr; + ulong len; +} IOchunk; + +extern void _exits(char*); + +extern void abort(void); +/* extern int access(char*, int); */ +extern long p9alarm(ulong); +extern int await(char*, int); +extern int awaitfor(int, char*, int); +extern int awaitnohang(char*, int); +/* extern int bind(char*, char*, int); give up */ +/* extern int brk(void*); <unistd.h> */ +extern int p9chdir(char*); +extern int close(int); +extern int p9create(char*, int, ulong); +extern int p9dup(int, int); +extern int errstr(char*, uint); +extern int p9exec(char*, char*[]); +extern int p9execl(char*, ...); +/* extern int p9fork(void); */ +extern int p9rfork(int); +/* not implemented +extern int fauth(int, char*); +extern int fstat(int, uchar*, int); +extern int fwstat(int, uchar*, int); +extern int fversion(int, int, char*, int); +extern int mount(int, int, char*, int, char*); +extern int unmount(char*, char*); +*/ +extern int noted(int); +extern int notify(void(*)(void*, char*)); +extern int noteenable(char*); +extern int notedisable(char*); +extern int notifyon(char*); +extern int notifyoff(char*); +extern int p9open(char*, int); +extern int fd2path(int, char*, int); +extern int p9pipe(int*); +/* + * use defs from <unistd.h> +extern long pread(int, void*, long, vlong); +extern long preadv(int, IOchunk*, int, vlong); +extern long pwrite(int, void*, long, vlong); +extern long pwritev(int, IOchunk*, int, vlong); +extern long read(int, void*, long); + */ +extern long readn(int, void*, long); +/* extern long readv(int, IOchunk*, int); <unistd.h> */ +extern int remove(const char*); +/* extern void* sbrk(ulong); <unistd.h> */ +/* extern long oseek(int, long, int); */ +extern vlong p9seek(int, vlong, int); +/* give up +extern long segattach(int, char*, void*, ulong); +extern int segbrk(void*, void*); +extern int segdetach(void*); +extern int segflush(void*, ulong); +extern int segfree(void*, ulong); +*/ +extern int p9sleep(long); +/* extern int stat(char*, uchar*, int); give up */ +extern Waitmsg* p9wait(void); +extern Waitmsg* p9waitfor(int); +extern Waitmsg* waitnohang(void); +extern int p9waitpid(void); +/* <unistd.h> +extern long write(int, void*, long); +extern long writev(int, IOchunk*, int); +*/ +/* extern int wstat(char*, uchar*, int); give up */ +extern ulong rendezvous(ulong, ulong); + +#ifndef NOPLAN9DEFINES +#define alarm p9alarm +#define dup p9dup +#define exec p9exec +#define execl p9execl +#define seek p9seek +#define sleep p9sleep +#define wait p9wait +#define waitpid p9waitpid +/* #define fork p9fork */ +#define rfork p9rfork +/* #define access p9access */ +#define create p9create +#undef open +#define open p9open +#define pipe p9pipe +#define waitfor p9waitfor +#endif + +extern Dir* dirstat(char*); +extern Dir* dirfstat(int); +extern int dirwstat(char*, Dir*); +extern int dirfwstat(int, Dir*); +extern long dirread(int, Dir**); +extern void nulldir(Dir*); +extern long dirreadall(int, Dir**); +/* extern int getpid(void); <unistd.h> */ +/* extern int getppid(void); */ +extern void rerrstr(char*, uint); +extern char* sysname(void); +extern void werrstr(char*, ...); +extern char* getns(void); +extern char* get9root(void); +extern char* unsharp(char*); +extern int sendfd(int, int); +extern int recvfd(int); +extern int post9pservice(int, char*); + +/* external names that we don't want to step on */ +#ifndef NOPLAN9DEFINES +#define main p9main +#endif + +/* compiler directives on plan 9 */ +#define SET(x) ((x)=0) +#define USED(x) if(x){}else{} +#ifdef __GNUC__ +# if __GNUC__ >= 3 +# undef USED +# define USED(x) { ulong __y __attribute__ ((unused)); __y = (ulong)(x); } +# endif +#endif + +#if defined(__OpenBSD__) || (defined(__NetBSD__) && !defined(sched_yield)) +#define sched_yield() \ + do { \ + struct timespec ts; \ + ts.tv_sec = 0; \ + ts.tv_nsec = 10; \ + nanosleep(&ts, NULL); \ + } while(0) +#endif + +/* command line */ +extern char *argv0; +extern void __fixargv0(void); +#define ARGBEGIN for((argv0?0:(argv0=(__fixargv0(),*argv))),argv++,argc--;\ + argv[0] && argv[0][0]=='-' && argv[0][1];\ + argc--, argv++) {\ + char *_args, *_argt;\ + Rune _argc;\ + _args = &argv[0][1];\ + if(_args[0]=='-' && _args[1]==0){\ + argc--; argv++; break;\ + }\ + _argc = 0;\ + while(*_args && (_args += chartorune(&_argc, _args)))\ + switch(_argc) +#define ARGEND SET(_argt);USED(_argt);USED(_argc);USED(_args);}USED(argv);USED(argc); +#define ARGF() (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): 0)) +#define EARGF(x) (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0))) + +#define ARGC() _argc + +#if defined(__cplusplus) +} +#endif +#endif /* _LIB9_H_ */ diff --git a/lib9/lnrand.c b/lib9/lnrand.c @@ -0,0 +1,18 @@ +#include <u.h> +#include <libc.h> + +#define MASK 0x7fffffffL + +long +lnrand(long n) +{ + long slop, v; + + if(n < 0) + return n; + slop = MASK % n; + do + v = lrand(); + while(v <= slop); + return v % n; +} diff --git a/lib9/lock.c b/lib9/lock.c @@ -0,0 +1,55 @@ +#include <u.h> +#include <unistd.h> +#include <sys/time.h> +#include <sched.h> +#include <errno.h> +#include <libc.h> + +static pthread_mutex_t initmutex = PTHREAD_MUTEX_INITIALIZER; + +static void +lockinit(Lock *lk) +{ + pthread_mutexattr_t attr; + + pthread_mutex_lock(&initmutex); + if(lk->init == 0){ + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + pthread_mutex_init(&lk->mutex, &attr); + pthread_mutexattr_destroy(&attr); + lk->init = 1; + } + pthread_mutex_unlock(&initmutex); +} + +void +lock(Lock *lk) +{ + if(!lk->init) + lockinit(lk); + if(pthread_mutex_lock(&lk->mutex) != 0) + abort(); +} + +int +canlock(Lock *lk) +{ + int r; + + if(!lk->init) + lockinit(lk); + r = pthread_mutex_trylock(&lk->mutex); + if(r == 0) + return 1; + if(r == EBUSY) + return 0; + abort(); +} + +void +unlock(Lock *lk) +{ + if(pthread_mutex_unlock(&lk->mutex) != 0) + abort(); +} diff --git a/lib9/lrand.c b/lib9/lrand.c @@ -0,0 +1,83 @@ +#include <u.h> +#include <libc.h> + +/* + * algorithm by + * D. P. Mitchell & J. A. Reeds + */ + +#define LEN 607 +#define TAP 273 +#define MASK 0x7fffffffL +#define A 48271 +#define M 2147483647 +#define Q 44488 +#define R 3399 +#define NORM (1.0/(1.0+MASK)) + +static ulong rng_vec[LEN]; +static ulong* rng_tap = rng_vec; +static ulong* rng_feed = 0; +static Lock lk; + +static void +isrand(long seed) +{ + long lo, hi, x; + int i; + + rng_tap = rng_vec; + rng_feed = rng_vec+LEN-TAP; + seed = seed%M; + if(seed < 0) + seed += M; + if(seed == 0) + seed = 89482311; + x = seed; + /* + * Initialize by x[n+1] = 48271 * x[n] mod (2**31 - 1) + */ + for(i = -20; i < LEN; i++) { + hi = x / Q; + lo = x % Q; + x = A*lo - R*hi; + if(x < 0) + x += M; + if(i >= 0) + rng_vec[i] = x; + } +} + +void +p9srand(long seed) +{ + lock(&lk); + isrand(seed); + unlock(&lk); +} + +long +p9lrand(void) +{ + ulong x; + + lock(&lk); + + rng_tap--; + if(rng_tap < rng_vec) { + if(rng_feed == 0) { + isrand(1); + rng_tap--; + } + rng_tap += LEN; + } + rng_feed--; + if(rng_feed < rng_vec) + rng_feed += LEN; + x = (*rng_feed + *rng_tap) & MASK; + *rng_feed = x; + + unlock(&lk); + + return x; +} diff --git a/lib9/main.c b/lib9/main.c @@ -0,0 +1,13 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> + +extern void p9main(int, char**); + +int +main(int argc, char **argv) +{ + p9main(argc, argv); + exits("main"); + return 99; +} diff --git a/lib9/malloc.c b/lib9/malloc.c @@ -0,0 +1,56 @@ +/* + * These are here mainly so that I can link against + * debugmalloc.c instead and not recompile the world. + */ + +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> + +static Lock malloclock; + +void* +p9malloc(ulong n) +{ + void *v; + + if(n == 0) + n++; + lock(&malloclock); + v = malloc(n); + unlock(&malloclock); + return v; +} + +void +p9free(void *v) +{ + if(v == nil) + return; + lock(&malloclock); + free(v); + unlock(&malloclock); +} + +void* +p9calloc(ulong a, ulong b) +{ + void *v; + + if(a*b == 0) + a = b = 1; + + lock(&malloclock); + v = calloc(a*b, 1); + unlock(&malloclock); + return v; +} + +void* +p9realloc(void *v, ulong n) +{ + lock(&malloclock); + v = realloc(v, n); + unlock(&malloclock); + return v; +} diff --git a/lib9/malloctag.c b/lib9/malloctag.c @@ -0,0 +1,19 @@ +#include <lib9.h> + +extern long p9lrand(void); +#define USED(x) if(x){}else{} +#define lrand p9lrand + +void +setmalloctag(void *v, ulong t) +{ + USED(v); + USED(t); +} + +void +setrealloctag(void *v, ulong t) +{ + USED(v); + USED(t); +} diff --git a/lib9/mallocz.c b/lib9/mallocz.c @@ -0,0 +1,15 @@ +#include <u.h> +#include <unistd.h> +#include <string.h> +#include <libc.h> + +void* +mallocz(unsigned long n, int clr) +{ + void *v; + + v = malloc(n); + if(clr && v) + memset(v, 0, n); + return v; +} diff --git a/lib9/nan.c b/lib9/nan.c @@ -0,0 +1,27 @@ +#include <u.h> +#include <libc.h> +#include "nan.h" + +double +NaN(void) +{ + return __NaN(); +} + +double +Inf(int sign) +{ + return __Inf(sign); +} + +int +isNaN(double x) +{ + return __isNaN(x); +} + +int +isInf(double x, int sign) +{ + return __isInf(x, sign); +} diff --git a/lib9/nan.h b/lib9/nan.h @@ -0,0 +1,4 @@ +extern double __NaN(void); +extern double __Inf(int); +extern int __isNaN(double); +extern int __isInf(double, int); diff --git a/lib9/needsrcquote.c b/lib9/needsrcquote.c @@ -0,0 +1,12 @@ +#include <u.h> +#include <libc.h> + +int +needsrcquote(int c) +{ + if(c <= ' ') + return 1; + if(strchr("`^#*[]=|\\?${}()'<>&;", c)) + return 1; + return 0; +} diff --git a/lib9/needstack.c b/lib9/needstack.c @@ -0,0 +1,8 @@ +#include <u.h> +#include <libc.h> + +void +needstack(int howmuch) +{ + USED(howmuch); +} diff --git a/lib9/netmkaddr.c b/lib9/netmkaddr.c @@ -0,0 +1,52 @@ +#include <u.h> +#include <libc.h> +#include <ctype.h> + +/* + * make an address, add the defaults + */ +char * +netmkaddr(char *linear, char *defnet, char *defsrv) +{ + static char addr[256]; + char *cp; + + /* + * dump network name + */ + cp = strchr(linear, '!'); + if(cp == 0){ + if(defnet==0){ + if(defsrv) + snprint(addr, sizeof(addr), "net!%s!%s", + linear, defsrv); + else + snprint(addr, sizeof(addr), "net!%s", linear); + } + else { + if(defsrv) + snprint(addr, sizeof(addr), "%s!%s!%s", defnet, + linear, defsrv); + else + snprint(addr, sizeof(addr), "%s!%s", defnet, + linear); + } + return addr; + } + + /* + * if there is already a service, use it + */ + cp = strchr(cp+1, '!'); + if(cp) + return linear; + + /* + * add default service + */ + if(defsrv == 0) + return linear; + snprint(addr, sizeof(addr), "%s!%s", linear, defsrv); + + return addr; +} diff --git a/lib9/notify.c b/lib9/notify.c @@ -0,0 +1,272 @@ +/* + * Signal handling for Plan 9 programs. + * We stubbornly use the strings from Plan 9 instead + * of the enumerated Unix constants. + * There are some weird translations. In particular, + * a "kill" note is the same as SIGTERM in Unix. + * There is no equivalent note to Unix's SIGKILL, since + * it's not a deliverable signal anyway. + * + * We do not handle SIGABRT or SIGSEGV, mainly because + * the thread library queues its notes for later, and we want + * to dump core with the state at time of delivery. + * + * We have to add some extra entry points to provide the + * ability to tweak which signals are deliverable and which + * are acted upon. Notifydisable and notifyenable play with + * the process signal mask. Notifyignore enables the signal + * but will not call notifyf when it comes in. This is occasionally + * useful. + */ + +#include <u.h> +#include <signal.h> +#define NOPLAN9DEFINES +#include <libc.h> + +extern char *_p9sigstr(int, char*); +extern int _p9strsig(char*); + +typedef struct Sig Sig; +struct Sig +{ + int sig; /* signal number */ + int flags; +}; + +enum +{ + Restart = 1<<0, + Ignore = 1<<1, +}; + +static Sig sigs[] = { + SIGHUP, 0, + SIGINT, 0, + SIGQUIT, 0, + SIGILL, 0, + SIGTRAP, 0, +/* SIGABRT, 0, */ +#ifdef SIGEMT + SIGEMT, 0, +#endif + SIGFPE, 0, + SIGBUS, 0, +/* SIGSEGV, 0, */ + SIGCHLD, Restart|Ignore, + SIGSYS, 0, + SIGPIPE, Ignore, + SIGALRM, 0, + SIGTERM, 0, + SIGTSTP, Restart|Ignore, +/* SIGTTIN, Restart|Ignore, */ +/* SIGTTOU, Restart|Ignore, */ + SIGXCPU, 0, + SIGXFSZ, 0, + SIGVTALRM, 0, + SIGUSR1, 0, + SIGUSR2, 0, +#ifdef SIGWINCH + SIGWINCH, Restart|Ignore, +#endif +#ifdef SIGINFO + SIGINFO, Restart|Ignore, +#endif +}; + +static Sig* +findsig(int s) +{ + int i; + + for(i=0; i<nelem(sigs); i++) + if(sigs[i].sig == s) + return &sigs[i]; + return nil; +} + +/* + * The thread library initializes _notejmpbuf to its own + * routine which provides a per-pthread jump buffer. + * If we're not using the thread library, we assume we are + * single-threaded. + */ +typedef struct Jmp Jmp; +struct Jmp +{ + p9jmp_buf b; +}; + +static Jmp onejmp; + +static Jmp* +getonejmp(void) +{ + return &onejmp; +} + +Jmp *(*_notejmpbuf)(void) = getonejmp; +static void noteinit(void); + +/* + * Actual signal handler. + */ + +static void (*notifyf)(void*, char*); /* Plan 9 handler */ + +static void +signotify(int sig) +{ + char tmp[64]; + Jmp *j; + Sig *s; + + j = (*_notejmpbuf)(); + switch(p9setjmp(j->b)){ + case 0: + if(notifyf) + (*notifyf)(nil, _p9sigstr(sig, tmp)); + /* fall through */ + case 1: /* noted(NDFLT) */ + if(0)print("DEFAULT %d\n", sig); + s = findsig(sig); + if(s && (s->flags&Ignore)) + return; + signal(sig, SIG_DFL); + raise(sig); + _exit(1); + case 2: /* noted(NCONT) */ + if(0)print("HANDLED %d\n", sig); + return; + } +} + +static void +signonotify(int sig) +{ + USED(sig); +} + +int +noted(int v) +{ + p9longjmp((*_notejmpbuf)()->b, v==NCONT ? 2 : 1); + abort(); + return 0; +} + +int +notify(void (*f)(void*, char*)) +{ + static int init; + + notifyf = f; + if(!init){ + init = 1; + noteinit(); + } + return 0; +} + +/* + * Nonsense about enabling and disabling signals. + */ +typedef void Sighandler(int); +static Sighandler* +handler(int s) +{ + struct sigaction sa; + + sigaction(s, nil, &sa); + return sa.sa_handler; +} + +static int +notesetenable(int sig, int enabled) +{ + sigset_t mask, omask; + + if(sig == 0) + return -1; + + sigemptyset(&mask); + sigaddset(&mask, sig); + sigprocmask(enabled ? SIG_UNBLOCK : SIG_BLOCK, &mask, &omask); + return !sigismember(&omask, sig); +} + +int +noteenable(char *msg) +{ + return notesetenable(_p9strsig(msg), 1); +} + +int +notedisable(char *msg) +{ + return notesetenable(_p9strsig(msg), 0); +} + +static int +notifyseton(int s, int on) +{ + Sig *sig; + struct sigaction sa, osa; + + sig = findsig(s); + if(sig == nil) + return -1; + memset(&sa, 0, sizeof sa); + sa.sa_handler = on ? signotify : signonotify; + if(sig->flags&Restart) + sa.sa_flags |= SA_RESTART; + + /* + * We can't allow signals within signals because there's + * only one jump buffer. + */ + sigfillset(&sa.sa_mask); + + /* + * Install handler. + */ + sigaction(sig->sig, &sa, &osa); + return osa.sa_handler == signotify; +} + +int +notifyon(char *msg) +{ + return notifyseton(_p9strsig(msg), 1); +} + +int +notifyoff(char *msg) +{ + return notifyseton(_p9strsig(msg), 0); +} + +/* + * Initialization follows sigs table. + */ +static void +noteinit(void) +{ + int i; + Sig *sig; + + for(i=0; i<nelem(sigs); i++){ + sig = &sigs[i]; + /* + * If someone has already installed a handler, + * It's probably some ld preload nonsense, + * like pct (a SIGVTALRM-based profiler). + * Or maybe someone has already called notifyon/notifyoff. + * Leave it alone. + */ + if(handler(sig->sig) != SIG_DFL) + continue; + notifyseton(sig->sig, 1); + } +} + diff --git a/lib9/nrand.c b/lib9/nrand.c @@ -0,0 +1,19 @@ +#include <lib9.h> + +extern long p9lrand(void); +#define lrand p9lrand +#define MASK 0x7fffffffL + +int +nrand(int n) +{ + long slop, v; + + if(n < 0) + return n; + slop = MASK % n; + do + v = lrand(); + while(v <= slop); + return v % n; +} diff --git a/lib9/nulldir.c b/lib9/nulldir.c @@ -0,0 +1,9 @@ +#include <u.h> +#include <libc.h> + +void +nulldir(Dir *d) +{ + memset(d, ~0, sizeof(Dir)); + d->name = d->uid = d->gid = d->muid = ""; +} diff --git a/lib9/open.c b/lib9/open.c @@ -0,0 +1,62 @@ +#define _GNU_SOURCE /* for Linux O_DIRECT */ +#include <u.h> +#define NOPLAN9DEFINES +#include <sys/file.h> +#include <libc.h> +#ifndef O_DIRECT +#define O_DIRECT 0 +#endif + +int +p9open(char *name, int mode) +{ + int cexec, rclose; + int fd, umode, lock, rdwr; + struct flock fl; + + rdwr = mode&3; + umode = rdwr; + cexec = mode&OCEXEC; + rclose = mode&ORCLOSE; + lock = mode&OLOCK; + mode &= ~(3|OCEXEC|ORCLOSE|OLOCK); + if(mode&OTRUNC){ + umode |= O_TRUNC; + mode ^= OTRUNC; + } + if(mode&ODIRECT){ + umode |= O_DIRECT; + mode ^= ODIRECT; + } + if(mode&ONONBLOCK){ + umode |= O_NONBLOCK; + mode ^= ONONBLOCK; + } + if(mode&OAPPEND){ + umode |= O_APPEND; + mode ^= OAPPEND; + } + if(mode){ + werrstr("mode 0x%x not supported", mode); + return -1; + } + fd = open(name, umode); + if(fd >= 0){ + if(lock){ + fl.l_type = (rdwr==OREAD) ? F_RDLCK : F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + if(fcntl(fd, F_SETLK, &fl) < 0){ + close(fd); + werrstr("lock: %r"); + return -1; + } + } + if(cexec) + fcntl(fd, F_SETFL, FD_CLOEXEC); + if(rclose) + remove(name); + } + return fd; +} diff --git a/lib9/opentemp.c b/lib9/opentemp.c @@ -0,0 +1,15 @@ +#include <u.h> +#include <libc.h> + +int +opentemp(char *template) +{ + int fd; + + fd = mkstemp(template); + if(fd < 0) + return -1; + remove(template); + return fd; +} + diff --git a/lib9/pipe.c b/lib9/pipe.c @@ -0,0 +1,15 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> +#include <sys/socket.h> + +/* + * We use socketpair to get a two-way pipe. + * The pipe still doesn't preserve message boundaries. + * Worse, it cannot be reopened via /dev/fd/NNN on Linux. + */ +int +p9pipe(int fd[2]) +{ + return socketpair(AF_UNIX, SOCK_STREAM, 0, fd); +} diff --git a/lib9/portdate b/lib9/portdate @@ -0,0 +1,30 @@ +dofmt.c 2004/1225 +dorfmt.c 2004/1225 +errfmt.c 2004/1225 +fltfmt.c 2004/1225 +fmt.c 2004/1225 +fmtfd.c 2004/1225 +fmtlock.c 2004/1225 +fmtprint.c 2004/1225 +fmtquote.c 2004/1225 +fmtrune.c 2004/1225 +fmtstr.c 2004/1225 +fmtvprint.c 2004/1225 +fprint.c 2004/1225 +print.c 2004/1225 +runefmtstr.c 2004/1225 +runeseprint.c 2004/1225 +runesmprint.c 2004/1225 +runesnprint.c 2004/1225 +runesprint.c 2004/1225 +runevseprint.c 2004/1225 +runevsmprint.c 2004/1225 +runevsnprint.c 2004/1225 +seprint.c 2004/1225 +smprint.c 2004/1225 +snprint.c 2004/1225 +sprint.c 2004/1225 +vfprint.c 2004/1225 +vseprint.c 2004/1225 +vsmprint.c 2004/1225 +vsnprint.c 2004/1225 diff --git a/lib9/post9p.c b/lib9/post9p.c @@ -0,0 +1,46 @@ +#include <u.h> +#include <libc.h> + +int +post9pservice(int fd, char *name) +{ + int i; + char *ns, *s; + Waitmsg *w; + + if(strchr(name, '!')) /* assume is already network address */ + s = strdup(name); + else{ + if((ns = getns()) == nil) + return -1; + s = smprint("unix!%s/%s", ns, name); + free(ns); + } + if(s == nil) + return -1; + switch(fork()){ + case -1: + return -1; + case 0: + dup(fd, 0); + dup(fd, 1); + for(i=3; i<20; i++) + close(i); + execlp("9pserve", "9pserve", "-u", s, (char*)0); + fprint(2, "exec 9pserve: %r\n"); + _exits("exec"); + default: + w = wait(); + if(w == nil) + return -1; + close(fd); + free(s); + if(w->msg && w->msg[0]){ + free(w); + werrstr("9pserve failed"); + return -1; + } + free(w); + return 0; + } +} diff --git a/lib9/postnote.c b/lib9/postnote.c @@ -0,0 +1,34 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> + +#include <signal.h> + + +extern int _p9strsig(char*); + +int +postnote(int who, int pid, char *msg) +{ + int sig; + + sig = _p9strsig(msg); + if(sig == 0){ + werrstr("unknown note"); + return -1; + } + + switch(who){ + default: + werrstr("bad who in postnote"); + return -1; + case PNPROC: + return kill(pid, sig); + case PNGROUP: + if((pid = getpgid(pid)) < 0) + return -1; + return killpg(pid, sig); + } +} + + diff --git a/lib9/priv.c b/lib9/priv.c @@ -0,0 +1,32 @@ +#include <u.h> +#include <libc.h> +#include "9proc.h" + +static Lock privlock; +static ulong privmap; + +int +privalloc(void) +{ + int i; + + lock(&privlock); + for(i=0; i<NPRIV; i++) + if((privmap&(1<<i)) == 0){ + privmap |= (1<<i); + unlock(&privlock); + return i; + } + unlock(&privlock); + return -1; +} + +void** +privmem(int i) +{ + Uproc *up; + + up = _p9uproc(0); + return &up->priv[i]; +} + diff --git a/lib9/qlock.c b/lib9/qlock.c @@ -0,0 +1,166 @@ +#include <u.h> +#include <libc.h> + +/* + * The function pointers are supplied by the thread + * library during its initialization. If there is no thread + * library, there is no multithreading. + */ + +int (*_lock)(Lock*, int, ulong); +void (*_unlock)(Lock*, ulong); +int (*_qlock)(QLock*, int, ulong); /* do not use */ +void (*_qunlock)(QLock*, ulong); +void (*_rsleep)(Rendez*, ulong); /* do not use */ +int (*_rwakeup)(Rendez*, int, ulong); +int (*_rlock)(RWLock*, int, ulong); /* do not use */ +int (*_wlock)(RWLock*, int, ulong); +void (*_runlock)(RWLock*, ulong); +void (*_wunlock)(RWLock*, ulong); + +void +lock(Lock *l) +{ + if(_lock) + (*_lock)(l, 1, getcallerpc(&l)); + else + l->held = 1; +} + +int +canlock(Lock *l) +{ + if(_lock) + return (*_lock)(l, 0, getcallerpc(&l)); + else{ + if(l->held) + return 0; + l->held = 1; + return 1; + } +} + +void +unlock(Lock *l) +{ + if(_unlock) + (*_unlock)(l, getcallerpc(&l)); + else + l->held = 0; +} + +void +qlock(QLock *l) +{ + if(_qlock) + (*_qlock)(l, 1, getcallerpc(&l)); + else + l->l.held = 1; +} + +int +canqlock(QLock *l) +{ + if(_qlock) + return (*_qlock)(l, 0, getcallerpc(&l)); + else{ + if(l->l.held) + return 0; + l->l.held = 1; + return 1; + } +} + +void +qunlock(QLock *l) +{ + if(_qunlock) + (*_qunlock)(l, getcallerpc(&l)); + else + l->l.held = 0; +} + +void +rlock(RWLock *l) +{ + if(_rlock) + (*_rlock)(l, 1, getcallerpc(&l)); + else + l->readers++; +} + +int +canrlock(RWLock *l) +{ + if(_rlock) + return (*_rlock)(l, 0, getcallerpc(&l)); + else{ + if(l->writer) + return 0; + l->readers++; + return 1; + } +} + +void +runlock(RWLock *l) +{ + if(_runlock) + (*_runlock)(l, getcallerpc(&l)); + else + l->readers--; +} + +void +wlock(RWLock *l) +{ + if(_wlock) + (*_wlock)(l, 1, getcallerpc(&l)); + else + l->writer = (void*)1; +} + +int +canwlock(RWLock *l) +{ + if(_wlock) + return (*_wlock)(l, 0, getcallerpc(&l)); + else{ + if(l->writer || l->readers) + return 0; + l->writer = (void*)1; + return 1; + } +} + +void +wunlock(RWLock *l) +{ + if(_wunlock) + (*_wunlock)(l, getcallerpc(&l)); + else + l->writer = nil; +} + +void +rsleep(Rendez *r) +{ + if(_rsleep) + (*_rsleep)(r, getcallerpc(&r)); +} + +int +rwakeup(Rendez *r) +{ + if(_rwakeup) + return (*_rwakeup)(r, 0, getcallerpc(&r)); + return 0; +} + +int +rwakeupall(Rendez *r) +{ + if(_rwakeup) + return (*_rwakeup)(r, 1, getcallerpc(&r)); + return 0; +} diff --git a/lib9/quote.c b/lib9/quote.c @@ -0,0 +1,136 @@ +#include <u.h> +#include <libc.h> + + +/* in libfmt */ +extern int (*doquote)(int); +extern int __needsquotes(char*, int*); +extern int __runeneedsquotes(Rune*, int*); + +char* +unquotestrdup(char *s) +{ + char *t, *ret; + int quoting; + + ret = s = strdup(s); /* return unquoted copy */ + if(ret == nil) + return ret; + quoting = 0; + t = s; /* s is output string, t is input string */ + while(*t!='\0' && (quoting || (*t!=' ' && *t!='\t'))){ + if(*t != '\''){ + *s++ = *t++; + continue; + } + /* *t is a quote */ + if(!quoting){ + quoting = 1; + t++; + continue; + } + /* quoting and we're on a quote */ + if(t[1] != '\''){ + /* end of quoted section; absorb closing quote */ + t++; + quoting = 0; + continue; + } + /* doubled quote; fold one quote into two */ + t++; + *s++ = *t++; + } + if(t != s) + memmove(s, t, strlen(t)+1); + return ret; +} + +Rune* +unquoterunestrdup(Rune *s) +{ + Rune *t, *ret; + int quoting; + + ret = s = runestrdup(s); /* return unquoted copy */ + if(ret == nil) + return ret; + quoting = 0; + t = s; /* s is output string, t is input string */ + while(*t!='\0' && (quoting || (*t!=' ' && *t!='\t'))){ + if(*t != '\''){ + *s++ = *t++; + continue; + } + /* *t is a quote */ + if(!quoting){ + quoting = 1; + t++; + continue; + } + /* quoting and we're on a quote */ + if(t[1] != '\''){ + /* end of quoted section; absorb closing quote */ + t++; + quoting = 0; + continue; + } + /* doubled quote; fold one quote into two */ + t++; + *s++ = *t++; + } + if(t != s) + memmove(s, t, (runestrlen(t)+1)*sizeof(Rune)); + return ret; +} + +char* +quotestrdup(char *s) +{ + char *t, *u, *ret; + int quotelen; + Rune r; + + if(__needsquotes(s, &quotelen) == 0) + return strdup(s); + + ret = malloc(quotelen+1); + if(ret == nil) + return nil; + u = ret; + *u++ = '\''; + for(t=s; *t; t++){ + r = *t; + if(r == '\'') + *u++ = r; /* double the quote */ + *u++ = r; + } + *u++ = '\''; + *u = '\0'; + return ret; +} + +Rune* +quoterunestrdup(Rune *s) +{ + Rune *t, *u, *ret; + int quotelen; + Rune r; + + if(__runeneedsquotes(s, &quotelen) == 0) + return runestrdup(s); + + ret = malloc((quotelen+1)*sizeof(Rune)); + if(ret == nil) + return nil; + u = ret; + *u++ = '\''; + for(t=s; *t; t++){ + r = *t; + if(r == '\'') + *u++ = r; /* double the quote */ + *u++ = r; + } + *u++ = '\''; + *u = '\0'; + return ret; +} diff --git a/lib9/rand.c b/lib9/rand.c @@ -0,0 +1,10 @@ +#include <lib9.h> + +extern long p9lrand(void); +#define lrand p9lrand + +int +p9rand(void) +{ + return lrand() & 0x7fff; +} diff --git a/lib9/read9pmsg.c b/lib9/read9pmsg.c @@ -0,0 +1,31 @@ +#include <u.h> +#include <libc.h> +#include <fcall.h> + +int +read9pmsg(int fd, void *abuf, uint n) +{ + int m, len; + uchar *buf; + + buf = abuf; + + /* read count */ + m = readn(fd, buf, BIT32SZ); + if(m != BIT32SZ){ + if(m < 0) + return -1; + return 0; + } + + len = GBIT32(buf); + if(len <= BIT32SZ || len > n){ + werrstr("bad length in 9P2000 message header"); + return -1; + } + len -= BIT32SZ; + m = readn(fd, buf+BIT32SZ, len); + if(m < len) + return 0; + return BIT32SZ+m; +} diff --git a/lib9/readcons.c b/lib9/readcons.c @@ -0,0 +1,102 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> +#include <termios.h> +#include <sys/termios.h> + +static int +rawx(int fd, int echoing) +{ + int was; + static struct termios ttmode; + + if(echoing == -1) + return -1; + + if(tcgetattr(fd, &ttmode) < 0) + return -1; + was = (ttmode.c_lflag&(ECHO|ICANON)); + ttmode.c_lflag &= ~(ECHO|ICANON); + ttmode.c_lflag |= echoing; + if(tcsetattr(fd, TCSANOW, &ttmode) < 0) + return -1; + return was; +} + +char* +readcons(char *prompt, char *def, int secret) +{ + int fd, n, raw; + char line[10]; + char *s, *t; + int l; + + if((fd = open("/dev/tty", ORDWR)) < 0) + return nil; + + raw = -1; + if(secret){ + raw = rawx(fd, 0); + if(raw == -1) + return nil; + } + + if(def) + fprint(fd, "%s[%s]: ", prompt, def); + else + fprint(fd, "%s: ", prompt); + + s = strdup(""); + if(s == nil) + return nil; + + for(;;){ + n = read(fd, line, 1); + if(n < 0){ + Error: + if(secret){ + rawx(fd, raw); + write(fd, "\n", 1); + } + close(fd); + free(s); + return nil; + } + if(n > 0 && line[0] == 0x7F) + goto Error; + if(n == 0 || line[0] == 0x04 || line[0] == '\n' || line[0] == '\r'){ + if(secret){ + rawx(fd, raw); + write(fd, "\n", 1); + } + close(fd); + if(*s == 0 && def){ + free(s); + s = strdup(def); + } + return s; + } + if(line[0] == '\b'){ + if(strlen(s) > 0) + s[strlen(s)-1] = 0; + }else if(line[0] == 0x15){ /* ^U: line kill */ + if(def != nil) + fprint(fd, "\n%s[%s]: ", prompt, def); + else + fprint(fd, "\n%s: ", prompt); + s[0] = 0; + }else{ + l = strlen(s); + t = malloc(l+2); + if(t) + memmove(t, s, l); + memset(s, 'X', l); + free(s); + if(t == nil) + return nil; + t[l] = line[0]; + t[l+1] = 0; + s = t; + } + } +} diff --git a/lib9/readn.c b/lib9/readn.c @@ -0,0 +1,22 @@ +#include <lib9.h> +#include <unistd.h> + +long +readn(int f, void *av, long n) +{ + char *a; + long m, t; + + a = av; + t = 0; + while(t < n){ + m = read(f, a+t, n-t); + if(m <= 0){ + if(t == 0) + return m; + break; + } + t += m; + } + return t; +} diff --git a/lib9/regex/README b/lib9/regex/README @@ -0,0 +1,7 @@ +This is a Unix port of the Plan 9 regular expression library, +originally done for the Inferno operating system. + +Russ Cox repackaged this to build as a standalone +Unix library. Send comments about packaging to +Russ Cox <rsc@post.harvard.edu> + diff --git a/lib9/regex/cvt b/lib9/regex/cvt @@ -0,0 +1,11 @@ +#!/bin/sh + +/usr/bin/sed -E ' + s/\.(sp|rsp)/.s.\1/g + s/\.(ep|rep)/.e.\1/g + s/(\.|->)(cp|r|subid|right)([^a-zA-Z0-9_])/\1u1.\2\3/g + s/(\.|->)(left|next)([^a-z])/\1u2.\2\3/g + /#include <u.h>/d + s/<libc.h>/"lib9.h"/g + s/"regexp.h"/"regexp9.h"/g +' $* diff --git a/lib9/regex/lib9.h b/lib9/regex/lib9.h @@ -0,0 +1,2 @@ +#include <u.h> +#include <libc.h> diff --git a/lib9/regex/lib9.std.h b/lib9/regex/lib9.std.h @@ -0,0 +1,10 @@ +#include <fmt.h> +#include <setjmp.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#define exits(x) exit(x && *x ? 1 : 0) + +#define nil 0 + diff --git a/lib9/regex/mkfile b/lib9/regex/mkfile @@ -0,0 +1,25 @@ +<$PLAN9/src/mkhdr + +LIB=libregexp9.a + +OFILES=\ + regcomp.$O\ + regerror.$O\ + regexec.$O\ + regsub.$O\ + regaux.$O\ + rregexec.$O\ + rregsub.$O\ + +HFILES=\ + $PLAN9/include/regexp9.h\ + regcomp.h\ + +<$PLAN9/src/mksyslib + +test: test.$O $LIB + $CC -o test test.$O $LIB -L/usr/local/lib -lfmt -lutf + +test2: test2.$O $LIB + $CC -o test2 test2.$O $LIB -L/usr/local/lib -lfmt -lutf + diff --git a/lib9/regex/regaux.c b/lib9/regex/regaux.c @@ -0,0 +1,112 @@ +#include "lib9.h" +#include "regexp9.h" +#include "regcomp.h" + + +/* + * save a new match in mp + */ +extern void +_renewmatch(Resub *mp, int ms, Resublist *sp) +{ + int i; + + if(mp==0 || ms<=0) + return; + if(mp[0].s.sp==0 || sp->m[0].s.sp<mp[0].s.sp || + (sp->m[0].s.sp==mp[0].s.sp && sp->m[0].e.ep>mp[0].e.ep)){ + for(i=0; i<ms && i<NSUBEXP; i++) + mp[i] = sp->m[i]; + for(; i<ms; i++) + mp[i].s.sp = mp[i].e.ep = 0; + } +} + +/* + * Note optimization in _renewthread: + * *lp must be pending when _renewthread called; if *l has been looked + * at already, the optimization is a bug. + */ +extern Relist* +_renewthread(Relist *lp, /* _relist to add to */ + Reinst *ip, /* instruction to add */ + int ms, + Resublist *sep) /* pointers to subexpressions */ +{ + Relist *p; + + for(p=lp; p->inst; p++){ + if(p->inst == ip){ + if(sep->m[0].s.sp < p->se.m[0].s.sp){ + if(ms > 1) + p->se = *sep; + else + p->se.m[0] = sep->m[0]; + } + return 0; + } + } + p->inst = ip; + if(ms > 1) + p->se = *sep; + else + p->se.m[0] = sep->m[0]; + (++p)->inst = 0; + return p; +} + +/* + * same as renewthread, but called with + * initial empty start pointer. + */ +extern Relist* +_renewemptythread(Relist *lp, /* _relist to add to */ + Reinst *ip, /* instruction to add */ + int ms, + char *sp) /* pointers to subexpressions */ +{ + Relist *p; + + for(p=lp; p->inst; p++){ + if(p->inst == ip){ + if(sp < p->se.m[0].s.sp) { + if(ms > 1) + memset(&p->se, 0, sizeof(p->se)); + p->se.m[0].s.sp = sp; + } + return 0; + } + } + p->inst = ip; + if(ms > 1) + memset(&p->se, 0, sizeof(p->se)); + p->se.m[0].s.sp = sp; + (++p)->inst = 0; + return p; +} + +extern Relist* +_rrenewemptythread(Relist *lp, /* _relist to add to */ + Reinst *ip, /* instruction to add */ + int ms, + Rune *rsp) /* pointers to subexpressions */ +{ + Relist *p; + + for(p=lp; p->inst; p++){ + if(p->inst == ip){ + if(rsp < p->se.m[0].s.rsp) { + if(ms > 1) + memset(&p->se, 0, sizeof(p->se)); + p->se.m[0].s.rsp = rsp; + } + return 0; + } + } + p->inst = ip; + if(ms > 1) + memset(&p->se, 0, sizeof(p->se)); + p->se.m[0].s.rsp = rsp; + (++p)->inst = 0; + return p; +} diff --git a/lib9/regex/regcomp.c b/lib9/regex/regcomp.c @@ -0,0 +1,555 @@ +#include "lib9.h" +#include "regexp9.h" +#include "regcomp.h" + +#define TRUE 1 +#define FALSE 0 + +/* + * Parser Information + */ +typedef +struct Node +{ + Reinst* first; + Reinst* last; +}Node; + +#define NSTACK 20 +static Node andstack[NSTACK]; +static Node *andp; +static int atorstack[NSTACK]; +static int* atorp; +static int cursubid; /* id of current subexpression */ +static int subidstack[NSTACK]; /* parallel to atorstack */ +static int* subidp; +static int lastwasand; /* Last token was operand */ +static int nbra; +static char* exprp; /* pointer to next character in source expression */ +static int lexdone; +static int nclass; +static Reclass*classp; +static Reinst* freep; +static int errors; +static Rune yyrune; /* last lex'd rune */ +static Reclass*yyclassp; /* last lex'd class */ + +/* predeclared crap */ +static void operator(int); +static void pushand(Reinst*, Reinst*); +static void pushator(int); +static void evaluntil(int); +static int bldcclass(void); + +static jmp_buf regkaboom; + +static void +rcerror(char *s) +{ + errors++; + regerror(s); + longjmp(regkaboom, 1); +} + +static Reinst* +newinst(int t) +{ + freep->type = t; + freep->u2.left = 0; + freep->u1.right = 0; + return freep++; +} + +static void +operand(int t) +{ + Reinst *i; + + if(lastwasand) + operator(CAT); /* catenate is implicit */ + i = newinst(t); + + if(t == CCLASS || t == NCCLASS) + i->u1.cp = yyclassp; + if(t == RUNE) + i->u1.r = yyrune; + + pushand(i, i); + lastwasand = TRUE; +} + +static void +operator(int t) +{ + if(t==RBRA && --nbra<0) + rcerror("unmatched right paren"); + if(t==LBRA){ + if(++cursubid >= NSUBEXP) + rcerror ("too many subexpressions"); + nbra++; + if(lastwasand) + operator(CAT); + } else + evaluntil(t); + if(t != RBRA) + pushator(t); + lastwasand = FALSE; + if(t==STAR || t==QUEST || t==PLUS || t==RBRA) + lastwasand = TRUE; /* these look like operands */ +} + +static void +regerr2(char *s, int c) +{ + char buf[100]; + char *cp = buf; + while(*s) + *cp++ = *s++; + *cp++ = c; + *cp = '\0'; + rcerror(buf); +} + +static void +cant(char *s) +{ + char buf[100]; + strcpy(buf, "can't happen: "); + strcat(buf, s); + rcerror(buf); +} + +static void +pushand(Reinst *f, Reinst *l) +{ + if(andp >= &andstack[NSTACK]) + cant("operand stack overflow"); + andp->first = f; + andp->last = l; + andp++; +} + +static void +pushator(int t) +{ + if(atorp >= &atorstack[NSTACK]) + cant("operator stack overflow"); + *atorp++ = t; + *subidp++ = cursubid; +} + +static Node* +popand(int op) +{ + Reinst *inst; + + if(andp <= &andstack[0]){ + regerr2("missing operand for ", op); + inst = newinst(NOP); + pushand(inst,inst); + } + return --andp; +} + +static int +popator(void) +{ + if(atorp <= &atorstack[0]) + cant("operator stack underflow"); + --subidp; + return *--atorp; +} + +static void +evaluntil(int pri) +{ + Node *op1, *op2; + Reinst *inst1, *inst2; + + while(pri==RBRA || atorp[-1]>=pri){ + switch(popator()){ + default: + rcerror("unknown operator in evaluntil"); + break; + case LBRA: /* must have been RBRA */ + op1 = popand('('); + inst2 = newinst(RBRA); + inst2->u1.subid = *subidp; + op1->last->u2.next = inst2; + inst1 = newinst(LBRA); + inst1->u1.subid = *subidp; + inst1->u2.next = op1->first; + pushand(inst1, inst2); + return; + case OR: + op2 = popand('|'); + op1 = popand('|'); + inst2 = newinst(NOP); + op2->last->u2.next = inst2; + op1->last->u2.next = inst2; + inst1 = newinst(OR); + inst1->u1.right = op1->first; + inst1->u2.left = op2->first; + pushand(inst1, inst2); + break; + case CAT: + op2 = popand(0); + op1 = popand(0); + op1->last->u2.next = op2->first; + pushand(op1->first, op2->last); + break; + case STAR: + op2 = popand('*'); + inst1 = newinst(OR); + op2->last->u2.next = inst1; + inst1->u1.right = op2->first; + pushand(inst1, inst1); + break; + case PLUS: + op2 = popand('+'); + inst1 = newinst(OR); + op2->last->u2.next = inst1; + inst1->u1.right = op2->first; + pushand(op2->first, inst1); + break; + case QUEST: + op2 = popand('?'); + inst1 = newinst(OR); + inst2 = newinst(NOP); + inst1->u2.left = inst2; + inst1->u1.right = op2->first; + op2->last->u2.next = inst2; + pushand(inst1, inst2); + break; + } + } +} + +static Reprog* +optimize(Reprog *pp) +{ + Reinst *inst, *target; + int size; + Reprog *npp; + Reclass *cl; + int diff; + + /* + * get rid of NOOP chains + */ + for(inst=pp->firstinst; inst->type!=END; inst++){ + target = inst->u2.next; + while(target->type == NOP) + target = target->u2.next; + inst->u2.next = target; + } + + /* + * The original allocation is for an area larger than + * necessary. Reallocate to the actual space used + * and then relocate the code. + */ + size = sizeof(Reprog) + (freep - pp->firstinst)*sizeof(Reinst); + npp = realloc(pp, size); + if(npp==0 || npp==pp) + return pp; + diff = (char *)npp - (char *)pp; + freep = (Reinst *)((char *)freep + diff); + for(inst=npp->firstinst; inst<freep; inst++){ + switch(inst->type){ + case OR: + case STAR: + case PLUS: + case QUEST: + *(char **)&inst->u1.right += diff; + break; + case CCLASS: + case NCCLASS: + *(char **)&inst->u1.right += diff; + cl = inst->u1.cp; + *(char **)&cl->end += diff; + break; + } + *(char **)&inst->u2.left += diff; + } + *(char **)&npp->startinst += diff; + return npp; +} + +#ifdef DEBUG +static void +dumpstack(void){ + Node *stk; + int *ip; + + print("operators\n"); + for(ip=atorstack; ip<atorp; ip++) + print("0%o\n", *ip); + print("operands\n"); + for(stk=andstack; stk<andp; stk++) + print("0%o\t0%o\n", stk->first->type, stk->last->type); +} + +static void +dump(Reprog *pp) +{ + Reinst *l; + Rune *p; + + l = pp->firstinst; + do{ + print("%d:\t0%o\t%d\t%d", l-pp->firstinst, l->type, + l->u2.left-pp->firstinst, l->u1.right-pp->firstinst); + if(l->type == RUNE) + print("\t%C\n", l->u1.r); + else if(l->type == CCLASS || l->type == NCCLASS){ + print("\t["); + if(l->type == NCCLASS) + print("^"); + for(p = l->u1.cp->spans; p < l->u1.cp->end; p += 2) + if(p[0] == p[1]) + print("%C", p[0]); + else + print("%C-%C", p[0], p[1]); + print("]\n"); + } else + print("\n"); + }while(l++->type); +} +#endif + +static Reclass* +newclass(void) +{ + if(nclass >= NCLASS) + regerr2("too many character classes; limit", NCLASS+'0'); + return &(classp[nclass++]); +} + +static int +nextc(Rune *rp) +{ + if(lexdone){ + *rp = 0; + return 1; + } + exprp += chartorune(rp, exprp); + if(*rp == '\\'){ + exprp += chartorune(rp, exprp); + return 1; + } + if(*rp == 0) + lexdone = 1; + return 0; +} + +static int +lex(int literal, int dot_type) +{ + int quoted; + + quoted = nextc(&yyrune); + if(literal || quoted){ + if(yyrune == 0) + return END; + return RUNE; + } + + switch(yyrune){ + case 0: + return END; + case '*': + return STAR; + case '?': + return QUEST; + case '+': + return PLUS; + case '|': + return OR; + case '.': + return dot_type; + case '(': + return LBRA; + case ')': + return RBRA; + case '^': + return BOL; + case '$': + return EOL; + case '[': + return bldcclass(); + } + return RUNE; +} + +static int +bldcclass(void) +{ + int type; + Rune r[NCCRUNE]; + Rune *p, *ep, *np; + Rune rune; + int quoted; + + /* we have already seen the '[' */ + type = CCLASS; + yyclassp = newclass(); + + /* look ahead for negation */ + /* SPECIAL CASE!!! negated classes don't match \n */ + ep = r; + quoted = nextc(&rune); + if(!quoted && rune == '^'){ + type = NCCLASS; + quoted = nextc(&rune); + *ep++ = '\n'; + *ep++ = '\n'; + } + + /* parse class into a set of spans */ + for(; ep<&r[NCCRUNE];){ + if(rune == 0){ + rcerror("malformed '[]'"); + return 0; + } + if(!quoted && rune == ']') + break; + if(!quoted && rune == '-'){ + if(ep == r){ + rcerror("malformed '[]'"); + return 0; + } + quoted = nextc(&rune); + if((!quoted && rune == ']') || rune == 0){ + rcerror("malformed '[]'"); + return 0; + } + *(ep-1) = rune; + } else { + *ep++ = rune; + *ep++ = rune; + } + quoted = nextc(&rune); + } + + /* sort on span start */ + for(p = r; p < ep; p += 2){ + for(np = p; np < ep; np += 2) + if(*np < *p){ + rune = np[0]; + np[0] = p[0]; + p[0] = rune; + rune = np[1]; + np[1] = p[1]; + p[1] = rune; + } + } + + /* merge spans */ + np = yyclassp->spans; + p = r; + if(r == ep) + yyclassp->end = np; + else { + np[0] = *p++; + np[1] = *p++; + for(; p < ep; p += 2) + if(p[0] <= np[1]){ + if(p[1] > np[1]) + np[1] = p[1]; + } else { + np += 2; + np[0] = p[0]; + np[1] = p[1]; + } + yyclassp->end = np+2; + } + + return type; +} + +static Reprog* +regcomp1(char *s, int literal, int dot_type) +{ + int token; + Reprog *volatile pp; + + /* get memory for the program */ + pp = malloc(sizeof(Reprog) + 6*sizeof(Reinst)*strlen(s)); + if(pp == 0){ + regerror("out of memory"); + return 0; + } + freep = pp->firstinst; + classp = pp->class; + errors = 0; + + if(setjmp(regkaboom)) + goto out; + + /* go compile the sucker */ + lexdone = 0; + exprp = s; + nclass = 0; + nbra = 0; + atorp = atorstack; + andp = andstack; + subidp = subidstack; + lastwasand = FALSE; + cursubid = 0; + + /* Start with a low priority operator to prime parser */ + pushator(START-1); + while((token = lex(literal, dot_type)) != END){ + if((token&0300) == OPERATOR) + operator(token); + else + operand(token); + } + + /* Close with a low priority operator */ + evaluntil(START); + + /* Force END */ + operand(END); + evaluntil(START); +#ifdef DEBUG + dumpstack(); +#endif + if(nbra) + rcerror("unmatched left paren"); + --andp; /* points to first and only operand */ + pp->startinst = andp->first; +#ifdef DEBUG + dump(pp); +#endif + pp = optimize(pp); +#ifdef DEBUG + print("start: %d\n", andp->first-pp->firstinst); + dump(pp); +#endif +out: + if(errors){ + free(pp); + pp = 0; + } + return pp; +} + +extern Reprog* +regcomp(char *s) +{ + return regcomp1(s, 0, ANY); +} + +extern Reprog* +regcomplit(char *s) +{ + return regcomp1(s, 1, ANY); +} + +extern Reprog* +regcompnl(char *s) +{ + return regcomp1(s, 0, ANYNL); +} diff --git a/lib9/regex/regcomp.h b/lib9/regex/regcomp.h @@ -0,0 +1,74 @@ +/* + * substitution list + */ +#define uchar __reuchar +typedef unsigned char uchar; +#define nelem(x) (sizeof(x)/sizeof((x)[0])) + +#define NSUBEXP 32 +typedef struct Resublist Resublist; +struct Resublist +{ + Resub m[NSUBEXP]; +}; + +/* max character classes per program */ +extern Reprog RePrOg; +#define NCLASS (sizeof(RePrOg.class)/sizeof(Reclass)) + +/* max rune ranges per character class */ +#define NCCRUNE (sizeof(Reclass)/sizeof(Rune)) + +/* + * Actions and Tokens (Reinst types) + * + * 02xx are operators, value == precedence + * 03xx are tokens, i.e. operands for operators + */ +#define RUNE 0177 +#define OPERATOR 0200 /* Bitmask of all operators */ +#define START 0200 /* Start, used for marker on stack */ +#define RBRA 0201 /* Right bracket, ) */ +#define LBRA 0202 /* Left bracket, ( */ +#define OR 0203 /* Alternation, | */ +#define CAT 0204 /* Concatentation, implicit operator */ +#define STAR 0205 /* Closure, * */ +#define PLUS 0206 /* a+ == aa* */ +#define QUEST 0207 /* a? == a|nothing, i.e. 0 or 1 a's */ +#define ANY 0300 /* Any character except newline, . */ +#define ANYNL 0301 /* Any character including newline, . */ +#define NOP 0302 /* No operation, internal use only */ +#define BOL 0303 /* Beginning of line, ^ */ +#define EOL 0304 /* End of line, $ */ +#define CCLASS 0305 /* Character class, [] */ +#define NCCLASS 0306 /* Negated character class, [] */ +#define END 0377 /* Terminate: match found */ + +/* + * regexec execution lists + */ +#define LISTSIZE 10 +#define BIGLISTSIZE (10*LISTSIZE) +typedef struct Relist Relist; +struct Relist +{ + Reinst* inst; /* Reinstruction of the thread */ + Resublist se; /* matched subexpressions in this thread */ +}; +typedef struct Reljunk Reljunk; +struct Reljunk +{ + Relist* relist[2]; + Relist* reliste[2]; + int starttype; + Rune startchar; + char* starts; + char* eol; + Rune* rstarts; + Rune* reol; +}; + +extern Relist* _renewthread(Relist*, Reinst*, int, Resublist*); +extern void _renewmatch(Resub*, int, Resublist*); +extern Relist* _renewemptythread(Relist*, Reinst*, int, char*); +extern Relist* _rrenewemptythread(Relist*, Reinst*, int, Rune*); diff --git a/lib9/regex/regerror.c b/lib9/regex/regerror.c @@ -0,0 +1,14 @@ +#include "lib9.h" +#include "regexp9.h" + +void +regerror(char *s) +{ + char buf[132]; + + strcpy(buf, "regerror: "); + strcat(buf, s); + strcat(buf, "\n"); + write(2, buf, strlen(buf)); + exits("regerr"); +} diff --git a/lib9/regex/regexec.c b/lib9/regex/regexec.c @@ -0,0 +1,231 @@ +#include "lib9.h" +#include "regexp9.h" +#include "regcomp.h" + + +/* + * return 0 if no match + * >0 if a match + * <0 if we ran out of _relist space + */ +static int +regexec1(Reprog *progp, /* program to run */ + char *bol, /* string to run machine on */ + Resub *mp, /* subexpression elements */ + int ms, /* number of elements at mp */ + Reljunk *j +) +{ + int flag=0; + Reinst *inst; + Relist *tlp; + char *s; + int i, checkstart; + Rune r, *rp, *ep; + int n; + Relist* tl; /* This list, next list */ + Relist* nl; + Relist* tle; /* ends of this and next list */ + Relist* nle; + int match; + char *p; + + match = 0; + checkstart = j->starttype; + if(mp) + for(i=0; i<ms; i++) { + mp[i].s.sp = 0; + mp[i].e.ep = 0; + } + j->relist[0][0].inst = 0; + j->relist[1][0].inst = 0; + + /* Execute machine once for each character, including terminal NUL */ + s = j->starts; + do{ + /* fast check for first char */ + if(checkstart) { + switch(j->starttype) { + case RUNE: + p = utfrune(s, j->startchar); + if(p == 0 || s == j->eol) + return match; + s = p; + break; + case BOL: + if(s == bol) + break; + p = utfrune(s, '\n'); + if(p == 0 || s == j->eol) + return match; + s = p; + break; + } + } + r = *(uchar*)s; + if(r < Runeself) + n = 1; + else + n = chartorune(&r, s); + + /* switch run lists */ + tl = j->relist[flag]; + tle = j->reliste[flag]; + nl = j->relist[flag^=1]; + nle = j->reliste[flag]; + nl->inst = 0; + + /* Add first instruction to current list */ + if(match == 0) + _renewemptythread(tl, progp->startinst, ms, s); + + /* Execute machine until current list is empty */ + for(tlp=tl; tlp->inst; tlp++){ /* assignment = */ + for(inst = tlp->inst; ; inst = inst->u2.next){ + switch(inst->type){ + case RUNE: /* regular character */ + if(inst->u1.r == r){ + if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) + return -1; + } + break; + case LBRA: + tlp->se.m[inst->u1.subid].s.sp = s; + continue; + case RBRA: + tlp->se.m[inst->u1.subid].e.ep = s; + continue; + case ANY: + if(r != '\n') + if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) + return -1; + break; + case ANYNL: + if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) + return -1; + break; + case BOL: + if(s == bol || *(s-1) == '\n') + continue; + break; + case EOL: + if(s == j->eol || r == 0 || r == '\n') + continue; + break; + case CCLASS: + ep = inst->u1.cp->end; + for(rp = inst->u1.cp->spans; rp < ep; rp += 2) + if(r >= rp[0] && r <= rp[1]){ + if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) + return -1; + break; + } + break; + case NCCLASS: + ep = inst->u1.cp->end; + for(rp = inst->u1.cp->spans; rp < ep; rp += 2) + if(r >= rp[0] && r <= rp[1]) + break; + if(rp == ep) + if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) + return -1; + break; + case OR: + /* evaluate right choice later */ + if(_renewthread(tlp, inst->u1.right, ms, &tlp->se) == tle) + return -1; + /* efficiency: advance and re-evaluate */ + continue; + case END: /* Match! */ + match = 1; + tlp->se.m[0].e.ep = s; + if(mp != 0) + _renewmatch(mp, ms, &tlp->se); + break; + } + break; + } + } + if(s == j->eol) + break; + checkstart = j->starttype && nl->inst==0; + s += n; + }while(r); + return match; +} + +static int +regexec2(Reprog *progp, /* program to run */ + char *bol, /* string to run machine on */ + Resub *mp, /* subexpression elements */ + int ms, /* number of elements at mp */ + Reljunk *j +) +{ + int rv; + Relist *relist0, *relist1; + + /* mark space */ + relist0 = malloc(BIGLISTSIZE*sizeof(Relist)); + if(relist0 == nil) + return -1; + relist1 = malloc(BIGLISTSIZE*sizeof(Relist)); + if(relist1 == nil){ + free(relist1); + return -1; + } + j->relist[0] = relist0; + j->relist[1] = relist1; + j->reliste[0] = relist0 + BIGLISTSIZE - 2; + j->reliste[1] = relist1 + BIGLISTSIZE - 2; + + rv = regexec1(progp, bol, mp, ms, j); + free(relist0); + free(relist1); + return rv; +} + +extern int +regexec(Reprog *progp, /* program to run */ + char *bol, /* string to run machine on */ + Resub *mp, /* subexpression elements */ + int ms) /* number of elements at mp */ +{ + Reljunk j; + Relist relist0[LISTSIZE], relist1[LISTSIZE]; + int rv; + + /* + * use user-specified starting/ending location if specified + */ + j.starts = bol; + j.eol = 0; + if(mp && ms>0){ + if(mp->s.sp) + j.starts = mp->s.sp; + if(mp->e.ep) + j.eol = mp->e.ep; + } + j.starttype = 0; + j.startchar = 0; + if(progp->startinst->type == RUNE && progp->startinst->u1.r < Runeself) { + j.starttype = RUNE; + j.startchar = progp->startinst->u1.r; + } + if(progp->startinst->type == BOL) + j.starttype = BOL; + + /* mark space */ + j.relist[0] = relist0; + j.relist[1] = relist1; + j.reliste[0] = relist0 + nelem(relist0) - 2; + j.reliste[1] = relist1 + nelem(relist1) - 2; + + rv = regexec1(progp, bol, mp, ms, &j); + if(rv >= 0) + return rv; + rv = regexec2(progp, bol, mp, ms, &j); + if(rv >= 0) + return rv; + return -1; +} diff --git a/lib9/regex/regsub.c b/lib9/regex/regsub.c @@ -0,0 +1,63 @@ +#include "lib9.h" +#include "regexp9.h" + +/* substitute into one string using the matches from the last regexec() */ +extern void +regsub(char *sp, /* source string */ + char *dp, /* destination string */ + int dlen, + Resub *mp, /* subexpression elements */ + int ms) /* number of elements pointed to by mp */ +{ + char *ssp, *ep; + int i; + + ep = dp+dlen-1; + while(*sp != '\0'){ + if(*sp == '\\'){ + switch(*++sp){ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + i = *sp-'0'; + if(mp[i].s.sp != 0 && mp!=0 && ms>i) + for(ssp = mp[i].s.sp; + ssp < mp[i].e.ep; + ssp++) + if(dp < ep) + *dp++ = *ssp; + break; + case '\\': + if(dp < ep) + *dp++ = '\\'; + break; + case '\0': + sp--; + break; + default: + if(dp < ep) + *dp++ = *sp; + break; + } + }else if(*sp == '&'){ + if(mp[0].s.sp != 0 && mp!=0 && ms>0) + if(mp[0].s.sp != 0) + for(ssp = mp[0].s.sp; + ssp < mp[0].e.ep; ssp++) + if(dp < ep) + *dp++ = *ssp; + }else{ + if(dp < ep) + *dp++ = *sp; + } + sp++; + } + *dp = '\0'; +} diff --git a/lib9/regex/rregexec.c b/lib9/regex/rregexec.c @@ -0,0 +1,213 @@ +#include "lib9.h" +#include "regexp9.h" +#include "regcomp.h" + +/* + * return 0 if no match + * >0 if a match + * <0 if we ran out of _relist space + */ +static int +rregexec1(Reprog *progp, /* program to run */ + Rune *bol, /* string to run machine on */ + Resub *mp, /* subexpression elements */ + int ms, /* number of elements at mp */ + Reljunk *j) +{ + int flag=0; + Reinst *inst; + Relist *tlp; + Rune *s; + int i, checkstart; + Rune r, *rp, *ep; + Relist* tl; /* This list, next list */ + Relist* nl; + Relist* tle; /* ends of this and next list */ + Relist* nle; + int match; + + match = 0; + checkstart = j->startchar; + if(mp) + for(i=0; i<ms; i++) { + mp[i].s.rsp = 0; + mp[i].e.rep = 0; + } + j->relist[0][0].inst = 0; + j->relist[1][0].inst = 0; + + /* Execute machine once for each character, including terminal NUL */ + s = j->rstarts; + do{ + + /* fast check for first char */ + if(checkstart) { + switch(j->starttype) { + case RUNE: + while(*s != j->startchar) { + if(*s == 0 || s == j->reol) + return match; + s++; + } + break; + case BOL: + if(s == bol) + break; + while(*s != '\n') { + if(*s == 0 || s == j->reol) + return match; + s++; + } + break; + } + } + + r = *s; + + /* switch run lists */ + tl = j->relist[flag]; + tle = j->reliste[flag]; + nl = j->relist[flag^=1]; + nle = j->reliste[flag]; + nl->inst = 0; + + /* Add first instruction to current list */ + _rrenewemptythread(tl, progp->startinst, ms, s); + + /* Execute machine until current list is empty */ + for(tlp=tl; tlp->inst; tlp++){ + for(inst=tlp->inst; ; inst = inst->u2.next){ + switch(inst->type){ + case RUNE: /* regular character */ + if(inst->u1.r == r) + if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) + return -1; + break; + case LBRA: + tlp->se.m[inst->u1.subid].s.rsp = s; + continue; + case RBRA: + tlp->se.m[inst->u1.subid].e.rep = s; + continue; + case ANY: + if(r != '\n') + if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) + return -1; + break; + case ANYNL: + if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) + return -1; + break; + case BOL: + if(s == bol || *(s-1) == '\n') + continue; + break; + case EOL: + if(s == j->reol || r == 0 || r == '\n') + continue; + break; + case CCLASS: + ep = inst->u1.cp->end; + for(rp = inst->u1.cp->spans; rp < ep; rp += 2) + if(r >= rp[0] && r <= rp[1]){ + if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) + return -1; + break; + } + break; + case NCCLASS: + ep = inst->u1.cp->end; + for(rp = inst->u1.cp->spans; rp < ep; rp += 2) + if(r >= rp[0] && r <= rp[1]) + break; + if(rp == ep) + if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle) + return -1; + break; + case OR: + /* evaluate right choice later */ + if(_renewthread(tlp, inst->u1.right, ms, &tlp->se) == tle) + return -1; + /* efficiency: advance and re-evaluate */ + continue; + case END: /* Match! */ + match = 1; + tlp->se.m[0].e.rep = s; + if(mp != 0) + _renewmatch(mp, ms, &tlp->se); + break; + } + break; + } + } + if(s == j->reol) + break; + checkstart = j->startchar && nl->inst==0; + s++; + }while(r); + return match; +} + +static int +rregexec2(Reprog *progp, /* program to run */ + Rune *bol, /* string to run machine on */ + Resub *mp, /* subexpression elements */ + int ms, /* number of elements at mp */ + Reljunk *j +) +{ + Relist relist0[5*LISTSIZE], relist1[5*LISTSIZE]; + + /* mark space */ + j->relist[0] = relist0; + j->relist[1] = relist1; + j->reliste[0] = relist0 + nelem(relist0) - 2; + j->reliste[1] = relist1 + nelem(relist1) - 2; + + return rregexec1(progp, bol, mp, ms, j); +} + +extern int +rregexec(Reprog *progp, /* program to run */ + Rune *bol, /* string to run machine on */ + Resub *mp, /* subexpression elements */ + int ms) /* number of elements at mp */ +{ + Reljunk j; + Relist relist0[LISTSIZE], relist1[LISTSIZE]; + int rv; + + /* + * use user-specified starting/ending location if specified + */ + j.rstarts = bol; + j.reol = 0; + if(mp && ms>0){ + if(mp->s.sp) + j.rstarts = mp->s.rsp; + if(mp->e.ep) + j.reol = mp->e.rep; + } + j.starttype = 0; + j.startchar = 0; + if(progp->startinst->type == RUNE && progp->startinst->u1.r < Runeself) { + j.starttype = RUNE; + j.startchar = progp->startinst->u1.r; + } + if(progp->startinst->type == BOL) + j.starttype = BOL; + + /* mark space */ + j.relist[0] = relist0; + j.relist[1] = relist1; + j.reliste[0] = relist0 + nelem(relist0) - 2; + j.reliste[1] = relist1 + nelem(relist1) - 2; + + rv = rregexec1(progp, bol, mp, ms, &j); + if(rv >= 0) + return rv; + rv = rregexec2(progp, bol, mp, ms, &j); + if(rv >= 0) + return rv; + return -1; +} diff --git a/lib9/regex/rregsub.c b/lib9/regex/rregsub.c @@ -0,0 +1,63 @@ +#include "lib9.h" +#include "regexp9.h" + +/* substitute into one string using the matches from the last regexec() */ +extern void +rregsub(Rune *sp, /* source string */ + Rune *dp, /* destination string */ + int dlen, + Resub *mp, /* subexpression elements */ + int ms) /* number of elements pointed to by mp */ +{ + Rune *ssp, *ep; + int i; + + ep = dp+(dlen/sizeof(Rune))-1; + while(*sp != '\0'){ + if(*sp == '\\'){ + switch(*++sp){ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + i = *sp-'0'; + if(mp[i].s.rsp != 0 && mp!=0 && ms>i) + for(ssp = mp[i].s.rsp; + ssp < mp[i].e.rep; + ssp++) + if(dp < ep) + *dp++ = *ssp; + break; + case '\\': + if(dp < ep) + *dp++ = '\\'; + break; + case '\0': + sp--; + break; + default: + if(dp < ep) + *dp++ = *sp; + break; + } + }else if(*sp == '&'){ + if(mp[0].s.rsp != 0 && mp!=0 && ms>0) + if(mp[0].s.rsp != 0) + for(ssp = mp[0].s.rsp; + ssp < mp[0].e.rep; ssp++) + if(dp < ep) + *dp++ = *ssp; + }else{ + if(dp < ep) + *dp++ = *sp; + } + sp++; + } + *dp = '\0'; +} diff --git a/lib9/regex/test.c b/lib9/regex/test.c @@ -0,0 +1,46 @@ +#include "lib9.h" +#include <regexp9.h> + +struct x +{ + char *re; + char *s; + Reprog *p; +}; + +struct x t[] = { + { "^[^!@]+$", "/bin/upas/aliasmail '&'", 0 }, + { "^local!(.*)$", "/mail/box/\\1/mbox", 0 }, + { "^plan9!(.*)$", "\\1", 0 }, + { "^helix!(.*)$", "\\1", 0 }, + { "^([^!]+)@([^!@]+)$", "\\2!\\1", 0 }, + { "^(uk\\.[^!]*)(!.*)$", "/bin/upas/uk2uk '\\1' '\\2'", 0 }, + { "^[^!]*\\.[^!]*!.*$", "inet!&", 0 }, + { "^\xE2\x98\xBA$", "smiley", 0 }, + { "^(coma|research|pipe|pyxis|inet|hunny|gauss)!(.*)$", "/mail/lib/qmail '\\s' 'net!\\1' '\\2'", 0 }, + { "^.*$", "/mail/lib/qmail '\\s' 'net!research' '&'", 0 }, + { 0, 0, 0 }, +}; + +main(int ac, char **av) +{ + Resub rs[10]; + char dst[128]; + int n; + struct x *tp; + + for(tp = t; tp->re; tp++) + tp->p = regcomp(tp->re); + + + for(tp = t; tp->re; tp++){ + print("%s VIA %s", av[1], tp->re); + memset(rs, 0, sizeof rs); + if(regexec(tp->p, av[1], rs, 10)){ + regsub(tp->s, dst, sizeof dst, rs, 10); + print(" sub %s -> %s", tp->s, dst); + } + print("\n"); + } + exit(0); +} diff --git a/lib9/regex/test2.c b/lib9/regex/test2.c @@ -0,0 +1,20 @@ +#include "lib9.h" +#include <regexp9.h> + + +main(int ac, char **av) +{ + Resub rs[10]; + Reprog *p; + char *s; + int i; + + p = regcomp("[^a-z]"); + s = "\n"; + if(regexec(p, s, rs, 10)) + print("%s %lux %lux %lux\n", s, s, rs[0].sp, rs[0].ep); + s = "0"; + if(regexec(p, s, rs, 10)) + print("%s %lux %lux %lux\n", s, s, rs[0].sp, rs[0].ep); + exit(0); +} diff --git a/lib9/regexp.7 b/lib9/regexp.7 @@ -0,0 +1,133 @@ +.TH REGEXP 7 +.SH NAME +regexp \- Plan 9 regular expression notation +.SH DESCRIPTION +This manual page describes the regular expression +syntax used by the Plan 9 regular expression library +.IR regexp (3). +It is the form used by +.IR egrep (1) +before +.I egrep +got complicated. +.PP +A +.I "regular expression" +specifies +a set of strings of characters. +A member of this set of strings is said to be +.I matched +by the regular expression. In many applications +a delimiter character, commonly +.LR / , +bounds a regular expression. +In the following specification for regular expressions +the word `character' means any character (rune) but newline. +.PP +The syntax for a regular expression +.B e0 +is +.IP +.EX +e3: literal | charclass | '.' | '^' | '$' | '(' e0 ')' + +e2: e3 + | e2 REP + +REP: '*' | '+' | '?' + +e1: e2 + | e1 e2 + +e0: e1 + | e0 '|' e1 +.EE +.PP +A +.B literal +is any non-metacharacter, or a metacharacter +(one of +.BR .*+?[]()|\e^$ ), +or the delimiter +preceded by +.LR \e . +.PP +A +.B charclass +is a nonempty string +.I s +bracketed +.BI [ \|s\| ] +(or +.BI [^ s\| ]\fR); +it matches any character in (or not in) +.IR s . +A negated character class never +matches newline. +A substring +.IB a - b\f1, +with +.I a +and +.I b +in ascending +order, stands for the inclusive +range of +characters between +.I a +and +.IR b . +In +.IR s , +the metacharacters +.LR - , +.LR ] , +an initial +.LR ^ , +and the regular expression delimiter +must be preceded by a +.LR \e ; +other metacharacters +have no special meaning and +may appear unescaped. +.PP +A +.L . +matches any character. +.PP +A +.L ^ +matches the beginning of a line; +.L $ +matches the end of the line. +.PP +The +.B REP +operators match zero or more +.RB ( * ), +one or more +.RB ( + ), +zero or one +.RB ( ? ), +instances respectively of the preceding regular expression +.BR e2 . +.PP +A concatenated regular expression, +.BR "e1\|e2" , +matches a match to +.B e1 +followed by a match to +.BR e2 . +.PP +An alternative regular expression, +.BR "e0\||\|e1" , +matches either a match to +.B e0 +or a match to +.BR e1 . +.PP +A match to any part of a regular expression +extends as far as possible without preventing +a match to the remainder of the regular expression. +.SH "SEE ALSO" +.IR regexp (3) diff --git a/lib9/regexp.h b/lib9/regexp.h @@ -0,0 +1 @@ +#include <regexp9.h> diff --git a/lib9/regexp9.h b/lib9/regexp9.h @@ -0,0 +1,96 @@ +#ifndef _REGEXP9_H_ +#define _REGEXP9_H_ 1 +#if defined(__cplusplus) +extern "C" { +#endif + +#ifdef AUTOLIB +AUTOLIB(regexp9) +#endif + +#include <utf.h> + +typedef struct Resub Resub; +typedef struct Reclass Reclass; +typedef struct Reinst Reinst; +typedef struct Reprog Reprog; + +/* + * Sub expression matches + */ +struct Resub{ + union + { + char *sp; + Rune *rsp; + }s; + union + { + char *ep; + Rune *rep; + }e; +}; + +/* + * character class, each pair of rune's defines a range + */ +struct Reclass{ + Rune *end; + Rune spans[64]; +}; + +/* + * Machine instructions + */ +struct Reinst{ + int type; + union { + Reclass *cp; /* class pointer */ + Rune r; /* character */ + int subid; /* sub-expression id for RBRA and LBRA */ + Reinst *right; /* right child of OR */ + }u1; + union { /* regexp relies on these two being in the same union */ + Reinst *left; /* left child of OR */ + Reinst *next; /* next instruction for CAT & LBRA */ + }u2; +}; + +/* + * Reprogram definition + */ +struct Reprog{ + Reinst *startinst; /* start pc */ + Reclass class[16]; /* .data */ + Reinst firstinst[5]; /* .text */ +}; + +extern Reprog *regcomp9(char*); +extern Reprog *regcomplit9(char*); +extern Reprog *regcompnl9(char*); +extern void regerror9(char*); +extern int regexec9(Reprog*, char*, Resub*, int); +extern void regsub9(char*, char*, int, Resub*, int); + +extern int rregexec9(Reprog*, Rune*, Resub*, int); +extern void rregsub9(Rune*, Rune*, int, Resub*, int); + +/* + * Darwin simply cannot handle having routines that + * override other library routines. + */ +#ifndef NOPLAN9DEFINES +#define regcomp regcomp9 +#define regcomplit regcomplit9 +#define regcompnl regcompnl9 +#define regerror regerror9 +#define regexec regexec9 +#define regsub regsub9 +#define rregexec rregexec9 +#define rregsub rregsub9 +#endif + +#if defined(__cplusplus) +} +#endif +#endif diff --git a/lib9/rfork.c b/lib9/rfork.c @@ -0,0 +1,124 @@ +#include <u.h> +#include <sys/wait.h> +#include <signal.h> +#include <libc.h> +#undef rfork + +static void +nop(int x) +{ + USED(x); +} + +int +p9rfork(int flags) +{ + int pid, status; + int p[2]; + int n; + char buf[128], *q; + + if((flags&(RFPROC|RFFDG|RFMEM)) == (RFPROC|RFFDG)){ + /* check other flags before we commit */ + flags &= ~(RFPROC|RFFDG); + n = (flags & ~(RFNOTEG|RFNAMEG|RFNOWAIT)); + if(n){ + werrstr("unknown flags %08ux in rfork", n); + return -1; + } + if(flags&RFNOWAIT){ + /* + * BUG - should put the signal handler back after we + * finish, but I just don't care. If a program calls with + * NOWAIT once, they're not likely to want child notes + * after that. + */ + signal(SIGCHLD, nop); + if(pipe(p) < 0) + return -1; + } + pid = fork(); + if(pid == -1) + return -1; + if(flags&RFNOWAIT){ + flags &= ~RFNOWAIT; + if(pid){ + /* + * Parent - wait for child to fork wait-free child. + * Then read pid from pipe. Assume pipe buffer can absorb the write. + */ + close(p[1]); + status = 0; + if(wait4(pid, &status, 0, 0) < 0){ + werrstr("pipe dance - wait4 - %r"); + close(p[0]); + return -1; + } + n = readn(p[0], buf, sizeof buf-1); + close(p[0]); + if(!WIFEXITED(status) || WEXITSTATUS(status)!=0 || n <= 0){ + if(!WIFEXITED(status)) + werrstr("pipe dance - !exited 0x%ux", status); + else if(WEXITSTATUS(status) != 0) + werrstr("pipe dance - non-zero status 0x%ux", status); + else if(n < 0) + werrstr("pipe dance - pipe read error - %r"); + else if(n == 0) + werrstr("pipe dance - pipe read eof"); + else + werrstr("pipe dance - unknown failure"); + return -1; + } + buf[n] = 0; + if(buf[0] == 'x'){ + werrstr("%s", buf+2); + return -1; + } + pid = strtol(buf, &q, 0); + }else{ + /* + * Child - fork a new child whose wait message can't + * get back to the parent because we're going to exit! + */ + signal(SIGCHLD, SIG_IGN); + close(p[0]); + pid = fork(); + if(pid){ + /* Child parent - send status over pipe and exit. */ + if(pid > 0) + fprint(p[1], "%d", pid); + else + fprint(p[1], "x %r"); + close(p[1]); + _exit(0); + }else{ + /* Child child - close pipe. */ + close(p[1]); + } + } + } + if(pid != 0) + return pid; + } + if(flags&RFPROC){ + werrstr("cannot use rfork for shared memory -- use ffork"); + return -1; + } + if(flags&RFNAMEG){ + /* XXX set $NAMESPACE to a new directory */ + flags &= ~RFNAMEG; + } + if(flags&RFNOTEG){ + setpgid(0, getpid()); + flags &= ~RFNOTEG; + } + if(flags&RFNOWAIT){ + werrstr("cannot use RFNOWAIT without RFPROC"); + return -1; + } + if(flags){ + werrstr("unknown flags %08ux in rfork", flags); + return -1; + } + return 0; +} diff --git a/lib9/searchpath.c b/lib9/searchpath.c @@ -0,0 +1,62 @@ +#include <u.h> +#include <libc.h> + +/* + * Search $PATH for an executable with the given name. + * Like in rc, mid-name slashes do not disable search. + * Should probably handle escaped colons, + * but I don't know what the syntax is. + */ +char* +searchpath(char *name) +{ + char *path, *p, *next; + char *s, *ss; + int ns, l; + + s = nil; + ns = 0; + if((name[0] == '.' && name[1] == '/') + || (name[0] == '.' && name[1] == '.' && name[2] == '/') + || (name[0] == '/')){ + if(access(name, AEXEC) >= 0) + return strdup(name); + return nil; + } + + path = getenv("PATH"); + for(p=path; p && *p; p=next){ + if((next = strchr(p, ':')) != nil) + *next++ = 0; + if(*p == 0){ + if(access(name, AEXEC) >= 0){ + free(s); + free(path); + return strdup(name); + } + }else{ + l = strlen(p)+1+strlen(name)+1; + if(l > ns){ + ss = realloc(s, l); + if(ss == nil){ + free(s); + free(path); + return nil; + } + s = ss; + ns = l; + } + strcpy(s, p); + strcat(s, "/"); + strcat(s, name); + if(access(s, AEXEC) >= 0){ + free(path); + return s; + } + } + } + free(s); + free(path); + return nil; +} + diff --git a/lib9/seek.c b/lib9/seek.c @@ -0,0 +1,8 @@ +#include <u.h> +#include <libc.h> + +vlong +seek(int fd, vlong offset, int whence) +{ + return lseek(fd, offset, whence); +} diff --git a/lib9/sendfd.c b/lib9/sendfd.c @@ -0,0 +1,85 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <unistd.h> +#include <errno.h> + +#ifndef CMSG_ALIGN +# ifdef __sun__ +# define CMSG_ALIGN _CMSG_DATA_ALIGN +# else +# define CMSG_ALIGN(len) (((len)+sizeof(long)-1) & ~(sizeof(long)-1)) +# endif +#endif + +#ifndef CMSG_SPACE +# define CMSG_SPACE(len) (CMSG_ALIGN(sizeof(struct cmsghdr))+CMSG_ALIGN(len)) +#endif + +#ifndef CMSG_LEN +# define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr))+(len)) +#endif + +int +sendfd(int s, int fd) +{ + char buf[1]; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + int n; + char cms[CMSG_SPACE(sizeof(int))]; + + buf[0] = 0; + iov.iov_base = buf; + iov.iov_len = 1; + + memset(&msg, 0, sizeof msg); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = (caddr_t)cms; + msg.msg_controllen = CMSG_LEN(sizeof(int)); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *(int*)CMSG_DATA(cmsg) = fd; + + if((n=sendmsg(s, &msg, 0)) != iov.iov_len) + return -1; + return 0; +} + +int +recvfd(int s) +{ + int n; + int fd; + char buf[1]; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + char cms[CMSG_SPACE(sizeof(int))]; + + iov.iov_base = buf; + iov.iov_len = 1; + + memset(&msg, 0, sizeof msg); + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_control = (caddr_t)cms; + msg.msg_controllen = sizeof cms; + + if((n=recvmsg(s, &msg, 0)) < 0) + return -1; + + cmsg = CMSG_FIRSTHDR(&msg); + fd = *(int*)CMSG_DATA(cmsg); + return fd; +} diff --git a/lib9/sleep.c b/lib9/sleep.c @@ -0,0 +1,35 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <sys/time.h> +#include <sched.h> +#include <libc.h> + +int +p9sleep(long milli) +{ + struct timeval tv; + + if(milli == 0){ + sched_yield(); + return 0; + } + + tv.tv_sec = milli/1000; + tv.tv_usec = (milli%1000)*1000; + return select(0, 0, 0, 0, &tv); +} + +long +p9alarm(ulong milli) +{ + struct itimerval itv; + struct itimerval oitv; + + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 0; + itv.it_value.tv_sec = milli/1000; + itv.it_value.tv_usec = (milli%1000)*1000; + if(setitimer(ITIMER_REAL, &itv, &oitv) < 0) + return -1; + return oitv.it_value.tv_sec*1000+oitv.it_value.tv_usec/1000; +} diff --git a/lib9/st4nkO6D b/lib9/st4nkO6D @@ -0,0 +1 @@ +!<arch> diff --git a/lib9/strdup.c b/lib9/strdup.c @@ -0,0 +1,17 @@ +#include <u.h> +#include <libc.h> + +char* +strdup(char *s) +{ + char *t; + int l; + + l = strlen(s); + t = malloc(l+1); + if(t == nil) + return nil; + memmove(t, s, l+1); + return t; +} + diff --git a/lib9/strecpy.c b/lib9/strecpy.c @@ -0,0 +1,16 @@ +#include <lib9.h> + +char* +strecpy(char *to, char *e, char *from) +{ + if(to >= e) + return to; + to = memccpy(to, from, '\0', e - to); + if(to == nil){ + to = e - 1; + *to = '\0'; + }else{ + to--; + } + return to; +} diff --git a/lib9/sysfatal.c b/lib9/sysfatal.c @@ -0,0 +1,26 @@ +#include <lib9.h> +#include <stdarg.h> +#include "fmt.h" + +extern char *argv0; +extern void __fixargv0(void); +extern void exits(char*); +void (*_sysfatal)(char*, ...); + +void +sysfatal(char *fmt, ...) +{ + char buf[256]; + va_list arg; + + va_start(arg, fmt); + if(_sysfatal) + (*_sysfatal)(fmt, arg); + vseprint(buf, buf+sizeof buf, fmt, arg); + va_end(arg); + + __fixargv0(); + fprint(2, "%s: %s\n", argv0 ? argv0 : "<prog>", buf); + exits("fatal"); +} + diff --git a/lib9/syslog.c b/lib9/syslog.c @@ -0,0 +1,119 @@ +#include <u.h> +#include <libc.h> + +static struct +{ + int fd; + int consfd; + char *name; + Dir *d; + Dir *consd; + Lock lk; +} sl = +{ + -1, -1, +}; + +static void +_syslogopen(void) +{ + char buf[1024], *p; + + if(sl.fd >= 0) + close(sl.fd); + snprint(buf, sizeof(buf), "#9/log/%s", sl.name); + p = unsharp(buf); + sl.fd = open(p, OWRITE|OCEXEC|OAPPEND); + free(p); +} + +/* + * Print + * sysname: time: mesg + * on /sys/log/logname. + * If cons or log file can't be opened, print on the system console, too. + */ +void +syslog(int cons, char *logname, char *fmt, ...) +{ + char buf[1024]; + char *ctim, *p; + va_list arg; + int n; + Dir *d; + char err[ERRMAX]; + + err[0] = '\0'; + errstr(err, sizeof err); + lock(&sl.lk); + + /* + * paranoia makes us stat to make sure a fork+close + * hasn't broken our fd's + */ + d = dirfstat(sl.fd); + if(sl.fd < 0 + || sl.name == nil + || strcmp(sl.name, logname)!=0 + || sl.d == nil + || d == nil + || d->dev != sl.d->dev + || d->type != sl.d->type + || d->qid.path != sl.d->qid.path){ + free(sl.name); + sl.name = strdup(logname); + if(sl.name == nil) + cons = 1; + else{ + _syslogopen(); + if(sl.fd < 0) + cons = 1; + free(sl.d); + sl.d = d; + d = nil; /* don't free it */ + } + } + free(d); + if(cons){ + d = dirfstat(sl.consfd); + if(sl.consfd < 0 + || d == nil + || sl.consd == nil + || d->dev != sl.consd->dev + || d->type != sl.consd->type + || d->qid.path != sl.consd->qid.path){ + sl.consfd = open("/dev/tty", OWRITE|OCEXEC); + free(sl.consd); + sl.consd = d; + d = nil; /* don't free it */ + } + free(d); + } + + if(fmt == nil){ + unlock(&sl.lk); + return; + } + + ctim = ctime(time(0)); + werrstr(err); + p = buf + snprint(buf, sizeof(buf)-1, "%s ", sysname()); + strncpy(p, ctim+4, 15); + p += 15; + *p++ = ' '; + va_start(arg, fmt); + p = vseprint(p, buf+sizeof(buf)-1, fmt, arg); + va_end(arg); + *p++ = '\n'; + n = p - buf; + + if(sl.fd >= 0){ + seek(sl.fd, 0, 2); + write(sl.fd, buf, n); + } + + if(cons && sl.consfd >=0) + write(sl.consfd, buf, n); + + unlock(&sl.lk); +} diff --git a/lib9/sysname.c b/lib9/sysname.c @@ -0,0 +1,30 @@ +#include <u.h> +#include <libc.h> + +char* +sysname(void) +{ + static char buf[512]; + char *p, *q; + + if(buf[0]) + return buf; + + if((q = getenv("sysname")) != nil && q[0] != 0){ + utfecpy(buf, buf+sizeof buf, q); + free(q); + return buf; + } + if(q) + free(q); + + if(gethostname(buf, sizeof buf) >= 0){ + buf[sizeof buf-1] = 0; + if((p = strchr(buf, '.')) != nil) + *p = 0; + return buf; + } + + strcpy(buf, "gnot"); + return buf; +} diff --git a/lib9/testfork.c b/lib9/testfork.c @@ -0,0 +1,21 @@ +#include <lib9.h> + +void +sayhi(void *v) +{ + USED(v); + + print("hello from subproc\n"); + print("rendez got %lu from main\n", rendezvous(0x1234, 1234)); + exits(0); +} + +int +main(int argc, char **argv) +{ + print("hello from main\n"); + ffork(RFMEM|RFPROC, sayhi, nil); + + print("rendez got %lu from subproc\n", rendezvous(0x1234, 0)); + exits(0); +} diff --git a/lib9/time.c b/lib9/time.c @@ -0,0 +1,58 @@ +#include <u.h> +#include <sys/time.h> +#include <time.h> +#include <sys/resource.h> +#define NOPLAN9DEFINES +#include <libc.h> + +long +p9times(long *t) +{ + struct rusage ru, cru; + + if(getrusage(0, &ru) < 0 || getrusage(-1, &cru) < 0) + return -1; + + t[0] = ru.ru_utime.tv_sec*1000 + ru.ru_utime.tv_usec/1000; + t[1] = ru.ru_stime.tv_sec*1000 + ru.ru_stime.tv_usec/1000; + t[2] = cru.ru_utime.tv_sec*1000 + cru.ru_utime.tv_usec/1000; + t[3] = cru.ru_stime.tv_sec*1000 + cru.ru_stime.tv_usec/1000; + + /* BUG */ + return t[0]+t[1]+t[2]+t[3]; +} + +double +p9cputime(void) +{ + long t[4]; + double d; + + if(p9times(t) < 0) + return -1.0; + + d = (double)t[0]+(double)t[1]+(double)t[2]+(double)t[3]; + return d/1000.0; +} + +vlong +p9nsec(void) +{ + struct timeval tv; + + if(gettimeofday(&tv, 0) < 0) + return -1; + + return (vlong)tv.tv_sec*1000*1000*1000 + tv.tv_usec*1000; +} + +long +p9time(long *tt) +{ + long t; + t = time(0); + if(tt) + *tt = t; + return t; +} + diff --git a/lib9/tokenize.c b/lib9/tokenize.c @@ -0,0 +1,106 @@ +#include <lib9.h> + +static char qsep[] = " \t\r\n"; + +static char* +qtoken(char *s, char *sep) +{ + int quoting; + char *t; + + quoting = 0; + t = s; /* s is output string, t is input string */ + while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){ + if(*t != '\''){ + *s++ = *t++; + continue; + } + /* *t is a quote */ + if(!quoting){ + quoting = 1; + t++; + continue; + } + /* quoting and we're on a quote */ + if(t[1] != '\''){ + /* end of quoted section; absorb closing quote */ + t++; + quoting = 0; + continue; + } + /* doubled quote; fold one quote into two */ + t++; + *s++ = *t++; + } + if(*s != '\0'){ + *s = '\0'; + if(t == s) + t++; + } + return t; +} + +static char* +etoken(char *t, char *sep) +{ + int quoting; + + /* move to end of next token */ + quoting = 0; + while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){ + if(*t != '\''){ + t++; + continue; + } + /* *t is a quote */ + if(!quoting){ + quoting = 1; + t++; + continue; + } + /* quoting and we're on a quote */ + if(t[1] != '\''){ + /* end of quoted section; absorb closing quote */ + t++; + quoting = 0; + continue; + } + /* doubled quote; fold one quote into two */ + t += 2; + } + return t; +} + +int +gettokens(char *s, char **args, int maxargs, char *sep) +{ + int nargs; + + for(nargs=0; nargs<maxargs; nargs++){ + while(*s!='\0' && utfrune(sep, *s)!=nil) + *s++ = '\0'; + if(*s == '\0') + break; + args[nargs] = s; + s = etoken(s, sep); + } + + return nargs; +} + +int +tokenize(char *s, char **args, int maxargs) +{ + int nargs; + + for(nargs=0; nargs<maxargs; nargs++){ + while(*s!='\0' && utfrune(qsep, *s)!=nil) + s++; + if(*s == '\0') + break; + args[nargs] = s; + s = qtoken(s, qsep); + } + + return nargs; +} diff --git a/lib9/truerand.c b/lib9/truerand.c @@ -0,0 +1,25 @@ +#include <u.h> +#include <libc.h> + +ulong +truerand(void) +{ + int i, n; + uchar buf[sizeof(ulong)]; + static int randfd = -1; + static char *randfile; + + if(randfd < 0){ + randfd = open(randfile="/dev/random", OREAD); + /* OpenBSD lets you open /dev/random but not read it! */ + if(randfd < 0 || read(randfd, buf, 1) != 1) + randfd = open(randfile="/dev/srandom", OREAD); /* OpenBSD */ + if(randfd < 0) + sysfatal("can't open /dev/random: %r"); + fcntl(randfd, F_SETFD, FD_CLOEXEC); + } + for(i=0; i<sizeof(buf); i += n) + if((n = readn(randfd, buf+i, sizeof(buf)-i)) < 0) + sysfatal("can't read %s: %r", randfile); + return *((ulong*)buf); +} diff --git a/lib9/u.h b/lib9/u.h @@ -0,0 +1,170 @@ +#ifndef _U_H_ +#define _U_H_ 1 +#if defined(__cplusplus) +extern "C" { +#endif + +#define __BSD_VISIBLE 1 /* FreeBSD 5.x */ +#if defined(__sun__) +# define __EXTENSIONS__ 1 /* SunOS */ +# if defined(__SunOS5_6__) || defined(__SunOS5_7__) || defined(__SunOS5_8__) + /* NOT USING #define __MAKECONTEXT_V2_SOURCE 1 / * SunOS */ +# else +# define __MAKECONTEXT_V2_SOURCE 1 +# endif +#endif +#define _BSD_SOURCE 1 +#define _NETBSD_SOURCE 1 /* NetBSD */ +#define _SVID_SOURCE 1 +#if !defined(__APPLE__) && !defined(__OpenBSD__) +# define _XOPEN_SOURCE 1000 +# define _XOPEN_SOURCE_EXTENDED 1 +#endif +#define _LARGEFILE64_SOURCE 1 +#define _FILE_OFFSET_BITS 64 + +#include <inttypes.h> + +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <fcntl.h> +#include <assert.h> +#include <setjmp.h> +#include <stddef.h> +#include <utf.h> +#include <fmt.h> +#include <math.h> +#include <ctype.h> /* for tolower */ + +/* + * OS-specific crap + */ +#define _NEEDUCHAR 1 +#define _NEEDUSHORT 1 +#define _NEEDUINT 1 +#define _NEEDULONG 1 + +typedef long p9jmp_buf[sizeof(sigjmp_buf)/sizeof(long)]; + +#if defined(__linux__) +# include <sys/types.h> +# if defined(__Linux26__) +# include <pthread.h> +# define PLAN9PORT_USING_PTHREADS 1 +# endif +# if defined(__USE_MISC) +# undef _NEEDUSHORT +# undef _NEEDUINT +# undef _NEEDULONG +# endif +#elif defined(__sun__) +# include <sys/types.h> +# include <pthread.h> +# define PLAN9PORT_USING_PTHREADS 1 +# undef _NEEDUSHORT +# undef _NEEDUINT +# undef _NEEDULONG +# define nil 0 /* no cast to void* */ +#elif defined(__FreeBSD__) +# include <sys/types.h> +# include <osreldate.h> +# if __FreeBSD_version >= 500000 +# define PLAN9PORT_USING_PTHREADS 1 +# include <pthread.h> +# endif +# if !defined(_POSIX_SOURCE) +# undef _NEEDUSHORT +# undef _NEEDUINT +# endif +#elif defined(__APPLE__) +# include <sys/types.h> +# include <pthread.h> +# define PLAN9PORT_USING_PTHREADS 1 +# if __GNUC__ < 4 +# undef _NEEDUSHORT +# undef _NEEDUINT +# endif +# undef _ANSI_SOURCE +# undef _POSIX_C_SOURCE +# undef _XOPEN_SOURCE +# if !defined(NSIG) +# define NSIG 32 +# endif +# define _NEEDLL 1 +#elif defined(__NetBSD__) +# include <sched.h> +# include <sys/types.h> +# undef _NEEDUSHORT +# undef _NEEDUINT +# undef _NEEDULONG +#elif defined(__OpenBSD__) +# include <sys/types.h> +# undef _NEEDUSHORT +# undef _NEEDUINT +# undef _NEEDULONG +#else + /* No idea what system this is -- try some defaults */ +# include <pthread.h> +# define PLAN9PORT_USING_PTHREADS 1 +#endif + +#ifndef O_DIRECT +#define O_DIRECT 0 +#endif + +typedef signed char schar; + +#ifdef _NEEDUCHAR + typedef unsigned char uchar; +#endif +#ifdef _NEEDUSHORT + typedef unsigned short ushort; +#endif +#ifdef _NEEDUINT + typedef unsigned int uint; +#endif +#ifdef _NEEDULONG + typedef unsigned long ulong; +#endif +typedef unsigned long long uvlong; +typedef long long vlong; + +typedef uint64_t u64int; +typedef int64_t s64int; +typedef uint8_t u8int; +typedef int8_t s8int; +typedef uint16_t u16int; +typedef int16_t s16int; +typedef uintptr_t uintptr; +typedef uint32_t u32int; +typedef int32_t s32int; + +#undef _NEEDUCHAR +#undef _NEEDUSHORT +#undef _NEEDUINT +#undef _NEEDULONG + +/* + * Funny-named symbols to tip off 9l to autolink. + */ +#define AUTOLIB(x) static int __p9l_autolib_ ## x = 1; + +/* + * Gcc is too smart for its own good. + */ +#if defined(__GNUC__) +# if __GNUC__ >= 4 || (__GNUC__==3 && !defined(__APPLE_CC__)) +# undef AUTOLIB +# define AUTOLIB(x) int __p9l_autolib_ ## x __attribute__ ((weak)); +# else +# undef AUTOLIB +# define AUTOLIB(x) static int __p9l_autolib_ ## x __attribute__ ((unused)); +# endif +#endif + +#if defined(__cplusplus) +} +#endif +#endif diff --git a/lib9/u16.c b/lib9/u16.c @@ -0,0 +1,52 @@ +#include <lib9.h> +static char t16e[] = "0123456789ABCDEF"; + +int +dec16(uchar *out, int lim, char *in, int n) +{ + int c, w = 0, i = 0; + uchar *start = out; + uchar *eout = out + lim; + + while(n-- > 0){ + c = *in++; + if('0' <= c && c <= '9') + c = c - '0'; + else if('a' <= c && c <= 'z') + c = c - 'a' + 10; + else if('A' <= c && c <= 'Z') + c = c - 'A' + 10; + else + continue; + w = (w<<4) + c; + i++; + if(i == 2){ + if(out + 1 > eout) + goto exhausted; + *out++ = w; + w = 0; + i = 0; + } + } +exhausted: + return out - start; +} + +int +enc16(char *out, int lim, uchar *in, int n) +{ + uint c; + char *eout = out + lim; + char *start = out; + + while(n-- > 0){ + c = *in++; + if(out + 2 >= eout) + goto exhausted; + *out++ = t16e[c>>4]; + *out++ = t16e[c&0xf]; + } +exhausted: + *out = 0; + return out - start; +} diff --git a/lib9/u32.c b/lib9/u32.c @@ -0,0 +1,109 @@ +#include <lib9.h> + +int +dec32(uchar *dest, int ndest, char *src, int nsrc) +{ + char *s, *tab; + uchar *start; + int i, u[8]; + + if(ndest+1 < (5*nsrc+7)/8) + return -1; + start = dest; + tab = "23456789abcdefghijkmnpqrstuvwxyz"; + while(nsrc>=8){ + for(i=0; i<8; i++){ + s = strchr(tab,(int)src[i]); + u[i] = s ? s-tab : 0; + } + *dest++ = (u[0]<<3) | (0x7 & (u[1]>>2)); + *dest++ = ((0x3 & u[1])<<6) | (u[2]<<1) | (0x1 & (u[3]>>4)); + *dest++ = ((0xf & u[3])<<4) | (0xf & (u[4]>>1)); + *dest++ = ((0x1 & u[4])<<7) | (u[5]<<2) | (0x3 & (u[6]>>3)); + *dest++ = ((0x7 & u[6])<<5) | u[7]; + src += 8; + nsrc -= 8; + } + if(nsrc > 0){ + if(nsrc == 1 || nsrc == 3 || nsrc == 6) + return -1; + for(i=0; i<nsrc; i++){ + s = strchr(tab,(int)src[i]); + u[i] = s ? s-tab : 0; + } + *dest++ = (u[0]<<3) | (0x7 & (u[1]>>2)); + if(nsrc == 2) + goto out; + *dest++ = ((0x3 & u[1])<<6) | (u[2]<<1) | (0x1 & (u[3]>>4)); + if(nsrc == 4) + goto out; + *dest++ = ((0xf & u[3])<<4) | (0xf & (u[4]>>1)); + if(nsrc == 5) + goto out; + *dest++ = ((0x1 & u[4])<<7) | (u[5]<<2) | (0x3 & (u[6]>>3)); + } +out: + return dest-start; +} + +int +enc32(char *dest, int ndest, uchar *src, int nsrc) +{ + char *tab, *start; + int j; + + if(ndest <= (8*nsrc+4)/5 ) + return -1; + start = dest; + tab = "23456789abcdefghijkmnpqrstuvwxyz"; + while(nsrc>=5){ + j = (0x1f & (src[0]>>3)); + *dest++ = tab[j]; + j = (0x1c & (src[0]<<2)) | (0x03 & (src[1]>>6)); + *dest++ = tab[j]; + j = (0x1f & (src[1]>>1)); + *dest++ = tab[j]; + j = (0x10 & (src[1]<<4)) | (0x0f & (src[2]>>4)); + *dest++ = tab[j]; + j = (0x1e & (src[2]<<1)) | (0x01 & (src[3]>>7)); + *dest++ = tab[j]; + j = (0x1f & (src[3]>>2)); + *dest++ = tab[j]; + j = (0x18 & (src[3]<<3)) | (0x07 & (src[4]>>5)); + *dest++ = tab[j]; + j = (0x1f & (src[4])); + *dest++ = tab[j]; + src += 5; + nsrc -= 5; + } + if(nsrc){ + j = (0x1f & (src[0]>>3)); + *dest++ = tab[j]; + j = (0x1c & (src[0]<<2)); + if(nsrc == 1) + goto out; + j |= (0x03 & (src[1]>>6)); + *dest++ = tab[j]; + j = (0x1f & (src[1]>>1)); + if(nsrc == 2) + goto out; + *dest++ = tab[j]; + j = (0x10 & (src[1]<<4)); + if(nsrc == 3) + goto out; + j |= (0x0f & (src[2]>>4)); + *dest++ = tab[j]; + j = (0x1e & (src[2]<<1)); + if(nsrc == 4) + goto out; + j |= (0x01 & (src[3]>>7)); + *dest++ = tab[j]; + j = (0x1f & (src[3]>>2)); + *dest++ = tab[j]; + j = (0x18 & (src[3]<<3)); +out: + *dest++ = tab[j]; + } + *dest = 0; + return dest-start; +} diff --git a/lib9/u64.c b/lib9/u64.c @@ -0,0 +1,126 @@ +#include <lib9.h> + +enum { + INVAL= 255 +}; + +static uchar t64d[256] = { + INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL, + INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL, + INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL, 62,INVAL,INVAL,INVAL, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL, + INVAL, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,INVAL,INVAL,INVAL,INVAL,INVAL, + INVAL, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,INVAL,INVAL,INVAL,INVAL,INVAL, + INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL, + INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL, + INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL, + INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL, + INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL, + INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL, + INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL, + INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL +}; +static char t64e[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +int +dec64(uchar *out, int lim, char *in, int n) +{ + ulong b24; + uchar *start = out; + uchar *e = out + lim; + int i, c; + + b24 = 0; + i = 0; + while(n-- > 0){ + + c = t64d[*(uchar*)in++]; + if(c == INVAL) + continue; + switch(i){ + case 0: + b24 = c<<18; + break; + case 1: + b24 |= c<<12; + break; + case 2: + b24 |= c<<6; + break; + case 3: + if(out + 3 > e) + goto exhausted; + + b24 |= c; + *out++ = b24>>16; + *out++ = b24>>8; + *out++ = b24; + i = -1; + break; + } + i++; + } + switch(i){ + case 2: + if(out + 1 > e) + goto exhausted; + *out++ = b24>>16; + break; + case 3: + if(out + 2 > e) + goto exhausted; + *out++ = b24>>16; + *out++ = b24>>8; + break; + } +exhausted: + return out - start; +} + +int +enc64(char *out, int lim, uchar *in, int n) +{ + int i; + ulong b24; + char *start = out; + char *e = out + lim; + + for(i = n/3; i > 0; i--){ + b24 = (*in++)<<16; + b24 |= (*in++)<<8; + b24 |= *in++; + if(out + 4 >= e) + goto exhausted; + *out++ = t64e[(b24>>18)]; + *out++ = t64e[(b24>>12)&0x3f]; + *out++ = t64e[(b24>>6)&0x3f]; + *out++ = t64e[(b24)&0x3f]; + } + + switch(n%3){ + case 2: + b24 = (*in++)<<16; + b24 |= (*in)<<8; + if(out + 4 >= e) + goto exhausted; + *out++ = t64e[(b24>>18)]; + *out++ = t64e[(b24>>12)&0x3f]; + *out++ = t64e[(b24>>6)&0x3f]; + *out++ = '='; + break; + case 1: + b24 = (*in)<<16; + if(out + 4 >= e) + goto exhausted; + *out++ = t64e[(b24>>18)]; + *out++ = t64e[(b24>>12)&0x3f]; + *out++ = '='; + *out++ = '='; + break; + } +exhausted: + *out = 0; + return out - start; +} diff --git a/lib9/udp.c b/lib9/udp.c @@ -0,0 +1,52 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> +#include <ip.h> + +#include <sys/socket.h> +#include <netinet/in.h> + +/* + * prefix of all v4 addresses + * copied from libip because libc cannot depend on libip + */ +static uchar v4prefix[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0, 0, 0, 0 +}; + +long +udpread(int fd, Udphdr *hdr, void *buf, long n) +{ + struct sockaddr_in sin; + socklen_t len; + + len = sizeof sin; + n = recvfrom(fd, buf, n, 0, (struct sockaddr*)&sin, &len); + if(len != sizeof sin){ + werrstr("recvfrom acting weird"); + return -1; + } + if(n < 0) + return -1; + memset(hdr, 0, sizeof *hdr); + memmove(hdr->raddr, v4prefix, IPaddrlen); + *(u32int*)(hdr->raddr+12) = *(u32int*)&sin.sin_addr; + *(u16int*)hdr->rport = *(u16int*)&sin.sin_port; + return n; +} + +long +udpwrite(int fd, Udphdr *hdr, void *buf, long n) +{ + struct sockaddr_in sin; + + memset(&sin, 0, sizeof sin); + sin.sin_family = AF_INET; + *(u32int*)&sin.sin_addr = *(u32int*)(hdr->raddr+12); + *(u16int*)&sin.sin_port = *(u16int*)hdr->rport; + return sendto(fd, buf, n, 0, (struct sockaddr*)&sin, sizeof sin); +} + diff --git a/lib9/unsharp.c b/lib9/unsharp.c @@ -0,0 +1,44 @@ +#include <u.h> +#include <libc.h> + +/* + * I don't want too many of these, + * but the ones we have are just too useful. + */ +static struct { + char *old; + char *new; +} replace[] = { + "#9", nil, /* must be first */ + "#d", "/dev/fd", +}; + +char* +unsharp(char *old) +{ + char *new; + int i, olen, nlen, len; + + if(replace[0].new == nil) + replace[0].new = get9root(); + + for(i=0; i<nelem(replace); i++){ + if(!replace[i].new) + continue; + olen = strlen(replace[i].old); + if(strncmp(old, replace[i].old, olen) != 0 + || (old[olen] != '\0' && old[olen] != '/')) + continue; + nlen = strlen(replace[i].new); + len = strlen(old)+nlen-olen; + new = malloc(len+1); + if(new == nil) + /* Most callers won't check the return value... */ + sysfatal("out of memory translating %s to %s%s", old, replace[i].new, old+olen); + strcpy(new, replace[i].new); + strcpy(new+nlen, old+olen); + assert(strlen(new) == len); + return new; + } + return old; +} diff --git a/lib9/utf.h b/lib9/utf.h @@ -0,0 +1,53 @@ +#ifndef _UTF_H_ +#define _UTF_H_ 1 +#if defined(__cplusplus) +extern "C" { +#endif + +typedef unsigned short Rune; /* 16 bits */ + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0xFFFD, /* decoding error in UTF */ +}; + +/* Edit .+1,/^$/ | cfn $PLAN9/src/lib9/utf/?*.c | grep -v static |grep -v __ */ +int chartorune(Rune *rune, char *str); +int fullrune(char *str, int n); +int isalpharune(Rune c); +int islowerrune(Rune c); +int isspacerune(Rune c); +int istitlerune(Rune c); +int isupperrune(Rune c); +int runelen(long c); +int runenlen(Rune *r, int nrune); +Rune* runestrcat(Rune *s1, Rune *s2); +Rune* runestrchr(Rune *s, Rune c); +int runestrcmp(Rune *s1, Rune *s2); +Rune* runestrcpy(Rune *s1, Rune *s2); +Rune* runestrdup(Rune *s) ; +Rune* runestrecpy(Rune *s1, Rune *es1, Rune *s2); +long runestrlen(Rune *s); +Rune* runestrncat(Rune *s1, Rune *s2, long n); +int runestrncmp(Rune *s1, Rune *s2, long n); +Rune* runestrncpy(Rune *s1, Rune *s2, long n); +Rune* runestrrchr(Rune *s, Rune c); +Rune* runestrstr(Rune *s1, Rune *s2); +int runetochar(char *str, Rune *rune); +Rune tolowerrune(Rune c); +Rune totitlerune(Rune c); +Rune toupperrune(Rune c); +char* utfecpy(char *to, char *e, char *from); +int utflen(char *s); +int utfnlen(char *s, long m); +char* utfrrune(char *s, long c); +char* utfrune(char *s, long c); +char* utfutf(char *s1, char *s2); + +#if defined(__cplusplus) +} +#endif +#endif diff --git a/lib9/utf/LICENSE b/lib9/utf/LICENSE @@ -0,0 +1,13 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 1998-2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ diff --git a/lib9/utf/plan9.h b/lib9/utf/plan9.h @@ -0,0 +1,29 @@ +/* + * compiler directive on Plan 9 + */ +#ifndef USED +#define USED(x) if(x);else +#endif + +/* + * easiest way to make sure these are defined + */ +#define uchar _utfuchar +#define ushort _utfushort +#define uint _utfuint +#define ulong _utfulong +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +/* + * nil cannot be ((void*)0) on ANSI C, + * because it is used for function pointers + */ +#undef nil +#define nil 0 + +#undef nelem +#define nelem(x) (sizeof (x)/sizeof (x)[0]) + diff --git a/lib9/utf/portdate b/lib9/utf/portdate @@ -0,0 +1,20 @@ +rune.c 2004/1225 +runestrcat.c 2004/1225 +runestrchr.c 2004/1225 +runestrcmp.c 2004/1225 +runestrcpy.c 2004/1225 +runestrdup.c 2004/1225 +runestrecpy.c 2004/1225 +runestrlen.c 2004/1225 +runestrncat.c 2004/1225 +runestrncmp.c 2004/1225 +runestrncpy.c 2004/1225 +runestrrchr.c 2004/1225 +runestrstr.c 2004/1225 +runetype.c 2004/1225 +utfecpy.c 2004/1225 +utflen.c 2004/1225 +utfnlen.c 2004/1225 +utfrrune.c 2004/1225 +utfrune.c 2004/1225 +utfutf.c 2004/1225 diff --git a/lib9/utf/rune.c b/lib9/utf/rune.c @@ -0,0 +1,177 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +enum +{ + Bit1 = 7, + Bitx = 6, + Bit2 = 5, + Bit3 = 4, + Bit4 = 3, + + T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */ + Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */ + T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */ + T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */ + T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */ + + Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */ + Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */ + Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */ + + Maskx = (1<<Bitx)-1, /* 0011 1111 */ + Testx = Maskx ^ 0xFF, /* 1100 0000 */ + + Bad = Runeerror, +}; + +int +chartorune(Rune *rune, char *str) +{ + int c, c1, c2; + long l; + + /* + * one character sequence + * 00000-0007F => T1 + */ + c = *(uchar*)str; + if(c < Tx) { + *rune = c; + return 1; + } + + /* + * two character sequence + * 0080-07FF => T2 Tx + */ + c1 = *(uchar*)(str+1) ^ Tx; + if(c1 & Testx) + goto bad; + if(c < T3) { + if(c < T2) + goto bad; + l = ((c << Bitx) | c1) & Rune2; + if(l <= Rune1) + goto bad; + *rune = l; + return 2; + } + + /* + * three character sequence + * 0800-FFFF => T3 Tx Tx + */ + c2 = *(uchar*)(str+2) ^ Tx; + if(c2 & Testx) + goto bad; + if(c < T4) { + l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3; + if(l <= Rune2) + goto bad; + *rune = l; + return 3; + } + + /* + * bad decoding + */ +bad: + *rune = Bad; + return 1; +} + +int +runetochar(char *str, Rune *rune) +{ + long c; + + /* + * one character sequence + * 00000-0007F => 00-7F + */ + c = *rune; + if(c <= Rune1) { + str[0] = c; + return 1; + } + + /* + * two character sequence + * 0080-07FF => T2 Tx + */ + if(c <= Rune2) { + str[0] = T2 | (c >> 1*Bitx); + str[1] = Tx | (c & Maskx); + return 2; + } + + /* + * three character sequence + * 0800-FFFF => T3 Tx Tx + */ + str[0] = T3 | (c >> 2*Bitx); + str[1] = Tx | ((c >> 1*Bitx) & Maskx); + str[2] = Tx | (c & Maskx); + return 3; +} + +int +runelen(long c) +{ + Rune rune; + char str[10]; + + rune = c; + return runetochar(str, &rune); +} + +int +runenlen(Rune *r, int nrune) +{ + int nb, c; + + nb = 0; + while(nrune--) { + c = *r++; + if(c <= Rune1) + nb++; + else + if(c <= Rune2) + nb += 2; + else + nb += 3; + } + return nb; +} + +int +fullrune(char *str, int n) +{ + int c; + + if(n > 0) { + c = *(uchar*)str; + if(c < Tx) + return 1; + if(n > 1) + if(c < T3 || n > 2) + return 1; + } + return 0; +} diff --git a/lib9/utf/runestrcat.c b/lib9/utf/runestrcat.c @@ -0,0 +1,25 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +Rune* +runestrcat(Rune *s1, Rune *s2) +{ + + runestrcpy(runestrchr(s1, 0), s2); + return s1; +} diff --git a/lib9/utf/runestrchr.c b/lib9/utf/runestrchr.c @@ -0,0 +1,35 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +Rune* +runestrchr(Rune *s, Rune c) +{ + Rune c0 = c; + Rune c1; + + if(c == 0) { + while(*s++) + ; + return s-1; + } + + while(c1 = *s++) + if(c1 == c0) + return s-1; + return 0; +} diff --git a/lib9/utf/runestrcmp.c b/lib9/utf/runestrcmp.c @@ -0,0 +1,35 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +int +runestrcmp(Rune *s1, Rune *s2) +{ + Rune c1, c2; + + for(;;) { + c1 = *s1++; + c2 = *s2++; + if(c1 != c2) { + if(c1 > c2) + return 1; + return -1; + } + if(c1 == 0) + return 0; + } +} diff --git a/lib9/utf/runestrcpy.c b/lib9/utf/runestrcpy.c @@ -0,0 +1,28 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +Rune* +runestrcpy(Rune *s1, Rune *s2) +{ + Rune *os1; + + os1 = s1; + while(*s1++ = *s2++) + ; + return os1; +} diff --git a/lib9/utf/runestrdup.c b/lib9/utf/runestrdup.c @@ -0,0 +1,30 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include "plan9.h" +#include "utf.h" + +Rune* +runestrdup(Rune *s) +{ + Rune *ns; + + ns = malloc(sizeof(Rune)*(runestrlen(s) + 1)); + if(ns == 0) + return 0; + + return runestrcpy(ns, s); +} diff --git a/lib9/utf/runestrecpy.c b/lib9/utf/runestrecpy.c @@ -0,0 +1,32 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +Rune* +runestrecpy(Rune *s1, Rune *es1, Rune *s2) +{ + if(s1 >= es1) + return s1; + + while(*s1++ = *s2++){ + if(s1 == es1){ + *--s1 = '\0'; + break; + } + } + return s1; +} diff --git a/lib9/utf/runestrlen.c b/lib9/utf/runestrlen.c @@ -0,0 +1,24 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +long +runestrlen(Rune *s) +{ + + return runestrchr(s, 0) - s; +} diff --git a/lib9/utf/runestrncat.c b/lib9/utf/runestrncat.c @@ -0,0 +1,32 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +Rune* +runestrncat(Rune *s1, Rune *s2, long n) +{ + Rune *os1; + + os1 = s1; + s1 = runestrchr(s1, 0); + while(*s1++ = *s2++) + if(--n < 0) { + s1[-1] = 0; + break; + } + return os1; +} diff --git a/lib9/utf/runestrncmp.c b/lib9/utf/runestrncmp.c @@ -0,0 +1,37 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +int +runestrncmp(Rune *s1, Rune *s2, long n) +{ + Rune c1, c2; + + while(n > 0) { + c1 = *s1++; + c2 = *s2++; + n--; + if(c1 != c2) { + if(c1 > c2) + return 1; + return -1; + } + if(c1 == 0) + break; + } + return 0; +} diff --git a/lib9/utf/runestrncpy.c b/lib9/utf/runestrncpy.c @@ -0,0 +1,33 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +Rune* +runestrncpy(Rune *s1, Rune *s2, long n) +{ + int i; + Rune *os1; + + os1 = s1; + for(i = 0; i < n; i++) + if((*s1++ = *s2++) == 0) { + while(++i < n) + *s1++ = 0; + return os1; + } + return os1; +} diff --git a/lib9/utf/runestrrchr.c b/lib9/utf/runestrrchr.c @@ -0,0 +1,30 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +Rune* +runestrrchr(Rune *s, Rune c) +{ + Rune *r; + + if(c == 0) + return runestrchr(s, 0); + r = 0; + while(s = runestrchr(s, c)) + r = s++; + return r; +} diff --git a/lib9/utf/runestrstr.c b/lib9/utf/runestrstr.c @@ -0,0 +1,44 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +/* + * Return pointer to first occurrence of s2 in s1, + * 0 if none + */ +Rune* +runestrstr(Rune *s1, Rune *s2) +{ + Rune *p, *pa, *pb; + int c0, c; + + c0 = *s2; + if(c0 == 0) + return s1; + s2++; + for(p=runestrchr(s1, c0); p; p=runestrchr(p+1, c0)) { + pa = p; + for(pb=s2;; pb++) { + c = *pb; + if(c == 0) + return p; + if(c != *++pa) + break; + } + } + return 0; +} diff --git a/lib9/utf/runetype.c b/lib9/utf/runetype.c @@ -0,0 +1,1151 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +/* + * alpha ranges - + * only covers ranges not in lower||upper + */ +static +Rune __alpha2[] = +{ + 0x00d8, 0x00f6, /* Ø - ö */ + 0x00f8, 0x01f5, /* ø - ǵ */ + 0x0250, 0x02a8, /* ɐ - ʨ */ + 0x038e, 0x03a1, /* Ύ - Ρ */ + 0x03a3, 0x03ce, /* Σ - ώ */ + 0x03d0, 0x03d6, /* ϐ - ϖ */ + 0x03e2, 0x03f3, /* Ϣ - ϳ */ + 0x0490, 0x04c4, /* Ґ - ӄ */ + 0x0561, 0x0587, /* ա - և */ + 0x05d0, 0x05ea, /* א - ת */ + 0x05f0, 0x05f2, /* װ - ײ */ + 0x0621, 0x063a, /* ء - غ */ + 0x0640, 0x064a, /* ـ - ي */ + 0x0671, 0x06b7, /* ٱ - ڷ */ + 0x06ba, 0x06be, /* ں - ھ */ + 0x06c0, 0x06ce, /* ۀ - ێ */ + 0x06d0, 0x06d3, /* ې - ۓ */ + 0x0905, 0x0939, /* अ - ह */ + 0x0958, 0x0961, /* क़ - ॡ */ + 0x0985, 0x098c, /* অ - ঌ */ + 0x098f, 0x0990, /* এ - ঐ */ + 0x0993, 0x09a8, /* ও - ন */ + 0x09aa, 0x09b0, /* প - র */ + 0x09b6, 0x09b9, /* শ - হ */ + 0x09dc, 0x09dd, /* ড় - ঢ় */ + 0x09df, 0x09e1, /* য় - ৡ */ + 0x09f0, 0x09f1, /* ৰ - ৱ */ + 0x0a05, 0x0a0a, /* ਅ - ਊ */ + 0x0a0f, 0x0a10, /* ਏ - ਐ */ + 0x0a13, 0x0a28, /* ਓ - ਨ */ + 0x0a2a, 0x0a30, /* ਪ - ਰ */ + 0x0a32, 0x0a33, /* ਲ - ਲ਼ */ + 0x0a35, 0x0a36, /* ਵ - ਸ਼ */ + 0x0a38, 0x0a39, /* ਸ - ਹ */ + 0x0a59, 0x0a5c, /* ਖ਼ - ੜ */ + 0x0a85, 0x0a8b, /* અ - ઋ */ + 0x0a8f, 0x0a91, /* એ - ઑ */ + 0x0a93, 0x0aa8, /* ઓ - ન */ + 0x0aaa, 0x0ab0, /* પ - ર */ + 0x0ab2, 0x0ab3, /* લ - ળ */ + 0x0ab5, 0x0ab9, /* વ - હ */ + 0x0b05, 0x0b0c, /* ଅ - ଌ */ + 0x0b0f, 0x0b10, /* ଏ - ଐ */ + 0x0b13, 0x0b28, /* ଓ - ନ */ + 0x0b2a, 0x0b30, /* ପ - ର */ + 0x0b32, 0x0b33, /* ଲ - ଳ */ + 0x0b36, 0x0b39, /* ଶ - ହ */ + 0x0b5c, 0x0b5d, /* ଡ଼ - ଢ଼ */ + 0x0b5f, 0x0b61, /* ୟ - ୡ */ + 0x0b85, 0x0b8a, /* அ - ஊ */ + 0x0b8e, 0x0b90, /* எ - ஐ */ + 0x0b92, 0x0b95, /* ஒ - க */ + 0x0b99, 0x0b9a, /* ங - ச */ + 0x0b9e, 0x0b9f, /* ஞ - ட */ + 0x0ba3, 0x0ba4, /* ண - த */ + 0x0ba8, 0x0baa, /* ந - ப */ + 0x0bae, 0x0bb5, /* ம - வ */ + 0x0bb7, 0x0bb9, /* ஷ - ஹ */ + 0x0c05, 0x0c0c, /* అ - ఌ */ + 0x0c0e, 0x0c10, /* ఎ - ఐ */ + 0x0c12, 0x0c28, /* ఒ - న */ + 0x0c2a, 0x0c33, /* ప - ళ */ + 0x0c35, 0x0c39, /* వ - హ */ + 0x0c60, 0x0c61, /* ౠ - ౡ */ + 0x0c85, 0x0c8c, /* ಅ - ಌ */ + 0x0c8e, 0x0c90, /* ಎ - ಐ */ + 0x0c92, 0x0ca8, /* ಒ - ನ */ + 0x0caa, 0x0cb3, /* ಪ - ಳ */ + 0x0cb5, 0x0cb9, /* ವ - ಹ */ + 0x0ce0, 0x0ce1, /* ೠ - ೡ */ + 0x0d05, 0x0d0c, /* അ - ഌ */ + 0x0d0e, 0x0d10, /* എ - ഐ */ + 0x0d12, 0x0d28, /* ഒ - ന */ + 0x0d2a, 0x0d39, /* പ - ഹ */ + 0x0d60, 0x0d61, /* ൠ - ൡ */ + 0x0e01, 0x0e30, /* ก - ะ */ + 0x0e32, 0x0e33, /* า - ำ */ + 0x0e40, 0x0e46, /* เ - ๆ */ + 0x0e5a, 0x0e5b, /* ๚ - ๛ */ + 0x0e81, 0x0e82, /* ກ - ຂ */ + 0x0e87, 0x0e88, /* ງ - ຈ */ + 0x0e94, 0x0e97, /* ດ - ທ */ + 0x0e99, 0x0e9f, /* ນ - ຟ */ + 0x0ea1, 0x0ea3, /* ມ - ຣ */ + 0x0eaa, 0x0eab, /* ສ - ຫ */ + 0x0ead, 0x0eae, /* ອ - ຮ */ + 0x0eb2, 0x0eb3, /* າ - ຳ */ + 0x0ec0, 0x0ec4, /* ເ - ໄ */ + 0x0edc, 0x0edd, /* ໜ - ໝ */ + 0x0f18, 0x0f19, /* ༘ - ༙ */ + 0x0f40, 0x0f47, /* ཀ - ཇ */ + 0x0f49, 0x0f69, /* ཉ - ཀྵ */ + 0x10d0, 0x10f6, /* ა - ჶ */ + 0x1100, 0x1159, /* ᄀ - ᅙ */ + 0x115f, 0x11a2, /* ᅟ - ᆢ */ + 0x11a8, 0x11f9, /* ᆨ - ᇹ */ + 0x1e00, 0x1e9b, /* Ḁ - ẛ */ + 0x1f50, 0x1f57, /* ὐ - ὗ */ + 0x1f80, 0x1fb4, /* ᾀ - ᾴ */ + 0x1fb6, 0x1fbc, /* ᾶ - ᾼ */ + 0x1fc2, 0x1fc4, /* ῂ - ῄ */ + 0x1fc6, 0x1fcc, /* ῆ - ῌ */ + 0x1fd0, 0x1fd3, /* ῐ - ΐ */ + 0x1fd6, 0x1fdb, /* ῖ - Ί */ + 0x1fe0, 0x1fec, /* ῠ - Ῥ */ + 0x1ff2, 0x1ff4, /* ῲ - ῴ */ + 0x1ff6, 0x1ffc, /* ῶ - ῼ */ + 0x210a, 0x2113, /* ℊ - ℓ */ + 0x2115, 0x211d, /* ℕ - ℝ */ + 0x2120, 0x2122, /* ℠ - ™ */ + 0x212a, 0x2131, /* K - ℱ */ + 0x2133, 0x2138, /* ℳ - ℸ */ + 0x3041, 0x3094, /* ぁ - ゔ */ + 0x30a1, 0x30fa, /* ァ - ヺ */ + 0x3105, 0x312c, /* ㄅ - ㄬ */ + 0x3131, 0x318e, /* ㄱ - ㆎ */ + 0x3192, 0x319f, /* ㆒ - ㆟ */ + 0x3260, 0x327b, /* ㉠ - ㉻ */ + 0x328a, 0x32b0, /* ㊊ - ㊰ */ + 0x32d0, 0x32fe, /* ㋐ - ㋾ */ + 0x3300, 0x3357, /* ㌀ - ㍗ */ + 0x3371, 0x3376, /* ㍱ - ㍶ */ + 0x337b, 0x3394, /* ㍻ - ㎔ */ + 0x3399, 0x339e, /* ㎙ - ㎞ */ + 0x33a9, 0x33ad, /* ㎩ - ㎭ */ + 0x33b0, 0x33c1, /* ㎰ - ㏁ */ + 0x33c3, 0x33c5, /* ㏃ - ㏅ */ + 0x33c7, 0x33d7, /* ㏇ - ㏗ */ + 0x33d9, 0x33dd, /* ㏙ - ㏝ */ + 0x4e00, 0x9fff, /* 一 - 鿿 */ + 0xac00, 0xd7a3, /* 가 - 힣 */ + 0xf900, 0xfb06, /* 豈 - st */ + 0xfb13, 0xfb17, /* ﬓ - ﬗ */ + 0xfb1f, 0xfb28, /* ײַ - ﬨ */ + 0xfb2a, 0xfb36, /* שׁ - זּ */ + 0xfb38, 0xfb3c, /* טּ - לּ */ + 0xfb40, 0xfb41, /* נּ - סּ */ + 0xfb43, 0xfb44, /* ףּ - פּ */ + 0xfb46, 0xfbb1, /* צּ - ﮱ */ + 0xfbd3, 0xfd3d, /* ﯓ - ﴽ */ + 0xfd50, 0xfd8f, /* ﵐ - ﶏ */ + 0xfd92, 0xfdc7, /* ﶒ - ﷇ */ + 0xfdf0, 0xfdf9, /* ﷰ - ﷹ */ + 0xfe70, 0xfe72, /* ﹰ - ﹲ */ + 0xfe76, 0xfefc, /* ﹶ - ﻼ */ + 0xff66, 0xff6f, /* ヲ - ッ */ + 0xff71, 0xff9d, /* ア - ン */ + 0xffa0, 0xffbe, /* ᅠ - ᄒ */ + 0xffc2, 0xffc7, /* ᅡ - ᅦ */ + 0xffca, 0xffcf, /* ᅧ - ᅬ */ + 0xffd2, 0xffd7, /* ᅭ - ᅲ */ + 0xffda, 0xffdc, /* ᅳ - ᅵ */ +}; + +/* + * alpha singlets - + * only covers ranges not in lower||upper + */ +static +Rune __alpha1[] = +{ + 0x00aa, /* ª */ + 0x00b5, /* µ */ + 0x00ba, /* º */ + 0x03da, /* Ϛ */ + 0x03dc, /* Ϝ */ + 0x03de, /* Ϟ */ + 0x03e0, /* Ϡ */ + 0x06d5, /* ە */ + 0x09b2, /* ল */ + 0x0a5e, /* ਫ਼ */ + 0x0a8d, /* ઍ */ + 0x0ae0, /* ૠ */ + 0x0b9c, /* ஜ */ + 0x0cde, /* ೞ */ + 0x0e4f, /* ๏ */ + 0x0e84, /* ຄ */ + 0x0e8a, /* ຊ */ + 0x0e8d, /* ຍ */ + 0x0ea5, /* ລ */ + 0x0ea7, /* ວ */ + 0x0eb0, /* ະ */ + 0x0ebd, /* ຽ */ + 0x1fbe, /* ι */ + 0x207f, /* ⁿ */ + 0x20a8, /* ₨ */ + 0x2102, /* ℂ */ + 0x2107, /* ℇ */ + 0x2124, /* ℤ */ + 0x2126, /* Ω */ + 0x2128, /* ℨ */ + 0xfb3e, /* מּ */ + 0xfe74, /* ﹴ */ +}; + +/* + * space ranges + */ +static +Rune __space2[] = +{ + 0x0009, 0x000a, /* tab and newline */ + 0x0020, 0x0020, /* space */ + 0x00a0, 0x00a0, /*   */ + 0x2000, 0x200b, /*   - ​ */ + 0x2028, 0x2029, /* 
 - 
 */ + 0x3000, 0x3000, /*   */ + 0xfeff, 0xfeff, /*  */ +}; + +/* + * lower case ranges + * 3rd col is conversion excess 500 + */ +static +Rune __toupper2[] = +{ + 0x0061, 0x007a, 468, /* a-z A-Z */ + 0x00e0, 0x00f6, 468, /* à-ö À-Ö */ + 0x00f8, 0x00fe, 468, /* ø-þ Ø-Þ */ + 0x0256, 0x0257, 295, /* ɖ-ɗ Ɖ-Ɗ */ + 0x0258, 0x0259, 298, /* ɘ-ə Ǝ-Ə */ + 0x028a, 0x028b, 283, /* ʊ-ʋ Ʊ-Ʋ */ + 0x03ad, 0x03af, 463, /* έ-ί Έ-Ί */ + 0x03b1, 0x03c1, 468, /* α-ρ Α-Ρ */ + 0x03c3, 0x03cb, 468, /* σ-ϋ Σ-Ϋ */ + 0x03cd, 0x03ce, 437, /* ύ-ώ Ύ-Ώ */ + 0x0430, 0x044f, 468, /* а-я А-Я */ + 0x0451, 0x045c, 420, /* ё-ќ Ё-Ќ */ + 0x045e, 0x045f, 420, /* ў-џ Ў-Џ */ + 0x0561, 0x0586, 452, /* ա-ֆ Ա-Ֆ */ + 0x1f00, 0x1f07, 508, /* ἀ-ἇ Ἀ-Ἇ */ + 0x1f10, 0x1f15, 508, /* ἐ-ἕ Ἐ-Ἕ */ + 0x1f20, 0x1f27, 508, /* ἠ-ἧ Ἠ-Ἧ */ + 0x1f30, 0x1f37, 508, /* ἰ-ἷ Ἰ-Ἷ */ + 0x1f40, 0x1f45, 508, /* ὀ-ὅ Ὀ-Ὅ */ + 0x1f60, 0x1f67, 508, /* ὠ-ὧ Ὠ-Ὧ */ + 0x1f70, 0x1f71, 574, /* ὰ-ά Ὰ-Ά */ + 0x1f72, 0x1f75, 586, /* ὲ-ή Ὲ-Ή */ + 0x1f76, 0x1f77, 600, /* ὶ-ί Ὶ-Ί */ + 0x1f78, 0x1f79, 628, /* ὸ-ό Ὸ-Ό */ + 0x1f7a, 0x1f7b, 612, /* ὺ-ύ Ὺ-Ύ */ + 0x1f7c, 0x1f7d, 626, /* ὼ-ώ Ὼ-Ώ */ + 0x1f80, 0x1f87, 508, /* ᾀ-ᾇ ᾈ-ᾏ */ + 0x1f90, 0x1f97, 508, /* ᾐ-ᾗ ᾘ-ᾟ */ + 0x1fa0, 0x1fa7, 508, /* ᾠ-ᾧ ᾨ-ᾯ */ + 0x1fb0, 0x1fb1, 508, /* ᾰ-ᾱ Ᾰ-Ᾱ */ + 0x1fd0, 0x1fd1, 508, /* ῐ-ῑ Ῐ-Ῑ */ + 0x1fe0, 0x1fe1, 508, /* ῠ-ῡ Ῠ-Ῡ */ + 0x2170, 0x217f, 484, /* ⅰ-ⅿ Ⅰ-Ⅿ */ + 0x24d0, 0x24e9, 474, /* ⓐ-ⓩ Ⓐ-Ⓩ */ + 0xff41, 0xff5a, 468, /* a-z A-Z */ +}; + +/* + * lower case singlets + * 2nd col is conversion excess 500 + */ +static +Rune __toupper1[] = +{ + 0x00ff, 621, /* ÿ Ÿ */ + 0x0101, 499, /* ā Ā */ + 0x0103, 499, /* ă Ă */ + 0x0105, 499, /* ą Ą */ + 0x0107, 499, /* ć Ć */ + 0x0109, 499, /* ĉ Ĉ */ + 0x010b, 499, /* ċ Ċ */ + 0x010d, 499, /* č Č */ + 0x010f, 499, /* ď Ď */ + 0x0111, 499, /* đ Đ */ + 0x0113, 499, /* ē Ē */ + 0x0115, 499, /* ĕ Ĕ */ + 0x0117, 499, /* ė Ė */ + 0x0119, 499, /* ę Ę */ + 0x011b, 499, /* ě Ě */ + 0x011d, 499, /* ĝ Ĝ */ + 0x011f, 499, /* ğ Ğ */ + 0x0121, 499, /* ġ Ġ */ + 0x0123, 499, /* ģ Ģ */ + 0x0125, 499, /* ĥ Ĥ */ + 0x0127, 499, /* ħ Ħ */ + 0x0129, 499, /* ĩ Ĩ */ + 0x012b, 499, /* ī Ī */ + 0x012d, 499, /* ĭ Ĭ */ + 0x012f, 499, /* į Į */ + 0x0131, 268, /* ı I */ + 0x0133, 499, /* ij IJ */ + 0x0135, 499, /* ĵ Ĵ */ + 0x0137, 499, /* ķ Ķ */ + 0x013a, 499, /* ĺ Ĺ */ + 0x013c, 499, /* ļ Ļ */ + 0x013e, 499, /* ľ Ľ */ + 0x0140, 499, /* ŀ Ŀ */ + 0x0142, 499, /* ł Ł */ + 0x0144, 499, /* ń Ń */ + 0x0146, 499, /* ņ Ņ */ + 0x0148, 499, /* ň Ň */ + 0x014b, 499, /* ŋ Ŋ */ + 0x014d, 499, /* ō Ō */ + 0x014f, 499, /* ŏ Ŏ */ + 0x0151, 499, /* ő Ő */ + 0x0153, 499, /* œ Œ */ + 0x0155, 499, /* ŕ Ŕ */ + 0x0157, 499, /* ŗ Ŗ */ + 0x0159, 499, /* ř Ř */ + 0x015b, 499, /* ś Ś */ + 0x015d, 499, /* ŝ Ŝ */ + 0x015f, 499, /* ş Ş */ + 0x0161, 499, /* š Š */ + 0x0163, 499, /* ţ Ţ */ + 0x0165, 499, /* ť Ť */ + 0x0167, 499, /* ŧ Ŧ */ + 0x0169, 499, /* ũ Ũ */ + 0x016b, 499, /* ū Ū */ + 0x016d, 499, /* ŭ Ŭ */ + 0x016f, 499, /* ů Ů */ + 0x0171, 499, /* ű Ű */ + 0x0173, 499, /* ų Ų */ + 0x0175, 499, /* ŵ Ŵ */ + 0x0177, 499, /* ŷ Ŷ */ + 0x017a, 499, /* ź Ź */ + 0x017c, 499, /* ż Ż */ + 0x017e, 499, /* ž Ž */ + 0x017f, 200, /* ſ S */ + 0x0183, 499, /* ƃ Ƃ */ + 0x0185, 499, /* ƅ Ƅ */ + 0x0188, 499, /* ƈ Ƈ */ + 0x018c, 499, /* ƌ Ƌ */ + 0x0192, 499, /* ƒ Ƒ */ + 0x0199, 499, /* ƙ Ƙ */ + 0x01a1, 499, /* ơ Ơ */ + 0x01a3, 499, /* ƣ Ƣ */ + 0x01a5, 499, /* ƥ Ƥ */ + 0x01a8, 499, /* ƨ Ƨ */ + 0x01ad, 499, /* ƭ Ƭ */ + 0x01b0, 499, /* ư Ư */ + 0x01b4, 499, /* ƴ Ƴ */ + 0x01b6, 499, /* ƶ Ƶ */ + 0x01b9, 499, /* ƹ Ƹ */ + 0x01bd, 499, /* ƽ Ƽ */ + 0x01c5, 499, /* Dž DŽ */ + 0x01c6, 498, /* dž DŽ */ + 0x01c8, 499, /* Lj LJ */ + 0x01c9, 498, /* lj LJ */ + 0x01cb, 499, /* Nj NJ */ + 0x01cc, 498, /* nj NJ */ + 0x01ce, 499, /* ǎ Ǎ */ + 0x01d0, 499, /* ǐ Ǐ */ + 0x01d2, 499, /* ǒ Ǒ */ + 0x01d4, 499, /* ǔ Ǔ */ + 0x01d6, 499, /* ǖ Ǖ */ + 0x01d8, 499, /* ǘ Ǘ */ + 0x01da, 499, /* ǚ Ǚ */ + 0x01dc, 499, /* ǜ Ǜ */ + 0x01df, 499, /* ǟ Ǟ */ + 0x01e1, 499, /* ǡ Ǡ */ + 0x01e3, 499, /* ǣ Ǣ */ + 0x01e5, 499, /* ǥ Ǥ */ + 0x01e7, 499, /* ǧ Ǧ */ + 0x01e9, 499, /* ǩ Ǩ */ + 0x01eb, 499, /* ǫ Ǫ */ + 0x01ed, 499, /* ǭ Ǭ */ + 0x01ef, 499, /* ǯ Ǯ */ + 0x01f2, 499, /* Dz DZ */ + 0x01f3, 498, /* dz DZ */ + 0x01f5, 499, /* ǵ Ǵ */ + 0x01fb, 499, /* ǻ Ǻ */ + 0x01fd, 499, /* ǽ Ǽ */ + 0x01ff, 499, /* ǿ Ǿ */ + 0x0201, 499, /* ȁ Ȁ */ + 0x0203, 499, /* ȃ Ȃ */ + 0x0205, 499, /* ȅ Ȅ */ + 0x0207, 499, /* ȇ Ȇ */ + 0x0209, 499, /* ȉ Ȉ */ + 0x020b, 499, /* ȋ Ȋ */ + 0x020d, 499, /* ȍ Ȍ */ + 0x020f, 499, /* ȏ Ȏ */ + 0x0211, 499, /* ȑ Ȑ */ + 0x0213, 499, /* ȓ Ȓ */ + 0x0215, 499, /* ȕ Ȕ */ + 0x0217, 499, /* ȗ Ȗ */ + 0x0253, 290, /* ɓ Ɓ */ + 0x0254, 294, /* ɔ Ɔ */ + 0x025b, 297, /* ɛ Ɛ */ + 0x0260, 295, /* ɠ Ɠ */ + 0x0263, 293, /* ɣ Ɣ */ + 0x0268, 291, /* ɨ Ɨ */ + 0x0269, 289, /* ɩ Ɩ */ + 0x026f, 289, /* ɯ Ɯ */ + 0x0272, 287, /* ɲ Ɲ */ + 0x0283, 282, /* ʃ Ʃ */ + 0x0288, 282, /* ʈ Ʈ */ + 0x0292, 281, /* ʒ Ʒ */ + 0x03ac, 462, /* ά Ά */ + 0x03cc, 436, /* ό Ό */ + 0x03d0, 438, /* ϐ Β */ + 0x03d1, 443, /* ϑ Θ */ + 0x03d5, 453, /* ϕ Φ */ + 0x03d6, 446, /* ϖ Π */ + 0x03e3, 499, /* ϣ Ϣ */ + 0x03e5, 499, /* ϥ Ϥ */ + 0x03e7, 499, /* ϧ Ϧ */ + 0x03e9, 499, /* ϩ Ϩ */ + 0x03eb, 499, /* ϫ Ϫ */ + 0x03ed, 499, /* ϭ Ϭ */ + 0x03ef, 499, /* ϯ Ϯ */ + 0x03f0, 414, /* ϰ Κ */ + 0x03f1, 420, /* ϱ Ρ */ + 0x0461, 499, /* ѡ Ѡ */ + 0x0463, 499, /* ѣ Ѣ */ + 0x0465, 499, /* ѥ Ѥ */ + 0x0467, 499, /* ѧ Ѧ */ + 0x0469, 499, /* ѩ Ѩ */ + 0x046b, 499, /* ѫ Ѫ */ + 0x046d, 499, /* ѭ Ѭ */ + 0x046f, 499, /* ѯ Ѯ */ + 0x0471, 499, /* ѱ Ѱ */ + 0x0473, 499, /* ѳ Ѳ */ + 0x0475, 499, /* ѵ Ѵ */ + 0x0477, 499, /* ѷ Ѷ */ + 0x0479, 499, /* ѹ Ѹ */ + 0x047b, 499, /* ѻ Ѻ */ + 0x047d, 499, /* ѽ Ѽ */ + 0x047f, 499, /* ѿ Ѿ */ + 0x0481, 499, /* ҁ Ҁ */ + 0x0491, 499, /* ґ Ґ */ + 0x0493, 499, /* ғ Ғ */ + 0x0495, 499, /* ҕ Ҕ */ + 0x0497, 499, /* җ Җ */ + 0x0499, 499, /* ҙ Ҙ */ + 0x049b, 499, /* қ Қ */ + 0x049d, 499, /* ҝ Ҝ */ + 0x049f, 499, /* ҟ Ҟ */ + 0x04a1, 499, /* ҡ Ҡ */ + 0x04a3, 499, /* ң Ң */ + 0x04a5, 499, /* ҥ Ҥ */ + 0x04a7, 499, /* ҧ Ҧ */ + 0x04a9, 499, /* ҩ Ҩ */ + 0x04ab, 499, /* ҫ Ҫ */ + 0x04ad, 499, /* ҭ Ҭ */ + 0x04af, 499, /* ү Ү */ + 0x04b1, 499, /* ұ Ұ */ + 0x04b3, 499, /* ҳ Ҳ */ + 0x04b5, 499, /* ҵ Ҵ */ + 0x04b7, 499, /* ҷ Ҷ */ + 0x04b9, 499, /* ҹ Ҹ */ + 0x04bb, 499, /* һ Һ */ + 0x04bd, 499, /* ҽ Ҽ */ + 0x04bf, 499, /* ҿ Ҿ */ + 0x04c2, 499, /* ӂ Ӂ */ + 0x04c4, 499, /* ӄ Ӄ */ + 0x04c8, 499, /* ӈ Ӈ */ + 0x04cc, 499, /* ӌ Ӌ */ + 0x04d1, 499, /* ӑ Ӑ */ + 0x04d3, 499, /* ӓ Ӓ */ + 0x04d5, 499, /* ӕ Ӕ */ + 0x04d7, 499, /* ӗ Ӗ */ + 0x04d9, 499, /* ә Ә */ + 0x04db, 499, /* ӛ Ӛ */ + 0x04dd, 499, /* ӝ Ӝ */ + 0x04df, 499, /* ӟ Ӟ */ + 0x04e1, 499, /* ӡ Ӡ */ + 0x04e3, 499, /* ӣ Ӣ */ + 0x04e5, 499, /* ӥ Ӥ */ + 0x04e7, 499, /* ӧ Ӧ */ + 0x04e9, 499, /* ө Ө */ + 0x04eb, 499, /* ӫ Ӫ */ + 0x04ef, 499, /* ӯ Ӯ */ + 0x04f1, 499, /* ӱ Ӱ */ + 0x04f3, 499, /* ӳ Ӳ */ + 0x04f5, 499, /* ӵ Ӵ */ + 0x04f9, 499, /* ӹ Ӹ */ + 0x1e01, 499, /* ḁ Ḁ */ + 0x1e03, 499, /* ḃ Ḃ */ + 0x1e05, 499, /* ḅ Ḅ */ + 0x1e07, 499, /* ḇ Ḇ */ + 0x1e09, 499, /* ḉ Ḉ */ + 0x1e0b, 499, /* ḋ Ḋ */ + 0x1e0d, 499, /* ḍ Ḍ */ + 0x1e0f, 499, /* ḏ Ḏ */ + 0x1e11, 499, /* ḑ Ḑ */ + 0x1e13, 499, /* ḓ Ḓ */ + 0x1e15, 499, /* ḕ Ḕ */ + 0x1e17, 499, /* ḗ Ḗ */ + 0x1e19, 499, /* ḙ Ḙ */ + 0x1e1b, 499, /* ḛ Ḛ */ + 0x1e1d, 499, /* ḝ Ḝ */ + 0x1e1f, 499, /* ḟ Ḟ */ + 0x1e21, 499, /* ḡ Ḡ */ + 0x1e23, 499, /* ḣ Ḣ */ + 0x1e25, 499, /* ḥ Ḥ */ + 0x1e27, 499, /* ḧ Ḧ */ + 0x1e29, 499, /* ḩ Ḩ */ + 0x1e2b, 499, /* ḫ Ḫ */ + 0x1e2d, 499, /* ḭ Ḭ */ + 0x1e2f, 499, /* ḯ Ḯ */ + 0x1e31, 499, /* ḱ Ḱ */ + 0x1e33, 499, /* ḳ Ḳ */ + 0x1e35, 499, /* ḵ Ḵ */ + 0x1e37, 499, /* ḷ Ḷ */ + 0x1e39, 499, /* ḹ Ḹ */ + 0x1e3b, 499, /* ḻ Ḻ */ + 0x1e3d, 499, /* ḽ Ḽ */ + 0x1e3f, 499, /* ḿ Ḿ */ + 0x1e41, 499, /* ṁ Ṁ */ + 0x1e43, 499, /* ṃ Ṃ */ + 0x1e45, 499, /* ṅ Ṅ */ + 0x1e47, 499, /* ṇ Ṇ */ + 0x1e49, 499, /* ṉ Ṉ */ + 0x1e4b, 499, /* ṋ Ṋ */ + 0x1e4d, 499, /* ṍ Ṍ */ + 0x1e4f, 499, /* ṏ Ṏ */ + 0x1e51, 499, /* ṑ Ṑ */ + 0x1e53, 499, /* ṓ Ṓ */ + 0x1e55, 499, /* ṕ Ṕ */ + 0x1e57, 499, /* ṗ Ṗ */ + 0x1e59, 499, /* ṙ Ṙ */ + 0x1e5b, 499, /* ṛ Ṛ */ + 0x1e5d, 499, /* ṝ Ṝ */ + 0x1e5f, 499, /* ṟ Ṟ */ + 0x1e61, 499, /* ṡ Ṡ */ + 0x1e63, 499, /* ṣ Ṣ */ + 0x1e65, 499, /* ṥ Ṥ */ + 0x1e67, 499, /* ṧ Ṧ */ + 0x1e69, 499, /* ṩ Ṩ */ + 0x1e6b, 499, /* ṫ Ṫ */ + 0x1e6d, 499, /* ṭ Ṭ */ + 0x1e6f, 499, /* ṯ Ṯ */ + 0x1e71, 499, /* ṱ Ṱ */ + 0x1e73, 499, /* ṳ Ṳ */ + 0x1e75, 499, /* ṵ Ṵ */ + 0x1e77, 499, /* ṷ Ṷ */ + 0x1e79, 499, /* ṹ Ṹ */ + 0x1e7b, 499, /* ṻ Ṻ */ + 0x1e7d, 499, /* ṽ Ṽ */ + 0x1e7f, 499, /* ṿ Ṿ */ + 0x1e81, 499, /* ẁ Ẁ */ + 0x1e83, 499, /* ẃ Ẃ */ + 0x1e85, 499, /* ẅ Ẅ */ + 0x1e87, 499, /* ẇ Ẇ */ + 0x1e89, 499, /* ẉ Ẉ */ + 0x1e8b, 499, /* ẋ Ẋ */ + 0x1e8d, 499, /* ẍ Ẍ */ + 0x1e8f, 499, /* ẏ Ẏ */ + 0x1e91, 499, /* ẑ Ẑ */ + 0x1e93, 499, /* ẓ Ẓ */ + 0x1e95, 499, /* ẕ Ẕ */ + 0x1ea1, 499, /* ạ Ạ */ + 0x1ea3, 499, /* ả Ả */ + 0x1ea5, 499, /* ấ Ấ */ + 0x1ea7, 499, /* ầ Ầ */ + 0x1ea9, 499, /* ẩ Ẩ */ + 0x1eab, 499, /* ẫ Ẫ */ + 0x1ead, 499, /* ậ Ậ */ + 0x1eaf, 499, /* ắ Ắ */ + 0x1eb1, 499, /* ằ Ằ */ + 0x1eb3, 499, /* ẳ Ẳ */ + 0x1eb5, 499, /* ẵ Ẵ */ + 0x1eb7, 499, /* ặ Ặ */ + 0x1eb9, 499, /* ẹ Ẹ */ + 0x1ebb, 499, /* ẻ Ẻ */ + 0x1ebd, 499, /* ẽ Ẽ */ + 0x1ebf, 499, /* ế Ế */ + 0x1ec1, 499, /* ề Ề */ + 0x1ec3, 499, /* ể Ể */ + 0x1ec5, 499, /* ễ Ễ */ + 0x1ec7, 499, /* ệ Ệ */ + 0x1ec9, 499, /* ỉ Ỉ */ + 0x1ecb, 499, /* ị Ị */ + 0x1ecd, 499, /* ọ Ọ */ + 0x1ecf, 499, /* ỏ Ỏ */ + 0x1ed1, 499, /* ố Ố */ + 0x1ed3, 499, /* ồ Ồ */ + 0x1ed5, 499, /* ổ Ổ */ + 0x1ed7, 499, /* ỗ Ỗ */ + 0x1ed9, 499, /* ộ Ộ */ + 0x1edb, 499, /* ớ Ớ */ + 0x1edd, 499, /* ờ Ờ */ + 0x1edf, 499, /* ở Ở */ + 0x1ee1, 499, /* ỡ Ỡ */ + 0x1ee3, 499, /* ợ Ợ */ + 0x1ee5, 499, /* ụ Ụ */ + 0x1ee7, 499, /* ủ Ủ */ + 0x1ee9, 499, /* ứ Ứ */ + 0x1eeb, 499, /* ừ Ừ */ + 0x1eed, 499, /* ử Ử */ + 0x1eef, 499, /* ữ Ữ */ + 0x1ef1, 499, /* ự Ự */ + 0x1ef3, 499, /* ỳ Ỳ */ + 0x1ef5, 499, /* ỵ Ỵ */ + 0x1ef7, 499, /* ỷ Ỷ */ + 0x1ef9, 499, /* ỹ Ỹ */ + 0x1f51, 508, /* ὑ Ὑ */ + 0x1f53, 508, /* ὓ Ὓ */ + 0x1f55, 508, /* ὕ Ὕ */ + 0x1f57, 508, /* ὗ Ὗ */ + 0x1fb3, 509, /* ᾳ ᾼ */ + 0x1fc3, 509, /* ῃ ῌ */ + 0x1fe5, 507, /* ῥ Ῥ */ + 0x1ff3, 509, /* ῳ ῼ */ +}; + +/* + * upper case ranges + * 3rd col is conversion excess 500 + */ +static +Rune __tolower2[] = +{ + 0x0041, 0x005a, 532, /* A-Z a-z */ + 0x00c0, 0x00d6, 532, /* À-Ö à-ö */ + 0x00d8, 0x00de, 532, /* Ø-Þ ø-þ */ + 0x0189, 0x018a, 705, /* Ɖ-Ɗ ɖ-ɗ */ + 0x018e, 0x018f, 702, /* Ǝ-Ə ɘ-ə */ + 0x01b1, 0x01b2, 717, /* Ʊ-Ʋ ʊ-ʋ */ + 0x0388, 0x038a, 537, /* Έ-Ί έ-ί */ + 0x038e, 0x038f, 563, /* Ύ-Ώ ύ-ώ */ + 0x0391, 0x03a1, 532, /* Α-Ρ α-ρ */ + 0x03a3, 0x03ab, 532, /* Σ-Ϋ σ-ϋ */ + 0x0401, 0x040c, 580, /* Ё-Ќ ё-ќ */ + 0x040e, 0x040f, 580, /* Ў-Џ ў-џ */ + 0x0410, 0x042f, 532, /* А-Я а-я */ + 0x0531, 0x0556, 548, /* Ա-Ֆ ա-ֆ */ + 0x10a0, 0x10c5, 548, /* Ⴀ-Ⴥ ა-ჵ */ + 0x1f08, 0x1f0f, 492, /* Ἀ-Ἇ ἀ-ἇ */ + 0x1f18, 0x1f1d, 492, /* Ἐ-Ἕ ἐ-ἕ */ + 0x1f28, 0x1f2f, 492, /* Ἠ-Ἧ ἠ-ἧ */ + 0x1f38, 0x1f3f, 492, /* Ἰ-Ἷ ἰ-ἷ */ + 0x1f48, 0x1f4d, 492, /* Ὀ-Ὅ ὀ-ὅ */ + 0x1f68, 0x1f6f, 492, /* Ὠ-Ὧ ὠ-ὧ */ + 0x1f88, 0x1f8f, 492, /* ᾈ-ᾏ ᾀ-ᾇ */ + 0x1f98, 0x1f9f, 492, /* ᾘ-ᾟ ᾐ-ᾗ */ + 0x1fa8, 0x1faf, 492, /* ᾨ-ᾯ ᾠ-ᾧ */ + 0x1fb8, 0x1fb9, 492, /* Ᾰ-Ᾱ ᾰ-ᾱ */ + 0x1fba, 0x1fbb, 426, /* Ὰ-Ά ὰ-ά */ + 0x1fc8, 0x1fcb, 414, /* Ὲ-Ή ὲ-ή */ + 0x1fd8, 0x1fd9, 492, /* Ῐ-Ῑ ῐ-ῑ */ + 0x1fda, 0x1fdb, 400, /* Ὶ-Ί ὶ-ί */ + 0x1fe8, 0x1fe9, 492, /* Ῠ-Ῡ ῠ-ῡ */ + 0x1fea, 0x1feb, 388, /* Ὺ-Ύ ὺ-ύ */ + 0x1ff8, 0x1ff9, 372, /* Ὸ-Ό ὸ-ό */ + 0x1ffa, 0x1ffb, 374, /* Ὼ-Ώ ὼ-ώ */ + 0x2160, 0x216f, 516, /* Ⅰ-Ⅿ ⅰ-ⅿ */ + 0x24b6, 0x24cf, 526, /* Ⓐ-Ⓩ ⓐ-ⓩ */ + 0xff21, 0xff3a, 532, /* A-Z a-z */ +}; + +/* + * upper case singlets + * 2nd col is conversion excess 500 + */ +static +Rune __tolower1[] = +{ + 0x0100, 501, /* Ā ā */ + 0x0102, 501, /* Ă ă */ + 0x0104, 501, /* Ą ą */ + 0x0106, 501, /* Ć ć */ + 0x0108, 501, /* Ĉ ĉ */ + 0x010a, 501, /* Ċ ċ */ + 0x010c, 501, /* Č č */ + 0x010e, 501, /* Ď ď */ + 0x0110, 501, /* Đ đ */ + 0x0112, 501, /* Ē ē */ + 0x0114, 501, /* Ĕ ĕ */ + 0x0116, 501, /* Ė ė */ + 0x0118, 501, /* Ę ę */ + 0x011a, 501, /* Ě ě */ + 0x011c, 501, /* Ĝ ĝ */ + 0x011e, 501, /* Ğ ğ */ + 0x0120, 501, /* Ġ ġ */ + 0x0122, 501, /* Ģ ģ */ + 0x0124, 501, /* Ĥ ĥ */ + 0x0126, 501, /* Ħ ħ */ + 0x0128, 501, /* Ĩ ĩ */ + 0x012a, 501, /* Ī ī */ + 0x012c, 501, /* Ĭ ĭ */ + 0x012e, 501, /* Į į */ + 0x0130, 301, /* İ i */ + 0x0132, 501, /* IJ ij */ + 0x0134, 501, /* Ĵ ĵ */ + 0x0136, 501, /* Ķ ķ */ + 0x0139, 501, /* Ĺ ĺ */ + 0x013b, 501, /* Ļ ļ */ + 0x013d, 501, /* Ľ ľ */ + 0x013f, 501, /* Ŀ ŀ */ + 0x0141, 501, /* Ł ł */ + 0x0143, 501, /* Ń ń */ + 0x0145, 501, /* Ņ ņ */ + 0x0147, 501, /* Ň ň */ + 0x014a, 501, /* Ŋ ŋ */ + 0x014c, 501, /* Ō ō */ + 0x014e, 501, /* Ŏ ŏ */ + 0x0150, 501, /* Ő ő */ + 0x0152, 501, /* Œ œ */ + 0x0154, 501, /* Ŕ ŕ */ + 0x0156, 501, /* Ŗ ŗ */ + 0x0158, 501, /* Ř ř */ + 0x015a, 501, /* Ś ś */ + 0x015c, 501, /* Ŝ ŝ */ + 0x015e, 501, /* Ş ş */ + 0x0160, 501, /* Š š */ + 0x0162, 501, /* Ţ ţ */ + 0x0164, 501, /* Ť ť */ + 0x0166, 501, /* Ŧ ŧ */ + 0x0168, 501, /* Ũ ũ */ + 0x016a, 501, /* Ū ū */ + 0x016c, 501, /* Ŭ ŭ */ + 0x016e, 501, /* Ů ů */ + 0x0170, 501, /* Ű ű */ + 0x0172, 501, /* Ų ų */ + 0x0174, 501, /* Ŵ ŵ */ + 0x0176, 501, /* Ŷ ŷ */ + 0x0178, 379, /* Ÿ ÿ */ + 0x0179, 501, /* Ź ź */ + 0x017b, 501, /* Ż ż */ + 0x017d, 501, /* Ž ž */ + 0x0181, 710, /* Ɓ ɓ */ + 0x0182, 501, /* Ƃ ƃ */ + 0x0184, 501, /* Ƅ ƅ */ + 0x0186, 706, /* Ɔ ɔ */ + 0x0187, 501, /* Ƈ ƈ */ + 0x018b, 501, /* Ƌ ƌ */ + 0x0190, 703, /* Ɛ ɛ */ + 0x0191, 501, /* Ƒ ƒ */ + 0x0193, 705, /* Ɠ ɠ */ + 0x0194, 707, /* Ɣ ɣ */ + 0x0196, 711, /* Ɩ ɩ */ + 0x0197, 709, /* Ɨ ɨ */ + 0x0198, 501, /* Ƙ ƙ */ + 0x019c, 711, /* Ɯ ɯ */ + 0x019d, 713, /* Ɲ ɲ */ + 0x01a0, 501, /* Ơ ơ */ + 0x01a2, 501, /* Ƣ ƣ */ + 0x01a4, 501, /* Ƥ ƥ */ + 0x01a7, 501, /* Ƨ ƨ */ + 0x01a9, 718, /* Ʃ ʃ */ + 0x01ac, 501, /* Ƭ ƭ */ + 0x01ae, 718, /* Ʈ ʈ */ + 0x01af, 501, /* Ư ư */ + 0x01b3, 501, /* Ƴ ƴ */ + 0x01b5, 501, /* Ƶ ƶ */ + 0x01b7, 719, /* Ʒ ʒ */ + 0x01b8, 501, /* Ƹ ƹ */ + 0x01bc, 501, /* Ƽ ƽ */ + 0x01c4, 502, /* DŽ dž */ + 0x01c5, 501, /* Dž dž */ + 0x01c7, 502, /* LJ lj */ + 0x01c8, 501, /* Lj lj */ + 0x01ca, 502, /* NJ nj */ + 0x01cb, 501, /* Nj nj */ + 0x01cd, 501, /* Ǎ ǎ */ + 0x01cf, 501, /* Ǐ ǐ */ + 0x01d1, 501, /* Ǒ ǒ */ + 0x01d3, 501, /* Ǔ ǔ */ + 0x01d5, 501, /* Ǖ ǖ */ + 0x01d7, 501, /* Ǘ ǘ */ + 0x01d9, 501, /* Ǚ ǚ */ + 0x01db, 501, /* Ǜ ǜ */ + 0x01de, 501, /* Ǟ ǟ */ + 0x01e0, 501, /* Ǡ ǡ */ + 0x01e2, 501, /* Ǣ ǣ */ + 0x01e4, 501, /* Ǥ ǥ */ + 0x01e6, 501, /* Ǧ ǧ */ + 0x01e8, 501, /* Ǩ ǩ */ + 0x01ea, 501, /* Ǫ ǫ */ + 0x01ec, 501, /* Ǭ ǭ */ + 0x01ee, 501, /* Ǯ ǯ */ + 0x01f1, 502, /* DZ dz */ + 0x01f2, 501, /* Dz dz */ + 0x01f4, 501, /* Ǵ ǵ */ + 0x01fa, 501, /* Ǻ ǻ */ + 0x01fc, 501, /* Ǽ ǽ */ + 0x01fe, 501, /* Ǿ ǿ */ + 0x0200, 501, /* Ȁ ȁ */ + 0x0202, 501, /* Ȃ ȃ */ + 0x0204, 501, /* Ȅ ȅ */ + 0x0206, 501, /* Ȇ ȇ */ + 0x0208, 501, /* Ȉ ȉ */ + 0x020a, 501, /* Ȋ ȋ */ + 0x020c, 501, /* Ȍ ȍ */ + 0x020e, 501, /* Ȏ ȏ */ + 0x0210, 501, /* Ȑ ȑ */ + 0x0212, 501, /* Ȓ ȓ */ + 0x0214, 501, /* Ȕ ȕ */ + 0x0216, 501, /* Ȗ ȗ */ + 0x0386, 538, /* Ά ά */ + 0x038c, 564, /* Ό ό */ + 0x03e2, 501, /* Ϣ ϣ */ + 0x03e4, 501, /* Ϥ ϥ */ + 0x03e6, 501, /* Ϧ ϧ */ + 0x03e8, 501, /* Ϩ ϩ */ + 0x03ea, 501, /* Ϫ ϫ */ + 0x03ec, 501, /* Ϭ ϭ */ + 0x03ee, 501, /* Ϯ ϯ */ + 0x0460, 501, /* Ѡ ѡ */ + 0x0462, 501, /* Ѣ ѣ */ + 0x0464, 501, /* Ѥ ѥ */ + 0x0466, 501, /* Ѧ ѧ */ + 0x0468, 501, /* Ѩ ѩ */ + 0x046a, 501, /* Ѫ ѫ */ + 0x046c, 501, /* Ѭ ѭ */ + 0x046e, 501, /* Ѯ ѯ */ + 0x0470, 501, /* Ѱ ѱ */ + 0x0472, 501, /* Ѳ ѳ */ + 0x0474, 501, /* Ѵ ѵ */ + 0x0476, 501, /* Ѷ ѷ */ + 0x0478, 501, /* Ѹ ѹ */ + 0x047a, 501, /* Ѻ ѻ */ + 0x047c, 501, /* Ѽ ѽ */ + 0x047e, 501, /* Ѿ ѿ */ + 0x0480, 501, /* Ҁ ҁ */ + 0x0490, 501, /* Ґ ґ */ + 0x0492, 501, /* Ғ ғ */ + 0x0494, 501, /* Ҕ ҕ */ + 0x0496, 501, /* Җ җ */ + 0x0498, 501, /* Ҙ ҙ */ + 0x049a, 501, /* Қ қ */ + 0x049c, 501, /* Ҝ ҝ */ + 0x049e, 501, /* Ҟ ҟ */ + 0x04a0, 501, /* Ҡ ҡ */ + 0x04a2, 501, /* Ң ң */ + 0x04a4, 501, /* Ҥ ҥ */ + 0x04a6, 501, /* Ҧ ҧ */ + 0x04a8, 501, /* Ҩ ҩ */ + 0x04aa, 501, /* Ҫ ҫ */ + 0x04ac, 501, /* Ҭ ҭ */ + 0x04ae, 501, /* Ү ү */ + 0x04b0, 501, /* Ұ ұ */ + 0x04b2, 501, /* Ҳ ҳ */ + 0x04b4, 501, /* Ҵ ҵ */ + 0x04b6, 501, /* Ҷ ҷ */ + 0x04b8, 501, /* Ҹ ҹ */ + 0x04ba, 501, /* Һ һ */ + 0x04bc, 501, /* Ҽ ҽ */ + 0x04be, 501, /* Ҿ ҿ */ + 0x04c1, 501, /* Ӂ ӂ */ + 0x04c3, 501, /* Ӄ ӄ */ + 0x04c7, 501, /* Ӈ ӈ */ + 0x04cb, 501, /* Ӌ ӌ */ + 0x04d0, 501, /* Ӑ ӑ */ + 0x04d2, 501, /* Ӓ ӓ */ + 0x04d4, 501, /* Ӕ ӕ */ + 0x04d6, 501, /* Ӗ ӗ */ + 0x04d8, 501, /* Ә ә */ + 0x04da, 501, /* Ӛ ӛ */ + 0x04dc, 501, /* Ӝ ӝ */ + 0x04de, 501, /* Ӟ ӟ */ + 0x04e0, 501, /* Ӡ ӡ */ + 0x04e2, 501, /* Ӣ ӣ */ + 0x04e4, 501, /* Ӥ ӥ */ + 0x04e6, 501, /* Ӧ ӧ */ + 0x04e8, 501, /* Ө ө */ + 0x04ea, 501, /* Ӫ ӫ */ + 0x04ee, 501, /* Ӯ ӯ */ + 0x04f0, 501, /* Ӱ ӱ */ + 0x04f2, 501, /* Ӳ ӳ */ + 0x04f4, 501, /* Ӵ ӵ */ + 0x04f8, 501, /* Ӹ ӹ */ + 0x1e00, 501, /* Ḁ ḁ */ + 0x1e02, 501, /* Ḃ ḃ */ + 0x1e04, 501, /* Ḅ ḅ */ + 0x1e06, 501, /* Ḇ ḇ */ + 0x1e08, 501, /* Ḉ ḉ */ + 0x1e0a, 501, /* Ḋ ḋ */ + 0x1e0c, 501, /* Ḍ ḍ */ + 0x1e0e, 501, /* Ḏ ḏ */ + 0x1e10, 501, /* Ḑ ḑ */ + 0x1e12, 501, /* Ḓ ḓ */ + 0x1e14, 501, /* Ḕ ḕ */ + 0x1e16, 501, /* Ḗ ḗ */ + 0x1e18, 501, /* Ḙ ḙ */ + 0x1e1a, 501, /* Ḛ ḛ */ + 0x1e1c, 501, /* Ḝ ḝ */ + 0x1e1e, 501, /* Ḟ ḟ */ + 0x1e20, 501, /* Ḡ ḡ */ + 0x1e22, 501, /* Ḣ ḣ */ + 0x1e24, 501, /* Ḥ ḥ */ + 0x1e26, 501, /* Ḧ ḧ */ + 0x1e28, 501, /* Ḩ ḩ */ + 0x1e2a, 501, /* Ḫ ḫ */ + 0x1e2c, 501, /* Ḭ ḭ */ + 0x1e2e, 501, /* Ḯ ḯ */ + 0x1e30, 501, /* Ḱ ḱ */ + 0x1e32, 501, /* Ḳ ḳ */ + 0x1e34, 501, /* Ḵ ḵ */ + 0x1e36, 501, /* Ḷ ḷ */ + 0x1e38, 501, /* Ḹ ḹ */ + 0x1e3a, 501, /* Ḻ ḻ */ + 0x1e3c, 501, /* Ḽ ḽ */ + 0x1e3e, 501, /* Ḿ ḿ */ + 0x1e40, 501, /* Ṁ ṁ */ + 0x1e42, 501, /* Ṃ ṃ */ + 0x1e44, 501, /* Ṅ ṅ */ + 0x1e46, 501, /* Ṇ ṇ */ + 0x1e48, 501, /* Ṉ ṉ */ + 0x1e4a, 501, /* Ṋ ṋ */ + 0x1e4c, 501, /* Ṍ ṍ */ + 0x1e4e, 501, /* Ṏ ṏ */ + 0x1e50, 501, /* Ṑ ṑ */ + 0x1e52, 501, /* Ṓ ṓ */ + 0x1e54, 501, /* Ṕ ṕ */ + 0x1e56, 501, /* Ṗ ṗ */ + 0x1e58, 501, /* Ṙ ṙ */ + 0x1e5a, 501, /* Ṛ ṛ */ + 0x1e5c, 501, /* Ṝ ṝ */ + 0x1e5e, 501, /* Ṟ ṟ */ + 0x1e60, 501, /* Ṡ ṡ */ + 0x1e62, 501, /* Ṣ ṣ */ + 0x1e64, 501, /* Ṥ ṥ */ + 0x1e66, 501, /* Ṧ ṧ */ + 0x1e68, 501, /* Ṩ ṩ */ + 0x1e6a, 501, /* Ṫ ṫ */ + 0x1e6c, 501, /* Ṭ ṭ */ + 0x1e6e, 501, /* Ṯ ṯ */ + 0x1e70, 501, /* Ṱ ṱ */ + 0x1e72, 501, /* Ṳ ṳ */ + 0x1e74, 501, /* Ṵ ṵ */ + 0x1e76, 501, /* Ṷ ṷ */ + 0x1e78, 501, /* Ṹ ṹ */ + 0x1e7a, 501, /* Ṻ ṻ */ + 0x1e7c, 501, /* Ṽ ṽ */ + 0x1e7e, 501, /* Ṿ ṿ */ + 0x1e80, 501, /* Ẁ ẁ */ + 0x1e82, 501, /* Ẃ ẃ */ + 0x1e84, 501, /* Ẅ ẅ */ + 0x1e86, 501, /* Ẇ ẇ */ + 0x1e88, 501, /* Ẉ ẉ */ + 0x1e8a, 501, /* Ẋ ẋ */ + 0x1e8c, 501, /* Ẍ ẍ */ + 0x1e8e, 501, /* Ẏ ẏ */ + 0x1e90, 501, /* Ẑ ẑ */ + 0x1e92, 501, /* Ẓ ẓ */ + 0x1e94, 501, /* Ẕ ẕ */ + 0x1ea0, 501, /* Ạ ạ */ + 0x1ea2, 501, /* Ả ả */ + 0x1ea4, 501, /* Ấ ấ */ + 0x1ea6, 501, /* Ầ ầ */ + 0x1ea8, 501, /* Ẩ ẩ */ + 0x1eaa, 501, /* Ẫ ẫ */ + 0x1eac, 501, /* Ậ ậ */ + 0x1eae, 501, /* Ắ ắ */ + 0x1eb0, 501, /* Ằ ằ */ + 0x1eb2, 501, /* Ẳ ẳ */ + 0x1eb4, 501, /* Ẵ ẵ */ + 0x1eb6, 501, /* Ặ ặ */ + 0x1eb8, 501, /* Ẹ ẹ */ + 0x1eba, 501, /* Ẻ ẻ */ + 0x1ebc, 501, /* Ẽ ẽ */ + 0x1ebe, 501, /* Ế ế */ + 0x1ec0, 501, /* Ề ề */ + 0x1ec2, 501, /* Ể ể */ + 0x1ec4, 501, /* Ễ ễ */ + 0x1ec6, 501, /* Ệ ệ */ + 0x1ec8, 501, /* Ỉ ỉ */ + 0x1eca, 501, /* Ị ị */ + 0x1ecc, 501, /* Ọ ọ */ + 0x1ece, 501, /* Ỏ ỏ */ + 0x1ed0, 501, /* Ố ố */ + 0x1ed2, 501, /* Ồ ồ */ + 0x1ed4, 501, /* Ổ ổ */ + 0x1ed6, 501, /* Ỗ ỗ */ + 0x1ed8, 501, /* Ộ ộ */ + 0x1eda, 501, /* Ớ ớ */ + 0x1edc, 501, /* Ờ ờ */ + 0x1ede, 501, /* Ở ở */ + 0x1ee0, 501, /* Ỡ ỡ */ + 0x1ee2, 501, /* Ợ ợ */ + 0x1ee4, 501, /* Ụ ụ */ + 0x1ee6, 501, /* Ủ ủ */ + 0x1ee8, 501, /* Ứ ứ */ + 0x1eea, 501, /* Ừ ừ */ + 0x1eec, 501, /* Ử ử */ + 0x1eee, 501, /* Ữ ữ */ + 0x1ef0, 501, /* Ự ự */ + 0x1ef2, 501, /* Ỳ ỳ */ + 0x1ef4, 501, /* Ỵ ỵ */ + 0x1ef6, 501, /* Ỷ ỷ */ + 0x1ef8, 501, /* Ỹ ỹ */ + 0x1f59, 492, /* Ὑ ὑ */ + 0x1f5b, 492, /* Ὓ ὓ */ + 0x1f5d, 492, /* Ὕ ὕ */ + 0x1f5f, 492, /* Ὗ ὗ */ + 0x1fbc, 491, /* ᾼ ᾳ */ + 0x1fcc, 491, /* ῌ ῃ */ + 0x1fec, 493, /* Ῥ ῥ */ + 0x1ffc, 491, /* ῼ ῳ */ +}; + +/* + * title characters are those between + * upper and lower case. ie DZ Dz dz + */ +static +Rune __totitle1[] = +{ + 0x01c4, 501, /* DŽ Dž */ + 0x01c6, 499, /* dž Dž */ + 0x01c7, 501, /* LJ Lj */ + 0x01c9, 499, /* lj Lj */ + 0x01ca, 501, /* NJ Nj */ + 0x01cc, 499, /* nj Nj */ + 0x01f1, 501, /* DZ Dz */ + 0x01f3, 499, /* dz Dz */ +}; + +static Rune* +bsearch(Rune c, Rune *t, int n, int ne) +{ + Rune *p; + int m; + + while(n > 1) { + m = n/2; + p = t + m*ne; + if(c >= p[0]) { + t = p; + n = n-m; + } else + n = m; + } + if(n && c >= t[0]) + return t; + return 0; +} + +Rune +tolowerrune(Rune c) +{ + Rune *p; + + p = bsearch(c, __tolower2, nelem(__tolower2)/3, 3); + if(p && c >= p[0] && c <= p[1]) + return c + p[2] - 500; + p = bsearch(c, __tolower1, nelem(__tolower1)/2, 2); + if(p && c == p[0]) + return c + p[1] - 500; + return c; +} + +Rune +toupperrune(Rune c) +{ + Rune *p; + + p = bsearch(c, __toupper2, nelem(__toupper2)/3, 3); + if(p && c >= p[0] && c <= p[1]) + return c + p[2] - 500; + p = bsearch(c, __toupper1, nelem(__toupper1)/2, 2); + if(p && c == p[0]) + return c + p[1] - 500; + return c; +} + +Rune +totitlerune(Rune c) +{ + Rune *p; + + p = bsearch(c, __totitle1, nelem(__totitle1)/2, 2); + if(p && c == p[0]) + return c + p[1] - 500; + return c; +} + +int +islowerrune(Rune c) +{ + Rune *p; + + p = bsearch(c, __toupper2, nelem(__toupper2)/3, 3); + if(p && c >= p[0] && c <= p[1]) + return 1; + p = bsearch(c, __toupper1, nelem(__toupper1)/2, 2); + if(p && c == p[0]) + return 1; + return 0; +} + +int +isupperrune(Rune c) +{ + Rune *p; + + p = bsearch(c, __tolower2, nelem(__tolower2)/3, 3); + if(p && c >= p[0] && c <= p[1]) + return 1; + p = bsearch(c, __tolower1, nelem(__tolower1)/2, 2); + if(p && c == p[0]) + return 1; + return 0; +} + +int +isalpharune(Rune c) +{ + Rune *p; + + if(isupperrune(c) || islowerrune(c)) + return 1; + p = bsearch(c, __alpha2, nelem(__alpha2)/2, 2); + if(p && c >= p[0] && c <= p[1]) + return 1; + p = bsearch(c, __alpha1, nelem(__alpha1), 1); + if(p && c == p[0]) + return 1; + return 0; +} + +int +istitlerune(Rune c) +{ + return isupperrune(c) && islowerrune(c); +} + +int +isspacerune(Rune c) +{ + Rune *p; + + p = bsearch(c, __space2, nelem(__space2)/2, 2); + if(p && c >= p[0] && c <= p[1]) + return 1; + return 0; +} diff --git a/lib9/utf/utfdef.h b/lib9/utf/utfdef.h @@ -0,0 +1,33 @@ +/* + * compiler directive on Plan 9 + */ +#ifndef USED +#define USED(x) if(x);else +#endif + +/* + * easiest way to make sure these are defined + */ +#define uchar _fmtuchar +#define ushort _fmtushort +#define uint _fmtuint +#define ulong _fmtulong +#define vlong _fmtvlong +#define uvlong _fmtuvlong +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned long long uvlong; +typedef long long vlong; + +/* + * nil cannot be ((void*)0) on ANSI C, + * because it is used for function pointers + */ +#undef nil +#define nil 0 + +#undef nelem +#define nelem ((void*)0) + diff --git a/lib9/utf/utfecpy.c b/lib9/utf/utfecpy.c @@ -0,0 +1,36 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +char* +utfecpy(char *to, char *e, char *from) +{ + char *end; + + if(to >= e) + return to; + end = memccpy(to, from, '\0', e - to); + if(end == nil){ + end = e-1; + while(end>to && (*--end&0xC0)==0x80) + ; + *end = '\0'; + }else{ + end--; + } + return end; +} diff --git a/lib9/utf/utflen.c b/lib9/utf/utflen.c @@ -0,0 +1,37 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +int +utflen(char *s) +{ + int c; + long n; + Rune rune; + + n = 0; + for(;;) { + c = *(uchar*)s; + if(c < Runeself) { + if(c == 0) + return n; + s++; + } else + s += chartorune(&rune, s); + n++; + } +} diff --git a/lib9/utf/utfnlen.c b/lib9/utf/utfnlen.c @@ -0,0 +1,41 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +int +utfnlen(char *s, long m) +{ + int c; + long n; + Rune rune; + char *es; + + es = s + m; + for(n = 0; s < es; n++) { + c = *(uchar*)s; + if(c < Runeself){ + if(c == '\0') + break; + s++; + continue; + } + if(!fullrune(s, es-s)) + break; + s += chartorune(&rune, s); + } + return n; +} diff --git a/lib9/utf/utfrrune.c b/lib9/utf/utfrrune.c @@ -0,0 +1,45 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +char* +utfrrune(char *s, long c) +{ + long c1; + Rune r; + char *s1; + + if(c < Runesync) /* not part of utf sequence */ + return strrchr(s, c); + + s1 = 0; + for(;;) { + c1 = *(uchar*)s; + if(c1 < Runeself) { /* one byte rune */ + if(c1 == 0) + return s1; + if(c1 == c) + s1 = s; + s++; + continue; + } + c1 = chartorune(&r, s); + if(r == c) + s1 = s; + s += c1; + } +} diff --git a/lib9/utf/utfrune.c b/lib9/utf/utfrune.c @@ -0,0 +1,44 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + +char* +utfrune(char *s, long c) +{ + long c1; + Rune r; + int n; + + if(c < Runesync) /* not part of utf sequence */ + return strchr(s, c); + + for(;;) { + c1 = *(uchar*)s; + if(c1 < Runeself) { /* one byte rune */ + if(c1 == 0) + return 0; + if(c1 == c) + return s; + s++; + continue; + } + n = chartorune(&r, s); + if(r == c) + return s; + s += n; + } +} diff --git a/lib9/utf/utfutf.c b/lib9/utf/utfutf.c @@ -0,0 +1,41 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE + * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "utf.h" + + +/* + * Return pointer to first occurrence of s2 in s1, + * 0 if none + */ +char* +utfutf(char *s1, char *s2) +{ + char *p; + long f, n1, n2; + Rune r; + + n1 = chartorune(&r, s2); + f = r; + if(f <= Runesync) /* represents self */ + return strstr(s1, s2); + + n2 = strlen(s2); + for(p=s1; p=utfrune(p, f); p+=n1) + if(strncmp(p, s2, n2) == 0) + return p; + return 0; +} diff --git a/lib9/wait.c b/lib9/wait.c @@ -0,0 +1,54 @@ +#include <u.h> +#include <libc.h> + +static Waitmsg* +_wait(int n, char *buf) +{ + int l; + char *fld[5]; + Waitmsg *w; + + if(n <= 0) + return nil; + buf[n] = '\0'; + if(tokenize(buf, fld, nelem(fld)) != nelem(fld)){ + werrstr("couldn't parse wait message"); + return nil; + } + l = strlen(fld[4])+1; + w = malloc(sizeof(Waitmsg)+l); + if(w == nil) + return nil; + w->pid = atoi(fld[0]); + w->time[0] = atoi(fld[1]); + w->time[1] = atoi(fld[2]); + w->time[2] = atoi(fld[3]); + w->msg = (char*)&w[1]; + memmove(w->msg, fld[4], l); + return w; +} + +Waitmsg* +wait(void) +{ + char buf[256]; + + return _wait(await(buf, sizeof buf-1), buf); +} + +Waitmsg* +waitnohang(void) +{ + char buf[256]; + + return _wait(awaitnohang(buf, sizeof buf-1), buf); +} + +Waitmsg* +waitfor(int pid) +{ + char buf[256]; + + return _wait(awaitfor(pid, buf, sizeof buf-1), buf); +} + diff --git a/lib9/waitpid.c b/lib9/waitpid.c @@ -0,0 +1,20 @@ +#include <u.h> +#include <libc.h> + +int +waitpid(void) +{ + int n; + char buf[512], *fld[5]; + + n = await(buf, sizeof buf-1); + if(n <= 0) + return -1; + buf[n] = '\0'; + if(tokenize(buf, fld, nelem(fld)) != nelem(fld)){ + werrstr("couldn't parse wait message"); + return -1; + } + return atoi(fld[0]); +} + diff --git a/rc/Makefile b/rc/Makefile @@ -0,0 +1,54 @@ +# rc - rc shell unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = rc + +OFILES = code.o exec.o getflags.o glob.o here.o io.o lex.o \ + pcmd.o pfnc.o simple.o subr.o trap.o tree.o unixcrap.o \ + var.o y.tab.o plan9ish.o + +YFILES = syn.y + +MANFILES = rc.1 + +RCMAIN = rcmain + +all: + @if [ ! -f y.tab.c ]; then \ + ${MAKE} -f Makefile depend;\ + fi + @${MAKE} -f Makefile ${TARG} + @echo built ${TARG} + +depend: + @echo YACC ${YFILES} + @${YACC} -d ${YFILES} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/lib + @cp -f ${RCMAIN} ${DESTDIR}${PREFIX}/lib + @chmod 755 ${DESTDIR}${PREFIX}/lib/${RCMAIN} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/lib/${RCMAIN} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} y.tab.c x.tab.h + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 diff --git a/rc/code.c b/rc/code.c @@ -0,0 +1,430 @@ +#include "rc.h" +#include "io.h" +#include "exec.h" +#include "fns.h" +#include "getflags.h" +#define c0 t->child[0] +#define c1 t->child[1] +#define c2 t->child[2] +int codep, ncode; +#define emitf(x) ((void)(codep!=ncode || morecode()), codebuf[codep].f=(x), codep++) +#define emiti(x) ((void)(codep!=ncode || morecode()), codebuf[codep].i=(x), codep++) +#define emits(x) ((void)(codep!=ncode || morecode()), codebuf[codep].s=(x), codep++) +void stuffdot(int); +char *fnstr(tree*); +void outcode(tree*, int); +void codeswitch(tree*, int); +int iscase(tree*); +code *codecopy(code*); +void codefree(code*); +int morecode(void){ + ncode+=100; + codebuf=(code *)realloc((char *)codebuf, ncode*sizeof codebuf[0]); + if(codebuf==0) panic("Can't realloc %d bytes in morecode!", + ncode*sizeof codebuf[0]); + return 0; +} +void stuffdot(int a){ + if(a<0 || codep<=a) panic("Bad address %d in stuffdot", a); + codebuf[a].i=codep; +} +int compile(tree *t) +{ + ncode=100; + codebuf=(code *)emalloc(ncode*sizeof codebuf[0]); + codep=0; + emiti(0); /* reference count */ + outcode(t, flag['e']?1:0); + if(nerror){ + efree((char *)codebuf); + return 0; + } + readhere(); + emitf(Xreturn); + emitf(0); + return 1; +} +void cleanhere(char *f) +{ + emitf(Xdelhere); + emits(strdup(f)); +} +char *fnstr(tree *t) +{ + io *f=openstr(); + char *v; + extern char nl; + char svnl=nl; + nl=';'; + pfmt(f, "%t", t); + nl=svnl; + v=f->strp; + f->strp=0; + closeio(f); + return v; +} +void outcode(tree *t, int eflag) +{ + int p, q; + tree *tt; + if(t==0) return; + if(t->type!=NOT && t->type!=';') runq->iflast=0; + switch(t->type){ + default: + pfmt(err, "bad type %d in outcode\n", t->type); + break; + case '$': + emitf(Xmark); + outcode(c0, eflag); + emitf(Xdol); + break; + case '"': + emitf(Xmark); + outcode(c0, eflag); + emitf(Xqdol); + break; + case SUB: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xmark); + outcode(c1, eflag); + emitf(Xsub); + break; + case '&': + emitf(Xasync); + p=emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + break; + case ';': + outcode(c0, eflag); + outcode(c1, eflag); + break; + case '^': + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xconc); + break; + case '`': + emitf(Xbackq); + p=emiti(0); + outcode(c0, 0); + emitf(Xexit); + stuffdot(p); + break; + case ANDAND: + outcode(c0, 0); + emitf(Xtrue); + p=emiti(0); + outcode(c1, eflag); + stuffdot(p); + break; + case ARGLIST: + outcode(c1, eflag); + outcode(c0, eflag); + break; + case BANG: + outcode(c0, eflag); + emitf(Xbang); + break; + case PCMD: + case BRACE: + outcode(c0, eflag); + break; + case COUNT: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xcount); + break; + case FN: + emitf(Xmark); + outcode(c0, eflag); + if(c1){ + emitf(Xfn); + p=emiti(0); + emits(fnstr(c1)); + outcode(c1, eflag); + emitf(Xunlocal); /* get rid of $* */ + emitf(Xreturn); + stuffdot(p); + } + else + emitf(Xdelfn); + break; + case IF: + outcode(c0, 0); + emitf(Xif); + p=emiti(0); + outcode(c1, eflag); + emitf(Xwastrue); + stuffdot(p); + break; + case NOT: + if(!runq->iflast) yyerror("`if not' does not follow `if(...)'"); + emitf(Xifnot); + p=emiti(0); + outcode(c0, eflag); + stuffdot(p); + break; + case OROR: + outcode(c0, 0); + emitf(Xfalse); + p=emiti(0); + outcode(c1, eflag); + stuffdot(p); + break; + case PAREN: + outcode(c0, eflag); + break; + case SIMPLE: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xsimple); + if(eflag) emitf(Xeflag); + break; + case SUBSHELL: + emitf(Xsubshell); + p=emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + if(eflag) emitf(Xeflag); + break; + case SWITCH: + codeswitch(t, eflag); + break; + case TWIDDLE: + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xmatch); + if(eflag) emitf(Xeflag); + break; + case WHILE: + q=codep; + outcode(c0, 0); + if(q==codep) emitf(Xsettrue); /* empty condition == while(true) */ + emitf(Xtrue); + p=emiti(0); + outcode(c1, eflag); + emitf(Xjump); + emiti(q); + stuffdot(p); + break; + case WORDS: + outcode(c1, eflag); + outcode(c0, eflag); + break; + case FOR: + emitf(Xmark); + if(c1){ + outcode(c1, eflag); + emitf(Xglob); + } + else{ + emitf(Xmark); + emitf(Xword); + emits(strdup("*")); + emitf(Xdol); + } + emitf(Xmark); /* dummy value for Xlocal */ + emitf(Xmark); + outcode(c0, eflag); + emitf(Xlocal); + p=emitf(Xfor); + q=emiti(0); + outcode(c2, eflag); + emitf(Xjump); + emiti(p); + stuffdot(q); + emitf(Xunlocal); + break; + case WORD: + emitf(Xword); + emits(strdup(t->str)); + break; + case DUP: + if(t->rtype==DUPFD){ + emitf(Xdup); + emiti(t->fd0); + emiti(t->fd1); + } + else{ + emitf(Xclose); + emiti(t->fd0); + } + outcode(c1, eflag); + emitf(Xpopredir); + break; + case PIPEFD: + emitf(Xpipefd); + emiti(t->rtype); + p=emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + break; + case REDIR: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xglob); + switch(t->rtype){ + case APPEND: + emitf(Xappend); + break; + case WRITE: + emitf(Xwrite); + break; + case READ: + case HERE: + emitf(Xread); + break; + } + emiti(t->fd0); + outcode(c1, eflag); + emitf(Xpopredir); + break; + case '=': + tt=t; + for(;t && t->type=='=';t=c2); + if(t){ + for(t=tt;t->type=='=';t=c2){ + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xlocal); + } + t=tt; + outcode(c2, eflag); + for(;t->type=='=';t=c2) emitf(Xunlocal); + } + else{ + for(t=tt;t;t=c2){ + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xassign); + } + } + t=tt; /* so tests below will work */ + break; + case PIPE: + emitf(Xpipe); + emiti(t->fd0); + emiti(t->fd1); + p=emiti(0); + q=emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + outcode(c1, eflag); + emitf(Xreturn); + stuffdot(q); + emitf(Xpipewait); + break; + } + if(t->type!=NOT && t->type!=';') + runq->iflast=t->type==IF; + else if(c0) runq->iflast=c0->type==IF; +} +/* + * switch code looks like this: + * Xmark + * (get switch value) + * Xjump 1f + * out: Xjump leave + * 1: Xmark + * (get case values) + * Xcase 1f + * (commands) + * Xjump out + * 1: Xmark + * (get case values) + * Xcase 1f + * (commands) + * Xjump out + * 1: + * leave: + * Xpopm + */ +void codeswitch(tree *t, int eflag) +{ + int leave; /* patch jump address to leave switch */ + int out; /* jump here to leave switch */ + int nextcase; /* patch jump address to next case */ + tree *tt; + if(c1->child[0]==nil + || c1->child[0]->type!=';' + || !iscase(c1->child[0]->child[0])){ + yyerror("case missing in switch"); + return; + } + emitf(Xmark); + outcode(c0, eflag); + emitf(Xjump); + nextcase=emiti(0); + out=emitf(Xjump); + leave=emiti(0); + stuffdot(nextcase); + t=c1->child[0]; + while(t->type==';'){ + tt=c1; + emitf(Xmark); + for(t=c0->child[0];t->type==ARGLIST;t=c0) outcode(c1, eflag); + emitf(Xcase); + nextcase=emiti(0); + t=tt; + for(;;){ + if(t->type==';'){ + if(iscase(c0)) break; + outcode(c0, eflag); + t=c1; + } + else{ + if(!iscase(t)) outcode(t, eflag); + break; + } + } + emitf(Xjump); + emiti(out); + stuffdot(nextcase); + } + stuffdot(leave); + emitf(Xpopm); +} +int iscase(tree *t) +{ + if(t->type!=SIMPLE) return 0; + do t=c0; while(t->type==ARGLIST); + return t->type==WORD && !t->quoted && strcmp(t->str, "case")==0; +} +code *codecopy(code *cp) +{ + cp[0].i++; + return cp; +} +void codefree(code *cp) +{ + code *p; + if(--cp[0].i!=0) return; + for(p=cp+1;p->f;p++){ + if(p->f==Xappend || p->f==Xclose || p->f==Xread || p->f==Xwrite + || p->f==Xasync || p->f==Xbackq || p->f==Xcase || p->f==Xfalse + || p->f==Xfor || p->f==Xjump + || p->f==Xsubshell || p->f==Xtrue) p++; + else if(p->f==Xdup || p->f==Xpipefd) p+=2; + else if(p->f==Xpipe) p+=4; + else if(p->f==Xword || p->f==Xdelhere) efree((++p)->s); + else if(p->f==Xfn){ + efree(p[2].s); + p+=2; + } + } + efree((char *)cp); +} diff --git a/rc/exec.c b/rc/exec.c @@ -0,0 +1,946 @@ +#include <u.h> +#include <signal.h> +#if defined(PLAN9PORT) && defined(__sun__) +# define BSD_COMP /* sigh. for TIOCNOTTY */ +#endif +#include <sys/ioctl.h> +#include "rc.h" +#include "getflags.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +/* + * Start executing the given code at the given pc with the given redirection + */ +char *argv0="rc"; +void start(code *c, int pc, var *local) +{ + struct thread *p=new(struct thread); + p->code=codecopy(c); + p->pc=pc; + p->argv=0; + p->redir=p->startredir=runq?runq->redir:0; + p->local=local; + p->cmdfile=0; + p->cmdfd=0; + p->eof=0; + p->iflag=0; + p->lineno=1; + p->pid=-1; + p->ret=runq; + runq=p; +} +word *newword(char *wd, word *next) +{ + word *p=new(word); + p->word=strdup(wd); + p->next=next; + return p; +} +void pushword(char *wd) +{ + if(runq->argv==0) panic("pushword but no argv!", 0); + runq->argv->words=newword(wd, runq->argv->words); +} +void popword(void){ + word *p; + if(runq->argv==0) panic("popword but no argv!", 0); + p=runq->argv->words; + if(p==0) panic("popword but no word!", 0); + runq->argv->words=p->next; + efree(p->word); + efree((char *)p); +} +void freelist(word *w) +{ + word *nw; + while(w){ + nw=w->next; + efree(w->word); + efree((char *)w); + w=nw; + } +} +void pushlist(void){ + list *p=new(list); + p->next=runq->argv; + p->words=0; + runq->argv=p; +} +void poplist(void){ + list *p=runq->argv; + if(p==0) panic("poplist but no argv", 0); + freelist(p->words); + runq->argv=p->next; + efree((char *)p); +} +int count(word *w) +{ + int n; + for(n=0;w;n++) w=w->next; + return n; +} +void pushredir(int type, int from, int to){ + redir * rp=new(redir); + rp->type=type; + rp->from=from; + rp->to=to; + rp->next=runq->redir; + runq->redir=rp; +} +var *newvar(char *name, var *next) +{ + var *v=new(var); + v->name=name; + v->val=0; + v->fn=0; + v->changed=0; + v->fnchanged=0; + v->next=next; + v->changefn = 0; + return v; +} +/* + * get command line flags, initialize keywords & traps. + * get values from environment. + * set $pid, $cflag, $* + * fabricate bootstrap code and start it (*=(argv);. /usr/lib/rcmain $*) + * start interpreting code + */ +int +main(int argc, char *argv[]) +{ + code bootstrap[32]; + char num[12], *rcmain; + int i; + + argc=getflags(argc, argv, "srdiIlxepvVc:1m:1[command]", 1); + if(argc==-1) usage("[file [arg ...]]"); + if(argv[0][0]=='-') flag['l']=flagset; + if(flag['I']) flag['i'] = 0; + else if(flag['i']==0 && argc==1 && Isatty(0)) flag['i'] = flagset; + rcmain=flag['m']?flag['m'][0]:Rcmain(); + err=openfd(2); + kinit(); + Trapinit(); + Vinit(); + itoa(num, mypid=getpid()); + pathinit(); + setvar("pid", newword(num, (word *)0)); + setvar("cflag", flag['c']?newword(flag['c'][0], (word *)0) + :(word *)0); + setvar("rcname", newword(argv[0], (word *)0)); + i=0; + bootstrap[i++].i=1; + bootstrap[i++].f=Xmark; + bootstrap[i++].f=Xword; + bootstrap[i++].s="*"; + bootstrap[i++].f=Xassign; + bootstrap[i++].f=Xmark; + bootstrap[i++].f=Xmark; + bootstrap[i++].f=Xword; + bootstrap[i++].s="*"; + bootstrap[i++].f=Xdol; + bootstrap[i++].f=Xword; + bootstrap[i++].s=rcmain; + bootstrap[i++].f=Xword; + bootstrap[i++].s="."; + bootstrap[i++].f=Xsimple; + bootstrap[i++].f=Xexit; + bootstrap[i].i=0; + start(bootstrap, 1, (var *)0); + /* prime bootstrap argv */ + pushlist(); + argv0 = strdup(argv[0]); + for(i=argc-1;i!=0;--i) pushword(argv[i]); + for(;;){ + if(flag['r']) pfnc(err, runq); + runq->pc++; + (*runq->code[runq->pc-1].f)(); + if(ntrap) dotrap(); + } + return 0; +} +/* + * Opcode routines + * Arguments on stack (...) + * Arguments in line [...] + * Code in line with jump around {...} + * + * Xappend(file)[fd] open file to append + * Xassign(name, val) assign val to name + * Xasync{... Xexit} make thread for {}, no wait + * Xbackq{... Xreturn} make thread for {}, push stdout + * Xbang complement condition + * Xcase(pat, value){...} exec code on match, leave (value) on + * stack + * Xclose[i] close file descriptor + * Xconc(left, right) concatenate, push results + * Xcount(name) push var count + * Xdelfn(name) delete function definition + * Xdeltraps(names) delete named traps + * Xdol(name) get variable value + * Xqdol(name) concatenate variable components + * Xdup[i j] dup file descriptor + * Xexit rc exits with status + * Xfalse{...} execute {} if false + * Xfn(name){... Xreturn} define function + * Xfor(var, list){... Xreturn} for loop + * Xjump[addr] goto + * Xlocal(name, val) create local variable, assign value + * Xmark mark stack + * Xmatch(pat, str) match pattern, set status + * Xpipe[i j]{... Xreturn}{... Xreturn} construct a pipe between 2 new threads, + * wait for both + * Xpipefd[type]{... Xreturn} connect {} to pipe (input or output, + * depending on type), push /dev/fd/?? + * Xpopm(value) pop value from stack + * Xread(file)[fd] open file to read + * Xsettraps(names){... Xreturn} define trap functions + * Xshowtraps print trap list + * Xsimple(args) run command and wait + * Xreturn kill thread + * Xsubshell{... Xexit} execute {} in a subshell and wait + * Xtrue{...} execute {} if true + * Xunlocal delete local variable + * Xword[string] push string + * Xwrite(file)[fd] open file to write + */ +void Xappend(void){ + char *file; + int f; + switch(count(runq->argv->words)){ + default: Xerror1(">> requires singleton"); return; + case 0: Xerror1(">> requires file"); return; + case 1: break; + } + file=runq->argv->words->word; + if((f=open(file, 1))<0 && (f=Creat(file))<0){ + pfmt(err, "%s: ", file); + Xerror("can't open"); + return; + } + Seek(f, 0L, 2); + pushredir(ROPEN, f, runq->code[runq->pc].i); + runq->pc++; + poplist(); +} +void Xasync(void){ + int null=open("/dev/null", 0); + int tty; + int pid; + char npid[10]; + if(null<0){ + Xerror("Can't open /dev/null\n"); + return; + } + switch(pid=rfork(RFFDG|RFPROC|RFNOTEG)){ + case -1: + close(null); + Xerror("try again"); + break; + case 0: + /* + * I don't know what the right thing to do here is, + * so this is all experimentally determined. + * If we just dup /dev/null onto 0, then running + * ssh foo & will reopen /dev/tty, try to read a password, + * get a signal, and repeat, in a tight loop, forever. + * Arguably this is a bug in ssh (it behaves the same + * way under bash as under rc) but I'm fixing it here + * anyway. If we dissociate the process from the tty, + * then it won't be able to open /dev/tty ever again. + * The SIG_IGN on SIGTTOU makes writing the tty + * (via fd 1 or 2, for example) succeed even though + * our pgrp is not the terminal's controlling pgrp. + */ + if((tty=open("/dev/tty", OREAD)) >= 0){ + /* + * Should make reads of tty fail, writes succeed. + */ + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + ioctl(tty, TIOCNOTTY); + close(tty); + } + if(isatty(0)) + pushredir(ROPEN, null, 0); + else + close(null); + start(runq->code, runq->pc+1, runq->local); + runq->ret=0; + break; + default: + close(null); + runq->pc=runq->code[runq->pc].i; + itoa(npid, pid); + setvar("apid", newword(npid, (word *)0)); + break; + } +} +void Xsettrue(void){ + setstatus(""); +} +void Xbang(void){ + setstatus(truestatus()?"false":""); +} +void Xclose(void){ + pushredir(RCLOSE, runq->code[runq->pc].i, 0); + runq->pc++; +} +void Xdup(void){ + pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i); + runq->pc+=2; +} +void Xeflag(void){ + if(eflagok && !truestatus()) Xexit(); +} +void Xexit(void){ + struct var *trapreq; + struct word *starval; + static int beenhere=0; + if(getpid()==mypid && !beenhere){ + trapreq=vlook("sigexit"); + if(trapreq->fn){ + beenhere=1; + --runq->pc; + starval=vlook("*")->val; + start(trapreq->fn, trapreq->pc, (struct var *)0); + runq->local=newvar(strdup("*"), runq->local); + runq->local->val=copywords(starval, (struct word *)0); + runq->local->changed=1; + runq->redir=runq->startredir=0; + return; + } + } + Exit(getstatus()); +} +void Xfalse(void){ + if(truestatus()) runq->pc=runq->code[runq->pc].i; + else runq->pc++; +} +int ifnot; /* dynamic if not flag */ +void Xifnot(void){ + if(ifnot) + runq->pc++; + else + runq->pc=runq->code[runq->pc].i; +} +void Xjump(void){ + runq->pc=runq->code[runq->pc].i; +} +void Xmark(void){ + pushlist(); +} +void Xpopm(void){ + poplist(); +} +void Xread(void){ + char *file; + int f; + switch(count(runq->argv->words)){ + default: Xerror1("< requires singleton\n"); return; + case 0: Xerror1("< requires file\n"); return; + case 1: break; + } + file=runq->argv->words->word; + if((f=open(file, 0))<0){ + pfmt(err, "%s: ", file); + Xerror("can't open"); + return; + } + pushredir(ROPEN, f, runq->code[runq->pc].i); + runq->pc++; + poplist(); +} +void turfredir(void){ + while(runq->redir!=runq->startredir) + Xpopredir(); +} +void Xpopredir(void){ + struct redir *rp=runq->redir; + if(rp==0) panic("turfredir null!", 0); + runq->redir=rp->next; + if(rp->type==ROPEN) close(rp->from); + efree((char *)rp); +} +void Xreturn(void){ + struct thread *p=runq; + turfredir(); + while(p->argv) poplist(); + codefree(p->code); + runq=p->ret; + efree((char *)p); + if(runq==0) Exit(getstatus()); +} +void Xtrue(void){ + if(truestatus()) runq->pc++; + else runq->pc=runq->code[runq->pc].i; +} +void Xif(void){ + ifnot=1; + if(truestatus()) runq->pc++; + else runq->pc=runq->code[runq->pc].i; +} +void Xwastrue(void){ + ifnot=0; +} +void Xword(void){ + pushword(runq->code[runq->pc++].s); +} +void Xwrite(void){ + char *file; + int f; + switch(count(runq->argv->words)){ + default: Xerror1("> requires singleton\n"); return; + case 0: Xerror1("> requires file\n"); return; + case 1: break; + } + file=runq->argv->words->word; + if((f=Creat(file))<0){ + pfmt(err, "%s: ", file); + Xerror("can't open"); + return; + } + pushredir(ROPEN, f, runq->code[runq->pc].i); + runq->pc++; + poplist(); +} +char *_list2str(word *words, int c){ + char *value, *s, *t; + int len=0; + word *ap; + for(ap=words;ap;ap=ap->next) + len+=1+strlen(ap->word); + value=emalloc(len+1); + s=value; + for(ap=words;ap;ap=ap->next){ + for(t=ap->word;*t;) *s++=*t++; + *s++=c; + } + if(s==value) *s='\0'; + else s[-1]='\0'; + return value; +} +char *list2str(word *words){ + return _list2str(words, ' '); +} +void Xmatch(void){ + word *p; + char *subject; + subject=list2str(runq->argv->words); + setstatus("no match"); + for(p=runq->argv->next->words;p;p=p->next) + if(match(subject, p->word, '\0')){ + setstatus(""); + break; + } + efree(subject); + poplist(); + poplist(); +} +void Xcase(void){ + word *p; + char *s; + int ok=0; + s=list2str(runq->argv->next->words); + for(p=runq->argv->words;p;p=p->next){ + if(match(s, p->word, '\0')){ + ok=1; + break; + } + } + efree(s); + if(ok) + runq->pc++; + else + runq->pc=runq->code[runq->pc].i; + poplist(); +} +word *conclist(word *lp, word *rp, word *tail) +{ + char *buf; + word *v; + if(lp->next || rp->next) + tail=conclist(lp->next==0?lp:lp->next, rp->next==0?rp:rp->next, + tail); + buf=emalloc(strlen(lp->word)+strlen(rp->word)+1); + strcpy(buf, lp->word); + strcat(buf, rp->word); + v=newword(buf, tail); + efree(buf); + return v; +} +void Xconc(void){ + word *lp=runq->argv->words; + word *rp=runq->argv->next->words; + word *vp=runq->argv->next->next->words; + int lc=count(lp), rc=count(rp); + if(lc!=0 || rc!=0){ + if(lc==0 || rc==0){ + Xerror1("null list in concatenation"); + return; + } + if(lc!=1 && rc!=1 && lc!=rc){ + Xerror1("mismatched list lengths in concatenation"); + return; + } + vp=conclist(lp, rp, vp); + } + poplist(); + poplist(); + runq->argv->words=vp; +} +void Xassign(void){ + var *v; + if(count(runq->argv->words)!=1){ + Xerror1("variable name not singleton!"); + return; + } + deglob(runq->argv->words->word); + v=vlook(runq->argv->words->word); + poplist(); + globlist(); + freewords(v->val); + v->val=runq->argv->words; + v->changed=1; + if(v->changefn) + v->changefn(v); + runq->argv->words=0; + poplist(); +} +/* + * copy arglist a, adding the copy to the front of tail + */ +word *copywords(word *a, word *tail) +{ + word *v=0, **end; + for(end=&v;a;a=a->next,end=&(*end)->next) + *end=newword(a->word, 0); + *end=tail; + return v; +} +void Xdol(void){ + word *a, *star; + char *s, *t; + int n; + if(count(runq->argv->words)!=1){ + Xerror1("variable name not singleton!"); + return; + } + s=runq->argv->words->word; + deglob(s); + n=0; + for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0'; + a=runq->argv->next->words; + if(n==0 || *t) + a=copywords(vlook(s)->val, a); + else{ + star=vlook("*")->val; + if(star && 1<=n && n<=count(star)){ + while(--n) star=star->next; + a=newword(star->word, a); + } + } + poplist(); + runq->argv->words=a; +} +void Xqdol(void){ + word *a, *p; + char *s; + int n; + if(count(runq->argv->words)!=1){ + Xerror1("variable name not singleton!"); + return; + } + s=runq->argv->words->word; + deglob(s); + a=vlook(s)->val; + poplist(); + n=count(a); + if(n==0){ + pushword(""); + return; + } + for(p=a;p;p=p->next) n+=strlen(p->word); + s=emalloc(n); + if(a){ + strcpy(s, a->word); + for(p=a->next;p;p=p->next){ + strcat(s, " "); + strcat(s, p->word); + } + } + else + s[0]='\0'; + pushword(s); + efree(s); +} +word *subwords(word *val, int len, word *sub, word *a) +{ + int n; + char *s; + if(!sub) return a; + a=subwords(val, len, sub->next, a); + s=sub->word; + deglob(s); + n=0; + while('0'<=*s && *s<='9') n=n*10+ *s++ -'0'; + if(n<1 || len<n) return a; + for(;n!=1;--n) val=val->next; + return newword(val->word, a); +} +void Xsub(void){ + word *a, *v; + char *s; + if(count(runq->argv->next->words)!=1){ + Xerror1("variable name not singleton!"); + return; + } + s=runq->argv->next->words->word; + deglob(s); + a=runq->argv->next->next->words; + v=vlook(s)->val; + a=subwords(v, count(v), runq->argv->words, a); + poplist(); + poplist(); + runq->argv->words=a; +} +void Xcount(void){ + word *a; + char *s, *t; + int n; + char num[12]; + if(count(runq->argv->words)!=1){ + Xerror1("variable name not singleton!"); + return; + } + s=runq->argv->words->word; + deglob(s); + n=0; + for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0'; + if(n==0 || *t){ + a=vlook(s)->val; + itoa(num, count(a)); + } + else{ + a=vlook("*")->val; + itoa(num, a && 1<=n && n<=count(a)?1:0); + } + poplist(); + pushword(num); +} +void Xlocal(void){ + if(count(runq->argv->words)!=1){ + Xerror1("variable name must be singleton\n"); + return; + } + deglob(runq->argv->words->word); + runq->local=newvar(strdup(runq->argv->words->word), runq->local); + runq->local->val=copywords(runq->argv->next->words, (word *)0); + runq->local->changed=1; + poplist(); + poplist(); +} +void Xunlocal(void){ + var *v=runq->local, *hid; + if(v==0) panic("Xunlocal: no locals!", 0); + runq->local=v->next; + hid=vlook(v->name); + hid->changed=1; + efree(v->name); + freewords(v->val); + efree((char *)v); +} +void freewords(word *w) +{ + word *nw; + while(w){ + efree(w->word); + nw=w->next; + efree((char *)w); + w=nw; + } +} +void Xfn(void){ + var *v; + word *a; + int end; + end=runq->code[runq->pc].i; + for(a=runq->argv->words;a;a=a->next){ + v=gvlook(a->word); + if(v->fn) codefree(v->fn); + v->fn=codecopy(runq->code); + v->pc=runq->pc+2; + v->fnchanged=1; + } + runq->pc=end; + poplist(); +} +void Xdelfn(void){ + var *v; + word *a; + for(a=runq->argv->words;a;a=a->next){ + v=gvlook(a->word); + if(v->fn) codefree(v->fn); + v->fn=0; + v->fnchanged=1; + } + poplist(); +} +void Xpipe(void){ + struct thread *p=runq; + int pc=p->pc, forkid; + int lfd=p->code[pc++].i; + int rfd=p->code[pc++].i; + int pfd[2]; + if(pipe(pfd)<0){ + Xerror("can't get pipe"); + return; + } + switch(forkid=fork()){ + case -1: + Xerror("try again"); + break; + case 0: + start(p->code, pc+2, runq->local); + runq->ret=0; + close(pfd[PRD]); + pushredir(ROPEN, pfd[PWR], lfd); + break; + default: + start(p->code, p->code[pc].i, runq->local); + close(pfd[PWR]); + pushredir(ROPEN, pfd[PRD], rfd); + p->pc=p->code[pc+1].i; + p->pid=forkid; + break; + } +} +char *concstatus(char *s, char *t) +{ + static char v[NSTATUS+1]; + int n=strlen(s); + strncpy(v, s, NSTATUS); + if(n<NSTATUS){ + v[n]='|'; + strncpy(v+n+1, t, NSTATUS-n-1); + } + v[NSTATUS]='\0'; + return v; +} +void Xpipewait(void){ + char status[NSTATUS+1]; + if(runq->pid==-1) + setstatus(concstatus(runq->status, getstatus())); + else{ + strncpy(status, getstatus(), NSTATUS); + status[NSTATUS]='\0'; + Waitfor(runq->pid, 1); + runq->pid=-1; + setstatus(concstatus(getstatus(), status)); + } +} +void Xrdcmds(void){ + struct thread *p=runq; + word *prompt; + flush(err); + nerror=0; + if(flag['s'] && !truestatus()) + pfmt(err, "status=%v\n", vlook("status")->val); + if(runq->iflag){ + prompt=vlook("prompt")->val; + if(prompt) + promptstr=prompt->word; + else + promptstr="% "; + } + Noerror(); + if(yyparse()){ + if(!p->iflag || p->eof && !Eintr()){ + if(p->cmdfile) efree(p->cmdfile); + closeio(p->cmdfd); + Xreturn(); /* should this be omitted? */ + } + else{ + if(Eintr()){ + pchr(err, '\n'); + p->eof=0; + } + --p->pc; /* go back for next command */ + } + } + else{ + ntrap = 0; /* avoid double-interrupts during blocked writes */ + --p->pc; /* re-execute Xrdcmds after codebuf runs */ + start(codebuf, 1, runq->local); + } + freenodes(); +} +void Xerror(char *s) +{ + if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0) + pfmt(err, "rc: %s: %r\n", s); + else + pfmt(err, "rc (%s): %s: %r\n", argv0, s); + flush(err); + setstatus("error"); + while(!runq->iflag) Xreturn(); +} +void Xerror1(char *s) +{ + if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0) + pfmt(err, "rc: %s\n", s); + else + pfmt(err, "rc (%s): %s\n", argv0, s); + flush(err); + setstatus("error"); + while(!runq->iflag) Xreturn(); +} +void Xbackq(void){ + char wd[8193]; + int c; + char *s, *ewd=&wd[8192], *stop; + struct io *f; + var *ifs=vlook("ifs"); + word *v, *nextv; + int pfd[2]; + int pid; + stop=ifs->val?ifs->val->word:""; + if(pipe(pfd)<0){ + Xerror("can't make pipe"); + return; + } + switch(pid=fork()){ + case -1: Xerror("try again"); + close(pfd[PRD]); + close(pfd[PWR]); + return; + case 0: + close(pfd[PRD]); + start(runq->code, runq->pc+1, runq->local); + pushredir(ROPEN, pfd[PWR], 1); + return; + default: + close(pfd[PWR]); + f=openfd(pfd[PRD]); + s=wd; + v=0; + while((c=rchr(f))!=EOF){ + if(strchr(stop, c) || s==ewd){ + if(s!=wd){ + *s='\0'; + v=newword(wd, v); + s=wd; + } + } + else *s++=c; + } + if(s!=wd){ + *s='\0'; + v=newword(wd, v); + } + closeio(f); + Waitfor(pid, 0); + /* v points to reversed arglist -- reverse it onto argv */ + while(v){ + nextv=v->next; + v->next=runq->argv->words; + runq->argv->words=v; + v=nextv; + } + runq->pc=runq->code[runq->pc].i; + return; + } +} +/* + * Who should wait for the exit from the fork? + */ +void Xpipefd(void){ + struct thread *p=runq; + int pc=p->pc; + char name[40]; + int pfd[2]; + int sidefd, mainfd; + if(pipe(pfd)<0){ + Xerror("can't get pipe"); + return; + } + if(p->code[pc].i==READ){ + sidefd=pfd[PWR]; + mainfd=pfd[PRD]; + } + else{ + sidefd=pfd[PRD]; + mainfd=pfd[PWR]; + } + switch(fork()){ + case -1: + Xerror("try again"); + break; + case 0: + start(p->code, pc+2, runq->local); + close(mainfd); + pushredir(ROPEN, sidefd, p->code[pc].i==READ?1:0); + runq->ret=0; + break; + default: + close(sidefd); + pushredir(ROPEN, mainfd, mainfd); /* isn't this a noop? */ + strcpy(name, Fdprefix); + itoa(name+strlen(name), mainfd); + pushword(name); + p->pc=p->code[pc+1].i; + break; + } +} +void Xsubshell(void){ + int pid; + switch(pid=fork()){ + case -1: + Xerror("try again"); + break; + case 0: + start(runq->code, runq->pc+1, runq->local); + runq->ret=0; + break; + default: + Waitfor(pid, 1); + runq->pc=runq->code[runq->pc].i; + break; + } +} +void setstatus(char *s) +{ + setvar("status", newword(s, (word *)0)); +} +char *getstatus(void){ + var *status=vlook("status"); + return status->val?status->val->word:""; +} +int truestatus(void){ + char *s; + for(s=getstatus();*s;s++) + if(*s!='|' && *s!='0') return 0; + return 1; +} +void Xdelhere(void){ + Unlink(runq->code[runq->pc++].s); +} +void Xfor(void){ + if(runq->argv->words==0){ + poplist(); + runq->pc=runq->code[runq->pc].i; + } + else{ + freelist(runq->local->val); + runq->local->val=runq->argv->words; + runq->local->changed=1; + runq->argv->words=runq->argv->words->next; + runq->local->val->next=0; + runq->pc++; + } +} +void Xglob(void){ + globlist(); +} diff --git a/rc/exec.h b/rc/exec.h @@ -0,0 +1,72 @@ +/* + * Definitions used in the interpreter + */ +extern void Xappend(void), Xasync(void), Xbackq(void), Xbang(void), Xclose(void); +extern void Xconc(void), Xcount(void), Xdelfn(void), Xdol(void), Xqdol(void), Xdup(void); +extern void Xexit(void), Xfalse(void), Xfn(void), Xfor(void), Xglob(void); +extern void Xjump(void), Xmark(void), Xmatch(void), Xpipe(void), Xread(void); +extern void Xrdfn(void), Xunredir(void), Xstar(void), Xreturn(void), Xsubshell(void); +extern void Xtrue(void), Xword(void), Xwrite(void), Xpipefd(void), Xcase(void); +extern void Xlocal(void), Xunlocal(void), Xassign(void), Xsimple(void), Xpopm(void); +extern void Xrdcmds(void), Xwastrue(void), Xif(void), Xifnot(void), Xpipewait(void); +extern void Xdelhere(void), Xpopredir(void), Xsub(void), Xeflag(void), Xsettrue(void); +extern void Xerror(char*); +extern void Xerror1(char*); +/* + * word lists are in correct order, + * i.e. word0->word1->word2->word3->0 + */ +struct word{ + char *word; + word *next; +}; +struct list{ + word *words; + list *next; +}; +word *newword(char *, word *), *copywords(word *, word *); +struct redir{ + char type; /* what to do */ + short from, to; /* what to do it to */ + struct redir *next; /* what else to do (reverse order) */ +}; +#define NSTATUS ERRMAX /* length of status (from plan 9) */ +/* + * redir types + */ +#define ROPEN 1 /* dup2(from, to); close(from); */ +#define RDUP 2 /* dup2(from, to); */ +#define RCLOSE 3 /* close(from); */ +struct thread{ + union code *code; /* code for this thread */ + int pc; /* code[pc] is the next instruction */ + struct list *argv; /* argument stack */ + struct redir *redir; /* redirection stack */ + struct redir *startredir; /* redir inheritance point */ + struct var *local; /* list of local variables */ + char *cmdfile; /* file name in Xrdcmd */ + struct io *cmdfd; /* file descriptor for Xrdcmd */ + int iflast; /* static `if not' checking */ + int eof; /* is cmdfd at eof? */ + int iflag; /* interactive? */ + int lineno; /* linenumber */ + int pid; /* process for Xpipewait to wait for */ + int done; /* have we seen a wait message for this process? */ + char status[NSTATUS]; /* status for Xpipewait */ + tree *treenodes; /* tree nodes created by this process */ + thread *ret; /* who continues when this finishes */ +}; +thread *runq; +code *codecopy(code*); +code *codebuf; /* compiler output */ +int ntrap; /* number of outstanding traps */ +int trap[NSIG]; /* number of outstanding traps per type */ +extern struct builtin{ + char *name; + void (*fnc)(void); +}Builtin[]; +int eflagok; /* kludge flag so that -e doesn't exit in startup */ +void execcd(void), execwhatis(void), execeval(void), execexec(void); +void execexit(void), execshift(void); +void execwait(void), execumask(void), execdot(void), execflag(void); +void execfunc(var*), execcmds(io *); diff --git a/rc/fmtquote.c b/rc/fmtquote.c @@ -0,0 +1,162 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <u.h> +#include <libc.h> +#include "fmt.h" +#include "fmtdef.h" + +extern int (*doquote)(int); + +/* + * How many bytes of output UTF will be produced by quoting (if necessary) this string? + * How many runes? How much of the input will be consumed? + * The parameter q is filled in by _quotesetup. + * The string may be UTF or Runes (s or r). + * Return count does not include NUL. + * Terminate the scan at the first of: + * NUL in input + * count exceeded in input + * count exceeded on output + * *ninp is set to number of input bytes accepted. + * nin may be <0 initially, to avoid checking input by count. + */ +void +__quotesetup(char *s, int nin, int nout, Quoteinfo *q, int sharp) +{ + int c; + + q->quoted = 0; + q->nbytesout = 0; + q->nrunesout = 0; + q->nbytesin = 0; + q->nrunesin = 0; + if(sharp || nin==0 || *s=='\0'){ + if(nout < 2) + return; + q->quoted = 1; + q->nbytesout = 2; + q->nrunesout = 2; + } + for(; nin!=0; nin-=1){ + c = *s; + + if(c == '\0') + break; + if(q->nrunesout+1 > nout) + break; + + if((c <= L' ') || (c == L'\'') || (doquote!=nil && doquote(c))){ + if(!q->quoted){ + if(1+q->nrunesout+1+1 > nout) /* no room for quotes */ + break; + q->nrunesout += 2; /* include quotes */ + q->nbytesout += 2; /* include quotes */ + q->quoted = 1; + } + if(c == '\'') { + q->nbytesout++; + q->nrunesout++; /* quotes reproduce as two characters */ + } + } + + /* advance input */ + s++; + q->nbytesin++; + q->nrunesin++; + + /* advance output */ + q->nbytesout++; + q->nrunesout++; + } +} + +static int +qstrfmt(char *sin, Quoteinfo *q, Fmt *f) +{ + int r; + char *t, *s, *m, *me; + ulong fl; + int nc, w; + + m = sin; + me = m + q->nbytesin; + + w = f->width; + fl = f->flags; + if(!(fl & FmtLeft) && __fmtpad(f, w - q->nbytesout) < 0) + return -1; + t = f->to; + s = f->stop; + FMTCHAR(f, t, s, '\''); + for(nc = q->nrunesin; nc > 0; nc--){ + r = *(uchar*)m++; + FMTCHAR(f, t, s, r); + if(r == '\'') + FMTCHAR(f, t, s, r); + } + + FMTCHAR(f, t, s, '\''); + f->nfmt += t - (char *)f->to; + f->to = t; + if(fl & FmtLeft && __fmtpad(f, w - q->nbytesout) < 0) + return -1; + return 0; +} + +int +__quotestrfmt(int runesin, Fmt *f) +{ + int outlen; + char *s; + Quoteinfo q; + + f->flags &= ~FmtPrec; /* ignored for %q %Q, so disable for %s %S in easy case */ + s = va_arg(f->args, char *); + if(!s) + return __fmtcpy(f, "<nil>", 5, 5); + + if(f->flush) + outlen = 0x7FFFFFFF; /* if we can flush, no output limit */ + else + outlen = (char*)f->stop - (char*)f->to; + + __quotesetup(s, -1, outlen, &q, f->flags&FmtSharp); + + if(!q.quoted) + return __fmtcpy(f, s, q.nrunesin, q.nbytesin); + return qstrfmt(s, &q, f); +} + +int +quotestrfmt(Fmt *f) +{ + return __quotestrfmt(0, f); +} + +void +quotefmtinstall(void) +{ + fmtinstall('q', quotestrfmt); +} + +int +__needsquotes(char *s, int *quotelenp) +{ + Quoteinfo q; + + __quotesetup(s, -1, 0x7FFFFFFF, &q, 0); + *quotelenp = q.nbytesout; + + return q.quoted; +} diff --git a/rc/fns.h b/rc/fns.h @@ -0,0 +1,61 @@ +void Abort(void); +void Closedir(int); +int Creat(char*); +int Dup(int, int); +int Dup1(int); +int Eintr(void); +int Executable(char*); +void Execute(word*, word*); +void Exit(char*); +int Globsize(char*); +int Isatty(int); +void Memcpy(char*, char*, long); +void Noerror(void); +int Opendir(char*); +long Read(int, char*, long); +int Readdir(int, char*); +long Seek(int, long, long); +void Trapinit(void); +void Unlink(char*); +void Updenv(void); +void Vinit(void); +int Waitfor(int, int); +long Write(int, char*, long); +int advance(void); +int back(int); +void cleanhere(char*); +void codefree(code*); +int compile(tree*); +char * list2str(word*); +char * _list2str(word*, int); +int count(word*); +void deglob(char*); +void dotrap(void); +void freenodes(void); +void freewords(word*); +void globlist(void); +int idchr(int); +void itoa(char*, long); +void kinit(void); +int match(char*, char*, int); +int matchfn(char*, char*); +void panic(char*, int); +void pathinit(void); +void poplist(void); +void popword(void); +void pprompt(void); +void pushlist(void); +void pushredir(int, int, int); +void pushword(char*); +void readhere(void); +void setstatus(char*); +void setvar(char*, word*); +void _setvar(char*, word*, int); +void skipnl(void); +void start(code*, int, var*); +int truestatus(void); +void usage(char*); +int wordchr(int); +void yyerror(char*); +int yylex(void); +int yyparse(void); diff --git a/rc/getflags.c b/rc/getflags.c @@ -0,0 +1,217 @@ +/*% cyntax -DTEST % && cc -DTEST -go # % + */ +#include "rc.h" +#include "getflags.h" +#include "fns.h" +char *flagset[]={"<flag>"}; +char **flag[NFLAG]; +char cmdline[NCMDLINE+1]; +char *cmdname; +static char *flagarg=""; +static void reverse(char**, char**); +static int scanflag(int, char*); +static void errn(char*, int); +static void errs(char*); +static void errc(int); +static int reason; +#define RESET 1 +#define FEWARGS 2 +#define FLAGSYN 3 +#define BADFLAG 4 +static int badflag; +int getflags(int argc, char *argv[], char *flags, int stop) +{ + char *s, *t; + int i, j, c, count; + flagarg=flags; + if(cmdname==0) cmdname=argv[0]; + s=cmdline; + for(i=0;i!=argc;i++){ + for(t=argv[i];*t;t++) + if(s!=&cmdline[NCMDLINE]) + *s++=*t; + if(i!=argc-1 && s!=&cmdline[NCMDLINE]) + *s++=' '; + } + *s='\0'; + i=1; + while(i!=argc){ + if(argv[i][0]!='-' || argv[i][1]=='\0'){ + if(stop) return argc; + i++; + continue; + } + s=argv[i]+1; + while(*s){ + c=*s++; + count=scanflag(c, flags); + if(count==-1) return -1; + if(flag[c]){ reason=RESET; badflag=c; return -1; } + if(count==0){ + flag[c]=flagset; + if(*s=='\0'){ + for(j=i+1;j<=argc;j++) + argv[j-1]=argv[j]; + --argc; + } + } + else{ + if(*s=='\0'){ + for(j=i+1;j<=argc;j++) + argv[j-1]=argv[j]; + --argc; + s=argv[i]; + } + if(argc-i<count){ + reason=FEWARGS; + badflag=c; + return -1; + } + reverse(argv+i, argv+argc); + reverse(argv+i, argv+argc-count); + reverse(argv+argc-count+1, argv+argc); + argc-=count; + flag[c]=argv+argc+1; + flag[c][0]=s; + s=""; + } + } + } + return argc; +} +static void reverse(char **p, char **q) +{ + char *t; + for(;p<q;p++,--q){ t=*p; *p=*q; *q=t; } +} +static int scanflag(int c, char *f) +{ + int fc, count; + if(0<=c && c<NFLAG) while(*f){ + if(*f==' '){ + f++; + continue; + } + fc=*f++; + if(*f==':'){ + f++; + if(*f<'0' || '9'<*f){ reason=FLAGSYN; return -1; } + count=0; + while('0'<=*f && *f<='9') count=count*10+*f++-'0'; + } + else + count=0; + if(*f=='['){ + do{ + f++; + if(*f=='\0'){ reason=FLAGSYN; return -1; } + }while(*f!=']'); + f++; + } + if(c==fc) return count; + } + reason=BADFLAG; + badflag=c; + return -1; +} +void usage(char *tail) +{ + char *s, *t, c; + int count, nflag=0; + switch(reason){ + case RESET: + errs("Flag -"); + errc(badflag); + errs(": set twice\n"); + break; + case FEWARGS: + errs("Flag -"); + errc(badflag); + errs(": too few arguments\n"); + break; + case FLAGSYN: + errs("Bad argument to getflags!\n"); + break; + case BADFLAG: + errs("Illegal flag -"); + errc(badflag); + errc('\n'); + break; + } + errs("Usage: "); + errs(cmdname); + for(s=flagarg;*s;){ + c=*s; + if(*s++==' ') continue; + if(*s==':'){ + s++; + count=0; + while('0'<=*s && *s<='9') count=count*10+*s++-'0'; + } + else count=0; + if(count==0){ + if(nflag==0) errs(" [-"); + nflag++; + errc(c); + } + if(*s=='['){ + s++; + while(*s!=']' && *s!='\0') s++; + if(*s==']') s++; + } + } + if(nflag) errs("]"); + for(s=flagarg;*s;){ + c=*s; + if(*s++==' ') continue; + if(*s==':'){ + s++; + count=0; + while('0'<=*s && *s<='9') count=count*10+*s++-'0'; + } + else count=0; + if(count!=0){ + errs(" [-"); + errc(c); + if(*s=='['){ + s++; + t=s; + while(*s!=']' && *s!='\0') s++; + errs(" "); + errn(t, s-t); + if(*s==']') s++; + } + else + while(count--) errs(" arg"); + errs("]"); + } + else if(*s=='['){ + s++; + while(*s!=']' && *s!='\0') s++; + if(*s==']') s++; + } + } + if(tail){ + errs(" "); + errs(tail); + } + errs("\n"); + Exit("bad flags"); +} +static void errn(char *s, int count) +{ + while(count){ errc(*s++); --count; } +} +static void errs(char *s) +{ + while(*s) errc(*s++); +} +#define NBUF 80 +static char buf[NBUF], *bufp=buf; +static void errc(int c){ + *bufp++=c; + if(bufp==&buf[NBUF] || c=='\n'){ + Write(2, buf, bufp-buf); + bufp=buf; + } +} diff --git a/rc/getflags.h b/rc/getflags.h @@ -0,0 +1,7 @@ +#define NFLAG 128 +#define NCMDLINE 512 +extern char **flag[NFLAG]; +extern char cmdline[NCMDLINE+1]; +extern char *cmdname; +extern char *flagset[]; +int getflags(int, char*[], char*, int); diff --git a/rc/glob.c b/rc/glob.c @@ -0,0 +1,211 @@ +#include "rc.h" +#include "exec.h" +#include "fns.h" +char *globname; +struct word *globv; +/* + * delete all the GLOB marks from s, in place + */ +void deglob(char *s) +{ + char *t=s; + do{ + if(*t==GLOB) t++; + *s++=*t; + }while(*t++); +} +int globcmp(const void *s, const void *t) +{ + return strcmp(*(char**)s, *(char**)t); +} +void globsort(word *left, word *right) +{ + char **list; + word *a; + int n=0; + for(a=left;a!=right;a=a->next) n++; + list=(char **)emalloc(n*sizeof(char *)); + for(a=left,n=0;a!=right;a=a->next,n++) list[n]=a->word; + qsort((char *)list, n, sizeof(char *), globcmp); + for(a=left,n=0;a!=right;a=a->next,n++) a->word=list[n]; + efree((char *)list); +} +/* + * Push names prefixed by globname and suffixed by a match of p onto the astack. + * namep points to the end of the prefix in globname. + */ +void globdir(char *p, char *namep) +{ + char *t, *newp; + int f; + /* scan the pattern looking for a component with a metacharacter in it */ + if(*p=='\0'){ + globv=newword(globname, globv); + return; + } + t=namep; + newp=p; + while(*newp){ + if(*newp==GLOB) + break; + *t=*newp++; + if(*t++=='/'){ + namep=t; + p=newp; + } + } + /* If we ran out of pattern, append the name if accessible */ + if(*newp=='\0'){ + *t='\0'; + if(access(globname, 0)==0) + globv=newword(globname, globv); + return; + } + /* read the directory and recur for any entry that matches */ + *namep='\0'; + if((f=Opendir(globname[0]?globname:"."))<0) return; + while(*newp!='/' && *newp!='\0') newp++; + while(Readdir(f, namep)){ + if(matchfn(namep, p)){ + for(t=namep;*t;t++); + globdir(newp, t); + } + } + Closedir(f); +} +/* + * Push all file names matched by p on the current thread's stack. + * If there are no matches, the list consists of p. + */ +void glob(char *p) +{ + word *svglobv=globv; + int globlen=Globsize(p); + if(!globlen){ + deglob(p); + globv=newword(p, globv); + return; + } + globname=emalloc(globlen); + globname[0]='\0'; + globdir(p, globname); + efree(globname); + if(svglobv==globv){ + deglob(p); + globv=newword(p, globv); + } + else + globsort(globv, svglobv); +} +/* + * Do p and q point at equal utf codes + */ +int equtf(char *p, char *q){ + if(*p!=*q) return 0; + if(twobyte(*p)) return p[1]==q[1]; + if(threebyte(*p)){ + if(p[1]!=q[1]) return 0; + if(p[1]=='\0') return 1; /* broken code at end of string! */ + return p[2]==q[2]; + } + return 1; +} +/* + * Return a pointer to the next utf code in the string, + * not jumping past nuls in broken utf codes! + */ +char *nextutf(char *p){ + if(twobyte(*p)) return p[1]=='\0'?p+1:p+2; + if(threebyte(*p)) return p[1]=='\0'?p+1:p[2]=='\0'?p+2:p+3; + return p+1; +} +/* + * Convert the utf code at *p to a unicode value + */ +int unicode(char *p){ + int u=*p&0xff; + if(twobyte(u)) return ((u&0x1f)<<6)|(p[1]&0x3f); + if(threebyte(u)) return (u<<12)|((p[1]&0x3f)<<6)|(p[2]&0x3f); + return u; +} +/* + * Does the string s match the pattern p + * . and .. are only matched by patterns starting with . + * * matches any sequence of characters + * ? matches any single character + * [...] matches the enclosed list of characters + */ +int matchfn(char *s, char *p) +{ + if(s[0]=='.' && (s[1]=='\0' || s[1]=='.' && s[2]=='\0') && p[0]!='.') + return 0; + return match(s, p, '/'); +} +int match(char *s, char *p, int stop) +{ + int compl, hit, lo, hi, t, c; + for(;*p!=stop && *p!='\0';s=nextutf(s),p=nextutf(p)){ + if(*p!=GLOB){ + if(!equtf(p, s)) return 0; + } + else switch(*++p){ + case GLOB: + if(*s!=GLOB) return 0; + break; + case '*': + for(;;){ + if(match(s, nextutf(p), stop)) return 1; + if(!*s) break; + s=nextutf(s); + } + return 0; + case '?': + if(*s=='\0') return 0; + break; + case '[': + if(*s=='\0') return 0; + c=unicode(s); + p++; + compl=*p=='~'; + if(compl) p++; + hit=0; + while(*p!=']'){ + if(*p=='\0') return 0; /* syntax error */ + lo=unicode(p); + p=nextutf(p); + if(*p!='-') hi=lo; + else{ + p++; + if(*p=='\0') return 0; /* syntax error */ + hi=unicode(p); + p=nextutf(p); + if(hi<lo){ t=lo; lo=hi; hi=t; } + } + if(lo<=c && c<=hi) hit=1; + } + if(compl) hit=!hit; + if(!hit) return 0; + break; + } + } + return *s=='\0'; +} +void globlist1(word *gl) +{ + if(gl){ + globlist1(gl->next); + glob(gl->word); + } +} +void globlist(void){ + word *a; + globv=0; + globlist1(runq->argv->words); + poplist(); + pushlist(); + if(globv){ + for(a=globv;a->next;a=a->next); + a->next=runq->argv->words; + runq->argv->words=globv; + } +} diff --git a/rc/havefork.c b/rc/havefork.c @@ -0,0 +1,222 @@ +#include <u.h> +#include <signal.h> +#include "rc.h" +#include "getflags.h" +#include "exec.h" +#include "io.h" +#include "fns.h" + +int havefork = 1; + +void +Xasync(void) +{ + int null = open("/dev/null", 0); + int pid; + int tcpgrp, pgrp; + char npid[10]; + + if(null<0){ + Xerror("Can't open /dev/null\n"); + return; + } + switch(pid = rfork(RFFDG|RFPROC|RFNOTEG)){ + case -1: + close(null); + Xerror("try again"); + break; + case 0: + /* + * Should make reads of tty fail, writes succeed. + */ + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + + pushredir(ROPEN, null, 0); + start(runq->code, runq->pc+1, runq->local); + runq->ret = 0; + break; + default: + close(null); + runq->pc = runq->code[runq->pc].i; + inttoascii(npid, pid); + setvar("apid", newword(npid, (word *)0)); + break; + } +} + +void +Xpipe(void) +{ + struct thread *p = runq; + int pc = p->pc, forkid; + int lfd = p->code[pc++].i; + int rfd = p->code[pc++].i; + int pfd[2]; + if(pipe(pfd)<0){ + Xerror("can't get pipe"); + return; + } + switch(forkid = fork()){ + case -1: + Xerror("try again"); + break; + case 0: + start(p->code, pc+2, runq->local); + runq->ret = 0; + close(pfd[PRD]); + pushredir(ROPEN, pfd[PWR], lfd); + break; + default: + start(p->code, p->code[pc].i, runq->local); + close(pfd[PWR]); + pushredir(ROPEN, pfd[PRD], rfd); + p->pc = p->code[pc+1].i; + p->pid = forkid; + break; + } +} + +/* + * Who should wait for the exit from the fork? + */ +void +Xbackq(void) +{ + char wd[8193]; + int c; + char *s, *ewd=&wd[8192], *stop; + struct io *f; + var *ifs = vlook("ifs"); + word *v, *nextv; + int pfd[2]; + int pid; + stop = ifs->val?ifs->val->word:""; + if(pipe(pfd)<0){ + Xerror("can't make pipe"); + return; + } + switch(pid = fork()){ + case -1: + Xerror("try again"); + close(pfd[PRD]); + close(pfd[PWR]); + return; + case 0: + close(pfd[PRD]); + start(runq->code, runq->pc+1, runq->local); + pushredir(ROPEN, pfd[PWR], 1); + return; + default: + close(pfd[PWR]); + f = openfd(pfd[PRD]); + s = wd; + v = 0; + while((c = rchr(f))!=EOF){ + if(strchr(stop, c) || s==ewd){ + if(s!=wd){ + *s='\0'; + v = newword(wd, v); + s = wd; + } + } + else *s++=c; + } + if(s!=wd){ + *s='\0'; + v = newword(wd, v); + } + closeio(f); + Waitfor(pid, 0); + /* v points to reversed arglist -- reverse it onto argv */ + while(v){ + nextv = v->next; + v->next = runq->argv->words; + runq->argv->words = v; + v = nextv; + } + runq->pc = runq->code[runq->pc].i; + return; + } +} + +void +Xpipefd(void) +{ + struct thread *p = runq; + int pc = p->pc; + char name[40]; + int pfd[2]; + int sidefd, mainfd; + if(pipe(pfd)<0){ + Xerror("can't get pipe"); + return; + } + if(p->code[pc].i==READ){ + sidefd = pfd[PWR]; + mainfd = pfd[PRD]; + } + else{ + sidefd = pfd[PRD]; + mainfd = pfd[PWR]; + } + switch(fork()){ + case -1: + Xerror("try again"); + break; + case 0: + start(p->code, pc+2, runq->local); + close(mainfd); + pushredir(ROPEN, sidefd, p->code[pc].i==READ?1:0); + runq->ret = 0; + break; + default: + close(sidefd); + pushredir(ROPEN, mainfd, mainfd); /* isn't this a noop? */ + strcpy(name, Fdprefix); + inttoascii(name+strlen(name), mainfd); + pushword(name); + p->pc = p->code[pc+1].i; + break; + } +} + +void +Xsubshell(void) +{ + int pid; + switch(pid = fork()){ + case -1: + Xerror("try again"); + break; + case 0: + start(runq->code, runq->pc+1, runq->local); + runq->ret = 0; + break; + default: + Waitfor(pid, 1); + runq->pc = runq->code[runq->pc].i; + break; + } +} + +int +execforkexec(void) +{ + int pid; + int n; + char buf[ERRMAX]; + + switch(pid = fork()){ + case -1: + return -1; + case 0: + pushword("exec"); + execexec(); + strcpy(buf, "can't exec: "); + n = strlen(buf); + errstr(buf+n, ERRMAX-n); + Exit(buf); + } + return pid; +} diff --git a/rc/haventfork.c b/rc/haventfork.c @@ -0,0 +1,211 @@ +#include "rc.h" +#include "getflags.h" +#include "exec.h" +#include "io.h" +#include "fns.h" + +int havefork = 0; + +static char ** +rcargv(char *s) +{ + int argc; + char **argv; + word *p; + + p = vlook("*")->val; + argv = malloc((count(p)+6)*sizeof(char*)); + argc = 0; + argv[argc++] = argv0; + if(flag['e']) + argv[argc++] = "-Se"; + else + argv[argc++] = "-S"; + argv[argc++] = "-c"; + argv[argc++] = s; + for(p = vlook("*")->val; p; p = p->next) + argv[argc++] = p->word; + argv[argc] = 0; + return argv; +} + +void +Xasync(void) +{ + uint pid; + char buf[20], **argv; + + Updenv(); + + argv = rcargv(runq->code[runq->pc].s); + pid = ForkExecute(argv0, argv, -1, 1, 2); + free(argv); + + if(pid == 0) { + Xerror("proc failed"); + return; + } + + runq->pc++; + sprint(buf, "%d", pid); + setvar("apid", newword(buf, (word *)0)); +} + +void +Xbackq(void) +{ + char wd[8193], **argv; + int c; + char *s, *ewd=&wd[8192], *stop; + struct io *f; + var *ifs = vlook("ifs"); + word *v, *nextv; + int pfd[2]; + int pid; + + stop = ifs->val?ifs->val->word:""; + if(pipe(pfd)<0){ + Xerror("can't make pipe"); + return; + } + + Updenv(); + + argv = rcargv(runq->code[runq->pc].s); + pid = ForkExecute(argv0, argv, -1, pfd[1], 2); + free(argv); + + close(pfd[1]); + + if(pid == 0) { + Xerror("proc failed"); + close(pfd[0]); + return; + } + + f = openfd(pfd[0]); + s = wd; + v = 0; + while((c=rchr(f))!=EOF){ + if(strchr(stop, c) || s==ewd){ + if(s!=wd){ + *s='\0'; + v=newword(wd, v); + s=wd; + } + } + else *s++=c; + } + if(s!=wd){ + *s='\0'; + v=newword(wd, v); + } + closeio(f); + Waitfor(pid, 1); + /* v points to reversed arglist -- reverse it onto argv */ + while(v){ + nextv=v->next; + v->next=runq->argv->words; + runq->argv->words=v; + v=nextv; + } + runq->pc++; +} + +void +Xpipe(void) +{ + thread *p=runq; + int pc=p->pc, pid; + int rfd=p->code[pc+1].i; + int pfd[2]; + char **argv; + + if(pipe(pfd)<0){ + Xerror1("can't get pipe"); + return; + } + + Updenv(); + + argv = rcargv(runq->code[pc+2].s); + pid = ForkExecute(argv0, argv, 0, pfd[1], 2); + free(argv); + close(pfd[1]); + + if(pid == 0) { + Xerror("proc failed"); + close(pfd[0]); + return; + } + + start(p->code, pc+4, runq->local); + pushredir(ROPEN, pfd[0], rfd); + p->pc=p->code[pc+3].i; + p->pid=pid; +} + +void +Xpipefd(void) +{ + Abort(); +} + +void +Xsubshell(void) +{ + char **argv; + int pid; + + Updenv(); + + argv = rcargv(runq->code[runq->pc].s); + pid = ForkExecute(argv0, argv, -1, 1, 2); + free(argv); + + if(pid < 0) { + Xerror("proc failed"); + return; + } + + Waitfor(pid, 1); + runq->pc++; +} + +/* + * start a process running the cmd on the stack and return its pid. + */ +int +execforkexec(void) +{ + char **argv; + char file[1024]; + int nc; + word *path; + int pid; + + if(runq->argv->words==0) + return -1; + argv = mkargv(runq->argv->words); + + for(path = searchpath(runq->argv->words->word);path;path = path->next){ + nc = strlen(path->word); + if(nc<sizeof(file)){ + strcpy(file, path->word); + if(file[0]){ + strcat(file, "/"); + nc++; + } + if(nc+strlen(argv[1])<sizeof(file)){ + strcat(file, argv[1]); + pid = ForkExecute(file, argv+1, mapfd(0), mapfd(1), mapfd(2)); + if(pid >= 0){ + free(argv); + return pid; + } + } + } + } + free(argv); + return -1; +} diff --git a/rc/here.c b/rc/here.c @@ -0,0 +1,131 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +struct here *here, **ehere; +int ser=0; +char tmp[]="/tmp/here0000.0000"; +char hex[]="0123456789abcdef"; +void psubst(io*, char*); +void pstrs(io*, word*); +void hexnum(char *p, int n) +{ + *p++=hex[(n>>12)&0xF]; + *p++=hex[(n>>8)&0xF]; + *p++=hex[(n>>4)&0xF]; + *p=hex[n&0xF]; +} +tree *heredoc(tree *tag) +{ + struct here *h=new(struct here); + if(tag->type!=WORD) yyerror("Bad here tag"); + h->next=0; + if(here) + *ehere=h; + else + here=h; + ehere=&h->next; + h->tag=tag; + hexnum(&tmp[9], getpid()); + hexnum(&tmp[14], ser++); + h->name=strdup(tmp); + return token(tmp, WORD); +} +/* + * bug: lines longer than NLINE get split -- this can cause spurious + * missubstitution, or a misrecognized EOF marker. + */ +#define NLINE 4096 +void readhere(void){ + struct here *h, *nexth; + io *f; + char *s, *tag; + int c, subst; + char line[NLINE+1]; + for(h=here;h;h=nexth){ + subst=!h->tag->quoted; + tag=h->tag->str; + c=Creat(h->name); + if(c<0) yyerror("can't create here document"); + f=openfd(c); + s=line; + pprompt(); + while((c=rchr(runq->cmdfd))!=EOF){ + if(c=='\n' || s==&line[NLINE]){ + *s='\0'; + if(strcmp(line, tag)==0) break; + if(subst) psubst(f, line); + else pstr(f, line); + s=line; + if(c=='\n'){ + pprompt(); + pchr(f, c); + } + else *s++=c; + } + else *s++=c; + } + flush(f); + closeio(f); + cleanhere(h->name); + nexth=h->next; + efree((char *)h); + } + here=0; + doprompt=1; +} +void psubst(io *f, char *s) +{ + char *t, *u; + int savec, n; + word *star; + while(*s){ + if(*s!='$'){ + if(0xa0<=(*s&0xff) && (*s&0xff)<=0xf5){ + pchr(f, *s++); + if(*s=='\0') break; + } + else if(0xf6<=(*s&0xff) && (*s&0xff)<=0xf7){ + pchr(f, *s++); + if(*s=='\0') break; + pchr(f, *s++); + if(*s=='\0') break; + } + pchr(f, *s++); + } + else{ + t=++s; + if(*t=='$') pchr(f, *t++); + else{ + while(*t && idchr(*t)) t++; + savec=*t; + *t='\0'; + n=0; + for(u=s;*u && '0'<=*u && *u<='9';u++) n=n*10+*u-'0'; + if(n && *u=='\0'){ + star=vlook("*")->val; + if(star && 1<=n && n<=count(star)){ + while(--n) star=star->next; + pstr(f, star->word); + } + } + else + pstrs(f, vlook(s)->val); + *t=savec; + if(savec=='^') t++; + } + s=t; + } + } +} +void pstrs(io *f, word *a) +{ + if(a){ + while(a->next && a->next->word){ + pstr(f, a->word); + pchr(f, ' '); + a=a->next; + } + pstr(f, a->word); + } +} diff --git a/rc/io.c b/rc/io.c @@ -0,0 +1,182 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +int pfmtnest=0; +void pfmt(io *f, char *fmt, ...){ + va_list ap; + char err[ERRMAX]; + va_start(ap, fmt); + pfmtnest++; + for(;*fmt;fmt++) + if(*fmt!='%') pchr(f, *fmt); + else switch(*++fmt){ + case '\0': va_end(ap); return; + case 'c': pchr(f, va_arg(ap, int)); break; + case 'd': pdec(f, va_arg(ap, int)); break; + case 'o': poct(f, va_arg(ap, unsigned)); break; + case 'p': phex(f, (long)va_arg(ap, char *)); break; /*unportable*/ + case 'Q': pquo(f, va_arg(ap, char *)); break; + case 'q': pwrd(f, va_arg(ap, char *)); break; + case 'r': errstr(err, sizeof err); pstr(f, err); break; + case 's': pstr(f, va_arg(ap, char *)); break; + case 't': pcmd(f, va_arg(ap, struct tree *)); break; + case 'v': pval(f, va_arg(ap, struct word *)); break; + default: pchr(f, *fmt); break; + } + va_end(ap); + if(--pfmtnest==0) flush(f); +} +void pchr(io *b, int c) +{ + if(b->bufp==b->ebuf) fullbuf(b, c); + else *b->bufp++=c; +} +int rchr(io *b) +{ + if(b->bufp==b->ebuf) return emptybuf(b); + return *b->bufp++ & 0xFF; +} + +void pquo(io *f, char *s) +{ + pchr(f, '\''); + for(;*s;s++) + if(*s=='\'') pfmt(f, "''"); + else pchr(f, *s); + pchr(f, '\''); +} +void pwrd(io *f, char *s) +{ + char *t; + for(t=s;*t;t++) if(!wordchr(*t)) break; + if(t==s || *t) pquo(f, s); + else pstr(f, s); +} +void phex(io *f, long p) +{ + int n; + for(n=28;n>=0;n-=4) pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]); +} +void pstr(io *f, char *s) +{ + if(s==0) s="(null)"; + while(*s) pchr(f, *s++); +} +void pdec(io *f, long n) +{ + if(n<0){ + n=-n; + if(n>=0){ + pchr(f, '-'); + pdec(f, n); + return; + } + /* n is two's complement minimum integer */ + n=1-n; + pchr(f, '-'); + pdec(f, n/10); + pchr(f, n%10+'1'); + return; + } + if(n>9) pdec(f, n/10); + pchr(f, n%10+'0'); +} +void poct(io *f, ulong n) +{ + if(n>7) poct(f, n>>3); + pchr(f, (n&7)+'0'); +} +void pval(io *f, word *a) +{ + if(a){ + while(a->next && a->next->word){ + pwrd(f, a->word); + pchr(f, ' '); + a=a->next; + } + pwrd(f, a->word); + } +} +int fullbuf(io *f, int c) +{ + flush(f); + return *f->bufp++=c; +} +void flush(io *f) +{ + int n; + char *s; + if(f->strp){ + n=f->ebuf-f->strp; + f->strp=realloc(f->strp, n+101); + if(f->strp==0) panic("Can't realloc %d bytes in flush!", n+101); + f->bufp=f->strp+n; + f->ebuf=f->bufp+100; + for(s=f->bufp;s<=f->ebuf;s++) *s='\0'; + } + else{ + n=f->bufp-f->buf; + if(n && Write(f->fd, f->buf, n) < 0){ + Write(3, "Write error\n", 12); + if(ntrap) dotrap(); + } + f->bufp=f->buf; + f->ebuf=f->buf+NBUF; + } +} +io *openfd(int fd){ + io *f; + f=new(struct io); + f->fd=fd; + f->bufp=f->ebuf=f->buf; + f->strp=0; + return f; +} +io *openstr(void){ + io *f=new(struct io); + char *s; + f->fd=-1; + f->bufp=f->strp=emalloc(101); + f->ebuf=f->bufp+100; + for(s=f->bufp;s<=f->ebuf;s++) *s='\0'; + return f; +} +/* + * Open a corebuffer to read. EOF occurs after reading len + * characters from buf. + */ +io *opencore(char *s, int len) +{ + io *f=new(struct io); + char *buf=emalloc(len); + f->fd= -1 /*open("/dev/null", 0)*/; + f->bufp=f->strp=buf; + f->ebuf=buf+len; + Memcpy(buf, s, len); + return f; +} +/* +void rewind(io *io) +{ + if(io->fd==-1) io->bufp=io->strp; + else{ + io->bufp=io->ebuf=io->buf; + Seek(io->fd, 0L, 0); + } +} +*/ +void closeio(io *io) +{ + if(io->fd>=0) close(io->fd); + if(io->strp) efree(io->strp); + efree((char *)io); +} +int emptybuf(io *f) +{ + int n; + if(f->fd==-1 || (n=Read(f->fd, f->buf, NBUF))<=0) return EOF; + f->bufp=f->buf; + f->ebuf=f->buf+n; + return *f->bufp++&0xff; +} diff --git a/rc/io.h b/rc/io.h @@ -0,0 +1,30 @@ +/* + * on Mac OS X, err is something else, + * and assigning to it causes a bus error. + * what a crappy linker. + */ +#define err rc_err +#define EOF (-1) +#define NBUF 512 +struct io{ + int fd; + char *bufp, *ebuf, *strp, buf[NBUF]; +}; +io *err; +io *openfd(int), *openstr(void), *opencore(char *, int); +int emptybuf(io*); +void pchr(io*, int); +int rchr(io*); +void closeio(io*); +void flush(io*); +int fullbuf(io*, int); +void pdec(io*, long); +void poct(io*, ulong); +void phex(io*, long); +void pquo(io*, char*); +void pwrd(io*, char*); +void pstr(io*, char*); +void pcmd(io*, tree*); +void pval(io*, word*); +void pfnc(io*, thread*); +void pfmt(io*, char*, ...); diff --git a/rc/lex.c b/rc/lex.c @@ -0,0 +1,324 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "getflags.h" +#include "fns.h" +int getnext(void); +int wordchr(int c) +{ + return !strchr("\n \t#;&|^$=`'{}()<>", c) && c!=EOF; +} +int idchr(int c) +{ + /* + * Formerly: + * return 'a'<=c && c<='z' || 'A'<=c && c<='Z' || '0'<=c && c<='9' + * || c=='_' || c=='*'; + */ + return c>' ' && !strchr("!\"#$%&'()+,-./:;<=>?@[\\]^`{|}~", c); +} +int future=EOF; +int doprompt=1; +int inquote; +/* + * Look ahead in the input stream + */ +int nextc(void){ + if(future==EOF) future=getnext(); + return future; +} +/* + * Consume the lookahead character. + */ +int advance(void){ + int c=nextc(); + lastc=future; + future=EOF; + return c; +} +/* + * read a character from the input stream + */ +int getnext(void){ + register int c; + static int peekc=EOF; + if(peekc!=EOF){ + c=peekc; + peekc=EOF; + return c; + } + if(runq->eof) return EOF; + if(doprompt) pprompt(); + c=rchr(runq->cmdfd); + if(!inquote && c=='\\'){ + c=rchr(runq->cmdfd); + if(c=='\n'){ + doprompt=1; + c=' '; + } + else{ + peekc=c; + c='\\'; + } + } + doprompt=doprompt || c=='\n' || c==EOF; + if(c==EOF) runq->eof++; + else if(flag['V'] || ndot>=2 && flag['v']) pchr(err, c); + return c; +} +void pprompt(void){ + var *prompt; + if(runq->iflag){ + pstr(err, promptstr); + flush(err); + prompt=vlook("prompt"); + if(prompt->val && prompt->val->next) + promptstr=prompt->val->next->word; + else + promptstr="\t"; + } + runq->lineno++; + doprompt=0; +} +void skipwhite(void){ + int c; + for(;;){ + c=nextc(); + if(c=='#'){ /* Why did this used to be if(!inquote && c=='#') ?? */ + for(;;){ + c=nextc(); + if(c=='\n' || c==EOF) break; + advance(); + } + } + if(c==' ' || c=='\t') advance(); + else return; + } +} +void skipnl(void){ + register int c; + for(;;){ + skipwhite(); + c=nextc(); + if(c!='\n') return; + advance(); + } +} +int nextis(int c){ + if(nextc()==c){ + advance(); + return 1; + } + return 0; +} +char *addtok(char *p, int val){ + if(p==0) return 0; + if(p==&tok[NTOK]){ + *p=0; + yyerror("token buffer too short"); + return 0; + } + *p++=val; + return p; +} +char *addutf(char *p, int c){ + p=addtok(p, c); + if(twobyte(c)) /* 2-byte escape */ + return addtok(p, advance()); + if(threebyte(c)){ /* 3-byte escape */ + p=addtok(p, advance()); + return addtok(p, advance()); + } + return p; +} +int lastdol; /* was the last token read '$' or '$#' or '"'? */ +int lastword; /* was the last token read a word or compound word terminator? */ +int yylex(void){ + register int c, d=nextc(); + register char *w=tok; + register struct tree *t; + yylval.tree=0; + /* + * Embarassing sneakiness: if the last token read was a quoted or unquoted + * WORD then we alter the meaning of what follows. If the next character + * is `(', we return SUB (a subscript paren) and consume the `('. Otherwise, + * if the next character is the first character of a simple or compound word, + * we insert a `^' before it. + */ + if(lastword){ + lastword=0; + if(d=='('){ + advance(); + strcpy(tok, "( [SUB]"); + return SUB; + } + if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){ + strcpy(tok, "^"); + return '^'; + } + } + inquote=0; + skipwhite(); + switch(c=advance()){ + case EOF: + lastdol=0; + strcpy(tok, "EOF"); + return EOF; + case '$': + lastdol=1; + if(nextis('#')){ + strcpy(tok, "$#"); + return COUNT; + } + if(nextis('"')){ + strcpy(tok, "$\""); + return '"'; + } + strcpy(tok, "$"); + return '$'; + case '&': + lastdol=0; + if(nextis('&')){ + skipnl(); + strcpy(tok, "&&"); + return ANDAND; + } + strcpy(tok, "&"); + return '&'; + case '|': + lastdol=0; + if(nextis(c)){ + skipnl(); + strcpy(tok, "||"); + return OROR; + } + case '<': + case '>': + lastdol=0; + /* + * funny redirection tokens: + * redir: arrow | arrow '[' fd ']' + * arrow: '<' | '<<' | '>' | '>>' | '|' + * fd: digit | digit '=' | digit '=' digit + * digit: '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' + * some possibilities are nonsensical and get a message. + */ + *w++=c; + t=newtree(); + switch(c){ + case '|': + t->type=PIPE; + t->fd0=1; + t->fd1=0; + break; + case '>': + t->type=REDIR; + if(nextis(c)){ + t->rtype=APPEND; + *w++=c; + } + else t->rtype=WRITE; + t->fd0=1; + break; + case '<': + t->type=REDIR; + if(nextis(c)){ + t->rtype=HERE; + *w++=c; + } + else t->rtype=READ; + t->fd0=0; + break; + } + if(nextis('[')){ + *w++='['; + c=advance(); + if(c<'0' || '9'<c){ + RedirErr: + *w++ = c; + *w=0; + yyerror(t->type==PIPE?"pipe syntax" + :"redirection syntax"); + return EOF; + } + t->fd0=0; + do{ + t->fd0=t->fd0*10+c-'0'; + *w++=c; + c=advance(); + }while('0'<=c && c<='9'); + if(c=='='){ + *w++='='; + if(t->type==REDIR) t->type=DUP; + c=advance(); + if('0'<=c && c<='9'){ + t->rtype=DUPFD; + t->fd1=t->fd0; + t->fd0=0; + do{ + t->fd0=t->fd0*10+c-'0'; + *w++=c; + c=advance(); + }while('0'<=c && c<='9'); + } + else{ + if(t->type==PIPE) goto RedirErr; + t->rtype=CLOSE; + } + } + *w=0; + if(c!=']' + || t->type==DUP && (t->rtype==HERE || t->rtype==APPEND)) + goto RedirErr; + *w++=']'; + } + *w='\0'; + yylval.tree=t; + if(t->type==PIPE) skipnl(); + return t->type; + case '\'': + lastdol=0; + lastword=1; + inquote=1; + for(;;){ + c=advance(); + if(c==EOF) break; + if(c=='\''){ + if(nextc()!='\'') + break; + advance(); + } + w=addutf(w, c); + } + if(w!=0) *w='\0'; + t=token(tok, WORD); + t->quoted=1; + yylval.tree=t; + return t->type; + } + if(!wordchr(c)){ + lastdol=0; + tok[0]=c; + tok[1]='\0'; + return c; + } + for(;;){ + /* next line should have (char)c==GLOB, but ken's compiler is broken */ + if(c=='*' || c=='[' || c=='?' || c==(unsigned char)GLOB) + w=addtok(w, GLOB); + w=addutf(w, c); + c=nextc(); + if(lastdol?!idchr(c):!wordchr(c)) break; + advance(); + } + + lastword=1; + lastdol=0; + if(w!=0) *w='\0'; + t=klook(tok); + if(t->type!=WORD) lastword=0; + t->quoted=0; + yylval.tree=t; + return t->type; +} + diff --git a/rc/pcmd.c b/rc/pcmd.c @@ -0,0 +1,108 @@ +#include "rc.h" +#include "io.h" +#include "fns.h" +char nl='\n'; /* change to semicolon for bourne-proofing */ +#define c0 t->child[0] +#define c1 t->child[1] +#define c2 t->child[2] +void pdeglob(io *f, char *s) +{ + while(*s){ + if(*s==GLOB) s++; + pchr(f, *s++); + } +} +void pcmd(io *f, tree *t) +{ + if(t==0) return; + switch(t->type){ + default: pfmt(f, "bad %d %p %p %p", t->type, c0, c1, c2); break; + case '$': pfmt(f, "$%t", c0); break; + case '"': pfmt(f, "$\"%t", c0); break; + case '&': pfmt(f, "%t&", c0); break; + case '^': pfmt(f, "%t^%t", c0, c1); break; + case '`': pfmt(f, "`%t", c0); break; + case ANDAND: pfmt(f, "%t && %t", c0, c1); break; + case BANG: pfmt(f, "! %t", c0); break; + case BRACE: pfmt(f, "{%t}", c0); break; + case COUNT: pfmt(f, "$#%t", c0); break; + case FN: pfmt(f, "fn %t %t", c0, c1); break; + case IF: pfmt(f, "if%t%t", c0, c1); break; + case NOT: pfmt(f, "if not %t", c0); break; + case OROR: pfmt(f, "%t || %t", c0, c1); break; + case PCMD: + case PAREN: pfmt(f, "(%t)", c0); break; + case SUB: pfmt(f, "$%t(%t)", c0, c1); break; + case SIMPLE: pfmt(f, "%t", c0); break; + case SUBSHELL: pfmt(f, "@ %t", c0); break; + case SWITCH: pfmt(f, "switch %t %t", c0, c1); break; + case TWIDDLE: pfmt(f, "~ %t %t", c0, c1); break; + case WHILE: pfmt(f, "while %t%t", c0, c1); break; + case ARGLIST: + if(c0==0) + pfmt(f, "%t", c1); + else if(c1==0) + pfmt(f, "%t", c0); + else + pfmt(f, "%t %t", c0, c1); + break; + case ';': + if(c0){ + if(c1) pfmt(f, "%t%c%t", c0, nl, c1); + else pfmt(f, "%t", c0); + } + else pfmt(f, "%t", c1); + break; + case WORDS: + if(c0) pfmt(f, "%t ", c0); + pfmt(f, "%t", c1); + break; + case FOR: + pfmt(f, "for(%t", c0); + if(c1) pfmt(f, " in %t", c1); + pfmt(f, ")%t", c2); + break; + case WORD: + if(t->quoted) pfmt(f, "%Q", t->str); + else pdeglob(f, t->str); + break; + case DUP: + if(t->rtype==DUPFD) + pfmt(f, ">[%d=%d]", t->fd1, t->fd0); /* yes, fd1, then fd0; read lex.c */ + else + pfmt(f, ">[%d=]", t->fd0); + pfmt(f, "%t", c1); + break; + case PIPEFD: + case REDIR: + switch(t->rtype){ + case HERE: + pchr(f, '<'); + case READ: + pchr(f, '<'); + if(t->fd0!=0) pfmt(f, "[%d]", t->fd0); + break; + case APPEND: + pchr(f, '>'); + case WRITE: + pchr(f, '>'); + if(t->fd0!=1) pfmt(f, "[%d]", t->fd0); + break; + } + pfmt(f, "%t", c0); + if(c1) pfmt(f, " %t", c1); + break; + case '=': + pfmt(f, "%t=%t", c0, c1); + if(c2) pfmt(f, " %t", c2); + break; + case PIPE: + pfmt(f, "%t|", c0); + if(t->fd1==0){ + if(t->fd0!=1) pfmt(f, "[%d]", t->fd0); + } + else pfmt(f, "[%d=%d]", t->fd0, t->fd1); + pfmt(f, "%t", c1); + break; + } +} diff --git a/rc/pfnc.c b/rc/pfnc.c @@ -0,0 +1,67 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +struct{ + void (*f)(void); + char *name; +}fname[]={ + Xappend, "Xappend", + Xasync, "Xasync", + Xbang, "Xbang", + Xclose, "Xclose", + Xdup, "Xdup", + Xeflag, "Xeflag", + Xexit, "Xexit", + Xfalse, "Xfalse", + Xifnot, "Xifnot", + Xjump, "Xjump", + Xmark, "Xmark", + Xpopm, "Xpopm", + Xread, "Xread", + Xreturn, "Xreturn", + Xtrue, "Xtrue", + Xif, "Xif", + Xwastrue, "Xwastrue", + Xword, "Xword", + Xwrite, "Xwrite", + Xmatch, "Xmatch", + Xcase, "Xcase", + Xconc, "Xconc", + Xassign, "Xassign", + Xdol, "Xdol", + Xcount, "Xcount", + Xlocal, "Xlocal", + Xunlocal, "Xunlocal", + Xfn, "Xfn", + Xdelfn, "Xdelfn", + Xpipe, "Xpipe", + Xpipewait, "Xpipewait", + Xrdcmds, "Xrdcmds", + (void (*)(void))Xerror, "Xerror", + Xbackq, "Xbackq", + Xpipefd, "Xpipefd", + Xsubshell, "Xsubshell", + Xdelhere, "Xdelhere", + Xfor, "Xfor", + Xglob, "Xglob", + Xrdfn, "Xrdfn", + Xsimple, "Xsimple", + Xrdfn, "Xrdfn", + Xqdol, "Xqdol", +0}; +void pfnc(io *fd, thread *t) +{ + int i; + void (*fn)(void)=t->code[t->pc].f; + list *a; + pfmt(fd, "pid %d cycle %p %d ", getpid(), t->code, t->pc); + for(i=0;fname[i].f;i++) if(fname[i].f==fn){ + pstr(fd, fname[i].name); + break; + } + if(!fname[i].f) pfmt(fd, "%p", fn); + for(a=t->argv;a;a=a->next) pfmt(fd, " (%v)", a->words); + pchr(fd, '\n'); + flush(fd); +} diff --git a/rc/plan9ish.c b/rc/plan9ish.c @@ -0,0 +1,549 @@ +/* + * Plan 9 versions of system-specific functions + * By convention, exported routines herein have names beginning with an + * upper case letter. + */ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +#include "getflags.h" +char *Signame[]={ + "sigexit", "sighup", "sigint", "sigquit", + "sigalrm", "sigkill", "sigfpe", "sigterm", + 0 +}; +char *syssigname[]={ + "exit", /* can't happen */ + "hangup", + "interrupt", + "quit", /* can't happen */ + "alarm", + "kill", + "sys: fp: ", + "term", + 0 +}; +char* +Rcmain(void) +{ + static char Rcmain[] = PREFIX"/lib/rcmain"; + char *rcmain = getenv("RCMAIN"); + return rcmain ? rcmain : Rcmain; +} + +char Fdprefix[]="/dev/fd/"; +void execfinit(void); +void execbind(void); +void execmount(void); +void execulimit(void); +void execumask(void); +void execrfork(void); +builtin Builtin[]={ + "cd", execcd, + "whatis", execwhatis, + "eval", execeval, + "exec", execexec, /* but with popword first */ + "exit", execexit, + "shift", execshift, + "wait", execwait, + ".", execdot, + "finit", execfinit, + "flag", execflag, + "ulimit", execulimit, + "umask", execumask, + "rfork", execrfork, + 0 +}; + +void +execrfork(void) +{ + int arg; + char *s; + + switch(count(runq->argv->words)){ + case 1: + arg = RFENVG|RFNOTEG|RFNAMEG; + break; + case 2: + arg = 0; + for(s = runq->argv->words->next->word;*s;s++) switch(*s){ + default: + goto Usage; + case 'n': + arg|=RFNAMEG; break; + case 'N': + arg|=RFCNAMEG; + break; + case 'e': + /* arg|=RFENVG; */ break; + case 'E': + arg|=RFCENVG; break; + case 's': + arg|=RFNOTEG; break; + case 'f': + arg|=RFFDG; break; + case 'F': + arg|=RFCFDG; break; + } + break; + default: + Usage: + pfmt(err, "Usage: %s [nNeEsfF]\n", runq->argv->words->word); + setstatus("rfork usage"); + poplist(); + return; + } + if(rfork(arg)==-1){ + pfmt(err, "rc: %s failed\n", runq->argv->words->word); + setstatus("rfork failed"); + } + else + setstatus(""); + poplist(); +} + + + +#define SEP '\1' +char **environp; +struct word *enval(s) +register char *s; +{ + register char *t, c; + register struct word *v; + for(t=s;*t && *t!=SEP;t++); + c=*t; + *t='\0'; + v=newword(s, c=='\0'?(struct word *)0:enval(t+1)); + *t=c; + return v; +} +void Vinit(void){ + extern char **environ; + register char *s; + register char **env=environ; + environp=env; + for(;*env;env++){ + for(s=*env;*s && *s!='(' && *s!='=';s++); + switch(*s){ + case '\0': + // pfmt(err, "rc: odd environment %q?\n", *env); + break; + case '=': + *s='\0'; + setvar(*env, enval(s+1)); + *s='='; + break; + case '(': /* ignore functions for now */ + break; + } + } +} +char **envp; +void Xrdfn(void){ + char *p; + register char *s; + register int len; + for(;*envp;envp++){ + s = *envp; + if(strncmp(s, "fn#", 3) == 0){ + p = strchr(s, '='); + if(p == nil) + continue; + *p = ' '; + s[2] = ' '; + len = strlen(s); + execcmds(opencore(s, len)); + s[len] = '\0'; + return; + } +#if 0 + for(s=*envp;*s && *s!='(' && *s!='=';s++); + switch(*s){ + case '\0': + pfmt(err, "environment %q?\n", *envp); + break; + case '=': /* ignore variables */ + break; + case '(': /* Bourne again */ + s=*envp+3; + envp++; + len=strlen(s); + s[len]='\n'; + execcmds(opencore(s, len+1)); + s[len]='\0'; + return; + } +#endif + } + Xreturn(); +} +union code rdfns[4]; +void execfinit(void){ + static int first=1; + if(first){ + rdfns[0].i=1; + rdfns[1].f=Xrdfn; + rdfns[2].f=Xjump; + rdfns[3].i=1; + first=0; + } + Xpopm(); + envp=environp; + start(rdfns, 1, runq->local); +} +extern int mapfd(int); +int Waitfor(int pid, int unused0){ + thread *p; + Waitmsg *w; + char errbuf[ERRMAX]; + + while((w = wait()) != nil){ + if(w->pid==pid){ + if(strncmp(w->msg, "signal: ", 8) == 0) + fprint(mapfd(2), "%d: %s\n", w->pid, w->msg); + setstatus(w->msg); + free(w); + return 0; + } + if(strncmp(w->msg, "signal: ", 8) == 0) + fprint(2, "%d: %s\n", w->pid, w->msg); + for(p=runq->ret;p;p=p->ret) + if(p->pid==w->pid){ + p->pid=-1; + strcpy(p->status, w->msg); + } + free(w); + } + + errstr(errbuf, sizeof errbuf); + if(strcmp(errbuf, "interrupted")==0) return -1; + return 0; +} +char **mkargv(word *a) +{ + char **argv=(char **)emalloc((count(a)+2)*sizeof(char *)); + char **argp=argv+1; /* leave one at front for runcoms */ + for(;a;a=a->next) *argp++=a->word; + *argp=0; + return argv; +} +/* +void addenv(var *v) +{ + char envname[256]; + word *w; + int f; + io *fd; + if(v->changed){ + v->changed=0; + snprint(envname, sizeof envname, "/env/%s", v->name); + if((f=Creat(envname))<0) + pfmt(err, "rc: can't open %s: %r\n", envname); + else{ + for(w=v->val;w;w=w->next) + write(f, w->word, strlen(w->word)+1L); + close(f); + } + } + if(v->fnchanged){ + v->fnchanged=0; + snprint(envname, sizeof envname, "/env/fn#%s", v->name); + if((f=Creat(envname))<0) + pfmt(err, "rc: can't open %s: %r\n", envname); + else{ + if(v->fn){ + fd=openfd(f); + pfmt(fd, "fn %s %s\n", v->name, v->fn[v->pc-1].s); + closeio(fd); + } + close(f); + } + } +} +void updenvlocal(var *v) +{ + if(v){ + updenvlocal(v->next); + addenv(v); + } +} +void Updenv(void){ + var *v, **h; + for(h=gvar;h!=&gvar[NVAR];h++) + for(v=*h;v;v=v->next) + addenv(v); + if(runq) updenvlocal(runq->local); +} +*/ +int +cmpenv(const void *a, const void *b) +{ + return strcmp(*(char**)a, *(char**)b); +} +char **mkenv(){ + register char **env, **ep, *p, *q; + register struct var **h, *v; + register struct word *a; + register int nvar=0, nchr=0, sep; + /* + * Slightly kludgy loops look at locals then globals + */ + for(h=gvar-1;h!=&gvar[NVAR];h++) for(v=h>=gvar?*h:runq->local;v;v=v->next){ + if((v==vlook(v->name)) && v->val){ + nvar++; + nchr+=strlen(v->name)+1; + for(a=v->val;a;a=a->next) + nchr+=strlen(a->word)+1; + } + if(v->fn){ + nvar++; + nchr+=strlen(v->name)+strlen(v->fn[v->pc-1].s)+8; + } + } + env=(char **)emalloc((nvar+1)*sizeof(char *)+nchr); + ep=env; + p=(char *)&env[nvar+1]; + for(h=gvar-1;h!=&gvar[NVAR];h++) for(v=h>=gvar?*h:runq->local;v;v=v->next){ + if((v==vlook(v->name)) && v->val){ + *ep++=p; + q=v->name; + while(*q) *p++=*q++; + sep='='; + for(a=v->val;a;a=a->next){ + *p++=sep; + sep=SEP; + q=a->word; + while(*q) *p++=*q++; + } + *p++='\0'; + } + if(v->fn){ + *ep++=p; +#if 0 + *p++='#'; *p++='('; *p++=')'; /* to fool Bourne */ + *p++='f'; *p++='n'; *p++=' '; + q=v->name; + while(*q) *p++=*q++; + *p++=' '; +#endif + *p++='f'; *p++='n'; *p++='#'; + q=v->name; + while(*q) *p++=*q++; + *p++='='; + q=v->fn[v->pc-1].s; + while(*q) *p++=*q++; + *p++='\n'; + *p++='\0'; + } + } + *ep=0; + qsort((char *)env, nvar, sizeof ep[0], cmpenv); + return env; +} +void Updenv(void){} +void Execute(word *args, word *path) +{ + char **argv=mkargv(args); + char **env=mkenv(); + char file[1024]; + int nc; + Updenv(); + for(;path;path=path->next){ + nc=strlen(path->word); + if(nc<1024){ + strcpy(file, path->word); + if(file[0]){ + strcat(file, "/"); + nc++; + } + if(nc+strlen(argv[1])<1024){ + strcat(file, argv[1]); + execve(file, argv+1, env); + } + else werrstr("command name too long"); + } + } + rerrstr(file, sizeof file); + pfmt(err, "%s: %s\n", argv[1], file); + efree((char *)argv); +} +#define NDIR 256 /* shoud be a better way */ +int Globsize(char *p) +{ + ulong isglob=0, globlen=NDIR+1; + for(;*p;p++){ + if(*p==GLOB){ + p++; + if(*p!=GLOB) isglob++; + globlen+=*p=='*'?NDIR:1; + } + else + globlen++; + } + return isglob?globlen:0; +} +#define NFD 50 +#define NDBUF 32 +struct{ + Dir *dbuf; + int i; + int n; +}dir[NFD]; +int Opendir(char *name) +{ + Dir *db; + int f; + f=open(name, 0); + if(f==-1) + return f; + db = dirfstat(f); + if(db!=nil && (db->mode&DMDIR)){ + if(f<NFD){ + dir[f].i=0; + dir[f].n=0; + } + free(db); + return f; + } + free(db); + close(f); + return -1; +} +int Readdir(int f, char *p) +{ + int n; + if(f<0 || f>=NFD) + return 0; + if(dir[f].i==dir[f].n){ /* read */ + free(dir[f].dbuf); + dir[f].dbuf=0; + n=dirread(f, &dir[f].dbuf); + if(n>=0) + dir[f].n=n; + else + dir[f].n=0; + dir[f].i=0; + } + if(dir[f].i==dir[f].n) + return 0; + strcpy(p, dir[f].dbuf[dir[f].i].name); + dir[f].i++; + return 1; +} +void Closedir(int f){ + if(f>=0 && f<NFD){ + free(dir[f].dbuf); + dir[f].i=0; + dir[f].n=0; + dir[f].dbuf=0; + } + close(f); +} +int interrupted = 0; +void +notifyf(void *unused0, char *s) +{ + int i; + for(i=0;syssigname[i];i++) + if(strncmp(s, syssigname[i], strlen(syssigname[i]))==0){ + if(strncmp(s, "sys: ", 5)!=0){ + if(kidpid && !interrupted){ + interrupted=1; + postnote(PNGROUP, kidpid, s); + } + interrupted = 1; + } + goto Out; + } + if(strcmp(s, "sys: window size change") != 0) + if(strcmp(s, "sys: write on closed pipe") != 0) + if(strcmp(s, "sys: child") != 0) + pfmt(err, "rc: note: %s\n", s); + noted(NDFLT); + return; +Out: + if(strcmp(s, "interrupt")!=0 || trap[i]==0){ + trap[i]++; + ntrap++; + } + if(ntrap>=32){ /* rc is probably in a trap loop */ + pfmt(err, "rc: Too many traps (trap %s), aborting\n", s); + abort(); + } + noted(NCONT); +} +void Trapinit(void){ + notify(notifyf); +} +void Unlink(char *name) +{ + remove(name); +} +long Write(int fd, char *buf, long cnt) +{ + return write(fd, buf, (long)cnt); +} +long Read(int fd, char *buf, long cnt) +{ + int i; + + i = read(fd, buf, cnt); + if(ntrap) dotrap(); + return i; +} +long Seek(int fd, long cnt, long whence) +{ + return seek(fd, cnt, whence); +} +int Executable(char *file) +{ + Dir *statbuf; + int ret; + + statbuf = dirstat(file); + if(statbuf == nil) return 0; + ret = ((statbuf->mode&0111)!=0 && (statbuf->mode&DMDIR)==0); + free(statbuf); + return ret; +} +int Creat(char *file) +{ + return create(file, 1, 0666L); +} +int Dup(int a, int b){ + return dup(a, b); +} +int Dup1(int a){ + return dup(a, -1); +} +void Exit(char *stat) +{ + Updenv(); + setstatus(stat); + exits(truestatus()?"":getstatus()); +} +int Eintr(void){ + return interrupted; +} +void Noerror(void){ + interrupted=0; +} +int +Isatty(int fd){ + return isatty(fd); +} +void Abort(void){ + pfmt(err, "aborting\n"); + flush(err); + Exit("aborting"); +} +void Memcpy(char *a, char *b, long n) +{ + memmove(a, b, (long)n); +} +void *Malloc(ulong n){ + return malloc(n); +} diff --git a/rc/rc.1 b/rc/rc.1 @@ -0,0 +1,992 @@ +.TH RC 1 +.SH NAME +rc, cd, eval, exec, exit, flag, rfork, shift, wait, whatis, ., ~ \- command language +.SH SYNOPSIS +.B rc +[ +.B -srdiIlxepvV +] +[ +.B -c command +] +[ +.I file +[ +.I arg ... +]] +.SH DESCRIPTION +.I Rc +is the Plan 9 shell. +It executes command lines read from a terminal or a file or, with the +.B -c +flag, from +.I rc's +argument list. +.SS Command Lines +A command line is a sequence of commands, separated by ampersands or semicolons +.RB ( & +or +.BR ; ), +terminated by a newline. +The commands are executed in sequence +from left to right. +.I Rc +does not wait for a command followed by +.B & +to finish executing before starting +the following command. +Whenever a command followed by +.B & +is executed, its process id is assigned to the +.I rc +variable +.BR $apid . +Whenever a command +.I not +followed by +.B & +exits or is terminated, the +.I rc +variable +.B $status +gets the process's wait message (see +.IR wait (3)); +it will be the null string if the command was successful. +.PP +A long command line may be continued on subsequent lines by typing +a backslash +.RB ( \e ) +followed by a newline. +This sequence is treated as though it were a blank. +Backslash is not otherwise a special character. +.PP +A number-sign +.RB ( # ) +and any following characters up to (but not including) the next newline +are ignored, except in quotation marks. +.SS Simple Commands +A simple command is a sequence of arguments interspersed with I/O redirections. +If the first argument is the name of an +.I rc +function or of one of +.I rc's +built-in commands, it is executed by +.IR rc . +Otherwise if the name starts with a slash +.RB ( / ), +it must be the path name of the program to be executed. +Names containing no initial slash are searched for in +a list of directory names stored in +.BR $path . +The first executable file of the given name found +in a directory in +.B $path +is the program to be executed. +To be executable, the user must have execute permission (see +.IR stat (3)) +and the file must be either an executable binary +for the current machine's CPU type, or a shell script. +Shell scripts begin with a line containing the full path name of a shell +(usually +.BR /bin/rc ), +prefixed by +.LR #! . +.PP +The first word of a simple command cannot be a keyword unless it is +quoted or otherwise disguised. +The keywords are +.EX + for in while if not switch fn ~ ! @ +.EE +.SS Arguments and Variables +A number of constructions may be used where +.I rc's +syntax requires an argument to appear. +In many cases a construction's +value will be a list of arguments rather than a single string. +.PP +The simplest kind of argument is the unquoted word: +a sequence of one or more characters none of which is a blank, tab, +newline, or any of the following: +.EX + # ; & | ^ $ = ` ' { } ( ) < > +.EE +An unquoted word that contains any of the characters +.B * +.B ? +.B [ +is a pattern for matching against file names. +The character +.B * +matches any sequence of characters, +.B ? +matches any single character, and +.BI [ class ] +matches any character in the +.IR class . +If the first character of +.I class +is +.BR ~ , +the class is complemented. +The +.I class +may also contain pairs of characters separated by +.BR - , +standing for all characters lexically between the two. +The character +.B / +must appear explicitly in a pattern, as must the +first character of the path name components +.B . +and +.BR .. . +A pattern is replaced by a list of arguments, one for each path name matched, +except that a pattern matching no names is not replaced by the empty list, +but rather stands for itself. +Pattern matching is done after all other +operations. +Thus, +.EX + x=/tmp echo $x^/*.c +.EE +matches +.BR /tmp/*.c , +rather than matching +.B "/*.c +and then prefixing +.BR /tmp . +.PP +A quoted word is a sequence of characters surrounded by single quotes +.RB ( ' ). +A single quote is represented in a quoted word by a pair of quotes +.RB ( '' ). +.PP +Each of the following is an argument. +.PD 0 +.HP +.BI ( arguments ) +.br +The value of a sequence of arguments enclosed in parentheses is +a list comprising the members of each element of the sequence. +Argument lists have no recursive structure, although their syntax may +suggest it. +The following are entirely equivalent: +.EX + echo hi there everybody + ((echo) (hi there) everybody) +.EE +.HP +.BI $ argument +.HP +.BI $ argument ( subscript ) +.br +The +.I argument +after the +.B $ +is the name of a variable whose value is substituted. +Multiple levels +of indirection are possible, but of questionable utility. +Variable values +are lists of strings. +If +.I argument +is a number +.IR n , +the value is the +.IR n th +element of +.BR $* , +unless +.B $* +doesn't have +.I n +elements, in which case the value is empty. +If +.I argument +is followed by a parenthesized list of subscripts, the +value substituted is a list composed of the requested elements (origin 1). +The parenthesis must follow the variable name with no spaces. +Assignments to variables are described below. +.HP +.BI $# argument +.br +The value is the number of elements in the named variable. +A variable +never assigned a value has zero elements. +.HP +$"\c +.I argument +.br +The value is a single string containing the components of the named variable +separated by spaces. A variable with zero elements yields the empty string. +.HP +.BI `{ command } +.br +.I rc +executes the +.I command +and reads its standard output, splitting it into a list of arguments, +using characters in +.B $ifs +as separators. +If +.B $ifs +is not otherwise set, its value is +.BR "'\ \et\en'" . +.HP +.BI <{ command } +.HP +.BI >{ command } +.br +The +.I command +is executed asynchronously with its standard output or standard input +connected to a pipe. +The value of the argument is the name of a file +referring to the other end of the pipe. +This allows the construction of +non-linear pipelines. +For example, the following runs two commands +.B old +and +.B new +and uses +.B cmp +to compare their outputs +.EX + cmp <{old} <{new} +.EE +.HP +.IB argument ^ argument +.br +The +.B ^ +operator concatenates its two operands. +If the two operands +have the same number of components, they are concatenated pairwise. +If not, +then one operand must have one component, and the other must be non-empty, +and concatenation is distributive. +.PD +.SS Free Carets +In most circumstances, +.I rc +will insert the +.B ^ +operator automatically between words that are not separated by white space. +Whenever one of +.B $ +.B ' +.B ` +follows a quoted or unquoted word or an unquoted word follows a quoted word +with no intervening blanks or tabs, +a +.B ^ +is inserted between the two. +If an unquoted word immediately follows a +.BR $ +and contains a character other than an alphanumeric, underscore, +or +.BR * , +a +.B ^ +is inserted before the first such character. +Thus +.IP +.B cc -$flags $stem.c +.LP +is equivalent to +.IP +.B cc -^$flags $stem^.c +.SS I/O Redirections +The sequence +.BI > file +redirects the standard output file (file descriptor 1, normally the +terminal) to the named +.IR file ; +.BI >> file +appends standard output to the file. +The standard input file (file descriptor 0, also normally the terminal) +may be redirected from a file by the sequence +.BI < file \f1, +or from an inline `here document' +by the sequence +.BI << eof-marker\f1. +The contents of a here document are lines of text taken from the command +input stream up to a line containing nothing but the +.IR eof-marker , +which may be either a quoted or unquoted word. +If +.I eof-marker +is unquoted, variable names of the form +.BI $ word +have their values substituted from +.I rc's +environment. +If +.BI $ word +is followed by a caret +.RB ( ^ ), +the caret is deleted. +If +.I eof-marker +is quoted, no substitution occurs. +.PP +Redirections may be applied to a file-descriptor other than standard input +or output by qualifying the redirection operator +with a number in square brackets. +For example, the diagnostic output (file descriptor 2) +may be redirected by writing +.BR "cc junk.c >[2]junk" . +.PP +A file descriptor may be redirected to an already open descriptor by writing +.BI >[ fd0 = fd1 ] +or +.BI <[ fd0 = fd1 ]\f1. +.I Fd1 +is a previously opened file descriptor and +.I fd0 +becomes a new copy (in the sense of +.IR dup (3)) +of it. +A file descriptor may be closed by writing +.BI >[ fd0 =] +or +.BI <[ fd0 =]\f1. +.PP +Redirections are executed from left to right. +Therefore, +.B cc junk.c >/dev/null >[2=1] +and +.B cc junk.c >[2=1] >/dev/null +have different effects: the first puts standard output in +.BR /dev/null +and then puts diagnostic output in the same place, where the second +directs diagnostic output to the terminal and sends standard output to +.BR /dev/null . +.SS Compound Commands +A pair of commands separated by a pipe operator +.RB ( | ) +is a command. +The standard output of the left command is sent through a pipe +to the standard input of the right command. +The pipe operator may be decorated +to use different file descriptors. +.BI |[ fd ] +connects the output end of the pipe to file descriptor +.I fd +rather than 1. +.BI |[ fd0 = fd1 ] +connects output to +.I fd1 +of the left command and input to +.I fd0 +of the right command. +.PP +A pair of commands separated by +.B && +or +.B || +is a command. +In either case, the left command is executed and its exit status examined. +If the operator is +.B && +the right command is executed if the left command's status is null. +.B || +causes the right command to be executed if the left command's status is non-null. +.PP +The exit status of a command may be inverted (non-null is changed to null, null +is changed to non-null) by preceding it with a +.BR ! . +.PP +The +.B | +operator has highest precedence, and is left-associative (i.e. binds tighter +to the left than the right). +.B ! +has intermediate precedence, and +.B && +and +.B || +have the lowest precedence. +.PP +The unary +.B @ +operator, with precedence equal to +.BR ! , +causes its operand to be executed in a subshell. +.PP +Each of the following is a command. +.PD 0 +.HP +.B if ( +.I list +.B ) +.I command +.br +A +.I list +is a sequence of commands, separated by +.BR & , +.BR ; , +or newline. +It is executed and +if its exit status is null, the +.I command +is executed. +.HP +.B if not +.I command +.br +The immediately preceding command must have been +.BI if( list ) +.IR command . +If its condition was non-zero, the +.I command +is executed. +.HP +.BI for( name +.B in +.IB arguments ) +.I command +.HP +.BI for( name ) +.I command +.br +The +.I command +is executed once for each +.IR argument +with that argument assigned to +.IR name . +If the argument list is omitted, +.B $* +is used. +.HP +.BI while( list ) +.I command +.br +The +.I list +is executed repeatedly until its exit status is non-null. +Each time it returns null status, the +.I command +is executed. +An empty +.I list +is taken to give null status. +.HP +.BI "switch(" argument "){" list } +.br +The +.IR list +is searched for simple commands beginning with the word +.BR case . +(The search is only at the `top level' of the +.IR list . +That is, +.B cases +in nested constructs are not found.) +.I Argument +is matched against each word following +.B case +using the pattern-matching algorithm described above, except that +.B / +and the first characters of +.B . +and +.B .. +need not be matched explicitly. +When a match is found, commands in the list are executed up to the next +following +.B case +command (at the top level) or the closing brace. +.HP +.BI { list } +.br +Braces serve to alter the grouping of commands implied by operator +priorities. +The +.I body +is a sequence of commands separated by +.BR & , +.BR ; , +or newline. +.HP +.BI "fn " name { list } +.HP +.BI "fn " name +.br +The first form defines a function with the given +.IR name . +Subsequently, whenever a command whose first argument is +.I name +is encountered, the current value of +the remainder of the command's argument list will be assigned to +.BR $* , +after saving its current value, and +.I rc +will execute the +.IR list . +The second form removes +.IR name 's +function definition. +.HP +.BI "fn " note { list } +.br +.HP +.BI "fn " note +.br +A function with a special name will be called when +.I rc +receives a corresponding note; see +.IR notify (3). +The valid note names (and corresponding notes) are +.B sighup +.RB ( hangup ), +.B sigint +.RB ( interrupt ), +.BR sigalrm +.RB ( alarm ), +and +.B sigfpe +(floating point trap). +By default +.I rc +exits on receiving any signal, except when run interactively, +in which case interrupts and quits normally cause +.I rc +to stop whatever it's doing and start reading a new command. +The second form causes +.I rc +to handle a signal in the default manner. +.I Rc +recognizes an artificial note, +.BR sigexit , +which occurs when +.I rc +is about to finish executing. +.HP +.IB name = "argument command" +.br +Any command may be preceded by a sequence of assignments +interspersed with redirections. +The assignments remain in effect until the end of the command, unless +the command is empty (i.e. the assignments stand alone), in which case +they are effective until rescinded by later assignments. +.PD +.SS Built-in Commands +These commands are executed internally by +.IR rc , +usually because their execution changes or depends on +.IR rc 's +internal state. +.PD 0 +.HP +.BI . " file ..." +.br +Execute commands from +.IR file . +.B $* +is set for the duration to the remainder of the argument list following +.IR file . +.I File +is searched for using +.BR $path . +.HP +.BI builtin " command ..." +.br +Execute +.I command +as usual except that any function named +.I command +is ignored in favor of the built-in meaning. +.HP +.BI "cd [" dir "]" +.br +Change the current directory to +.IR dir . +The default argument is +.BR $home . +.I dir +is searched for in each of the directories mentioned in +.BR $cdpath . +.HP +.BI "eval [" "arg ..." "]" +.br +The arguments are concatenated separated by spaces into a single string, +read as input to +.IR rc , +and executed. +.HP +.BI "exec [" "command ..." "]" +.br +This instance of +.I rc +replaces itself with the given (non-built-in) +.IR command . +.HP +.BI "flag " f " [+-]" +.br +Either set +.RB ( + ), +clear +.RB ( - ), +or test (neither +.B + +nor +.BR - ) +the flag +.IR f , +where +.I f +is a single character, one of the command line flags (see Invocation, below). +.HP +.BI "exit [" status "]" +.br +Exit with the given exit status. +If none is given, the current value of +.B $status +is used. +.HP +.BR "rfork " [ nNeEsfFm ] +.br +Become a new process group using +.BI rfork( flags ) +where +.I flags +is composed of the bitwise OR of the +.B rfork +flags specified by the option letters +(see +.IR fork (2)). +If no +.I flags +are given, they default to +.BR ens . +The +.I flags +and their meanings are: +.B n +is +.BR RFNAMEG ; +.B N +is +.BR RFCNAMEG ; +.B e +is +.BR RFENVG ; +.B E +is +.BR RFCENVG ; +.B s +is +.BR RFNOTEG ; +.B f +is +.BR RFFDG ; +.B F +is +.BR RFCFDG ; +and +.B m +is +.BR RFNOMNT . +.HP +.BI "shift [" n "]" +.br +Delete the first +.IR n +(default 1) +elements of +.BR $* . +.HP +.BI "wait [" pid "]" +.br +Wait for the process with the given +.I pid +to exit. +If no +.I pid +is given, all outstanding processes are waited for. +.HP +.BI whatis " name ..." +.br +Print the value of each +.I name +in a form suitable for input to +.IR rc . +The output is +an assignment to any variable, +the definition of any function, +a call to +.B builtin +for any built-in command, or +the completed pathname of any executable file. +.HP +.BI ~ " subject pattern ..." +.br +The +.I subject +is matched against each +.I pattern +in sequence. +If it matches any pattern, +.B $status +is set to zero. +Otherwise, +.B $status +is set to one. +Patterns are the same as for file name matching, except that +.B / +and the first character of +.B . +and +.B .. +need not be matched explicitly. +The +.I patterns +are not subjected to +file name matching before the +.B ~ +command is executed, so they need not be enclosed in quotation marks. +.PD +.SS Environment +The +.I environment +is a list of strings made available to executing binaries by the +kernel. +.I Rc +creates an environment entry for each variable whose value is non-empty, +and for each function. +The string for a variable entry has the variable's name followed by +.B = +and its value. +If the value has more than one component, these +are separated by SOH (001) +characters. +The string for a function is just the +.I rc +input that defines the function. +The name of a function in the environment is the function name +preceded by +.LR fn# . +.PP +When +.I rc +starts executing it reads variable and function definitions from its +environment. +.SS Special Variables +The following variables are set or used by +.IR rc . +.PD 0 +.TP \w'\fL$promptXX'u +.B $* +Set to +.IR rc 's +argument list during initialization. +Whenever a +.B . +command or a function is executed, the current value is saved and +.B $* +receives the new argument list. +The saved value is restored on completion of the +.B . +or function. +.TP +.B $apid +Whenever a process is started asynchronously with +.BR & , +.B $apid +is set to its process id. +.TP +.B $home +The default directory for +.BR cd . +.TP +.B $ifs +The input field separators used in backquote substitutions. +If +.B $ifs +is not set in +.IR rc 's +environment, it is initialized to blank, tab and newline. +.TP +.B $path +The search path used to find commands and input files +for the +.B . +command. +If not set in the environment, it is initialized by +parsing the +.B $PATH +variable +(as in +.IR sh (1)) +or by +.BR "path=(.\ /bin)" . +The variables +.B $path +and +.B $PATH +are maintained together: changes to one will be reflected in the other. +.\" Its use is discouraged; instead use +.\" .IR bind (1) +.\" to build a +.\" .B /bin +.\" containing what's needed. +.TP +.B $pid +Set during initialization to +.IR rc 's +process id. +.TP +.B $prompt +When +.I rc +is run interactively, the first component of +.B $prompt +is printed before reading each command. +The second component is printed whenever a newline is typed and more lines +are required to complete the command. +If not set in the environment, it is initialized by +.BR "prompt=('%\ '\ '\ ')" . +.TP +.B $status +Set to the wait message of the last-executed program. +(unless started with +.BR &). +.B ! +and +.B ~ +also change +.BR $status . +Its value is used to control execution in +.BR && , +.BR || , +.B if +and +.B while +commands. +When +.I rc +exits at end-of-file of its input or on executing an +.B exit +command with no argument, +.B $status +is its exit status. +.PD +.SS Invocation +If +.I rc +is started with no arguments it reads commands from standard input. +Otherwise its first non-flag argument is the name of a file from which +to read commands (but see +.B -c +below). +Subsequent arguments become the initial value of +.BR $* . +.I Rc +accepts the following command-line flags. +.PD 0 +.TP \w'\fL-c\ \fIstring\fLXX'u +.BI -c " string" +Commands are read from +.IR string . +.TP +.B -s +Print out exit status after any command where the status is non-null. +.TP +.B -e +Exit if +.B $status +is non-null after executing a simple command. +.TP +.B -i +If +.B -i +is present, or +.I rc +is given no arguments and its standard input is a terminal, +it runs interactively. +Commands are prompted for using +.BR $prompt . +.TP +.B -I +Makes sure +.I rc +is not run interactively. +.TP +.B -l +If +.B -l +is given or the first character of argument zero is +.BR - , +.I rc +reads commands from +.BR $home/lib/profile , +if it exists, before reading its normal input. +.TP +.B -p +A no-op. +.TP +.B -d +A no-op. +.TP +.B -v +Echo input on file descriptor 2 as it is read. +.TP +.B -x +Print each simple command before executing it. +.TP +.B -r +Print debugging information (internal form of commands +as they are executed). +.PD +.SH SOURCE +.B \*9/src/cmd/rc +.SH "SEE ALSO" +Tom Duff, +``Rc \- The Plan 9 Shell''. +.SH BUGS +There should be a way to match patterns against whole lists rather than +just single strings. +.PP +Using +.B ~ +to check the value of +.B $status +changes +.BR $status . +.PP +Functions that use here documents don't work. +.PP +Free carets don't get inserted next to keywords. +.PP +The +.BI <{ command } +syntax depends on the underlying operating system +providing a file descriptor device tree at +.BR /dev/fd . +.PP +By default, FreeBSD 5 +does not provide file descriptors greater than 2 +in +.BR /dev/fd . +To fix this, add +.IP +.EX +/fdescfs /dev/fd fdescfs rw 0 0 +.EE +.LP +to +.BR /etc/fstab , +and then +.B mount +.BR /dev/fd . +(Adding the line to +.B fstab +ensures causes FreeBSD to mount the file system +automatically at boot time.) diff --git a/rc/rc.h b/rc/rc.h @@ -0,0 +1,145 @@ +/* + * Plan9 is defined for plan 9 + * V9 is defined for 9th edition + * Sun is defined for sun-os + * Please don't litter the code with ifdefs. The three below (and one in + * getflags) should be enough. + */ +#define Plan9 +#ifdef Plan9 +#include <u.h> +#include <libc.h> +#undef NSIG +#undef SIGINT +#undef SIGQUIT +#define NSIG 32 +#define SIGINT 2 +#define SIGQUIT 3 +#endif +#ifdef V9 +#include <signal.h> +#include <libc.h> +#endif +#ifdef Sun +#include <signal.h> +#endif +#define YYMAXDEPTH 500 +#ifndef PAREN +#ifndef YYMAJOR +#include "y.tab.h" +#endif +#endif + +#undef pipe /* so that /dev/fd works */ +#define searchpath rcsearchpath /* avoid new libc function */ + +typedef struct tree tree; +typedef struct word word; +typedef struct io io; +typedef union code code; +typedef struct var var; +typedef struct list list; +typedef struct redir redir; +typedef struct thread thread; +typedef struct builtin builtin; + +struct tree{ + int type; + int rtype, fd0, fd1; /* details of REDIR PIPE DUP tokens */ + char *str; + int quoted; + int iskw; + tree *child[3]; + tree *next; +}; +tree *newtree(void); +tree *token(char*, int), *klook(char*), *tree1(int, tree*); +tree *tree2(int, tree*, tree*), *tree3(int, tree*, tree*, tree*); +tree *mung1(tree*, tree*), *mung2(tree*, tree*, tree*); +tree *mung3(tree*, tree*, tree*, tree*), *epimung(tree*, tree*); +tree *simplemung(tree*), *heredoc(tree*); +void freetree(tree*); +tree *cmdtree; +/* + * The first word of any code vector is a reference count. + * Always create a new reference to a code vector by calling codecopy(.). + * Always call codefree(.) when deleting a reference. + */ +union code{ + void (*f)(void); + int i; + char *s; +}; +char *promptstr; +int doprompt; +#define NTOK 8192 +char tok[NTOK]; +#define APPEND 1 +#define WRITE 2 +#define READ 3 +#define HERE 4 +#define DUPFD 5 +#define CLOSE 6 +struct var{ + char *name; /* ascii name */ + word *val; /* value */ + int changed; + code *fn; /* pointer to function's code vector */ + int fnchanged; + int pc; /* pc of start of function */ + var *next; /* next on hash or local list */ + void (*changefn)(var*); +}; +var *vlook(char*), *gvlook(char*), *newvar(char*, var*); +#define NVAR 521 +var *gvar[NVAR]; /* hash for globals */ +#define new(type) ((type *)emalloc(sizeof(type))) +char *emalloc(long); +void *Malloc(ulong); +void efree(char*); +#define NOFILE 128 /* should come from <param.h> */ +struct here{ + tree *tag; + char *name; + struct here *next; +}; +int mypid; +/* + * Glob character escape in strings: + * In a string, GLOB must be followed by *?[ or GLOB. + * GLOB* matches any string + * GLOB? matches any single character + * GLOB[...] matches anything in the brackets + * GLOBGLOB matches GLOB + */ +#define GLOB ((char)0x01) +/* + * onebyte(c), twobyte(c), threebyte(c) + * Is c the first character of a one- two- or three-byte utf sequence? + */ +#define onebyte(c) ((c&0x80)==0x00) +#define twobyte(c) ((c&0xe0)==0xc0) +#define threebyte(c) ((c&0xf0)==0xe0) +char **argp; +char **args; +int nerror; /* number of errors encountered during compilation */ +int doprompt; /* is it time for a prompt? */ +/* + * Which fds are the reading/writing end of a pipe? + * Unfortunately, this can vary from system to system. + * 9th edition Unix doesn't care, the following defines + * work on plan 9. + */ +#define PRD 0 +#define PWR 1 +extern char *Rcmain(), Fdprefix[]; +#define register +/* + * How many dot commands have we executed? + * Used to ensure that -v flag doesn't print rcmain. + */ +int ndot; +char *getstatus(void); +int lastc; +int lastword; +int kidpid; diff --git a/rc/rcmain b/rc/rcmain @@ -0,0 +1,39 @@ +# rcmain: Plan 9 on Unix version +if(~ $#home 0) home=$HOME +if(~ $#home 0) home=/ +if(~ $#ifs 0) ifs=' +' +switch($#prompt){ +case 0 + prompt=('; ' ' ') +case 1 + prompt=($prompt ' ') +} +if(~ $rcname ?.out ?.rc */?.rc */?.out) prompt=('broken! ' ' ') +if(flag p) path=(/bin /usr/bin) +if not{ + finit + # should be taken care of by rc now, but leave just in case +} +fn sigexit +if(! ~ $#cflag 0){ + if(flag l && test -r $home/lib/profile) . $home/lib/profile + status='' + eval $cflag + exit $status +} +if(flag i){ + if(flag l && test -r $home/lib/profile) . $home/lib/profile + status='' + if(! ~ $#* 0) . $* + . -i '/dev/stdin' + exit $status +} +if(flag l && test -r $home/lib/profile) . $home/lib/profile +if(~ $#* 0){ + . /dev/stdin + exit $status +} +status='' +. $* +exit $status diff --git a/rc/simple.c b/rc/simple.c @@ -0,0 +1,445 @@ +/* + * Maybe `simple' is a misnomer. + */ +#include "rc.h" +#include "getflags.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +/* + * Search through the following code to see if we're just going to exit. + */ +int +exitnext(void){ + union code *c=&runq->code[runq->pc]; + while(c->f==Xpopredir) c++; + return c->f==Xexit; +} +void Xsimple(void){ + word *a; + thread *p=runq; + var *v; + struct builtin *bp; + int pid, n; + char buf[ERRMAX]; + globlist(); + a=runq->argv->words; + if(a==0){ + Xerror1("empty argument list"); + return; + } + if(flag['x']) + pfmt(err, "%v\n", p->argv->words); /* wrong, should do redirs */ + v=gvlook(a->word); + if(v->fn) + execfunc(v); + else{ + if(strcmp(a->word, "builtin")==0){ + if(count(a)==1){ + pfmt(err, "builtin: empty argument list\n"); + setstatus("empty arg list"); + poplist(); + return; + } + a=a->next; + popword(); + } + for(bp=Builtin;bp->name;bp++) + if(strcmp(a->word, bp->name)==0){ + (*bp->fnc)(); + return; + } + if(exitnext()){ + /* fork and wait is redundant */ + pushword("exec"); + execexec(); + Xexit(); + } + else{ + flush(err); + Updenv(); /* necessary so changes don't go out again */ + switch(pid=fork()){ + case -1: + Xerror("try again"); + return; + case 0: + pushword("exec"); + execexec(); + strcpy(buf, "can't exec: "); + n = strlen(buf); + errstr(buf+n, ERRMAX-n); + Exit(buf); + default: + kidpid = pid; + poplist(); + /* interrupts don't get us out */ + while(Waitfor(pid, 1) < 0) + ; + kidpid = 0; + } + } + } +} +struct word nullpath={ "", 0}; +void doredir(redir *rp) +{ + if(rp){ + doredir(rp->next); + switch(rp->type){ + case ROPEN: + if(rp->from!=rp->to){ + Dup(rp->from, rp->to); + close(rp->from); + } + break; + case RDUP: Dup(rp->from, rp->to); break; + case RCLOSE: close(rp->from); break; + } + } +} +word *searchpath(char *w){ + word *path; + if(strncmp(w, "/", 1)==0 +/* || strncmp(w, "#", 1)==0 */ + || strncmp(w, "./", 2)==0 + || strncmp(w, "../", 3)==0 + || (path=vlook("path")->val)==0) + path=&nullpath; + return path; +} +void execexec(void){ + popword(); /* "exec" */ + if(runq->argv->words==0){ + Xerror1("empty argument list"); + return; + } + doredir(runq->redir); + Execute(runq->argv->words, searchpath(runq->argv->words->word)); + poplist(); +} +void execfunc(var *func) +{ + word *starval; + popword(); + starval=runq->argv->words; + runq->argv->words=0; + poplist(); + start(func->fn, func->pc, (struct var *)0); + runq->local=newvar(strdup("*"), runq->local); + runq->local->val=starval; + runq->local->changed=1; +} +int dochdir(char *word){ + /* report to /dev/wdir if it exists and we're interactive */ + static int wdirfd = -2; + if(chdir(word)<0) return -1; + if(flag['i']!=0){ + if(wdirfd==-2) /* try only once */ + wdirfd = open("/dev/wdir", OWRITE|OCEXEC); + if(wdirfd>=0) + write(wdirfd, word, strlen(word)); + } + return 1; +} +void execcd(void){ + word *a=runq->argv->words; + word *cdpath; + char dir[512]; + setstatus("can't cd"); + cdpath=vlook("cdpath")->val; + switch(count(a)){ + default: + pfmt(err, "Usage: cd [directory]\n"); + break; + case 2: + if(a->next->word[0]=='/' || cdpath==0) cdpath=&nullpath; + for(;cdpath;cdpath=cdpath->next){ + strcpy(dir, cdpath->word); + if(dir[0]) strcat(dir, "/"); + strcat(dir, a->next->word); + if(dochdir(dir)>=0){ + if(strlen(cdpath->word) + && strcmp(cdpath->word, ".")!=0) + pfmt(err, "%s\n", dir); + setstatus(""); + break; + } + } + if(cdpath==0) pfmt(err, "Can't cd %s: %r\n", a->next->word); + break; + case 1: + a=vlook("HOME")->val; + if(count(a)>=1){ + if(dochdir(a->word)>=0) + setstatus(""); + else + pfmt(err, "Can't cd %s: %r\n", a->word); + } + else + pfmt(err, "Can't cd -- $home empty\n"); + break; + } + poplist(); +} +void execexit(void){ + switch(count(runq->argv->words)){ + default: pfmt(err, "Usage: exit [status]\nExiting anyway\n"); + case 2: setstatus(runq->argv->words->next->word); + case 1: Xexit(); + } +} +void execshift(void){ + int n; + word *a; + var *star; + switch(count(runq->argv->words)){ + default: + pfmt(err, "Usage: shift [n]\n"); + setstatus("shift usage"); + poplist(); + return; + case 2: n=atoi(runq->argv->words->next->word); break; + case 1: n=1; break; + } + star=vlook("*"); + for(;n && star->val;--n){ + a=star->val->next; + efree(star->val->word); + efree((char *)star->val); + star->val=a; + star->changed=1; + } + setstatus(""); + poplist(); +} +int octal(char *s) +{ + int n=0; + while(*s==' ' || *s=='\t' || *s=='\n') s++; + while('0'<=*s && *s<='7') n=n*8+*s++-'0'; + return n; +} +int mapfd(int fd) +{ + redir *rp; + for(rp=runq->redir;rp;rp=rp->next){ + switch(rp->type){ + case RCLOSE: + if(rp->from==fd) fd=-1; + break; + case RDUP: + case ROPEN: + if(rp->to==fd) fd=rp->from; + break; + } + } + return fd; +} +union code rdcmds[4]; +void execcmds(io *f) +{ + static int first=1; + if(first){ + rdcmds[0].i=1; + rdcmds[1].f=Xrdcmds; + rdcmds[2].f=Xreturn; + first=0; + } + start(rdcmds, 1, runq->local); + runq->cmdfd=f; + runq->iflast=0; +} +void execeval(void){ + char *cmdline, *s, *t; + int len=0; + word *ap; + if(count(runq->argv->words)<=1){ + Xerror1("Usage: eval cmd ..."); + return; + } + eflagok=1; + for(ap=runq->argv->words->next;ap;ap=ap->next) + len+=1+strlen(ap->word); + cmdline=emalloc(len); + s=cmdline; + for(ap=runq->argv->words->next;ap;ap=ap->next){ + for(t=ap->word;*t;) *s++=*t++; + *s++=' '; + } + s[-1]='\n'; + poplist(); + execcmds(opencore(cmdline, len)); + efree(cmdline); +} +union code dotcmds[14]; +void execdot(void){ + int iflag=0; + int fd; + list *av; + thread *p=runq; + char *zero; + static int first=1; + char file[512]; + word *path; + if(first){ + dotcmds[0].i=1; + dotcmds[1].f=Xmark; + dotcmds[2].f=Xword; + dotcmds[3].s="0"; + dotcmds[4].f=Xlocal; + dotcmds[5].f=Xmark; + dotcmds[6].f=Xword; + dotcmds[7].s="*"; + dotcmds[8].f=Xlocal; + dotcmds[9].f=Xrdcmds; + dotcmds[10].f=Xunlocal; + dotcmds[11].f=Xunlocal; + dotcmds[12].f=Xreturn; + first=0; + } + else + eflagok=1; + popword(); + if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){ + iflag=1; + popword(); + } + /* get input file */ + if(p->argv->words==0){ + Xerror1("Usage: . [-i] file [arg ...]"); + return; + } + zero=strdup(p->argv->words->word); + popword(); + fd=-1; + for(path=searchpath(zero);path;path=path->next){ + strcpy(file, path->word); + if(file[0]) strcat(file, "/"); + strcat(file, zero); + if(strcmp(file, "/dev/stdin")==0){ /* for sun & ucb */ + fd=Dup1(0); + if(fd>=0) break; + } + if((fd=open(file, 0))>=0) break; + } + if(fd<0){ + pfmt(err, "%s: ", zero); + setstatus("can't open"); + Xerror(".: can't open"); + return; + } + /* set up for a new command loop */ + start(dotcmds, 1, (struct var *)0); + pushredir(RCLOSE, fd, 0); + runq->cmdfile=zero; + runq->cmdfd=openfd(fd); + runq->iflag=iflag; + runq->iflast=0; + /* push $* value */ + pushlist(); + runq->argv->words=p->argv->words; + /* free caller's copy of $* */ + av=p->argv; + p->argv=av->next; + efree((char *)av); + /* push $0 value */ + pushlist(); + pushword(zero); + ndot++; +} +void execflag(void){ + char *letter, *val; + switch(count(runq->argv->words)){ + case 2: + setstatus(flag[(uchar)runq->argv->words->next->word[0]]?"":"flag not set"); + break; + case 3: + letter=runq->argv->words->next->word; + val=runq->argv->words->next->next->word; + if(strlen(letter)==1){ + if(strcmp(val, "+")==0){ + flag[(uchar)letter[0]]=flagset; + break; + } + if(strcmp(val, "-")==0){ + flag[(uchar)letter[0]]=0; + break; + } + } + default: + Xerror1("Usage: flag [letter] [+-]"); + return; + } + poplist(); +} +void execwhatis(void){ /* mildly wrong -- should fork before writing */ + word *a, *b, *path; + var *v; + struct builtin *bp; + char file[512]; + struct io out[1]; + int found, sep; + a=runq->argv->words->next; + if(a==0){ + Xerror1("Usage: whatis name ..."); + return; + } + setstatus(""); + out->fd=mapfd(1); + out->bufp=out->buf; + out->ebuf=&out->buf[NBUF]; + out->strp=0; + for(;a;a=a->next){ + v=vlook(a->word); + if(v->val){ + pfmt(out, "%s=", a->word); + if(v->val->next==0) + pfmt(out, "%q\n", v->val->word); + else{ + sep='('; + for(b=v->val;b && b->word;b=b->next){ + pfmt(out, "%c%q", sep, b->word); + sep=' '; + } + pfmt(out, ")\n"); + } + found=1; + } + else + found=0; + v=gvlook(a->word); + if(v->fn) pfmt(out, "fn %s %s\n", v->name, v->fn[v->pc-1].s); + else{ + for(bp=Builtin;bp->name;bp++) + if(strcmp(a->word, bp->name)==0){ + pfmt(out, "builtin %s\n", a->word); + break; + } + if(!bp->name){ + for(path=searchpath(a->word);path;path=path->next){ + strcpy(file, path->word); + if(file[0]) strcat(file, "/"); + strcat(file, a->word); + if(Executable(file)){ + pfmt(out, "%s\n", file); + break; + } + } + if(!path && !found){ + pfmt(err, "%s: not found\n", a->word); + setstatus("not found"); + } + } + } + } + poplist(); + flush(err); +} +void execwait(void){ + switch(count(runq->argv->words)){ + default: Xerror1("Usage: wait [pid]"); return; + case 2: Waitfor(atoi(runq->argv->words->next->word), 0); break; + case 1: Waitfor(-1, 0); break; + } + poplist(); +} diff --git a/rc/subr.c b/rc/subr.c @@ -0,0 +1,59 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +char *emalloc(long n){ + char *p=(char *)Malloc(n); + if(p==0) panic("Can't malloc %d bytes", n); +/* if(err){ pfmt(err, "malloc %d->%p\n", n, p); flush(err); } *//**/ + return p; +} +void efree(char *p) +{ +/* pfmt(err, "free %p\n", p); flush(err); *//**/ + if(p) free(p); + else pfmt(err, "free 0\n"); +} +extern int lastword, lastdol; +void yyerror(char *m) +{ + pfmt(err, "rc: "); + if(runq->cmdfile && !runq->iflag) + pfmt(err, "%s:%d: ", runq->cmdfile, runq->lineno); + else if(runq->cmdfile) + pfmt(err, "%s: ", runq->cmdfile); + else if(!runq->iflag) + pfmt(err, "line %d: ", runq->lineno); + if(tok[0] && tok[0]!='\n') pfmt(err, "token %q: ", tok); + pfmt(err, "%s\n", m); + flush(err); + lastword=0; + lastdol=0; + while(lastc!='\n' && lastc!=EOF) advance(); + nerror++; + setvar("status", newword(m, (word *)0)); +} +char *bp; +void iacvt(int n){ + if(n<0){ + *bp++='-'; + n=-n; /* doesn't work for n==-inf */ + } + if(n/10) + iacvt(n/10); + *bp++=n%10+'0'; +} +void itoa(char *s, long n) +{ + bp=s; + iacvt(n); + *bp='\0'; +} +void panic(char *s, int n) +{ + pfmt(err, "rc: "); + pfmt(err, s, n); + pchr(err, '\n'); + flush(err); + Abort(); +} diff --git a/rc/syn.y b/rc/syn.y @@ -0,0 +1,91 @@ +%term FOR IN WHILE IF NOT TWIDDLE BANG SUBSHELL SWITCH FN +%term WORD REDIR DUP PIPE SUB +%term SIMPLE ARGLIST WORDS BRACE PAREN PCMD PIPEFD /* not used in syntax */ +/* operator priorities -- lowest first */ +%left IF WHILE FOR SWITCH ')' NOT +%left ANDAND OROR +%left BANG SUBSHELL +%left PIPE +%left '^' +%right '$' COUNT '"' +%left SUB +%{ +#include "rc.h" +#include "fns.h" +%} +%union{ + struct tree *tree; +}; +%type<tree> line paren brace body cmdsa cmdsan assign epilog redir +%type<tree> cmd simple first word comword keyword words +%type<tree> NOT FOR IN WHILE IF TWIDDLE BANG SUBSHELL SWITCH FN +%type<tree> WORD REDIR DUP PIPE +%% +rc: { return 1;} +| line '\n' {return !compile($1);} +line: cmd +| cmdsa line {$$=tree2(';', $1, $2);} +body: cmd +| cmdsan body {$$=tree2(';', $1, $2);} +cmdsa: cmd ';' +| cmd '&' {$$=tree1('&', $1);} +cmdsan: cmdsa +| cmd '\n' +brace: '{' body '}' {$$=tree1(BRACE, $2);} +paren: '(' body ')' {$$=tree1(PCMD, $2);} +assign: first '=' word {$$=tree2('=', $1, $3);} +epilog: {$$=0;} +| redir epilog {$$=mung2($1, $1->child[0], $2);} +redir: REDIR word {$$=mung1($1, $1->rtype==HERE?heredoc($2):$2);} +| DUP +cmd: {$$=0;} +| brace epilog {$$=epimung($1, $2);} +| IF paren {skipnl();} cmd + {$$=mung2($1, $2, $4);} +| IF NOT {skipnl();} cmd {$$=mung1($2, $4);} +| FOR '(' word IN words ')' {skipnl();} cmd + /* + * if ``words'' is nil, we need a tree element to distinguish between + * for(i in ) and for(i), the former being a loop over the empty set + * and the latter being the implicit argument loop. so if $5 is nil + * (the empty set), we represent it as "()". don't parenthesize non-nil + * functions, to avoid growing parentheses every time we reread the + * definition. + */ + {$$=mung3($1, $3, $5 ? $5 : tree1(PAREN, $5), $8);} +| FOR '(' word ')' {skipnl();} cmd + {$$=mung3($1, $3, (struct tree *)0, $6);} +| WHILE paren {skipnl();} cmd + {$$=mung2($1, $2, $4);} +| SWITCH word {skipnl();} brace + {$$=tree2(SWITCH, $2, $4);} +| simple {$$=simplemung($1);} +| TWIDDLE word words {$$=mung2($1, $2, $3);} +| cmd ANDAND cmd {$$=tree2(ANDAND, $1, $3);} +| cmd OROR cmd {$$=tree2(OROR, $1, $3);} +| cmd PIPE cmd {$$=mung2($2, $1, $3);} +| redir cmd %prec BANG {$$=mung2($1, $1->child[0], $2);} +| assign cmd %prec BANG {$$=mung3($1, $1->child[0], $1->child[1], $2);} +| BANG cmd {$$=mung1($1, $2);} +| SUBSHELL cmd {$$=mung1($1, $2);} +| FN words brace {$$=tree2(FN, $2, $3);} +| FN words {$$=tree1(FN, $2);} +simple: first +| simple word {$$=tree2(ARGLIST, $1, $2);} +| simple redir {$$=tree2(ARGLIST, $1, $2);} +first: comword +| first '^' word {$$=tree2('^', $1, $3);} +word: keyword {lastword=1; $1->type=WORD;} +| comword +| word '^' word {$$=tree2('^', $1, $3);} +comword: '$' word {$$=tree1('$', $2);} +| '$' word SUB words ')' {$$=tree2(SUB, $2, $4);} +| '"' word {$$=tree1('"', $2);} +| COUNT word {$$=tree1(COUNT, $2);} +| WORD +| '`' brace {$$=tree1('`', $2);} +| '(' words ')' {$$=tree1(PAREN, $2);} +| REDIR brace {$$=mung1($1, $2); $$->type=PIPEFD;} +keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN +words: {$$=(struct tree*)0;} +| words word {$$=tree2(WORDS, $1, $2);} diff --git a/rc/trap.c b/rc/trap.c @@ -0,0 +1,34 @@ +#include "rc.h" +#include "exec.h" +#include "fns.h" +#include "io.h" +extern char *Signame[]; +void dotrap(void){ + register int i; + register struct var *trapreq; + register struct word *starval; + starval=vlook("*")->val; + while(ntrap) for(i=0;i!=NSIG;i++) while(trap[i]){ + --trap[i]; + --ntrap; + if(getpid()!=mypid) Exit(getstatus()); + trapreq=vlook(Signame[i]); + if(trapreq->fn){ + start(trapreq->fn, trapreq->pc, (struct var *)0); + runq->local=newvar(strdup("*"), runq->local); + runq->local->val=copywords(starval, (struct word *)0); + runq->local->changed=1; + runq->redir=runq->startredir=0; + } + else if(i==SIGINT || i==SIGQUIT){ + /* + * run the stack down until we uncover the + * command reading loop. Xreturn will exit + * if there is none (i.e. if this is not + * an interactive rc.) + */ + while(!runq->iflag) Xreturn(); + } + else Exit(getstatus()); + } +} diff --git a/rc/tree.c b/rc/tree.c @@ -0,0 +1,114 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +tree *treenodes; +/* + * create and clear a new tree node, and add it + * to the node list. + */ +tree *newtree(void){ + tree *t=new(tree); + t->iskw=0; + t->str=0; + t->child[0]=t->child[1]=t->child[2]=0; + t->next=treenodes; + treenodes=t; + return t; +} +void freenodes(void){ + tree *t, *u; + for(t=treenodes;t;t=u){ + u=t->next; + if(t->str) efree(t->str); + efree((char *)t); + } + treenodes=0; +} +tree *tree1(int type, tree *c0) +{ + return tree3(type, c0, (tree *)0, (tree *)0); +} +tree *tree2(int type, tree *c0, tree *c1) +{ + return tree3(type, c0, c1, (tree *)0); +} +tree *tree3(int type, tree *c0, tree *c1, tree *c2) +{ + tree *t; + if(type==';'){ + if(c0==0) return c1; + if(c1==0) return c0; + } + t=newtree(); + t->type=type; + t->child[0]=c0; + t->child[1]=c1; + t->child[2]=c2; + return t; +} +tree *mung1(tree *t, tree *c0) +{ + t->child[0]=c0; + return t; +} +tree *mung2(tree *t, tree *c0, tree *c1) +{ + t->child[0]=c0; + t->child[1]=c1; + return t; +} +tree *mung3(tree *t, tree *c0, tree *c1, tree *c2) +{ + t->child[0]=c0; + t->child[1]=c1; + t->child[2]=c2; + return t; +} +tree *epimung(tree *comp, tree *epi) +{ + tree *p; + if(epi==0) return comp; + for(p=epi;p->child[1];p=p->child[1]); + p->child[1]=comp; + return epi; +} +/* + * Add a SIMPLE node at the root of t and percolate all the redirections + * up to the root. + */ +tree *simplemung(tree *t) +{ + tree *u; + struct io *s; + t=tree1(SIMPLE, t); + s=openstr(); + pfmt(s, "%t", t); + t->str=strdup(s->strp); + closeio(s); + for(u=t->child[0];u->type==ARGLIST;u=u->child[0]){ + if(u->child[1]->type==DUP + || u->child[1]->type==REDIR){ + u->child[1]->child[1]=t; + t=u->child[1]; + u->child[1]=0; + } + } + return t; +} +tree *token(char *str, int type) +{ + tree *t=newtree(); + t->type=type; + t->str=strdup(str); + return t; +} +void freetree(tree *p) +{ + if(p==0) return; + freetree(p->child[0]); + freetree(p->child[1]); + freetree(p->child[2]); + if(p->str) efree(p->str); + efree((char *)p); +} diff --git a/rc/unixcrap.c b/rc/unixcrap.c @@ -0,0 +1,211 @@ +#include <u.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/resource.h> +#include <libc.h> +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +#include "getflags.h" + +extern char **mkargv(word*); +extern int mapfd(int); + +static char *eargs = "cdflmnstuv"; +static int rlx[] = { + RLIMIT_CORE, + RLIMIT_DATA, + RLIMIT_FSIZE, +#ifdef RLIMIT_MEMLOCK + RLIMIT_MEMLOCK, +#else + 0, +#endif +#ifdef RLIMIT_RSS + RLIMIT_RSS, +#else + 0, +#endif + RLIMIT_NOFILE, + RLIMIT_STACK, + RLIMIT_CPU, +#ifdef RLIMIT_NPROC + RLIMIT_NPROC, +#else + 0, +#endif +#ifdef RLIMIT_RSS + RLIMIT_RSS, +#else + 0, +#endif +}; + +static void +eusage(void) +{ + fprint(mapfd(2), "usage: ulimit [-SHa%s [limit]]\n", eargs); +} + +#define Notset -4 +#define Unlimited -3 +#define Hard -2 +#define Soft -1 + +void +execulimit(void) +{ + int fd, n, argc, sethard, setsoft, limit; + int flag[256]; + char **argv, **oargv, *p; + char *argv0; + struct rlimit rl; + + argv0 = nil; + setstatus(""); + oargv = mkargv(runq->argv->words); + argv = oargv+1; + for(argc=0; argv[argc]; argc++) + ; + + memset(flag, 0, sizeof flag); + ARGBEGIN{ + default: + if(strchr(eargs, ARGC()) == nil){ + eusage(); + return; + } + case 'S': + case 'H': + case 'a': + flag[ARGC()] = 1; + break; + }ARGEND + + if(argc > 1){ + eusage(); + goto out; + } + + fd = mapfd(1); + + sethard = 1; + setsoft = 1; + if(flag['S'] && flag['H']) + ; + else if(flag['S']) + sethard = 0; + else if(flag['H']) + setsoft = 0; + + limit = Notset; + if(argc>0){ + if(strcmp(argv[0], "unlimited") == 0) + limit = Unlimited; + else if(strcmp(argv[0], "hard") == 0) + limit = Hard; + else if(strcmp(argv[0], "soft") == 0) + limit = Soft; + else if((limit = strtol(argv[0], &p, 0)) < 0 || *p != 0){ + eusage(); + goto out; + } + } + if(flag['a']){ + for(p=eargs; *p; p++){ + getrlimit(rlx[p-eargs], &rl); + n = flag['H'] ? rl.rlim_max : rl.rlim_cur; + if(n == -1) + fprint(fd, "ulimit -%c unlimited\n", *p); + else + fprint(fd, "ulimit -%c %d\n", *p, n); + } + goto out; + } + for(p=eargs; *p; p++){ + if(flag[(uchar)*p]){ + n = 0; + getrlimit(rlx[p-eargs], &rl); + switch(limit){ + case Notset: + n = flag['H'] ? rl.rlim_max : rl.rlim_cur; + if(n == -1) + fprint(fd, "ulimit -%c unlimited\n", *p); + else + fprint(fd, "ulimit -%c %d\n", *p, n); + break; + case Hard: + n = rl.rlim_max; + goto set; + case Soft: + n = rl.rlim_cur; + goto set; + case Unlimited: + n = -1; + goto set; + default: + n = limit; + set: + if(setsoft) + rl.rlim_cur = n; + if(sethard) + rl.rlim_max = n; + if(setrlimit(rlx[p-eargs], &rl) < 0) + fprint(mapfd(2), "setrlimit: %r\n"); + } + } + } + +out: + free(oargv); + poplist(); + flush(err); +} + +void +execumask(void) +{ + int n, argc; + char **argv, **oargv, *p; + char *argv0; + + argv0 = nil; + setstatus(""); + oargv = mkargv(runq->argv->words); + argv = oargv+1; + for(argc=0; argv[argc]; argc++) + ; + + ARGBEGIN{ + default: + usage: + fprint(mapfd(2), "usage: umask [mode]\n"); + goto out; + }ARGEND + + if(argc > 1) + goto usage; + + if(argc == 1){ + n = strtol(argv[0], &p, 8); + if(*p != 0 || p == argv[0]) + goto usage; + umask(n); + goto out; + } + + n = umask(0); + umask(n); + if(n < 0){ + fprint(mapfd(2), "umask: %r\n"); + goto out; + } + + fprint(mapfd(1), "umask %03o\n", n); + +out: + free(oargv); + poplist(); + flush(err); +} diff --git a/rc/var.c b/rc/var.c @@ -0,0 +1,131 @@ +#include "rc.h" +#include "exec.h" +#include "fns.h" +int hash(char *s, int n) +{ + register int h=0, i=1; + while(*s) h+=*s++*i++; + h%=n; + return h<0?h+n:h; +} +#define NKW 30 +struct kw{ + char *name; + int type; + struct kw *next; +}*kw[NKW]; +void kenter(int type, char *name) +{ + register int h=hash(name, NKW); + register struct kw *p=new(struct kw); + p->type=type; + p->name=name; + p->next=kw[h]; + kw[h]=p; +} +void kinit(void){ + kenter(FOR, "for"); + kenter(IN, "in"); + kenter(WHILE, "while"); + kenter(IF, "if"); + kenter(NOT, "not"); + kenter(TWIDDLE, "~"); + kenter(BANG, "!"); + kenter(SUBSHELL, "@"); + kenter(SWITCH, "switch"); + kenter(FN, "fn"); +} +tree *klook(char *name) +{ + struct kw *p; + tree *t=token(name, WORD); + for(p=kw[hash(name, NKW)];p;p=p->next) + if(strcmp(p->name, name)==0){ + t->type=p->type; + t->iskw=1; + break; + } + return t; +} +var *gvlook(char *name) +{ + int h=hash(name, NVAR); + var *v; + for(v=gvar[h];v;v=v->next) if(strcmp(v->name, name)==0) return v; + return gvar[h]=newvar(strdup(name), gvar[h]); +} +var *vlook(char *name) +{ + var *v; + if(runq) + for(v=runq->local;v;v=v->next) + if(strcmp(v->name, name)==0) return v; + return gvlook(name); +} +void _setvar(char *name, word *val, int callfn) +{ + register struct var *v=vlook(name); + freewords(v->val); + v->val=val; + v->changed=1; + if(callfn && v->changefn) + v->changefn(v); +} +void setvar(char *name, word *val) +{ + _setvar(name, val, 1); +} +void bigpath(var *v) +{ + /* convert $PATH to $path */ + char *p, *q; + word **l, *w; + + if(v->val == nil){ + _setvar("path", nil, 0); + return; + } + p = v->val->word; + w = nil; + l = &w; + /* + * Doesn't handle escaped colon nonsense. + */ + if(p[0] == 0) + p = nil; + while(p){ + q = strchr(p, ':'); + if(q) + *q = 0; + *l = newword(p[0] ? p : ".", nil); + l = &(*l)->next; + if(q){ + *q = ':'; + p = q+1; + }else + p = nil; + } + _setvar("path", w, 0); +} +void littlepath(var *v) +{ + /* convert $path to $PATH */ + char *p; + word *w; + + p = _list2str(v->val, ':'); + w = new(word); + w->word = p; + w->next = nil; + _setvar("PATH", w, 1); /* 1: recompute $path to expose colon problems */ +} +void pathinit(void) +{ + var *v; + + v = gvlook("path"); + v->changefn = littlepath; + v = gvlook("PATH"); + v->changefn = bigpath; + bigpath(v); +} diff --git a/rc/y.tab.h b/rc/y.tab.h @@ -0,0 +1,30 @@ +#define FOR 57346 +#define IN 57347 +#define WHILE 57348 +#define IF 57349 +#define NOT 57350 +#define TWIDDLE 57351 +#define BANG 57352 +#define SUBSHELL 57353 +#define SWITCH 57354 +#define FN 57355 +#define WORD 57356 +#define REDIR 57357 +#define DUP 57358 +#define PIPE 57359 +#define SUB 57360 +#define SIMPLE 57361 +#define ARGLIST 57362 +#define WORDS 57363 +#define BRACE 57364 +#define PAREN 57365 +#define PCMD 57366 +#define PIPEFD 57367 +#define ANDAND 57368 +#define OROR 57369 +#define COUNT 57370 + +typedef union { + struct tree *tree; +} YYSTYPE; +extern YYSTYPE yylval; diff --git a/sed/Makefile b/sed/Makefile @@ -0,0 +1,36 @@ +# sed - sed unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = sed + +OFILES = sed.o + +MANFILES = sed.1 + +all: ${TARG} + echo built ${TARG} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 diff --git a/sed/sed.1 b/sed/sed.1 @@ -0,0 +1,385 @@ +.TH SED 1 +.SH NAME +sed \- stream editor +.SH SYNOPSIS +.B sed +[ +.B -n +] +[ +.B -g +] +[ +.B -e +.I script +] +[ +.B -f +.I sfile +] +[ +.I file ... +] +.SH DESCRIPTION +.I Sed +copies the named +.I files +(standard input default) to the standard output, +edited according to a script of commands. +The +.B -f +option causes the script to be taken from file +.IR sfile ; +these options accumulate. +If there is just one +.B -e +option and no +.BR -f 's, +the flag +.B -e +may be omitted. +The +.B -n +option suppresses the default output; +.B -g +causes all substitutions to be global, as if suffixed +.BR g . +.PP +A script consists of editing commands, one per line, +of the following form: +.IP +[\fIaddress\fR [\fL,\fI address\fR] ] \fIfunction\fR [\fIargument\fR ...] +.PP +In normal operation +.I sed +cyclically copies a line of input into a +.I pattern space +(unless there is something left after +a +.L D +command), +applies in sequence +all commands whose +.I addresses +select that pattern space, +and at the end of the script copies the pattern space +to the standard output (except under +.BR -n ) +and deletes the pattern space. +.PP +An +.I address +is either a decimal number that counts +input lines cumulatively across files, a +.L $ +that +addresses the last line of input, or a context address, +.BI / regular-expression / \f1, +in the style of +.IR regexp (7), +with the added convention that +.L \en +matches a +newline embedded in the pattern space. +.PP +A command line with no addresses selects every pattern space. +.PP +A command line with +one address selects each pattern space that matches the address. +.PP +A command line with +two addresses selects the inclusive range from the first +pattern space that matches the first address through +the next pattern space that matches +the second. +(If the second address is a number less than or equal +to the line number first selected, only one +line is selected.) +Thereafter the process is repeated, looking again for the +first address. +.PP +Editing commands can be applied to non-selected pattern +spaces by use of the negation function +.L ! +(below). +.PP +An argument denoted +.I text +consists of one or more lines, +all but the last of which end with +.L \e +to hide the +newline. +Backslashes in text are treated like backslashes +in the replacement string of an +.L s +command, +and may be used to protect initial blanks and tabs +against the stripping that is done on +every script line. +.PP +An argument denoted +.I rfile +or +.I wfile +must terminate the command +line and must be preceded by exactly one blank. +Each +.I wfile +is created before processing begins. +There can be at most 120 distinct +.I wfile +arguments. +.TP \w'\fL!\ \fIfunction\fLXXX'u +.B a\e +.br +.ns +.TP +.I text +Append. +Place +.I text +on the output before +reading the next input line. +.TP +.BI b " label" +Branch to the +.B : +command bearing the +.IR label . +If +.I label +is empty, branch to the end of the script. +.TP +.B c\e +.br +.ns +.TP +.I text +Change. +Delete the pattern space. +With 0 or 1 address or at the end of a 2-address range, place +.I text +on the output. +Start the next cycle. +.TP +.B d +Delete the pattern space. +Start the next cycle. +.TP +.B D +Delete the initial segment of the +pattern space through the first newline. +Start the next cycle. +.TP +.B g +Replace the contents of the pattern space +by the contents of the hold space. +.TP +.B G +Append the contents of the hold space to the pattern space. +.TP +.B h +Replace the contents of the hold space by the contents of the pattern space. +.TP +.B H +Append the contents of the pattern space to the hold space. +.ne 3 +.TP +.B i\e +.br +.ns +.TP +.I text +Insert. +Place +.I text +on the standard output. +.TP +.B n +Copy the pattern space to the standard output. +Replace the pattern space with the next line of input. +.TP +.B N +Append the next line of input to the pattern space +with an embedded newline. +(The current line number changes.) +.TP +.B p +Print. +Copy the pattern space to the standard output. +.TP +.B P +Copy the initial segment of the pattern space through +the first newline to the standard output. +.TP +.B q +Quit. +Branch to the end of the script. +Do not start a new cycle. +.TP +.BI r " rfile" +Read the contents of +.IR rfile . +Place them on the output before reading +the next input line. +.TP +.B s/\fIregular-expression\fP/\fIreplacement\fP/\fIflags +Substitute the +.I replacement +string for instances of the +.I regular-expression +in the pattern space. +Any character may be used instead of +.LR / . +For a fuller description see +.IR regexp (7). +.I Flags +is zero or more of +.RS +.TP +.B g +Global. +Substitute for all non-overlapping instances of the +.I regular expression +rather than just the +first one. +.TP +.B p +Print the pattern space if a replacement was made. +.TP +.BI w " wfile" +Write. +Append the pattern space to +.I wfile +if a replacement +was made. +.RE +.TP +.BI t " label" +Test. +Branch to the +.L : +command bearing the +.I label +if any +substitutions have been made since the most recent +reading of an input line or execution of a +.LR t . +If +.I label +is empty, branch to the end of the script. +.TP +.B w +.I wfile +.br +Write. +Append the pattern space to +.IR wfile . +.TP +.B x +Exchange the contents of the pattern and hold spaces. +.TP +.B y/\fIstring1\fP/\fIstring2\fP/ +Transform. +Replace all occurrences of characters in +.I string1 +with the corresponding character in +.IR string2 . +The lengths of +.I +string1 +and +.I string2 +must be equal. +.TP +.BI ! "function" +Don't. +Apply the +.I function +(or group, if +.I function +is +.LR { ) +only to lines +.I not +selected by the address(es). +.TP +.BI : " label" +This command does nothing; it bears a +.I label +for +.B b +and +.B t +commands to branch to. +.TP +.B = +Place the current line number on the standard output as a line. +.TP +.B { +Execute the following commands through a matching +.L } +only when the pattern space is selected. +.TP +.B " " +An empty command is ignored. +.ne 4 +.SH EXAMPLES +.TP +.B sed 10q file +Print the first 10 lines of the file. +.TP +.B sed '/^$/d' +Delete empty lines from standard input. +.TP +.B sed 's/UNIX/& system/g' +Replace every instance of +.L UNIX +by +.LR "UNIX system" . +.PP +.EX +sed 's/ *$// \fRdrop trailing blanks\fP +/^$/d \fRdrop empty lines\fP +s/ */\e \fRreplace blanks by newlines\fP +/g +/^$/d' chapter* +.EE +.ns +.IP +Print the files +.BR chapter1 , +.BR chapter2 , +etc. one word to a line. +.PP +.EX +nroff -ms manuscript | sed ' +${ + /^$/p \fRif last line of file is empty, print it\fP +} +//N \fRif current line is empty, append next line\fP +/^\en$/D' \fRif two lines are empty, delete the first\fP +.EE +.ns +.IP +Delete all but one of each group of empty lines from a +formatted manuscript. +.SH SOURCE +.B \*9/src/cmd/sed.c +.SH SEE ALSO +.IR ed (1), +.IR grep (1), +.IR awk (1), +.IR lex (1), +.IR sam (1), +.IR regexp (7) +.br +L. E. McMahon, +`SED \(em A Non-interactive Text Editor', +Unix Research System Programmer's Manual, Volume 2. +.SH BUGS +If input is from a pipe, buffering may consume +characters beyond a line on which a +.L q +command is executed. diff --git a/sed/sed.c b/sed/sed.c @@ -0,0 +1,1447 @@ +/* + * sed -- stream editor + * + * + */ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <regexp.h> + +enum { + DEPTH = 20, /* max nesting depth of {} */ + MAXCMDS = 512, /* max sed commands */ + ADDSIZE = 10000, /* size of add & read buffer */ + MAXADDS = 20, /* max pending adds and reads */ + LBSIZE = 8192, /* input line size */ + LABSIZE = 50, /* max label name size */ + MAXSUB = 10, /* max number of sub reg exp */ + MAXFILES = 120, /* max output files */ +}; + /* An address is a line #, a R.E., "$", a reference to the last + * R.E., or nothing. + */ +typedef struct { + enum { + A_NONE, + A_DOL, + A_LINE, + A_RE, + A_LAST, + }type; + union { + long line; /* Line # */ + Reprog *rp; /* Compiled R.E. */ + } u; +} Addr; + +typedef struct SEDCOM { + Addr ad1; /* optional start address */ + Addr ad2; /* optional end address */ + union { + Reprog *re1; /* compiled R.E. */ + Rune *text; /* added text or file name */ + struct SEDCOM *lb1; /* destination command of branch */ + } u; + Rune *rhs; /* Right-hand side of substitution */ + Biobuf* fcode; /* File ID for read and write */ + char command; /* command code -see below */ + char gfl; /* 'Global' flag for substitutions */ + char pfl; /* 'print' flag for substitutions */ + char active; /* 1 => data between start and end */ + char negfl; /* negation flag */ +} SedCom; + + /* Command Codes for field SedCom.command */ +#define ACOM 01 +#define BCOM 020 +#define CCOM 02 +#define CDCOM 025 +#define CNCOM 022 +#define COCOM 017 +#define CPCOM 023 +#define DCOM 03 +#define ECOM 015 +#define EQCOM 013 +#define FCOM 016 +#define GCOM 027 +#define CGCOM 030 +#define HCOM 031 +#define CHCOM 032 +#define ICOM 04 +#define LCOM 05 +#define NCOM 012 +#define PCOM 010 +#define QCOM 011 +#define RCOM 06 +#define SCOM 07 +#define TCOM 021 +#define WCOM 014 +#define CWCOM 024 +#define YCOM 026 +#define XCOM 033 + + +typedef struct label { /* Label symbol table */ + Rune asc[9]; /* Label name */ + SedCom *chain; + SedCom *address; /* Command associated with label */ +} Label; + +typedef struct FILE_CACHE { /* Data file control block */ + struct FILE_CACHE *next; /* Forward Link */ + char *name; /* Name of file */ +} FileCache; + +SedCom pspace[MAXCMDS]; /* Command storage */ +SedCom *pend = pspace+MAXCMDS; /* End of command storage */ +SedCom *rep = pspace; /* Current fill point */ + +Reprog *lastre = 0; /* Last regular expression */ +Resub subexp[MAXSUB]; /* sub-patterns of pattern match*/ + +Rune addspace[ADDSIZE]; /* Buffer for a, c, & i commands */ +Rune *addend = addspace+ADDSIZE; + +SedCom *abuf[MAXADDS]; /* Queue of pending adds & reads */ +SedCom **aptr = abuf; + +struct { /* Sed program input control block */ + enum PTYPE /* Either on command line or in file */ + { P_ARG, + P_FILE + } type; + union PCTL { /* Pointer to data */ + Biobuf *bp; + char *curr; + } pctl; +} prog; + +Rune genbuf[LBSIZE]; /* Miscellaneous buffer */ + +FileCache *fhead = 0; /* Head of File Cache Chain */ +FileCache *ftail = 0; /* Tail of File Cache Chain */ + +Rune *loc1; /* Start of pattern match */ +Rune *loc2; /* End of pattern match */ +Rune seof; /* Pattern delimiter char */ + +Rune linebuf[LBSIZE+1]; /* Input data buffer */ +Rune *lbend = linebuf+LBSIZE; /* End of buffer */ +Rune *spend = linebuf; /* End of input data */ +Rune *cp; /* Current scan point in linebuf */ + +Rune holdsp[LBSIZE+1]; /* Hold buffer */ +Rune *hend = holdsp+LBSIZE; /* End of hold buffer */ +Rune *hspend = holdsp; /* End of hold data */ + +int nflag; /* Command line flags */ +int gflag; + +int dolflag; /* Set when at true EOF */ +int sflag; /* Set when substitution done */ +int jflag; /* Set when jump required */ +int delflag; /* Delete current line when set */ + +long lnum = 0; /* Input line count */ + +char fname[MAXFILES][40]; /* File name cache */ +Biobuf *fcode[MAXFILES]; /* File ID cache */ +int nfiles = 0; /* Cache fill point */ + +Biobuf fout; /* Output stream */ +Biobuf bstdin; /* Default input */ +Biobuf* f = 0; /* Input data */ + +Label ltab[LABSIZE]; /* Label name symbol table */ +Label *labend = ltab+LABSIZE; /* End of label table */ +Label *lab = ltab+1; /* Current Fill point */ + +int depth = 0; /* {} stack pointer */ + +Rune bad; /* Dummy err ptr reference */ +Rune *badp = &bad; + + +char CGMES[] = "Command garbled: %S"; +char TMMES[] = "Too much text: %S"; +char LTL[] = "Label too long: %S"; +char AD0MES[] = "No addresses allowed: %S"; +char AD1MES[] = "Only one address allowed: %S"; + +void address(Addr *); +void arout(void); +int cmp(char *, char *); +int rcmp(Rune *, Rune *); +void command(SedCom *); +Reprog *compile(void); +Rune *compsub(Rune *, Rune *); +void dechain(void); +void dosub(Rune *); +int ecmp(Rune *, Rune *, int); +void enroll(char *); +void errexit(void); +int executable(SedCom *); +void execute(void); +void fcomp(void); +long getrune(void); +Rune *gline(Rune *); +int match(Reprog *, Rune *); +void newfile(enum PTYPE, char *); +int opendata(void); +Biobuf *open_file(char *); +Rune *place(Rune *, Rune *, Rune *); +void quit(char *, char *); +int rline(Rune *, Rune *); +Label *search(Label *); +int substitute(SedCom *); +char *text(char *); +Rune *stext(Rune *, Rune *); +int ycomp(SedCom *); +char * trans(int c); +void putline(Biobuf *bp, Rune *buf, int n); + +void +main(int argc, char **argv) +{ + int compfl; + + lnum = 0; + Binit(&fout, 1, OWRITE); + fcode[nfiles++] = &fout; + compfl = 0; + + if(argc == 1) + exits(0); + ARGBEGIN{ + case 'n': + nflag++; + continue; + case 'f': + if(argc <= 1) + quit("no pattern-file", 0); + newfile(P_FILE, ARGF()); + fcomp(); + compfl = 1; + continue; + case 'e': + if (argc <= 1) + quit("missing pattern", 0); + newfile(P_ARG, ARGF()); + fcomp(); + compfl = 1; + continue; + case 'g': + gflag++; + continue; + default: + fprint(2, "sed: Unknown flag: %c\n", ARGC()); + continue; + } ARGEND + + if(compfl == 0) { + if (--argc < 0) + quit("missing pattern", 0); + newfile(P_ARG, *argv++); + fcomp(); + } + + if(depth) + quit("Too many {'s", 0); + + ltab[0].address = rep; + + dechain(); + + if(argc <= 0) + enroll(0); /* Add stdin to cache */ + else while(--argc >= 0) { + enroll(*argv++); + } + execute(); + exits(0); +} +void +fcomp(void) +{ + Rune *tp; + SedCom *pt, *pt1; + int i; + Label *lpt; + + static Rune *p = addspace; + static SedCom **cmpend[DEPTH]; /* stack of {} operations */ + + while (rline(linebuf, lbend) >= 0) { + cp = linebuf; +comploop: + while(*cp == ' ' || *cp == '\t') + cp++; + if(*cp == '\0' || *cp == '#') + continue; + if(*cp == ';') { + cp++; + goto comploop; + } + + address(&rep->ad1); + if (rep->ad1.type != A_NONE) { + if (rep->ad1.type == A_LAST) { + if (!lastre) + quit("First RE may not be null", 0); + rep->ad1.type = A_RE; + rep->ad1.u.rp = lastre; + } + if(*cp == ',' || *cp == ';') { + cp++; + address(&rep->ad2); + if (rep->ad2.type == A_LAST) { + rep->ad1.type = A_RE; + rep->ad2.u.rp = lastre; + } + } else + rep->ad2.type = A_NONE; + } + while(*cp == ' ' || *cp == '\t') + cp++; + +swit: + switch(*cp++) { + + default: + quit("Unrecognized command: %S", (char *)linebuf); + + case '!': + rep->negfl = 1; + goto swit; + + case '{': + rep->command = BCOM; + rep->negfl = !(rep->negfl); + cmpend[depth++] = &rep->u.lb1; + if(++rep >= pend) + quit("Too many commands: %S", (char *) linebuf); + if(*cp == '\0') continue; + goto comploop; + + case '}': + if(rep->ad1.type != A_NONE) + quit(AD0MES, (char *) linebuf); + if(--depth < 0) + quit("Too many }'s", 0); + *cmpend[depth] = rep; + if(*cp == 0) continue; + goto comploop; + + case '=': + rep->command = EQCOM; + if(rep->ad2.type != A_NONE) + quit(AD1MES, (char *) linebuf); + break; + + case ':': + if(rep->ad1.type != A_NONE) + quit(AD0MES, (char *) linebuf); + + while(*cp == ' ') + cp++; + tp = lab->asc; + while (*cp && *cp != ';' && *cp != ' ' && *cp != '\t' && *cp != '#') { + *tp++ = *cp++; + if(tp >= &(lab->asc[8])) + quit(LTL, (char *) linebuf); + } + *tp = '\0'; + + if(lpt = search(lab)) { + if(lpt->address) + quit("Duplicate labels: %S", (char *) linebuf); + } else { + lab->chain = 0; + lpt = lab; + if(++lab >= labend) + quit("Too many labels: %S", (char *) linebuf); + } + lpt->address = rep; + if (*cp == '#') + continue; + rep--; /* reuse this slot */ + break; + + case 'a': + rep->command = ACOM; + if(rep->ad2.type != A_NONE) + quit(AD1MES, (char *) linebuf); + if(*cp == '\\') cp++; + if(*cp++ != '\n') + quit(CGMES, (char *) linebuf); + rep->u.text = p; + p = stext(p, addend); + break; + case 'c': + rep->command = CCOM; + if(*cp == '\\') cp++; + if(*cp++ != '\n') + quit(CGMES, (char *) linebuf); + rep->u.text = p; + p = stext(p, addend); + break; + case 'i': + rep->command = ICOM; + if(rep->ad2.type != A_NONE) + quit(AD1MES, (char *) linebuf); + if(*cp == '\\') cp++; + if(*cp++ != '\n') + quit(CGMES, (char *) linebuf); + rep->u.text = p; + p = stext(p, addend); + break; + + case 'g': + rep->command = GCOM; + break; + + case 'G': + rep->command = CGCOM; + break; + + case 'h': + rep->command = HCOM; + break; + + case 'H': + rep->command = CHCOM; + break; + + case 't': + rep->command = TCOM; + goto jtcommon; + + case 'b': + rep->command = BCOM; +jtcommon: + while(*cp == ' ')cp++; + if(*cp == '\0') { + if(pt = ltab[0].chain) { + while(pt1 = pt->u.lb1) + pt = pt1; + pt->u.lb1 = rep; + } else + ltab[0].chain = rep; + break; + } + tp = lab->asc; + while((*tp++ = *cp++)) + if(tp >= &(lab->asc[8])) + quit(LTL, (char *) linebuf); + cp--; + tp[-1] = '\0'; + + if(lpt = search(lab)) { + if(lpt->address) { + rep->u.lb1 = lpt->address; + } else { + pt = lpt->chain; + while(pt1 = pt->u.lb1) + pt = pt1; + pt->u.lb1 = rep; + } + } else { + lab->chain = rep; + lab->address = 0; + if(++lab >= labend) + quit("Too many labels: %S", + (char *) linebuf); + } + break; + + case 'n': + rep->command = NCOM; + break; + + case 'N': + rep->command = CNCOM; + break; + + case 'p': + rep->command = PCOM; + break; + + case 'P': + rep->command = CPCOM; + break; + + case 'r': + rep->command = RCOM; + if(rep->ad2.type != A_NONE) + quit(AD1MES, (char *) linebuf); + if(*cp++ != ' ') + quit(CGMES, (char *) linebuf); + rep->u.text = p; + p = stext(p, addend); + break; + + case 'd': + rep->command = DCOM; + break; + + case 'D': + rep->command = CDCOM; + rep->u.lb1 = pspace; + break; + + case 'q': + rep->command = QCOM; + if(rep->ad2.type != A_NONE) + quit(AD1MES, (char *) linebuf); + break; + + case 'l': + rep->command = LCOM; + break; + + case 's': + rep->command = SCOM; + seof = *cp++; + if ((rep->u.re1 = compile()) == 0) { + if(!lastre) + quit("First RE may not be null.", 0); + rep->u.re1 = lastre; + } + rep->rhs = p; + if((p = compsub(p, addend)) == 0) + quit(CGMES, (char *) linebuf); + if(*cp == 'g') { + cp++; + rep->gfl++; + } else if(gflag) + rep->gfl++; + + if(*cp == 'p') { + cp++; + rep->pfl = 1; + } + + if(*cp == 'P') { + cp++; + rep->pfl = 2; + } + + if(*cp == 'w') { + cp++; + if(*cp++ != ' ') + quit(CGMES, (char *) linebuf); + text(fname[nfiles]); + for(i = nfiles - 1; i >= 0; i--) + if(cmp(fname[nfiles],fname[i]) == 0) { + rep->fcode = fcode[i]; + goto done; + } + if(nfiles >= MAXFILES) + quit("Too many files in w commands 1", 0); + rep->fcode = open_file(fname[nfiles]); + } + break; + + case 'w': + rep->command = WCOM; + if(*cp++ != ' ') + quit(CGMES, (char *) linebuf); + text(fname[nfiles]); + for(i = nfiles - 1; i >= 0; i--) + if(cmp(fname[nfiles], fname[i]) == 0) { + rep->fcode = fcode[i]; + goto done; + } + if(nfiles >= MAXFILES){ + fprint(2, "sed: Too many files in w commands 2 \n"); + fprint(2, "nfiles = %d; MAXF = %d\n", nfiles, MAXFILES); + errexit(); + } + rep->fcode = open_file(fname[nfiles]); + break; + + case 'x': + rep->command = XCOM; + break; + + case 'y': + rep->command = YCOM; + seof = *cp++; + if (ycomp(rep) == 0) + quit(CGMES, (char *) linebuf); + break; + + } +done: + if(++rep >= pend) + quit("Too many commands, last: %S", (char *) linebuf); + + if(*cp++ != '\0') { + if(cp[-1] == ';') + goto comploop; + quit(CGMES, (char *) linebuf); + } + + } +} + +Biobuf * +open_file(char *name) +{ + Biobuf *bp; + int fd; + + if ((bp = malloc(sizeof(Biobuf))) == 0) + quit("Out of memory", 0); + if ((fd = open(name, OWRITE)) < 0 && + (fd = create(name, OWRITE, 0666)) < 0) + quit("Cannot create %s", name); + Binit(bp, fd, OWRITE); + Bseek(bp, 0, 2); + fcode[nfiles++] = bp; + return bp; +} + +Rune * +compsub(Rune *rhs, Rune *end) +{ + Rune r; + + while ((r = *cp++) != '\0') { + if(r == '\\') { + if (rhs < end) + *rhs++ = 0xFFFF; + else + return 0; + r = *cp++; + if(r == 'n') + r = '\n'; + } else { + if(r == seof) { + if (rhs < end) + *rhs++ = '\0'; + else + return 0; + return rhs; + } + } + if (rhs < end) + *rhs++ = r; + else + return 0; + + } + return 0; +} + +Reprog * +compile(void) +{ + Rune c; + char *ep; + char expbuf[512]; + + if((c = *cp++) == seof) /* '//' */ + return 0; + ep = expbuf; + do { + if (c == 0 || c == '\n') + quit(TMMES, (char *) linebuf); + if (c == '\\') { + if (ep >= expbuf+sizeof(expbuf)) + quit(TMMES, (char *) linebuf); + ep += runetochar(ep, &c); + if ((c = *cp++) == 'n') + c = '\n'; + } + if (ep >= expbuf+sizeof(expbuf)) + quit(TMMES, (char *) linebuf); + ep += runetochar(ep, &c); + } while ((c = *cp++) != seof); + *ep = 0; + return lastre = regcomp(expbuf); +} + +void +regerror(char *s) +{ + USED(s); + quit(CGMES, (char *) linebuf); +} + +void +newfile(enum PTYPE type, char *name) +{ + if (type == P_ARG) + prog.pctl.curr = name; + else if ((prog.pctl.bp = Bopen(name, OREAD)) == 0) + quit("Cannot open pattern-file: %s\n", name); + prog.type = type; +} + +int +rline(Rune *buf, Rune *end) +{ + long c; + Rune r; + + while ((c = getrune()) >= 0) { + r = c; + if (r == '\\') { + if (buf <= end) + *buf++ = r; + if ((c = getrune()) < 0) + break; + r = c; + } else if (r == '\n') { + *buf = '\0'; + return(1); + } + if (buf <= end) + *buf++ = r; + } + *buf = '\0'; + return(-1); +} + +long +getrune(void) +{ + char *p; + long c; + Rune r; + + if (prog.type == P_ARG) { + if ((p = prog.pctl.curr) != 0) { + if (*p) { + prog.pctl.curr += chartorune(&r, p); + c = r; + } else { + c = '\n'; /* fake an end-of-line */ + prog.pctl.curr = 0; + } + } else + c = -1; + } else if ((c = Bgetrune(prog.pctl.bp)) < 0) + Bterm(prog.pctl.bp); + return c; +} + +void +address(Addr *ap) +{ + int c; + long lno; + + if((c = *cp++) == '$') + ap->type = A_DOL; + else if(c == '/') { + seof = c; + if (ap->u.rp = compile()) + ap->type = A_RE; + else + ap->type = A_LAST; + } + else if (c >= '0' && c <= '9') { + lno = c-'0'; + while ((c = *cp) >= '0' && c <= '9') + lno = lno*10 + *cp++-'0'; + if(!lno) + quit("line number 0 is illegal",0); + ap->type = A_LINE; + ap->u.line = lno; + } + else { + cp--; + ap->type = A_NONE; + } +} + +int +cmp(char *a, char *b) /* compare characters */ +{ + while(*a == *b++) + if (*a == '\0') + return(0); + else a++; + return(1); +} + +int +rcmp(Rune *a, Rune *b) /* compare runes */ +{ + while(*a == *b++) + if (*a == '\0') + return(0); + else a++; + return(1); +} + +char * +text(char *p) /* extract character string */ +{ + Rune r; + + while(*cp == '\t' || *cp == ' ') + cp++; + while (*cp) { + if ((r = *cp++) == '\\') + if ((r = *cp++) == 0) + break;; + if (r == '\n') + while (*cp == '\t' || *cp == ' ') + cp++; + p += runetochar(p, &r); + } + *p++ = '\0'; + return p; +} + +Rune * +stext(Rune *p, Rune *end) /* extract rune string */ +{ + while(*cp == '\t' || *cp == ' ') + cp++; + while (*cp) { + if (*cp == '\\') + if (*++cp == 0) + break; + if (p >= end-1) + quit(TMMES, (char *) linebuf); + if ((*p++ = *cp++) == '\n') + while(*cp == '\t' || *cp == ' ') + cp++; + } + *p++ = 0; + return p; +} + + +Label * +search (Label *ptr) +{ + Label *rp; + + for (rp = ltab; rp < ptr; rp++) + if(rcmp(rp->asc, ptr->asc) == 0) + return(rp); + return(0); +} + +void +dechain(void) +{ + Label *lptr; + SedCom *rptr, *trptr; + + for(lptr = ltab; lptr < lab; lptr++) { + + if(lptr->address == 0) + quit("Undefined label: %S", (char *) lptr->asc); + + if(lptr->chain) { + rptr = lptr->chain; + while(trptr = rptr->u.lb1) { + rptr->u.lb1 = lptr->address; + rptr = trptr; + } + rptr->u.lb1 = lptr->address; + } + } +} + +int +ycomp(SedCom *r) +{ + int i; + Rune *rp; + Rune c, *tsp, highc; + Rune *sp; + + highc = 0; + for(tsp = cp; *tsp != seof; tsp++) { + if(*tsp == '\\') + tsp++; + if(*tsp == '\n' || *tsp == '\0') + return(0); + if (*tsp > highc) highc = *tsp; + } + tsp++; + if ((rp = r->u.text = (Rune *) malloc(sizeof(Rune)*(highc+2))) == 0) + quit("Out of memory", 0); + *rp++ = highc; /* save upper bound */ + for (i = 0; i <= highc; i++) + rp[i] = i; + sp = cp; + while((c = *sp++) != seof) { + if(c == '\\' && *sp == 'n') { + sp++; + c = '\n'; + } + if((rp[c] = *tsp++) == '\\' && *tsp == 'n') { + rp[c] = '\n'; + tsp++; + } + if(rp[c] == seof || rp[c] == '\0') { + free(r->u.re1); + r->u.re1 = 0; + return(0); + } + } + if(*tsp != seof) { + free(r->u.re1); + r->u.re1 = 0; + return(0); + } + cp = tsp+1; + return(1); +} + +void +execute(void) +{ + SedCom *ipc; + + while (spend = gline(linebuf)){ + for(ipc = pspace; ipc->command; ) { + if (!executable(ipc)) { + ipc++; + continue; + } + command(ipc); + + if(delflag) + break; + if(jflag) { + jflag = 0; + if((ipc = ipc->u.lb1) == 0) + break; + } else + ipc++; + + } + if(!nflag && !delflag) + putline(&fout, linebuf, spend-linebuf); + if(aptr > abuf) { + arout(); + } + delflag = 0; + } +} + /* determine if a statement should be applied to an input line */ +int +executable(SedCom *ipc) +{ + if (ipc->active) { /* Addr1 satisfied - accept until Addr2 */ + if (ipc->active == 1) /* Second line */ + ipc->active = 2; + switch(ipc->ad2.type) { + case A_NONE: /* No second addr; use first */ + ipc->active = 0; + break; + case A_DOL: /* Accept everything */ + return !ipc->negfl; + case A_LINE: /* Line at end of range? */ + if (lnum <= ipc->ad2.u.line) { + if (ipc->ad2.u.line == lnum) + ipc->active = 0; + return !ipc->negfl; + } + ipc->active = 0; /* out of range */ + return ipc->negfl; + case A_RE: /* Check for matching R.E. */ + if (match(ipc->ad2.u.rp, linebuf)) + ipc->active = 0; + return !ipc->negfl; + default: /* internal error */ + quit("Internal error", 0); + } + } + switch (ipc->ad1.type) { /* Check first address */ + case A_NONE: /* Everything matches */ + return !ipc->negfl; + case A_DOL: /* Only last line */ + if (dolflag) + return !ipc->negfl; + break; + case A_LINE: /* Check line number */ + if (ipc->ad1.u.line == lnum) { + ipc->active = 1; /* In range */ + return !ipc->negfl; + } + break; + case A_RE: /* Check R.E. */ + if (match(ipc->ad1.u.rp, linebuf)) { + ipc->active = 1; /* In range */ + return !ipc->negfl; + } + break; + default: + quit("Internal error", 0); + } + return ipc->negfl; +} + +int +match(Reprog *pattern, Rune *buf) +{ + if (!pattern) + return 0; + subexp[0].s.rsp = buf; + subexp[0].e.rep = 0; + if (rregexec(pattern, linebuf, subexp, MAXSUB)) { + loc1 = subexp[0].s.rsp; + loc2 = subexp[0].e.rep; + return 1; + } + loc1 = loc2 = 0; + return 0; +} + +int +substitute(SedCom *ipc) +{ + int len; + + if(!match(ipc->u.re1, linebuf)) + return 0; + + /* + * we have at least one match. some patterns, e.g. '$' or '^', can + * produce zero-length matches, so during a global substitute we + * must bump to the character after a zero-length match to keep from looping. + */ + sflag = 1; + if(ipc->gfl == 0) /* single substitution */ + dosub(ipc->rhs); + else + do{ /* global substitution */ + len = loc2-loc1; /* length of match */ + dosub(ipc->rhs); /* dosub moves loc2 */ + if(*loc2 == 0) /* end of string */ + break; + if(len == 0) /* zero-length R.E. match */ + loc2++; /* bump over zero-length match */ + if(*loc2 == 0) /* end of string */ + break; + } while(match(ipc->u.re1, loc2)); + return 1; +} + +void +dosub(Rune *rhsbuf) +{ + Rune *lp, *sp; + Rune *rp; + int c, n; + + lp = linebuf; + sp = genbuf; + rp = rhsbuf; + while (lp < loc1) + *sp++ = *lp++; + while(c = *rp++) { + if (c == '&') { + sp = place(sp, loc1, loc2); + continue; + } + if (c == 0xFFFF && (c = *rp++) >= '1' && c < MAXSUB+'0') { + n = c-'0'; + if (subexp[n].s.rsp && subexp[n].e.rep) { + sp = place(sp, subexp[n].s.rsp, subexp[n].e.rep); + continue; + } + else { + fprint(2, "sed: Invalid back reference \\%d\n",n); + errexit(); + } + } + *sp++ = c; + if (sp >= &genbuf[LBSIZE]) + fprint(2, "sed: Output line too long.\n"); + } + lp = loc2; + loc2 = sp - genbuf + linebuf; + while (*sp++ = *lp++) + if (sp >= &genbuf[LBSIZE]) + fprint(2, "sed: Output line too long.\n"); + lp = linebuf; + sp = genbuf; + while (*lp++ = *sp++) + ; + spend = lp-1; +} + +Rune * +place(Rune *sp, Rune *l1, Rune *l2) +{ + while (l1 < l2) { + *sp++ = *l1++; + if (sp >= &genbuf[LBSIZE]) + fprint(2, "sed: Output line too long.\n"); + } + return(sp); +} + +char * +trans(int c) +{ + static char buf[] = "\\x0000"; + static char hex[] = "0123456789abcdef"; + + switch(c) { + case '\b': + return "\\b"; + case '\n': + return "\\n"; + case '\r': + return "\\r"; + case '\t': + return "\\t"; + case '\\': + return "\\\\"; + } + buf[2] = hex[(c>>12)&0xF]; + buf[3] = hex[(c>>8)&0xF]; + buf[4] = hex[(c>>4)&0xF]; + buf[5] = hex[c&0xF]; + return buf; +} + +void +command(SedCom *ipc) +{ + int i, c; + Rune *p1, *p2; + char *ucp; + Rune *rp; + Rune *execp; + + switch(ipc->command) { + + case ACOM: + *aptr++ = ipc; + if(aptr >= abuf+MAXADDS) { + quit("sed: Too many appends after line %ld\n", + (char *) lnum); + } + *aptr = 0; + break; + case CCOM: + delflag = 1; + if(ipc->active == 1) { + for(rp = ipc->u.text; *rp; rp++) + Bputrune(&fout, *rp); + Bputc(&fout, '\n'); + } + break; + case DCOM: + delflag++; + break; + case CDCOM: + p1 = p2 = linebuf; + while(*p1 != '\n') { + if(*p1++ == 0) { + delflag++; + return; + } + } + p1++; + while(*p2++ = *p1++) + ; + spend = p2-1; + jflag++; + break; + case EQCOM: + Bprint(&fout, "%ld\n", lnum); + break; + case GCOM: + p1 = linebuf; + p2 = holdsp; + while(*p1++ = *p2++) + ; + spend = p1-1; + break; + case CGCOM: + *spend++ = '\n'; + p1 = spend; + p2 = holdsp; + while(*p1++ = *p2++) + if(p1 >= lbend) + break; + spend = p1-1; + break; + case HCOM: + p1 = holdsp; + p2 = linebuf; + while(*p1++ = *p2++); + hspend = p1-1; + break; + case CHCOM: + *hspend++ = '\n'; + p1 = hspend; + p2 = linebuf; + while(*p1++ = *p2++) + if(p1 >= hend) + break; + hspend = p1-1; + break; + case ICOM: + for(rp = ipc->u.text; *rp; rp++) + Bputrune(&fout, *rp); + Bputc(&fout, '\n'); + break; + case BCOM: + jflag = 1; + break; + case LCOM: + c = 0; + for (i = 0, rp = linebuf; *rp; rp++) { + c = *rp; + if(c >= 0x20 && c < 0x7F && c != '\\') { + Bputc(&fout, c); + if(i++ > 71) { + Bprint(&fout, "\\\n"); + i = 0; + } + } else { + for (ucp = trans(*rp); *ucp; ucp++){ + c = *ucp; + Bputc(&fout, c); + if(i++ > 71) { + Bprint(&fout, "\\\n"); + i = 0; + } + } + } + } + if(c == ' ') + Bprint(&fout, "\\n"); + Bputc(&fout, '\n'); + break; + case NCOM: + if(!nflag) + putline(&fout, linebuf, spend-linebuf); + + if(aptr > abuf) + arout(); + if((execp = gline(linebuf)) == 0) { + delflag = 1; + break; + } + spend = execp; + break; + case CNCOM: + if(aptr > abuf) + arout(); + *spend++ = '\n'; + if((execp = gline(spend)) == 0) { + delflag = 1; + break; + } + spend = execp; + break; + case PCOM: + putline(&fout, linebuf, spend-linebuf); + break; + case CPCOM: + cpcom: + for(rp = linebuf; *rp && *rp != '\n'; rp++) + Bputc(&fout, *rp); + Bputc(&fout, '\n'); + break; + case QCOM: + if(!nflag) + putline(&fout, linebuf, spend-linebuf); + if(aptr > abuf) + arout(); + exits(0); + case RCOM: + *aptr++ = ipc; + if(aptr >= &abuf[MAXADDS]) + quit("sed: Too many reads after line %ld\n", + (char *) lnum); + *aptr = 0; + break; + case SCOM: + i = substitute(ipc); + if(i && ipc->pfl) + if(ipc->pfl == 1) + putline(&fout, linebuf, spend-linebuf); + else + goto cpcom; + if(i && ipc->fcode) + goto wcom; + break; + + case TCOM: + if(sflag == 0) break; + sflag = 0; + jflag = 1; + break; + + wcom: + case WCOM: + putline(ipc->fcode,linebuf, spend-linebuf); + break; + case XCOM: + p1 = linebuf; + p2 = genbuf; + while(*p2++ = *p1++); + p1 = holdsp; + p2 = linebuf; + while(*p2++ = *p1++); + spend = p2 - 1; + p1 = genbuf; + p2 = holdsp; + while(*p2++ = *p1++); + hspend = p2 - 1; + break; + case YCOM: + p1 = linebuf; + p2 = ipc->u.text; + for (i = *p2++; *p1; p1++){ + if (*p1 <= i) *p1 = p2[*p1]; + } + break; + } + +} + +void +putline(Biobuf *bp, Rune *buf, int n) +{ + while (n--) + Bputrune(bp, *buf++); + Bputc(bp, '\n'); +} + +int +ecmp(Rune *a, Rune *b, int count) +{ + while(count--) + if(*a++ != *b++) return(0); + return(1); +} + +void +arout(void) +{ + Rune *p1; + Biobuf *fi; + int c; + char *s; + char buf[128]; + + for (aptr = abuf; *aptr; aptr++) { + if((*aptr)->command == ACOM) { + for(p1 = (*aptr)->u.text; *p1; p1++ ) + Bputrune(&fout, *p1); + Bputc(&fout, '\n'); + } else { + for(s = buf, p1= (*aptr)->u.text; *p1; p1++) + s += runetochar(s, p1); + *s = '\0'; + if((fi = Bopen(buf, OREAD)) == 0) + continue; + while((c = Bgetc(fi)) >= 0) + Bputc(&fout, c); + Bterm(fi); + } + } + aptr = abuf; + *aptr = 0; +} + +void +errexit(void) +{ + exits("error"); +} + +void +quit (char *msg, char *arg) +{ + fprint(2, "sed: "); + fprint(2, msg, arg); + fprint(2, "\n"); + errexit(); +} + +Rune * +gline(Rune *addr) +{ + long c; + Rune *p; + + static long peekc = 0; + + if (f == 0 && opendata() < 0) + return 0; + sflag = 0; + lnum++; +/* Bflush(&fout);********* dumped 4/30/92 - bobf****/ + do { + p = addr; + for (c = (peekc ? peekc : Bgetrune(f)); c >= 0; c = Bgetrune(f)) { + if (c == '\n') { + if ((peekc = Bgetrune(f)) < 0) { + if (fhead == 0) + dolflag = 1; + } + *p = '\0'; + return p; + } + if (c && p < lbend) + *p++ = c; + } + /* return partial final line, adding implicit newline */ + if(p != addr) { + *p = '\0'; + peekc = -1; + if (fhead == 0) + dolflag = 1; + return p; + } + peekc = 0; + Bterm(f); + } while (opendata() > 0); /* Switch to next stream */ + f = 0; + return 0; +} + + /* Data file input section - the intent is to transparently + * catenate all data input streams. + */ +void +enroll(char *filename) /* Add a file to the input file cache */ +{ + FileCache *fp; + + if ((fp = (FileCache *) malloc(sizeof (FileCache))) == 0) + quit("Out of memory", 0); + if (ftail == 0) + fhead = fp; + else + ftail->next = fp; + ftail = fp; + fp->next = 0; + fp->name = filename; /* 0 => stdin */ +} + +int +opendata(void) +{ + if (fhead == 0) + return -1; + if (fhead->name) { + if ((f = Bopen(fhead->name, OREAD)) == 0) + quit("Can't open %s", fhead->name); + } else { + Binit(&bstdin, 0, OREAD); + f = &bstdin; + } + fhead = fhead->next; + return 1; +} diff --git a/seq/Makefile b/seq/Makefile @@ -0,0 +1,36 @@ +# seq - seq unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = seq + +OFILES = seq.o + +MANFILES = seq.1 + +all: ${TARG} + @echo built ${TARG} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 diff --git a/seq/seq.1 b/seq/seq.1 @@ -0,0 +1,75 @@ +.TH SEQ 1 +.SH NAME +seq \- print sequences of numbers +.SH SYNOPSIS +.B seq +[ +.B -w +] +[ +.BI -f format +] +[ +.I first +[ +.I incr +] +] +.I last +.SH DESCRIPTION +.I Seq +prints a sequence of numbers, one per line, from +.I first +(default 1) to as near +.I last +as possible, in increments of +.I incr +(default 1). +The loop is: +.sp +.EX + for(val = min; val <= max; val += incr) print val; +.EE +.sp +The numbers are interpreted as floating point. +.PP +Normally integer values are printed as decimal integers. +The options are +.TP "\w'\fL-f \fIformat\fLXX'u" +.BI -f format +Use the +.IR print (3)-style +.I format +.IR print +for printing each (floating point) number. +The default is +.LR %g . +.TP +.B -w +Equalize the widths of all numbers by padding with +leading zeros as necessary. +Not effective with option +.BR -f , +nor with numbers in exponential notation. +.SH EXAMPLES +.TP +.L +seq 0 .05 .1 +Print +.BR "0 0.05 0.1" +(on separate lines). +.TP +.L +seq -w 0 .05 .1 +Print +.BR "0.00 0.05 0.10" . +.SH SOURCE +.B \*9/src/cmd/seq.c +.SH BUGS +Option +.B -w +always surveys every value in advance. +Thus +.L +seq -w 1000000000 +is a painful way to get an `infinite' sequence. diff --git a/seq/seq.c b/seq/seq.c @@ -0,0 +1,92 @@ +#include <u.h> +#include <libc.h> + +double min = 1.0; +double max = 0.0; +double incr = 1.0; +int constant = 0; +int nsteps; +char *format; + +void +usage(void) +{ + fprint(2, "usage: seq [-fformat] [-w] [first [incr]] last\n"); + exits("usage"); +} + +void +buildfmt(void) +{ + int i; + char *dp; + int w, p, maxw, maxp; + static char fmt[16]; + char buf[32]; + + format = "%g\n"; + if(!constant) + return; + maxw = 0; + maxp = 0; + for(i=0; i<=nsteps; i++){ + sprint(buf, "%g", min+i*incr); + if(strchr(buf, 'e')!=0) + return; + dp = strchr(buf,'.'); + w = dp==0? strlen(buf): dp-buf; + p = dp==0? 0: strlen(strchr(buf,'.')+1); + if(w>maxw) + maxw = w; + if(p>maxp) + maxp = p; + } + if(maxp > 0) + maxw += maxp+1; + sprint(fmt,"%%%d.%df\n", maxw, maxp); + format = fmt; +} + +void +main(int argc, char *argv[]){ + int i, j, n; + char buf[256], ffmt[4096]; + + ARGBEGIN{ + case 'w': + constant++; + break; + case 'f': + format = ARGF(); + if(format[strlen(format)-1] != '\n'){ + sprint(ffmt, "%s\n", format); + format = ffmt; + } + break; + default: + goto out; + }ARGEND + out: + if(argc<1 || argc>3) + usage(); + max = atof(argv[argc-1]); + if(argc > 1) + min = atof(argv[0]); + if(argc > 2) + incr = atof(argv[1]); + if(incr == 0){ + fprint(2, "seq: zero increment\n"); + exits("zero increment"); + } + nsteps = (max-min)/incr+.5; + if(!format) + buildfmt(); + for(i=0; i<=nsteps; i++){ + n = sprint(buf, format, min+i*incr); + if(constant) + for(j=0; buf[j]==' '; j++) + buf[j] ='0'; + write(1, buf, n); + } + exits(0); +} diff --git a/sleep/Makefile b/sleep/Makefile @@ -0,0 +1,37 @@ +# sleep - sleep unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = sleep + +OFILES = sleep.o + +MANFILES = sleep.1 + +all: ${TARG} + @echo built ${TARG} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} + @echo cleaned ${TARG} + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 diff --git a/sleep/sleep.1 b/sleep/sleep.1 @@ -0,0 +1,31 @@ +.TH SLEEP 1 +.SH NAME +sleep \- suspend execution for an interval +.SH SYNOPSIS +.B sleep +.I time +.SH DESCRIPTION +.I Sleep +suspends execution for +.I time +seconds. +.SH EXAMPLES +Execute a command +100 seconds hence. +.IP +.EX +{sleep 100; command}& +.EE +.PP +Repeat a command every 30 seconds. +.IP +.EX +while (){ + command + sleep 30 +} +.EE +.SH SOURCE +.B \*9/src/cmd/sleep.c +.SH "SEE ALSO" +.IR sleep (3) diff --git a/sleep/sleep.c b/sleep/sleep.c @@ -0,0 +1,13 @@ +#include <u.h> +#include <libc.h> + +void +main(int argc, char *argv[]) +{ + long secs; + + if(argc>1) + for(secs = atol(argv[1]); secs > 0; secs--) + sleep(1000); + exits(0); +} diff --git a/sort/Makefile b/sort/Makefile @@ -0,0 +1,36 @@ +# sort - sort unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = sort + +OFILES = sort.o + +MANFILES = sort.1 + +all: ${TARG} + @echo built ${TARG} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 diff --git a/sort/sort.1 b/sort/sort.1 @@ -0,0 +1,262 @@ +.TH SORT 1 +.SH NAME +sort \- sort and/or merge files +.SH SYNOPSIS +.B sort +[ +.BI -cmuMbdf\&inrwt x +] +[ +.BI + pos1 +[ +.BI - pos2 +] ... +] ... +[ +.B -k +.I pos1 +[ +.I ,pos2 +] +] ... +.br +\h'0.5in' +[ +.B -o +.I output +] +[ +.B -T +.I dir +\&... +] +[ +.I option +\&... +] +[ +.I file +\&... +] +.SH DESCRIPTION +.I Sort\^ +sorts +lines of all the +.I files +together and writes the result on +the standard output. +If no input files are named, the standard input is sorted. +.PP +The default sort key is an entire line. +Default ordering is +lexicographic by runes. +The ordering is affected globally by the following options, +one or more of which may appear. +.TP +.B -M +Compare as months. +The first three +non-white space characters +of the field +are folded +to upper case +and compared +so that +.L JAN +precedes +.LR FEB , +etc. +Invalid fields +compare low to +.LR JAN . +.TP +.B -b +Ignore leading white space (spaces and tabs) in field comparisons. +.TP +.B -d +`Phone directory' order: +only letters, +accented letters, +digits and white space +are significant in comparisons. +.TP +.B -f +Fold lower case +letters onto upper case. +Accented characters are folded to their +non-accented upper case form. +.TP +.B -i +Ignore characters outside the +.SM ASCII +range 040-0176 +in non-numeric comparisons. +.TP +.B -w +Like +.BR -i , +but ignore only tabs and spaces. +.TP +.B -n +An initial numeric string, +consisting of optional white space, +optional plus or minus sign, +and zero or more digits with optional decimal point, +is sorted by arithmetic value. +.TP +.B -g +Numbers, like +.B -n +but with optional +.BR e -style +exponents, are sorted by value. +.TP +.B -r +Reverse the sense of comparisons. +.TP +.BI -t x\^ +`Tab character' separating fields is +.IR x . +.PP +The notation +.BI + "pos1\| " - pos2\^ +restricts a sort key to a field beginning at +.I pos1\^ +and ending just before +.IR pos2 . +.I Pos1\^ +and +.I pos2\^ +each have the form +.IB m . n\f1, +optionally followed by one or more of the flags +.BR Mbdfginr , +where +.I m\^ +tells a number of fields to skip from the beginning of the line and +.I n\^ +tells a number of characters to skip further. +If any flags are present they override all the global +ordering options for this key. +A missing +.BI \&. n\^ +means +.BR \&.0 ; +a missing +.BI - pos2\^ +means the end of the line. +Under the +.BI -t x\^ +option, fields are strings separated by +.IR x ; +otherwise fields are +non-empty strings separated by white space. +White space before a field +is part of the field, except under option +.BR -b . +A +.B b +flag may be attached independently to +.IR pos1 +and +.IR pos2. +.PP +The notation +.B -k +.IR pos1 [, pos2 ] +is how POSIX +.I sort +defines fields: +.I pos1 +and +.I pos2 +have the same format but different meanings. +The value of +.I m\^ +is origin 1 instead of origin 0 +and a missing +.BI \&. n\^ +in +.I pos2 +is the end of the field. +.PP +When there are multiple sort keys, later keys +are compared only after all earlier keys +compare equal. +Lines that otherwise compare equal are ordered +with all bytes significant. +.PP +These option arguments are also understood: +.TP \w'\fL-z\fIrecsize\fLXX'u +.B -c +Check that the single input file is sorted according to the ordering rules; +give no output unless the file is out of sort. +.TP +.B -m +Merge; assume the input files are already sorted. +.TP +.B -u +Suppress all but one in each +set of equal lines. +Ignored bytes +and bytes outside keys +do not participate in +this comparison. +.TP +.B -o +The next argument is the name of an output file +to use instead of the standard output. +This file may be the same as one of the inputs. +.TP +.BI -T dir +Put temporary files in +.I dir +rather than in +.BR /var/tmp . +.ne 4 +.SH EXAMPLES +.TP +.L sort -u +0f +0 list +Print in alphabetical order all the unique spellings +in a list of words +where capitalized words differ from uncapitalized. +.TP +.L sort -t: +1 /adm/users +Print the users file +sorted by user name +(the second colon-separated field). +.TP +.L sort -umM dates +Print the first instance of each month in an already sorted file. +Options +.B -um +with just one input file make the choice of a +unique representative from a set of equal lines predictable. +.TP +.L +grep -n '^' input | sort -t: +1f +0n | sed 's/[0-9]*://' +A stable sort: input lines that compare equal will +come out in their original order. +.SH FILES +.BI /var/tmp/sort. <pid>.<ordinal> +.SH SOURCE +.B \*9/src/cmd/sort.c +.SH SEE ALSO +.IR uniq (1), +.IR look (1) +.SH DIAGNOSTICS +.I Sort +comments and exits with non-null status for various trouble +conditions and for disorder discovered under option +.BR -c . +.SH BUGS +An external null character can be confused +with an internally generated end-of-field character. +The result can make a sub-field not sort +less than a longer field. +.PP +Some of the options, e.g. +.B -i +and +.BR -M , +are hopelessly provincial. diff --git a/sort/sort.c b/sort/sort.c @@ -0,0 +1,1767 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +/* +bugs: + 00/ff for end of file can conflict with 00/ff characters +*/ + +enum +{ + Nline = 500000, /* default max number of lines saved in memory */ + Nmerge = 10, /* max number of temporary files merged */ + Nfield = 20, /* max number of argument fields */ + + Bflag = 1<<0, /* flags per field */ + B1flag = 1<<1, + + Dflag = 1<<2, + Fflag = 1<<3, + Gflag = 1<<4, + Iflag = 1<<5, + Mflag = 1<<6, + Nflag = 1<<7, + Rflag = 1<<8, + Wflag = 1<<9, + + NSstart = 0, /* states for number to key decoding */ + NSsign, + NSzero, + NSdigit, + NSpoint, + NSfract, + NSzerofract, + NSexp, + NSexpsign, + NSexpdigit, +}; + +typedef struct Line Line; +typedef struct Key Key; +typedef struct Merge Merge; +typedef struct Field Field; + +struct Line +{ + Key* key; + int llen; /* always >= 1 */ + uchar line[1]; /* always ends in '\n' */ +}; + +struct Merge +{ + Key* key; /* copy of line->key so (Line*) looks like (Merge*) */ + Line* line; /* line at the head of a merged temp file */ + int fd; /* file descriptor */ + Biobuf b; /* iobuf for reading a temp file */ +}; + +struct Key +{ + int klen; + uchar key[1]; +}; + +struct Field +{ + int beg1; + int beg2; + int end1; + int end2; + + long flags; + uchar mapto[256]; + + void (*dokey)(Key*, uchar*, uchar*, Field*); +}; + +struct args +{ + char* ofile; + char* tname; + Rune tabchar; + char cflag; + char uflag; + char vflag; + int nfield; + int nfile; + Field field[Nfield]; + + Line** linep; + long nline; /* number of lines in this temp file */ + long lineno; /* overall ordinal for -s option */ + int ntemp; + long mline; /* max lines per file */ +} args; + +extern int latinmap[]; +extern Rune* month[12]; + +void buildkey(Line*); +void doargs(int, char*[]); +void dofield(char*, int*, int*, int, int); +void dofile(Biobuf*); +void dokey_(Key*, uchar*, uchar*, Field*); +void dokey_dfi(Key*, uchar*, uchar*, Field*); +void dokey_gn(Key*, uchar*, uchar*, Field*); +void dokey_m(Key*, uchar*, uchar*, Field*); +void dokey_r(Key*, uchar*, uchar*, Field*); +void done(char*); +int kcmp(Key*, Key*); +void makemapd(Field*); +void makemapm(Field*); +void mergefiles(int, int, Biobuf*); +void mergeout(Biobuf*); +void newfield(void); +Line* newline(Biobuf*); +void nomem(void); +void notifyf(void*, char*); +void printargs(void); +void printout(Biobuf*); +void setfield(int, int); +uchar* skip(uchar*, int, int, int, int); +void sort4(void*, ulong); +char* tempfile(int); +void tempout(void); +void lineout(Biobuf*, Line*); + +void +main(int argc, char *argv[]) +{ + int i, f; + char *s; + Biobuf bbuf; + + notify(notifyf); /**/ + doargs(argc, argv); + if(args.vflag) + printargs(); + + for(i=1; i<argc; i++) { + s = argv[i]; + if(s == 0) + continue; + if(strcmp(s, "-") == 0) { + Binit(&bbuf, 0, OREAD); + dofile(&bbuf); + Bterm(&bbuf); + continue; + } + f = open(s, OREAD); + if(f < 0) { + fprint(2, "sort: open %s: %r\n", s); + done("open"); + } + Binit(&bbuf, f, OREAD); + dofile(&bbuf); + Bterm(&bbuf); + close(f); + } + if(args.nfile == 0) { + Binit(&bbuf, 0, OREAD); + dofile(&bbuf); + Bterm(&bbuf); + } + if(args.cflag) + done(0); + if(args.vflag) + fprint(2, "=========\n"); + + f = 1; + if(args.ofile) { + f = create(args.ofile, OWRITE, 0666); + if(f < 0) { + fprint(2, "sort: create %s: %r\n", args.ofile); + done("create"); + } + } + + Binit(&bbuf, f, OWRITE); + if(args.ntemp) { + tempout(); + mergeout(&bbuf); + } else { + printout(&bbuf); + } + Bterm(&bbuf); + done(0); +} + +void +dofile(Biobuf *b) +{ + Line *l, *ol; + int n; + + if(args.cflag) { + ol = newline(b); + if(ol == 0) + return; + for(;;) { + l = newline(b); + if(l == 0) + break; + n = kcmp(ol->key, l->key); + if(n > 0 || (n == 0 && args.uflag)) { + fprint(2, "sort: -c file not in sort\n"); /**/ + done("order"); + } + free(ol->key); + free(ol); + ol = l; + } + return; + } + + if(args.linep == 0) { + args.linep = malloc(args.mline * sizeof(args.linep)); + if(args.linep == 0) + nomem(); + } + for(;;) { + l = newline(b); + if(l == 0) + break; + if(args.nline >= args.mline) + tempout(); + args.linep[args.nline] = l; + args.nline++; + args.lineno++; + } +} + +void +notifyf(void *a, char *s) +{ + USED(a); + if(strcmp(s, "interrupt") == 0) + done(0); + if(strcmp(s, "hangup") == 0) + done(0); + if(strcmp(s, "kill") == 0) + done(0); + if(strncmp(s, "sys: write on closed pipe", 25) == 0) + done(0); + fprint(2, "sort: note: %s\n", s); + abort(); +} + +Line* +newline(Biobuf *b) +{ + Line *l; + char *p; + int n, c; + + p = Brdline(b, '\n'); + n = Blinelen(b); + if(p == 0) { + if(n == 0) + return 0; + l = 0; + for(n=0;;) { + if((n & 31) == 0) { + l = realloc(l, sizeof(Line) + + (n+31)*sizeof(l->line[0])); + if(l == 0) + nomem(); + } + c = Bgetc(b); + if(c < 0) { + fprint(2, "sort: newline added\n"); + c = '\n'; + } + l->line[n++] = c; + if(c == '\n') + break; + } + l->llen = n; + buildkey(l); + return l; + } + l = malloc(sizeof(Line) + + (n-1)*sizeof(l->line[0])); + if(l == 0) + nomem(); + l->llen = n; + memmove(l->line, p, n); + buildkey(l); + return l; +} + +void +lineout(Biobuf *b, Line *l) +{ + int n, m; + + n = l->llen; + m = Bwrite(b, l->line, n); + if(n != m) + exits("write"); +} + +void +tempout(void) +{ + long n; + Line **lp, *l; + char *tf; + int f; + Biobuf tb; + + sort4(args.linep, args.nline); + tf = tempfile(args.ntemp); + args.ntemp++; + f = create(tf, OWRITE, 0666); + if(f < 0) { + fprint(2, "sort: create %s: %r\n", tf); + done("create"); + } + + Binit(&tb, f, OWRITE); + lp = args.linep; + for(n=args.nline; n>0; n--) { + l = *lp++; + lineout(&tb, l); + free(l->key); + free(l); + } + args.nline = 0; + Bterm(&tb); + close(f); +} + +void +done(char *xs) +{ + int i; + + for(i=0; i<args.ntemp; i++) + remove(tempfile(i)); + exits(xs); +} + +void +nomem(void) +{ + fprint(2, "sort: out of memory\n"); + done("mem"); +} + +char* +tempfile(int n) +{ + static char file[100]; + static uint pid; + char *dir; + + dir = "/var/tmp"; + if(args.tname) + dir = args.tname; + if(strlen(dir) >= nelem(file)-20) { + fprint(2, "temp file directory name is too long: %s\n", dir); + done("tdir"); + } + + if(pid == 0) { + pid = getpid(); + if(pid == 0) { + pid = time(0); + if(pid == 0) + pid = 1; + } + } + + sprint(file, "%s/sort.%.4d.%.4d", dir, pid%10000, n); + return file; +} + +void +mergeout(Biobuf *b) +{ + int n, i, f; + char *tf; + Biobuf tb; + + for(i=0; i<args.ntemp; i+=n) { + n = args.ntemp - i; + if(n > Nmerge) { + tf = tempfile(args.ntemp); + args.ntemp++; + f = create(tf, OWRITE, 0666); + if(f < 0) { + fprint(2, "sort: create %s: %r\n", tf); + done("create"); + } + Binit(&tb, f, OWRITE); + + n = Nmerge; + mergefiles(i, n, &tb); + + Bterm(&tb); + close(f); + } else + mergefiles(i, n, b); + } +} + +void +mergefiles(int t, int n, Biobuf *b) +{ + Merge *m, *mp, **mmp; + Key *ok; + Line *l; + char *tf; + int i, f, nn; + + mmp = malloc(n*sizeof(*mmp)); + mp = malloc(n*sizeof(*mp)); + if(mmp == 0 || mp == 0) + nomem(); + + nn = 0; + m = mp; + for(i=0; i<n; i++,m++) { + tf = tempfile(t+i); + f = open(tf, OREAD); + if(f < 0) { + fprint(2, "sort: reopen %s: %r\n", tf); + done("open"); + } + m->fd = f; + Binit(&m->b, f, OREAD); + mmp[nn] = m; + + l = newline(&m->b); + if(l == 0) + continue; + nn++; + m->line = l; + m->key = l->key; + } + + ok = 0; + for(;;) { + sort4(mmp, nn); + m = *mmp; + if(nn == 0) + break; + for(;;) { + l = m->line; + if(args.uflag && ok && kcmp(ok, l->key) == 0) { + free(l->key); + free(l); + } else { + lineout(b, l); + if(ok) + free(ok); + ok = l->key; + free(l); + } + + l = newline(&m->b); + if(l == 0) { + nn--; + mmp[0] = mmp[nn]; + break; + } + m->line = l; + m->key = l->key; + if(nn > 1 && kcmp(mmp[0]->key, mmp[1]->key) > 0) + break; + } + } + if(ok) + free(ok); + + m = mp; + for(i=0; i<n; i++,m++) { + Bterm(&m->b); + close(m->fd); + } + + free(mp); + free(mmp); +} + +int +kcmp(Key *ka, Key *kb) +{ + int n, m; + + /* + * set n to length of smaller key + */ + n = ka->klen; + m = kb->klen; + if(n > m) + n = m; + return memcmp(ka->key, kb->key, n); +} + +void +printout(Biobuf *b) +{ + long n; + Line **lp, *l; + Key *ok; + + sort4(args.linep, args.nline); + lp = args.linep; + ok = 0; + for(n=args.nline; n>0; n--) { + l = *lp++; + if(args.uflag && ok && kcmp(ok, l->key) == 0) + continue; + lineout(b, l); + ok = l->key; + } +} + +void +setfield(int n, int c) +{ + Field *f; + + f = &args.field[n]; + switch(c) { + default: + fprint(2, "sort: unknown option: field.%C\n", c); + done("option"); + case 'b': /* skip blanks */ + f->flags |= Bflag; + break; + case 'd': /* directory order */ + f->flags |= Dflag; + break; + case 'f': /* fold case */ + f->flags |= Fflag; + break; + case 'g': /* floating point -n case */ + f->flags |= Gflag; + break; + case 'i': /* ignore non-ascii */ + f->flags |= Iflag; + break; + case 'M': /* month */ + f->flags |= Mflag; + break; + case 'n': /* numbers */ + f->flags |= Nflag; + break; + case 'r': /* reverse */ + f->flags |= Rflag; + break; + case 'w': /* ignore white */ + f->flags |= Wflag; + break; + } +} + +void +dofield(char *s, int *n1, int *n2, int off1, int off2) +{ + int c, n; + + c = *s++; + if(c >= '0' && c <= '9') { + n = 0; + while(c >= '0' && c <= '9') { + n = n*10 + (c-'0'); + c = *s++; + } + n -= off1; /* posix committee: rot in hell */ + if(n < 0) { + fprint(2, "sort: field offset must be positive\n"); + done("option"); + } + *n1 = n; + } + if(c == '.') { + c = *s++; + if(c >= '0' && c <= '9') { + n = 0; + while(c >= '0' && c <= '9') { + n = n*10 + (c-'0'); + c = *s++; + } + n -= off2; + if(n < 0) { + fprint(2, "sort: character offset must be positive\n"); + done("option"); + } + *n2 = n; + } + } + while(c != 0) { + setfield(args.nfield, c); + c = *s++; + } +} + +void +printargs(void) +{ + int i, n; + Field *f; + char *prefix; + + fprint(2, "sort"); + for(i=0; i<=args.nfield; i++) { + f = &args.field[i]; + prefix = " -"; + if(i) { + n = f->beg1; + if(n >= 0) + fprint(2, " +%d", n); + else + fprint(2, " +*"); + n = f->beg2; + if(n >= 0) + fprint(2, ".%d", n); + else + fprint(2, ".*"); + + if(f->flags & B1flag) + fprint(2, "b"); + + n = f->end1; + if(n >= 0) + fprint(2, " -%d", n); + else + fprint(2, " -*"); + n = f->end2; + if(n >= 0) + fprint(2, ".%d", n); + else + fprint(2, ".*"); + prefix = ""; + } + if(f->flags & Bflag) + fprint(2, "%sb", prefix); + if(f->flags & Dflag) + fprint(2, "%sd", prefix); + if(f->flags & Fflag) + fprint(2, "%sf", prefix); + if(f->flags & Gflag) + fprint(2, "%sg", prefix); + if(f->flags & Iflag) + fprint(2, "%si", prefix); + if(f->flags & Mflag) + fprint(2, "%sM", prefix); + if(f->flags & Nflag) + fprint(2, "%sn", prefix); + if(f->flags & Rflag) + fprint(2, "%sr", prefix); + if(f->flags & Wflag) + fprint(2, "%sw", prefix); + } + if(args.cflag) + fprint(2, " -c"); + if(args.uflag) + fprint(2, " -u"); + if(args.ofile) + fprint(2, " -o %s", args.ofile); + if(args.mline != Nline) + fprint(2, " -l %ld", args.mline); + fprint(2, "\n"); +} + +void +newfield(void) +{ + int n; + Field *f; + + n = args.nfield + 1; + if(n >= Nfield) { + fprint(2, "sort: too many fields specified\n"); + done("option"); + } + args.nfield = n; + f = &args.field[n]; + f->beg1 = -1; + f->beg2 = -1; + f->end1 = -1; + f->end2 = -1; +} + +void +doargs(int argc, char *argv[]) +{ + int i, c, hadplus; + char *s, *p, *q; + Field *f; + + hadplus = 0; + args.mline = Nline; + for(i=1; i<argc; i++) { + s = argv[i]; + c = *s++; + if(c == '-') { + c = *s; + if(c == 0) /* forced end of arg marker */ + break; + argv[i] = 0; /* clobber args processed */ + if(c == '.' || (c >= '0' && c <= '9')) { + if(!hadplus) + newfield(); + f = &args.field[args.nfield]; + dofield(s, &f->end1, &f->end2, 0, 0); + hadplus = 0; + continue; + } + + while(c = *s++) + switch(c) { + case '-': /* end of options */ + i = argc; + continue; + case 'T': /* temp directory */ + if(*s == 0) { + i++; + if(i < argc) { + args.tname = argv[i]; + argv[i] = 0; + } + } else + args.tname = s; + s = strchr(s, 0); + break; + case 'o': /* output file */ + if(*s == 0) { + i++; + if(i < argc) { + args.ofile = argv[i]; + argv[i] = 0; + } + } else + args.ofile = s; + s = strchr(s, 0); + break; + case 'k': /* posix key (what were they thinking?) */ + p = 0; + if(*s == 0) { + i++; + if(i < argc) { + p = argv[i]; + argv[i] = 0; + } + } else + p = s; + s = strchr(s, 0); + if(p == 0) + break; + + newfield(); + q = strchr(p, ','); + if(q) + *q++ = 0; + f = &args.field[args.nfield]; + dofield(p, &f->beg1, &f->beg2, 1, 1); + if(f->flags & Bflag) { + f->flags |= B1flag; + f->flags &= ~Bflag; + } + if(q) { + dofield(q, &f->end1, &f->end2, 1, 0); + if(f->end2 <= 0) + f->end1++; + } + hadplus = 0; + break; + case 't': /* tab character */ + if(*s == 0) { + i++; + if(i < argc) { + chartorune(&args.tabchar, argv[i]); + argv[i] = 0; + } + } else + s += chartorune(&args.tabchar, s); + if(args.tabchar == '\n') { + fprint(2, "aw come on, rob\n"); + done("rob"); + } + break; + case 'c': /* check order */ + args.cflag = 1; + break; + case 'u': /* unique */ + args.uflag = 1; + break; + case 'v': /* debugging noise */ + args.vflag = 1; + break; + case 'l': + if(*s == 0) { + i++; + if(i < argc) { + args.mline = atol(argv[i]); + argv[i] = 0; + } + } else + args.mline = atol(s); + s = strchr(s, 0); + break; + + case 'M': /* month */ + case 'b': /* skip blanks */ + case 'd': /* directory order */ + case 'f': /* fold case */ + case 'g': /* floating numbers */ + case 'i': /* ignore non-ascii */ + case 'n': /* numbers */ + case 'r': /* reverse */ + case 'w': /* ignore white */ + if(args.nfield > 0) + fprint(2, "sort: global field set after -k\n"); + setfield(0, c); + break; + case 'm': + /* option m silently ignored but required by posix */ + break; + default: + fprint(2, "sort: unknown option: -%C\n", c); + done("option"); + } + continue; + } + if(c == '+') { + argv[i] = 0; /* clobber args processed */ + c = *s; + if(c == '.' || (c >= '0' && c <= '9')) { + newfield(); + f = &args.field[args.nfield]; + dofield(s, &f->beg1, &f->beg2, 0, 0); + if(f->flags & Bflag) { + f->flags |= B1flag; + f->flags &= ~Bflag; + } + hadplus = 1; + continue; + } + fprint(2, "sort: unknown option: +%C\n", c); + done("option"); + } + args.nfile++; + } + + for(i=0; i<=args.nfield; i++) { + f = &args.field[i]; + + /* + * global options apply to fields that + * specify no options + */ + if(f->flags == 0) { + f->flags = args.field[0].flags; + if(args.field[0].flags & Bflag) + f->flags |= B1flag; + } + + + /* + * build buildkey specification + */ + switch(f->flags & ~(Bflag|B1flag)) { + default: + fprint(2, "sort: illegal combination of flags: %lx\n", f->flags); + done("option"); + case 0: + f->dokey = dokey_; + break; + case Rflag: + f->dokey = dokey_r; + break; + case Gflag: + case Nflag: + case Gflag|Nflag: + case Gflag|Rflag: + case Nflag|Rflag: + case Gflag|Nflag|Rflag: + f->dokey = dokey_gn; + break; + case Mflag: + case Mflag|Rflag: + f->dokey = dokey_m; + makemapm(f); + break; + case Dflag: + case Dflag|Fflag: + case Dflag|Fflag|Iflag: + case Dflag|Fflag|Iflag|Rflag: + case Dflag|Fflag|Iflag|Rflag|Wflag: + case Dflag|Fflag|Iflag|Wflag: + case Dflag|Fflag|Rflag: + case Dflag|Fflag|Rflag|Wflag: + case Dflag|Fflag|Wflag: + case Dflag|Iflag: + case Dflag|Iflag|Rflag: + case Dflag|Iflag|Rflag|Wflag: + case Dflag|Iflag|Wflag: + case Dflag|Rflag: + case Dflag|Rflag|Wflag: + case Dflag|Wflag: + case Fflag: + case Fflag|Iflag: + case Fflag|Iflag|Rflag: + case Fflag|Iflag|Rflag|Wflag: + case Fflag|Iflag|Wflag: + case Fflag|Rflag: + case Fflag|Rflag|Wflag: + case Fflag|Wflag: + case Iflag: + case Iflag|Rflag: + case Iflag|Rflag|Wflag: + case Iflag|Wflag: + case Wflag: + f->dokey = dokey_dfi; + makemapd(f); + break; + } + } + + /* + * random spot checks + */ + if(args.nfile > 1 && args.cflag) { + fprint(2, "sort: -c can have at most one input file\n"); + done("option"); + } + return; +} + +uchar* +skip(uchar *l, int n1, int n2, int bflag, int endfield) +{ + int i, c, tc; + Rune r; + + if(endfield && n1 < 0) + return 0; + + c = *l++; + tc = args.tabchar; + if(tc) { + if(tc < Runeself) { + for(i=n1; i>0; i--) { + while(c != tc) { + if(c == '\n') + return 0; + c = *l++; + } + if(!(endfield && i == 1)) + c = *l++; + } + } else { + l--; + l += chartorune(&r, (char*)l); + for(i=n1; i>0; i--) { + while(r != tc) { + if(r == '\n') + return 0; + l += chartorune(&r, (char*)l); + } + if(!(endfield && i == 1)) + l += chartorune(&r, (char*)l); + } + c = r; + } + } else { + for(i=n1; i>0; i--) { + while(c == ' ' || c == '\t') + c = *l++; + while(c != ' ' && c != '\t') { + if(c == '\n') + return 0; + c = *l++; + } + } + } + + if(bflag) + while(c == ' ' || c == '\t') + c = *l++; + + l--; + for(i=n2; i>0; i--) { + c = *l; + if(c < Runeself) { + if(c == '\n') + return 0; + l++; + continue; + } + l += chartorune(&r, (char*)l); + } + return l; +} + +void +dokey_gn(Key *k, uchar *lp, uchar *lpe, Field *f) +{ + uchar *kp; + int c, cl, dp; + int state, nzero, exp, expsign, rflag; + + cl = k->klen + 3; + kp = k->key + cl; /* skip place for sign, exponent[2] */ + + nzero = 0; /* number of trailing zeros */ + exp = 0; /* value of the exponent */ + expsign = 0; /* sign of the exponent */ + dp = 0x4040; /* location of decimal point */ + rflag = f->flags&Rflag; /* xor of rflag and - sign */ + state = NSstart; + + for(;; lp++) { + if(lp >= lpe) + break; + c = *lp; + + if(c == ' ' || c == '\t') { + switch(state) { + case NSstart: + case NSsign: + continue; + } + break; + } + if(c == '+' || c == '-') { + switch(state) { + case NSstart: + state = NSsign; + if(c == '-') + rflag = !rflag; + continue; + case NSexp: + state = NSexpsign; + if(c == '-') + expsign = 1; + continue; + } + break; + } + if(c == '0') { + switch(state) { + case NSdigit: + if(rflag) + c = ~c; + *kp++ = c; + cl++; + nzero++; + dp++; + state = NSdigit; + continue; + case NSfract: + if(rflag) + c = ~c; + *kp++ = c; + cl++; + nzero++; + state = NSfract; + continue; + case NSstart: + case NSsign: + case NSzero: + state = NSzero; + continue; + case NSzerofract: + case NSpoint: + dp--; + state = NSzerofract; + continue; + case NSexpsign: + case NSexp: + case NSexpdigit: + exp = exp*10 + (c - '0'); + state = NSexpdigit; + continue; + } + break; + } + if(c >= '1' && c <= '9') { + switch(state) { + case NSzero: + case NSstart: + case NSsign: + case NSdigit: + if(rflag) + c = ~c; + *kp++ = c; + cl++; + nzero = 0; + dp++; + state = NSdigit; + continue; + case NSzerofract: + case NSpoint: + case NSfract: + if(rflag) + c = ~c; + *kp++ = c; + cl++; + nzero = 0; + state = NSfract; + continue; + case NSexpsign: + case NSexp: + case NSexpdigit: + exp = exp*10 + (c - '0'); + state = NSexpdigit; + continue; + } + break; + } + if(c == '.') { + switch(state) { + case NSstart: + case NSsign: + state = NSpoint; + continue; + case NSzero: + state = NSzerofract; + continue; + case NSdigit: + state = NSfract; + continue; + } + break; + } + if((f->flags & Gflag) && (c == 'e' || c == 'E')) { + switch(state) { + case NSdigit: + case NSfract: + state = NSexp; + continue; + } + break; + } + break; + } + + switch(state) { + /* + * result is zero + */ + case NSstart: + case NSsign: + case NSzero: + case NSzerofract: + case NSpoint: + kp = k->key + k->klen; + k->klen += 2; + kp[0] = 0x20; /* between + and - */ + kp[1] = 0; + return; + /* + * result has exponent + */ + case NSexpsign: + case NSexp: + case NSexpdigit: + if(expsign) + exp = -exp; + dp += exp; + + /* + * result is fixed point number + */ + case NSdigit: + case NSfract: + kp -= nzero; + cl -= nzero; + break; + } + + /* + * end of number + */ + c = 0; + if(rflag) + c = ~c; + *kp = c; + + /* + * sign and exponent + */ + c = 0x30; + if(rflag) { + c = 0x10; + dp = ~dp; + } + kp = k->key + k->klen; + kp[0] = c; + kp[1] = (dp >> 8); + kp[2] = dp; + k->klen = cl+1; +} + +void +dokey_m(Key *k, uchar *lp, uchar *lpe, Field *f) +{ + uchar *kp; + Rune r, place[3]; + int c, cl, pc; + int rflag; + + rflag = f->flags&Rflag; + pc = 0; + + cl = k->klen; + kp = k->key + cl; + + for(;;) { + /* + * get the character + */ + if(lp >= lpe) + break; + c = *lp; + if(c >= Runeself) { + lp += chartorune(&r, (char*)lp); + c = r; + } else + lp++; + + if(c < nelem(f->mapto)) { + c = f->mapto[c]; + if(c == 0) + continue; + } + place[pc++] = c; + if(pc < 3) + continue; + for(c=11; c>=0; c--) + if(memcmp(month[c], place, sizeof(place)) == 0) + break; + c += 10; + if(rflag) + c = ~c; + *kp++ = c; + cl++; + break; + } + + c = 0; + if(rflag) + c = ~c; + *kp = c; + k->klen = cl+1; +} + +void +dokey_dfi(Key *k, uchar *lp, uchar *lpe, Field *f) +{ + uchar *kp; + Rune r; + int c, cl, n, rflag; + + cl = k->klen; + kp = k->key + cl; + rflag = f->flags & Rflag; + + for(;;) { + /* + * get the character + */ + if(lp >= lpe) + break; + c = *lp; + if(c >= Runeself) { + lp += chartorune(&r, (char*)lp); + c = r; + } else + lp++; + + /* + * do the various mappings. + * the common case is handled + * completely by the table. + */ + if(c != 0 && c < Runeself) { + c = f->mapto[c]; + if(c) { + *kp++ = c; + cl++; + } + continue; + } + + /* + * for characters out of range, + * the table does not do Rflag. + * ignore is based on mapto[255] + */ + if(c != 0 && c < nelem(f->mapto)) { + c = f->mapto[c]; + if(c == 0) + continue; + } else + if(f->mapto[nelem(f->mapto)-1] == 0) + continue; + + /* + * put it in the key + */ + r = c; + n = runetochar((char*)kp, &r); + kp += n; + cl += n; + if(rflag) + while(n > 0) { + kp[-n] = ~kp[-n]; + n--; + } + } + + /* + * end of key + */ + k->klen = cl+1; + if(rflag) { + *kp = ~0; + return; + } + *kp = 0; +} + +void +dokey_r(Key *k, uchar *lp, uchar *lpe, Field *f) +{ + int cl, n; + uchar *kp; + + USED(f); + n = lpe - lp; + if(n < 0) + n = 0; + cl = k->klen; + kp = k->key + cl; + k->klen = cl+n+1; + + lpe -= 3; + while(lp < lpe) { + kp[0] = ~lp[0]; + kp[1] = ~lp[1]; + kp[2] = ~lp[2]; + kp[3] = ~lp[3]; + kp += 4; + lp += 4; + } + + lpe += 3; + while(lp < lpe) + *kp++ = ~*lp++; + *kp = ~0; +} + +void +dokey_(Key *k, uchar *lp, uchar *lpe, Field *f) +{ + int n, cl; + uchar *kp; + + USED(f); + n = lpe - lp; + if(n < 0) + n = 0; + cl = k->klen; + kp = k->key + cl; + k->klen = cl+n+1; + memmove(kp, lp, n); + kp[n] = 0; +} + +void +buildkey(Line *l) +{ + Key *k; + uchar *lp, *lpe; + int ll, kl, cl, i, n; + Field *f; + + ll = l->llen - 1; + kl = 0; /* allocated length */ + cl = 0; /* current length */ + k = 0; + + for(i=1; i<=args.nfield; i++) { + f = &args.field[i]; + lp = skip(l->line, f->beg1, f->beg2, f->flags&B1flag, 0); + if(lp == 0) + lp = l->line + ll; + lpe = skip(l->line, f->end1, f->end2, f->flags&Bflag, 1); + if(lpe == 0) + lpe = l->line + ll; + n = (lpe - lp) + 1; + if(n <= 0) + n = 1; + if(cl+(n+4) > kl) { + kl = cl+(n+4); + k = realloc(k, sizeof(Key) + + (kl-1)*sizeof(k->key[0])); + if(k == 0) + nomem(); + } + k->klen = cl; + (*f->dokey)(k, lp, lpe, f); + cl = k->klen; + } + + /* + * global comparisons + */ + if(!(args.uflag && cl > 0)) { + f = &args.field[0]; + if(cl+(ll+4) > kl) { + kl = cl+(ll+4); + k = realloc(k, sizeof(Key) + + (kl-1)*sizeof(k->key[0])); + if(k == 0) + nomem(); + } + k->klen = cl; + (*f->dokey)(k, l->line, l->line+ll, f); + cl = k->klen; + } + + l->key = k; + k->klen = cl; + + if(args.vflag) { + write(2, l->line, l->llen); + for(i=0; i<k->klen; i++) { + fprint(2, " %.2x", k->key[i]); + if(k->key[i] == 0x00 || k->key[i] == 0xff) + fprint(2, "\n"); + } + } +} + +void +makemapm(Field *f) +{ + int i, c; + + for(i=0; i<nelem(f->mapto); i++) { + c = 1; + if(i == ' ' || i == '\t') + c = 0; + if(i >= 'a' && i <= 'z') + c = i + ('A' - 'a'); + if(i >= 'A' && i <= 'Z') + c = i; + f->mapto[i] = c; + if(args.vflag) { + if((i & 15) == 0) + fprint(2, " "); + fprint(2, " %.2x", c); + if((i & 15) == 15) + fprint(2, "\n"); + } + } +} + +void +makemapd(Field *f) +{ + int i, j, c; + + for(i=0; i<nelem(f->mapto); i++) { + c = i; + if(f->flags & Iflag) + if(c < 040 || c > 0176) + c = -1; + if((f->flags & Wflag) && c >= 0) + if(c == ' ' || c == '\t') + c = -1; + if((f->flags & Dflag) && c >= 0) + if(!(c == ' ' || c == '\t' || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9'))) { + for(j=0; latinmap[j]; j+=3) + if(c == latinmap[j+0] || + c == latinmap[j+1]) + break; + if(latinmap[j] == 0) + c = -1; + } + if((f->flags & Fflag) && c >= 0) { + if(c >= 'a' && c <= 'z') + c += 'A' - 'a'; + for(j=0; latinmap[j]; j+=3) + if(c == latinmap[j+0] || + c == latinmap[j+1]) { + c = latinmap[j+2]; + break; + } + } + if((f->flags & Rflag) && c >= 0 && i > 0 && i < Runeself) + c = ~c & 0xff; + if(c < 0) + c = 0; + f->mapto[i] = c; + if(args.vflag) { + if((i & 15) == 0) + fprint(2, " "); + fprint(2, " %.2x", c); + if((i & 15) == 15) + fprint(2, "\n"); + } + } +} + +int latinmap[] = +{ +/* lcase ucase fold */ + 0xe0, 0xc0, 0x41, /* L'à', L'À', L'A', */ + 0xe1, 0xc1, 0x41, /* L'á', L'Á', L'A', */ + 0xe2, 0xc2, 0x41, /* L'â', L'Â', L'A', */ + 0xe4, 0xc4, 0x41, /* L'ä', L'Ä', L'A', */ + 0xe3, 0xc3, 0x41, /* L'ã', L'Ã', L'A', */ + 0xe5, 0xc5, 0x41, /* L'å', L'Å', L'A', */ + 0xe8, 0xc8, 0x45, /* L'è', L'È', L'E', */ + 0xe9, 0xc9, 0x45, /* L'é', L'É', L'E', */ + 0xea, 0xca, 0x45, /* L'ê', L'Ê', L'E', */ + 0xeb, 0xcb, 0x45, /* L'ë', L'Ë', L'E', */ + 0xec, 0xcc, 0x49, /* L'ì', L'Ì', L'I', */ + 0xed, 0xcd, 0x49, /* L'í', L'Í', L'I', */ + 0xee, 0xce, 0x49, /* L'î', L'Î', L'I', */ + 0xef, 0xcf, 0x49, /* L'ï', L'Ï', L'I', */ + 0xf2, 0xd2, 0x4f, /* L'ò', L'Ò', L'O', */ + 0xf3, 0xd3, 0x4f, /* L'ó', L'Ó', L'O', */ + 0xf4, 0xd4, 0x4f, /* L'ô', L'Ô', L'O', */ + 0xf6, 0xd6, 0x4f, /* L'ö', L'Ö', L'O', */ + 0xf5, 0xd5, 0x4f, /* L'õ', L'Õ', L'O', */ + 0xf8, 0xd8, 0x4f, /* L'ø', L'Ø', L'O', */ + 0xf9, 0xd9, 0x55, /* L'ù', L'Ù', L'U', */ + 0xfa, 0xda, 0x55, /* L'ú', L'Ú', L'U', */ + 0xfb, 0xdb, 0x55, /* L'û', L'Û', L'U', */ + 0xfc, 0xdc, 0x55, /* L'ü', L'Ü', L'U', */ + 0xe6, 0xc6, 0x41, /* L'æ', L'Æ', L'A', */ + 0xf0, 0xd0, 0x44, /* L'ð', L'Ð', L'D', */ + 0xf1, 0xd1, 0x4e, /* L'ñ', L'Ñ', L'N', */ + 0xfd, 0xdd, 0x59, /* L'ý', L'Ý', L'Y', */ + 0xe7, 0xc7, 0x43, /* L'ç', L'Ç', L'C', */ + 0, +}; + +Rune LJAN[] = { 'J', 'A', 'N', 0 }; +Rune LFEB[] = { 'F', 'E', 'B', 0 }; +Rune LMAR[] = { 'M', 'A', 'R', 0 }; +Rune LAPR[] = { 'A', 'P', 'R', 0 }; +Rune LMAY[] = { 'M', 'A', 'Y', 0 }; +Rune LJUN[] = { 'J', 'U', 'N', 0 }; +Rune LJUL[] = { 'J', 'U', 'L', 0 }; +Rune LAUG[] = { 'A', 'U', 'G', 0 }; +Rune LSEP[] = { 'S', 'E', 'P', 0 }; +Rune LOCT[] = { 'O', 'C', 'T', 0 }; +Rune LNOV[] = { 'N', 'O', 'V', 0 }; +Rune LDEC[] = { 'D', 'E', 'C', 0 }; + +Rune* month[12] = +{ + LJAN, + LFEB, + LMAR, + LAPR, + LMAY, + LJUN, + LJUL, + LAUG, + LSEP, + LOCT, + LNOV, + LDEC, +}; + +/************** radix sort ***********/ + +enum +{ + Threshold = 14, +}; + +void rsort4(Key***, ulong, int); +void bsort4(Key***, ulong, int); + +void +sort4(void *a, ulong n) +{ + if(n > Threshold) + rsort4((Key***)a, n, 0); + else + bsort4((Key***)a, n, 0); +} + +void +rsort4(Key ***a, ulong n, int b) +{ + Key ***ea, ***t, ***u, **t1, **u1, *k; + Key ***part[257]; + static long count[257]; + long clist[257+257], *cp, *cp1; + int c, lowc, higc; + + /* + * pass 1 over all keys, + * count the number of each key[b]. + * find low count and high count. + */ + lowc = 256; + higc = 0; + ea = a+n; + for(t=a; t<ea; t++) { + k = **t; + n = k->klen; + if(b >= n) { + count[256]++; + continue; + } + c = k->key[b]; + n = count[c]++; + if(n == 0) { + if(c < lowc) + lowc = c; + if(c > higc) + higc = c; + } + } + + /* + * pass 2 over all counts, + * put partition pointers in part[c]. + * save compacted indexes and counts + * in clist[]. + */ + t = a; + n = count[256]; + clist[0] = n; + part[256] = t; + t += n; + + cp1 = clist+1; + cp = count+lowc; + for(c=lowc; c<=higc; c++,cp++) { + n = *cp; + if(n) { + cp1[0] = n; + cp1[1] = c; + cp1 += 2; + part[c] = t; + t += n; + } + } + *cp1 = 0; + + /* + * pass 3 over all counts. + * chase lowest pointer in each partition + * around a permutation until it comes + * back and is stored where it started. + * static array, count[], should be + * reduced to zero entries except maybe + * count[256]. + */ + for(cp1=clist+1; cp1[0]; cp1+=2) { + c = cp1[1]; + cp = count+c; + while(*cp) { + t1 = *part[c]; + for(;;) { + k = *t1; + n = 256; + if(b < k->klen) + n = k->key[b]; + u = part[n]++; + count[n]--; + u1 = *u; + *u = t1; + if(n == c) + break; + t1 = u1; + } + } + } + + /* + * pass 4 over all partitions. + * call recursively. + */ + b++; + t = a + clist[0]; + count[256] = 0; + for(cp1=clist+1; n=cp1[0]; cp1+=2) { + if(n > Threshold) + rsort4(t, n, b); + else + if(n > 1) + bsort4(t, n, b); + t += n; + } +} + +/* + * bubble sort to pick up + * the pieces. + */ +void +bsort4(Key ***a, ulong n, int b) +{ + Key ***i, ***j, ***k, ***l, **t; + Key *ka, *kb; + int n1, n2; + + l = a+n; + j = a; + +loop: + i = j; + j++; + if(j >= l) + return; + + ka = **i; + kb = **j; + n1 = ka->klen - b; + n2 = kb->klen - b; + if(n1 > n2) + n1 = n2; + if(n1 <= 0) + goto loop; + n2 = ka->key[b] - kb->key[b]; + if(n2 == 0) + n2 = memcmp(ka->key+b, kb->key+b, n1); + if(n2 <= 0) + goto loop; + + for(;;) { + k = i+1; + + t = *k; + *k = *i; + *i = t; + + if(i <= a) + goto loop; + + i--; + ka = **i; + kb = *t; + n1 = ka->klen - b; + n2 = kb->klen - b; + if(n1 > n2) + n1 = n2; + if(n1 <= 0) + goto loop; + n2 = ka->key[b] - kb->key[b]; + if(n2 == 0) + n2 = memcmp(ka->key+b, kb->key+b, n1); + if(n2 <= 0) + goto loop; + } +} diff --git a/tee/Makefile b/tee/Makefile @@ -0,0 +1,36 @@ +# tee - tee unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = tee + +OFILES = tee.o + +MANFILES = tee.1 + +all: ${TARG} + @echo built ${TARG} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 diff --git a/tee/tee.1 b/tee/tee.1 @@ -0,0 +1,28 @@ +.TH TEE 1 +.SH NAME +tee \- pipe fitting +.SH SYNOPSIS +.B tee +[ +.B -i +] +[ +.B -a +] +.I files +.SH DESCRIPTION +.I Tee +transcribes the standard input to the standard +output and makes copies in the +.IR files . +The options are +.TP +.B -i +Ignore interrupts. +.TP +.B -a +Append the output to the +.I files +rather than rewriting them. +.SH SOURCE +.B \*9/src/cmd/tee.c diff --git a/tee/tee.c b/tee/tee.c @@ -0,0 +1,75 @@ +/* + * tee-- pipe fitting + */ + +#include <u.h> +#include <libc.h> + +int uflag; +int aflag; +int openf[100]; + +char in[8192]; + +int intignore(void*, char*); + +void +main(int argc, char **argv) +{ + int i; + int r, n; + + ARGBEGIN { + case 'a': + aflag++; + break; + + case 'i': + atnotify(intignore, 1); + break; + + case 'u': + uflag++; + /* uflag is ignored and undocumented; it's a relic from Unix */ + break; + + default: + fprint(2, "usage: tee [-ai] [file ...]\n"); + exits("usage"); + } ARGEND + + USED(argc); + n = 0; + while(*argv) { + if(aflag) { + openf[n] = open(argv[0], OWRITE); + if(openf[n] < 0) + openf[n] = create(argv[0], OWRITE, 0666); + seek(openf[n], 0L, 2); + } else + openf[n] = create(argv[0], OWRITE, 0666); + if(openf[n] < 0) { + fprint(2, "tee: cannot open %s: %r\n", argv[0]); + } else + n++; + argv++; + } + openf[n++] = 1; + + for(;;) { + r = read(0, in, sizeof in); + if(r <= 0) + exits(nil); + for(i=0; i<n; i++) + write(openf[i], in, r); + } +} + +int +intignore(void *a, char *msg) +{ + USED(a); + if(strcmp(msg, "interrupt") == 0) + return 1; + return 0; +} diff --git a/test/Makefile b/test/Makefile @@ -0,0 +1,36 @@ +# test - test unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = test + +OFILES = test.o + +MANFILES = test.1 + +all: ${TARG} + @echo built ${TARG} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 diff --git a/test/test.1 b/test/test.1 @@ -0,0 +1,211 @@ +.TH TEST 1 +.SH NAME +test \- set status according to condition +.SH SYNOPSIS +.B test +.I expr +.SH DESCRIPTION +.I Test +evaluates the expression +.IR expr . +If the value is true the exit status is null; otherwise the +exit status is non-null. +If there are no arguments the exit status is non-null. +.PP +The following primitives are used to construct +.IR expr . +.TP "\w'\fIn1 \fL-eq \fIn2\fLXX'u" +.BI -r " file" +True if the file exists (is accessible) and is readable. +.PD0 +.TP +.BI -w " file" +True if the file exists and is writable. +.TP +.BI -x " file" +True if the file exists and has execute permission. +.TP +.BI -e " file +True if the file exists. +.TP +.BI -f " file" +True if the file exists and is a plain file. +.TP +.BI -d " file" +True if the file exists and is a directory. +.TP +.BI -s " file" +True if the file exists and has a size greater than zero. +.TP +.BI -t " fildes +True if the open file whose file descriptor number is +.I fildes +(1 by default) +is the same file as +.BR /dev/cons . +.TP +.BI -A " file" +True if the file exists and is append-only. +.TP +.BI -L " file" +True if the file exists and is exclusive-use. +.TP +.BI -T "file" +True if the file exists and is temporary. +.TP +.IB s1 " = " s2 +True +if the strings +.I s1 +and +.I s2 +are identical. +.TP +.IB s1 " != " s2 +True +if the strings +.I s1 +and +.I s2 +are not identical. +.TP +s1 +True if +.I s1 +is not the null string. +(Deprecated.) +.TP +.BI -n " s1" +True if the length of string +.I s1 +is non-zero. +.TP +.BI -z " s1" +True if the length of string +.I s1 +is zero. +.TP +.IB n1 " -eq " n2 +True if the integers +.I n1 +and +.I n2 +are arithmetically equal. +Any of the comparisons +.BR -ne , +.BR -gt , +.BR -ge , +.BR -lt , +or +.BR -le +may be used in place of +.BR -eq . +The (nonstandard) construct +.BI -l " string\f1, +meaning the length of +.IR string , +may be used in place of an integer. +.TP +.IB a " -nt " b +True if file +.I a +is newer than (modified after) file +.IR b . +.TP +.IB a " -ot " b +True if file +.I a +is older than (modified before) file +.IR b . +.TP +.IB f " -older " t +True if file +.I f +is older than (modified before) time +.IR t . +If +.I t +is a integer followed by the letters +.BR y (years), +.BR M (months), +.BR d (days), +.BR h (hours), +.BR m (minutes), +or +.BR s (seconds), +it represents current time minus the specified time. +If there is no letter, it represents seconds since +epoch. +You can also concatenate mixed units. For example, +.B 3d12h +means three days and twelve hours ago. +.PD +.PP +These primaries may be combined with the +following operators: +.TP "\w'\fL( \fIexpr\fL )XX'u" +.B ! +unary negation operator +.PD0 +.TP +.B -o +binary +.I or +operator +.TP +.B -a +binary +.I and +operator; higher precedence than +.BR -o +.TP +.BI "( " expr " )" +parentheses for grouping. +.PD +.PP +The primitives +.BR -b , +.BR -u , +.BR -g , +and +.BR -s +return false; they are recognized for compatibility with POSIX. +.PP +Notice that all the operators and flags are separate +arguments to +.IR test . +Notice also that parentheses and equal signs are meaningful +to +.I rc +and must be enclosed in quotes. +.SH EXAMPLES +.I Test +is a dubious way to check for specific character strings: +it uses a process to do what an +.IR rc (1) +match or switch statement can do. +The first example is not only inefficient but wrong, because +.I test +understands the purported string +.B \&"-c" +as an option. +.IP +.EX +if (test $1 '=' "-c") echo OK # wrong! +.EE +.LP +A better way is +.IP +.EX +if (~ $1 -c) echo OK +.EE +.PP +Test whether +.L abc +is in the current directory. +.IP +.B test -f abc -o -d abc +.SH SOURCE +.B \*9/src/cmd/test.c +.SH "SEE ALSO" +.IR rc (1) diff --git a/test/test.c b/test/test.c @@ -0,0 +1,292 @@ +/* + * POSIX standard + * test expression + * [ expression ] + * + * Plan 9 additions: + * -A file exists and is append-only + * -L file exists and is exclusive-use + */ + +#include <u.h> +#include <libc.h> +#define EQ(a,b) ((tmp=a)==0?0:(strcmp(tmp,b)==0)) + +extern int isatty(int); /* <unistd.h> */ + +int ap; +int ac; +char **av; +char *tmp; + +void synbad(char *, char *); +int fsizep(char *); +int isdir(char *); +int isreg(char *); +int isint(char *, int *); +int hasmode(char *, ulong); +int tio(char *, int); +int e(void), e1(void), e2(void), e3(void); + +void +main(int argc, char *argv[]) +{ + + ac = argc; av = argv; ap = 1; + if(EQ(argv[0],"[")) { + if(!EQ(argv[--ac],"]")) + synbad("] missing",""); + } + argv[ac] = 0; + if (ac<=1) exits("usage"); + exits(e()?0:"false"); +} + +char * +nxtarg(int mt) +{ + if(ap>=ac){ + if(mt){ + ap++; + return(0); + } + synbad("argument expected",""); + } + return(av[ap++]); +} + +int +nxtintarg(int *pans) +{ + if(ap<ac && isint(av[ap], pans)){ + ap++; + return 1; + } + return 0; +} + +int +e(void) { + int p1; + + p1 = e1(); + if (EQ(nxtarg(1), "-o")) return(p1 || e()); + ap--; + return(p1); +} + +int +e1(void) { + int p1; + + p1 = e2(); + if (EQ(nxtarg(1), "-a")) return (p1 && e1()); + ap--; + return(p1); +} + +int +e2(void) { + if (EQ(nxtarg(0), "!")) + return(!e2()); + ap--; + return(e3()); +} + +int +e3(void) { + int p1; + char *a; + char *p2; + int int1, int2; + + a = nxtarg(0); + if(EQ(a, "(")) { + p1 = e(); + if(!EQ(nxtarg(0), ")")) synbad(") expected",""); + return(p1); + } + + if(EQ(a, "-A")) + return(hasmode(nxtarg(0), DMAPPEND)); + + if(EQ(a, "-L")) + return(hasmode(nxtarg(0), DMEXCL)); + + if(EQ(a, "-f")) + return(isreg(nxtarg(0))); + + if(EQ(a, "-d")) + return(isdir(nxtarg(0))); + + if(EQ(a, "-r")) + return(tio(nxtarg(0), 4)); + + if(EQ(a, "-w")) + return(tio(nxtarg(0), 2)); + + if(EQ(a, "-x")) + return(tio(nxtarg(0), 1)); + + if(EQ(a, "-e")) + return(tio(nxtarg(0), 0)); + + if(EQ(a, "-c")) + return(0); + + if(EQ(a, "-b")) + return(0); + + if(EQ(a, "-u")) + return(0); + + if(EQ(a, "-g")) + return(0); + + if(EQ(a, "-s")) + return(fsizep(nxtarg(0))); + + if(EQ(a, "-t")) + if(ap>=ac || !nxtintarg(&int1)) + return(isatty(1)); + else + return(isatty(int1)); + + if(EQ(a, "-n")) + return(!EQ(nxtarg(0), "")); + if(EQ(a, "-z")) + return(EQ(nxtarg(0), "")); + + p2 = nxtarg(1); + if (p2==0) + return(!EQ(a,"")); + if(EQ(p2, "=")) + return(EQ(nxtarg(0), a)); + + if(EQ(p2, "!=")) + return(!EQ(nxtarg(0), a)); + + if(!isint(a, &int1)) + return(!EQ(a,"")); + + if(nxtintarg(&int2)){ + if(EQ(p2, "-eq")) + return(int1==int2); + if(EQ(p2, "-ne")) + return(int1!=int2); + if(EQ(p2, "-gt")) + return(int1>int2); + if(EQ(p2, "-lt")) + return(int1<int2); + if(EQ(p2, "-ge")) + return(int1>=int2); + if(EQ(p2, "-le")) + return(int1<=int2); + } + + synbad("unknown operator ",p2); + return 0; /* to shut ken up */ +} + +int +tio(char *a, int f) +{ + return access (a, f) >= 0; +} + +/* copy to local memory; clear names for safety */ +int +localstat(char *f, Dir *dir) +{ + Dir *d; + + d = dirstat(f); + if(d == 0) + return(-1); + *dir = *d; + dir->name = 0; + dir->uid = 0; + dir->gid = 0; + dir->muid = 0; + return 0; +} + +/* copy to local memory; clear names for safety */ +int +localfstat(int f, Dir *dir) +{ + Dir *d; + + d = dirfstat(f); + if(d == 0) + return(-1); + *dir = *d; + dir->name = 0; + dir->uid = 0; + dir->gid = 0; + dir->muid = 0; + return 0; +} + +int +hasmode(char *f, ulong m) +{ + Dir dir; + + if(localstat(f,&dir)<0) + return(0); + return(dir.mode&m); +} + +int +isdir(char *f) +{ + Dir dir; + + if(localstat(f,&dir)<0) + return(0); + return(dir.mode&DMDIR); +} + +int +isreg(char *f) +{ + Dir dir; + + if(localstat(f,&dir)<0) + return(0); + return(!(dir.mode&DMDIR)); +} + +int +fsizep(char *f) +{ + Dir dir; + + if(localstat(f,&dir)<0) + return(0); + return(dir.length>0); +} + +void +synbad(char *s1, char *s2) +{ + int len; + + write(2, "test: ", 6); + if ((len = strlen(s1)) != 0) + write(2, s1, len); + if ((len = strlen(s2)) != 0) + write(2, s2, len); + write(2, "\n", 1); + exits("bad syntax"); +} + +int +isint(char *s, int *pans) +{ + char *ep; + + *pans = strtol(s, &ep, 0); + return (*ep == 0); +} diff --git a/touch/Makefile b/touch/Makefile @@ -0,0 +1,36 @@ +# touch - touch unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = touch + +OFILES = touch.o + +MANFILES = touch.1 + +all: ${TARG} + @echo built ${TARG} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 diff --git a/touch/touch.1 b/touch/touch.1 @@ -0,0 +1,35 @@ +.TH TOUCH 1 +.SH NAME +touch \- set modification date of a file +.SH SYNOPSIS +.B touch +[ +.B -c +] +[ +.B -t +.I time +] +.I file ... +.SH DESCRIPTION +.I Touch +attempts to set the modification time of the +.I files +to +.I time +(by default, the current time). +If a +.I file +does not exist, +it will be created unless option +.B -c +is present. +.SH SOURCE +.B \*9/src/cmd/touch.c +.SH SEE ALSO +.IR ls (1), +.IR stat (3), +.IR chmod (1) +.SH BUGS +.I Touch +will not touch directories. diff --git a/touch/touch.c b/touch/touch.c @@ -0,0 +1,62 @@ +#include <u.h> +#include <libc.h> + +int touch(int, char *); +ulong now; + +void +usage(void) +{ + fprint(2, "usage: touch [-c] [-t time] files\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int nocreate = 0; + int status = 0; + + now = time(0); + ARGBEGIN{ + case 't': + now = strtoul(EARGF(usage()), 0, 0); + break; + case 'c': + nocreate = 1; + break; + default: + usage(); + }ARGEND + + if(!*argv) + usage(); + while(*argv) + status += touch(nocreate, *argv++); + if(status) + exits("touch"); + exits(0); +} + +int +touch(int nocreate, char *name) +{ + Dir stbuff; + int fd; + + nulldir(&stbuff); + stbuff.mtime = now; + if(dirwstat(name, &stbuff) >= 0) + return 0; + if(nocreate){ + fprint(2, "touch: %s: cannot wstat: %r\n", name); + return 1; + } + if ((fd = create(name, OREAD, 0666)) < 0) { + fprint(2, "touch: %s: cannot create: %r\n", name); + return 1; + } + dirfwstat(fd, &stbuff); + close(fd); + return 0; +} diff --git a/tr/Makefile b/tr/Makefile @@ -0,0 +1,36 @@ +# tr - tr unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = tr + +OFILES = tr.o + +MANFILES = tr.1 + +all: ${TARG} + @echo built ${TARG} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 diff --git a/tr/tr.1 b/tr/tr.1 @@ -0,0 +1,97 @@ +.TH TR 1 +.SH NAME +tr \- translate characters +.SH SYNOPSIS +.B tr +[ +.B -cds +] +[ +.I string1 +[ +.I string2 +] +] +.SH DESCRIPTION +.I Tr +copies the standard input to the standard output with +substitution or deletion of selected characters (runes). +Input characters found in +.I string1 +are mapped into the corresponding characters of +.IR string2 . +When +.I string2 +is short it is padded to the length of +.I string1 +by duplicating its last character. +Any combination of the options +.B -cds +may be used: +.TP +.B -c +Complement +.IR string1 : +replace it with a lexicographically ordered +list of all other characters. +.TP +.B -d +Delete from input all characters in +.IR string1 . +.TP +.B -s +Squeeze repeated output characters that occur in +.I string2 +to single characters. +.PP +In either string a noninitial sequence +.BI - x\f1, +where +.I x +is any character (possibly quoted), stands for +a range of characters: +a possibly empty sequence of codes running from +the successor of the previous code up through +the code for +.IR x . +The character +.L \e +followed by 1, 2 or 3 octal digits stands for the +character whose +16-bit +value is given by those digits. +The character sequence +.L \ex +followed by 1, 2, 3, or 4 hexadecimal digits stands +for the character whose +16-bit value is given by those digits. +A +.L \e +followed by any other character stands +for that character. +.SH EXAMPLES +Replace all upper-case +.SM ASCII +letters by lower-case. +.IP +.EX +tr A-Z a-z <mixed >lower +.EE +.PP +Create a list of all +the words in +.L file1 +one per line in +.LR file2 , +where a word is taken to be a maximal string of alphabetics. +.I String2 +is given as a quoted newline. +.IP +.EX +tr -cs A-Za-z ' +\&' <file1 >file2 +.EE +.SH SOURCE +.B \*9/src/cmd/tr.c +.SH "SEE ALSO" +.IR sed (1) diff --git a/tr/tr.c b/tr/tr.c @@ -0,0 +1,356 @@ +#include <u.h> +#include <libc.h> + +typedef struct PCB /* Control block controlling specification parse */ +{ + char *base; /* start of specification */ + char *current; /* current parse point */ + long last; /* last Rune returned */ + long final; /* final Rune in a span */ +} Pcb; + +uchar bits[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; + +#define SETBIT(a, c) ((a)[(c)/8] |= bits[(c)&07]) +#define CLEARBIT(a,c) ((a)[(c)/8] &= ~bits[(c)&07]) +#define BITSET(a,c) ((a)[(c)/8] & bits[(c)&07]) + +#define MAXRUNE 0xFFFF + +uchar f[(MAXRUNE+1)/8]; +uchar t[(MAXRUNE+1)/8]; +char wbuf[4096]; +char *wptr; + +Pcb pfrom, pto; + +int cflag; +int dflag; +int sflag; + +void complement(void); +void delete(void); +void squeeze(void); +void translit(void); +void error(char*); +long canon(Pcb*); +char *getrune(char*, Rune*); +void Pinit(Pcb*, char*); +void Prewind(Pcb *p); +int readrune(int, long*); +void wflush(int); +void writerune(int, Rune); + +void +main(int argc, char **argv) +{ + ARGBEGIN{ + case 's': sflag++; break; + case 'd': dflag++; break; + case 'c': cflag++; break; + default: error("bad option"); + }ARGEND + if(argc>0) + Pinit(&pfrom, argv[0]); + if(argc>1) + Pinit(&pto, argv[1]); + if(argc>2) + error("arg count"); + if(dflag) { + if ((sflag && argc != 2) || (!sflag && argc != 1)) + error("arg count"); + delete(); + } else { + if (argc != 2) + error("arg count"); + if (cflag) + complement(); + else translit(); + } + exits(0); +} + +void +delete(void) +{ + long c, last; + + if (cflag) { + memset((char *) f, 0xff, sizeof f); + while ((c = canon(&pfrom)) >= 0) + CLEARBIT(f, c); + } else { + while ((c = canon(&pfrom)) >= 0) + SETBIT(f, c); + } + if (sflag) { + while ((c = canon(&pto)) >= 0) + SETBIT(t, c); + } + + last = 0x10000; + while (readrune(0, &c) > 0) { + if(!BITSET(f, c) && (c != last || !BITSET(t,c))) { + last = c; + writerune(1, (Rune) c); + } + } + wflush(1); +} + +void +complement(void) +{ + Rune *p; + int i; + long from, to, lastc, high; + + lastc = 0; + high = 0; + while ((from = canon(&pfrom)) >= 0) { + if (from > high) high = from; + SETBIT(f, from); + } + while ((to = canon(&pto)) > 0) { + if (to > high) high = to; + SETBIT(t,to); + } + Prewind(&pto); + if ((p = (Rune *) malloc((high+1)*sizeof(Rune))) == 0) + error("can't allocate memory"); + for (i = 0; i <= high; i++){ + if (!BITSET(f,i)) { + if ((to = canon(&pto)) < 0) + to = lastc; + else lastc = to; + p[i] = to; + } + else p[i] = i; + } + if (sflag){ + lastc = 0x10000; + while (readrune(0, &from) > 0) { + if (from > high) + from = to; + else + from = p[from]; + if (from != lastc || !BITSET(t,from)) { + lastc = from; + writerune(1, (Rune) from); + } + } + + } else { + while (readrune(0, &from) > 0){ + if (from > high) + from = to; + else + from = p[from]; + writerune(1, (Rune) from); + } + } + wflush(1); +} + +void +translit(void) +{ + Rune *p; + int i; + long from, to, lastc, high; + + lastc = 0; + high = 0; + while ((from = canon(&pfrom)) >= 0) + if (from > high) high = from; + Prewind(&pfrom); + if ((p = (Rune *) malloc((high+1)*sizeof(Rune))) == 0) + error("can't allocate memory"); + for (i = 0; i <= high; i++) + p[i] = i; + while ((from = canon(&pfrom)) >= 0) { + if ((to = canon(&pto)) < 0) + to = lastc; + else lastc = to; + if (BITSET(f,from) && p[from] != to) + error("ambiguous translation"); + SETBIT(f,from); + p[from] = to; + SETBIT(t,to); + } + while ((to = canon(&pto)) >= 0) { + SETBIT(t,to); + } + if (sflag){ + lastc = 0x10000; + while (readrune(0, &from) > 0) { + if (from <= high) + from = p[from]; + if (from != lastc || !BITSET(t,from)) { + lastc = from; + writerune(1, (Rune) from); + } + } + + } else { + while (readrune(0, &from) > 0) { + if (from <= high) + from = p[from]; + writerune(1, (Rune) from); + } + } + wflush(1); +} + +int +readrune(int fd, long *rp) +{ + Rune r; + int j; + static int i, n; + static char buf[4096]; + + j = i; + for (;;) { + if (i >= n) { + wflush(1); + if (j != i) + memcpy(buf, buf+j, n-j); + i = n-j; + n = read(fd, &buf[i], sizeof(buf)-i); + if (n < 0) + error("read error"); + if (n == 0) + return 0; + j = 0; + n += i; + } + i++; + if (fullrune(&buf[j], i-j)) + break; + } + chartorune(&r, &buf[j]); + *rp = r; + return 1; +} + +void +writerune(int fd, Rune r) +{ + char buf[UTFmax]; + int n; + + if (!wptr) + wptr = wbuf; + n = runetochar(buf, (Rune*)&r); + if (wptr+n >= wbuf+sizeof(wbuf)) + wflush(fd); + memcpy(wptr, buf, n); + wptr += n; +} + +void +wflush(int fd) +{ + if (wptr && wptr > wbuf) + if (write(fd, wbuf, wptr-wbuf) != wptr-wbuf) + error("write error"); + wptr = wbuf; +} + +char * +getrune(char *s, Rune *rp) +{ + Rune r; + char *save; + int i, n; + + s += chartorune(rp, s); + if((r = *rp) == '\\' && *s){ + n = 0; + if (*s == 'x') { + s++; + for (i = 0; i < 4; i++) { + save = s; + s += chartorune(&r, s); + if ('0' <= r && r <= '9') + n = 16*n + r - '0'; + else if ('a' <= r && r <= 'f') + n = 16*n + r - 'a' + 10; + else if ('A' <= r && r <= 'F') + n = 16*n + r - 'A' + 10; + else { + if (i == 0) + *rp = 'x'; + else *rp = n; + return save; + } + } + } else { + for(i = 0; i < 3; i++) { + save = s; + s += chartorune(&r, s); + if('0' <= r && r <= '7') + n = 8*n + r - '0'; + else { + if (i == 0) + { + *rp = r; + return s; + } + *rp = n; + return save; + } + } + if(n > 0377) + error("char>0377"); + } + *rp = n; + } + return s; +} + +long +canon(Pcb *p) +{ + Rune r; + + if (p->final >= 0) { + if (p->last < p->final) + return ++p->last; + p->final = -1; + } + if (*p->current == '\0') + return -1; + if(*p->current == '-' && p->last >= 0 && p->current[1]){ + p->current = getrune(p->current+1, &r); + if (r < p->last) + error ("Invalid range specification"); + if (r > p->last) { + p->final = r; + return ++p->last; + } + } + p->current = getrune(p->current, &r); + p->last = r; + return p->last; +} + +void +Pinit(Pcb *p, char *cp) +{ + p->current = p->base = cp; + p->last = p->final = -1; +} +void +Prewind(Pcb *p) +{ + p->current = p->base; + p->last = p->final = -1; +} +void +error(char *s) +{ + fprint(2, "%s: %s\n", argv0, s); + exits(s); +} diff --git a/uniq/Makefile b/uniq/Makefile @@ -0,0 +1,36 @@ +# uniq - uniq unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = uniq + +OFILES = uniq.o + +MANFILES = uniq.1 + +all: ${TARG} + @echo built ${TARG} + +install: ${TARG} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/ + @chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG} + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + rm -f ${OFILES} ${TARG} + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 diff --git a/uniq/uniq.1 b/uniq/uniq.1 @@ -0,0 +1,59 @@ +.TH UNIQ 1 +.SH NAME +uniq \- report repeated lines in a file +.SH SYNOPSIS +.B uniq +[ +.B -udc +[ +.BI +- num +] +] +[ +.I file +] +.SH DESCRIPTION +.I Uniq +copies the input +.IR file , +or the standard input, to the +standard output, comparing adjacent lines. +In the normal case, the second and succeeding copies +of repeated lines are +removed. +Repeated lines must be adjacent +in order to be found. +.TP +.B -u +Print unique lines. +.TP +.B -d +Print (one copy of) duplicated lines. +.TP +.B -c +Prefix a repetition count and a tab to each output line. +Implies +.B -u +and +.BR -d . +.TP +.BI - num +The first +.IR num +fields +together with any blanks before each are ignored. +A field is defined as a string of non-space, non-tab characters +separated by tabs and spaces from its neighbors. +.TP +.BI + num +The first +.IR num +characters are ignored. +Fields are skipped before characters. +.SH SOURCE +.B \*9/src/cmd/uniq.c +.SH "SEE ALSO" +.IR sort (1) +.SH BUGS +Field selection and comparison should be compatible with +.IR sort (1). diff --git a/uniq/uniq.c b/uniq/uniq.c @@ -0,0 +1,169 @@ +/* + * Deal with duplicated lines in a file + */ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <ctype.h> + +#define SIZE 8000 + +int fields = 0; +int letters = 0; +int linec = 0; +char mode; +int uniq; +char *b1, *b2; +long bsize; +Biobuf fin; +Biobuf fout; + +int gline(char *buf); +void pline(char *buf); +int equal(char *b1, char *b2); +char* skip(char *s); + +void +main(int argc, char *argv[]) +{ + int f; + + bsize = SIZE; + b1 = malloc(bsize); + b2 = malloc(bsize); + f = 0; + while(argc > 1) { + if(*argv[1] == '-') { + if(isdigit((uchar)argv[1][1])) + fields = atoi(&argv[1][1]); + else + mode = argv[1][1]; + argc--; + argv++; + continue; + } + if(*argv[1] == '+') { + letters = atoi(&argv[1][1]); + argc--; + argv++; + continue; + } + f = open(argv[1], 0); + if(f < 0) { + fprint(2, "cannot open %s\n", argv[1]); + exits("open"); + } + break; + } + if(argc > 2) { + fprint(2, "unexpected argument %s\n", argv[2]); + exits("arg"); + } + Binit(&fin, f, OREAD); + Binit(&fout, 1, OWRITE); + + if(gline(b1)) + exits(0); + for(;;) { + linec++; + if(gline(b2)) { + pline(b1); + exits(0); + } + if(!equal(b1, b2)) { + pline(b1); + linec = 0; + do { + linec++; + if(gline(b1)) { + pline(b2); + exits(0); + } + } while(equal(b2, b1)); + pline(b2); + linec = 0; + } + } +} + +int +gline(char *buf) +{ + char *p; + + p = Brdline(&fin, '\n'); + if(p == 0) + return 1; + if(fin.rdline >= bsize-1) { + fprint(2, "line too long\n"); + exits("too long"); + } + memmove(buf, p, fin.rdline); + buf[fin.rdline-1] = 0; + return 0; +} + +void +pline(char *buf) +{ + + switch(mode) { + + case 'u': + if(uniq) { + uniq = 0; + return; + } + break; + + case 'd': + if(uniq) + break; + return; + + case 'c': + Bprint(&fout, "%4d ", linec); + } + uniq = 0; + Bprint(&fout, "%s\n", buf); +} + +int +equal(char *b1, char *b2) +{ + char c; + + if(fields || letters) { + b1 = skip(b1); + b2 = skip(b2); + } + for(;;) { + c = *b1++; + if(c != *b2++) { + if(c == 0 && mode == 's') + return 1; + return 0; + } + if(c == 0) { + uniq++; + return 1; + } + } +} + +char* +skip(char *s) +{ + int nf, nl; + + nf = nl = 0; + while(nf++ < fields) { + while(*s == ' ' || *s == '\t') + s++; + while(!(*s == ' ' || *s == '\t' || *s == 0) ) + s++; + } + while(nl++ < letters && *s != 0) + s++; + return s; +} diff --git a/yacc/9yacc b/yacc/9yacc @@ -0,0 +1,4 @@ +#!/bin/sh +PLAN9="`pwd`/.." +export PLAN9 +../yacc/yacc $* diff --git a/yacc/Makefile b/yacc/Makefile @@ -0,0 +1,27 @@ +# yacc - yacc unix port from plan9 +# Depends on ../lib9 + +include ../config.mk + +TARG = yacc + +OFILES = yacc.o + +MANFILES = yacc.1 + +all: ${TARG} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c + +clean: + @rm -f ${OFILES} ${TARG} + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9 + +install: + +uninstall: diff --git a/yacc/yacc.1 b/yacc/yacc.1 @@ -0,0 +1,176 @@ +.TH YACC 1 +.SH NAME +yacc \- yet another compiler-compiler +.SH SYNOPSIS +.B yacc +[ +.I option ... +] +.I grammar +.SH DESCRIPTION +.I Yacc +converts a context-free grammar and translation code +into a set of +tables for an LR(1) parser and translator. +The grammar may be ambiguous; +specified precedence rules are used to break ambiguities. +.PP +The output file, +.BR y.tab.c , +must be compiled by the C compiler +to produce a program +.LR yyparse . +This program must be loaded with a lexical analyzer function, +.B yylex(void) +(often generated by +.IR lex (1)), +with a +.B main(int argc, char *argv[]) +program, and with an error handling routine, +.BR yyerror(char*) . +.PP +The options are +.TP "\w'\fL-o \fIoutput\fLXX'u" +.BI -o " output +Direct output to the specified file instead of +.BR y.tab.c . +.TP +.BI -D n +Create file +.BR y.debug , +containing diagnostic messages. +To incorporate them in the parser, compile it with preprocessor symbol +.B yydebug +defined. +The amount of +diagnostic output from the parser is regulated by +value +.IR n . +The value 0 reports errors; 1 reports reductions; +higher values (up to 4) include more information about +state transitions. +.TP +.B -v +Create file +.BR y.output , +containing a description of the parsing tables and of +conflicts arising from ambiguities in the grammar. +.TP +.B -d +Create file +.BR y.tab.h , +containing +.B #define +statements that associate +.IR yacc -assigned +`token codes' with user-declared `token names'. +Include it in source files other than +.B y.tab.c +to give access to the token codes. +.TP +.BI -s " stem +Change the prefix +.L y +of the file names +.BR y.tab.c , +.BR y.tab.h , +.BR y.debug , +and +.B y.output +to +.IR stem . +.TP +.B -S +Write a parser that uses +Stdio +instead of the +.B print +routines in libc. +.TP +.BI -l +Disable #line directives in the generated parser. +.TP +.BI -a +Generate a parser that takes an argument of type Yyarg +and passes this argument to each invocation of the lexer +function, yylex. Yyarg contains per-instance state +and a single user-visible member, arg, of type void*. +.PP +The specification of +.I yacc +itself is essentially the same as the UNIX version +described in the references mentioned below. +Besides the +.B -D +option, the main relevant differences are: +.IP +The interface to the C environment is by default through +.B <libc.h> +rather than +.BR <stdio.h> ; +the +.B -S +option reverses this. +.IP +The parser accepts +.SM UTF +input text (see +.IR utf (7)), +which has a couple of effects. +First, the return value of +.B yylex() +no longer fits in a +.BR short ; +second, the starting value for non-terminals is now 0xE000 rather than 257. +.IP +The generated parser can be recursive: actions can call +.IR yyparse , +for example to implement a sort of +.B #include +statement in an interpreter. +.IP +Finally, some undocumented inner workings of the parser have been +changed, which may affect programs that know too much about its structure. +.SH FILES +.TF y.debug.xxxxx +.TP +.B y.output +.TP +.B y.tab.c +.TP +.B y.tab.h +.TP +.B y.debug +.TP +.B y.tmp.* +temporary file +.TP +.B y.acts.* +temporary file +.TP +.B \*9/lib/yaccpar +parser prototype +.TP +.B \*9/lib/yaccpars +parser prototype using stdio +.SH SOURCE +.B \*9/src/cmd/yacc.c +.SH "SEE ALSO" +.IR lex (1) +.br +S. C. Johnson and R. Sethi, +``Yacc: A parser generator'', +.I +Unix Research System Programmer's Manual, +Tenth Edition, Volume 2 +.br +B. W. Kernighan and Rob Pike, +.I +The UNIX Programming Environment, +Prentice Hall, 1984 +.SH BUGS +The parser may not have full information when it writes to +.B y.debug +so that the names of the tokens returned by +.L yylex +may be missing. diff --git a/yacc/yacc.c b/yacc/yacc.c @@ -0,0 +1,2980 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <ctype.h> + +#define Bungetrune Bungetc /* ok for now. */ + +/* + * all these are 32 bit + */ +#define TBITSET ((32+NTERMS)/32) /* BOTCH?? +31 */ +#define BIT(a,i) ((a)[(i)>>5] & (1<<((i)&037))) +#define SETBIT(a,i) ((a)[(i)>>5] |= (1<<((i)&037))) +#define NWORDS(n) (((n)+32)/32) + +char *PARSER = "#9/yacc/yaccpar"; +char *PARSERS = "#9/yacc/yaccpars"; + +#define TEMPNAME "y.tmp.XXXXXX" +#define ACTNAME "y.acts.XXXXXX" +#define OFILE "tab.c" +#define FILEU "output" +#define FILED "tab.h" +#define FILEDEBUG "debug" + +enum +{ +/* + * the following are adjustable + * according to memory size + */ + ACTSIZE = 40000, + MEMSIZE = 40000, + NSTATES = 2000, + NTERMS = 511, + NPROD = 1600, + NNONTERM = 600, + TEMPSIZE = 2000, + CNAMSZ = 10000, + LSETSIZE = 2400, + WSETSIZE = 350, + + NAMESIZE = 50, + NTYPES = 63, + ISIZE = 400, + + PRIVATE = 0xE000, /* unicode private use */ + + /* relationships which must hold: + TBITSET ints must hold NTERMS+1 bits... + WSETSIZE >= NNONTERM + LSETSIZE >= NNONTERM + TEMPSIZE >= NTERMS + NNONTERM + 1 + TEMPSIZE >= NSTATES + */ + + NTBASE = 010000, + ERRCODE = 8190, + ACCEPTCODE = 8191, + + NOASC = 0, /* no assoc. */ + LASC = 1, /* left assoc. */ + RASC = 2, /* right assoc. */ + BASC = 3, /* binary assoc. */ + + /* flags for state generation */ + + DONE = 0, + MUSTDO = 1, + MUSTLOOKAHEAD = 2, + + /* flags for a rule having an action, and being reduced */ + + ACTFLAG = 04, + REDFLAG = 010, + + /* output parser flags */ + YYFLAG1 = -1000, + + /* parse tokens */ + IDENTIFIER = PRIVATE, + MARK, + TERM, + LEFT, + RIGHT, + BINARY, + PREC, + LCURLY, + IDENTCOLON, + NUMBER, + START, + TYPEDEF, + TYPENAME, + UNION, + + ENDFILE = 0, + + EMPTY = 1, + WHOKNOWS = 0, + OK = 1, + NOMORE = -1000, +}; + + /* macros for getting associativity and precedence levels */ + +#define ASSOC(i) ((i)&03) +#define PLEVEL(i) (((i)>>4)&077) +#define TYPE(i) (((i)>>10)&077) + + /* macros for setting associativity and precedence levels */ + +#define SETASC(i,j) i |= j +#define SETPLEV(i,j) i |= (j<<4) +#define SETTYPE(i,j) i |= (j<<10) + + /* looping macros */ + +#define TLOOP(i) for(i=1; i<=ntokens; i++) +#define NTLOOP(i) for(i=0; i<=nnonter; i++) +#define PLOOP(s,i) for(i=s; i<nprod; i++) +#define SLOOP(i) for(i=0; i<nstate; i++) +#define WSBUMP(x) x++ +#define WSLOOP(s,j) for(j=s; j<cwp; j++) +#define ITMLOOP(i,p,q) for(q=pstate[i+1], p=pstate[i]; p<q; p++) +#define SETLOOP(i) for(i=0; i<tbitset; i++) + + /* command to clobber tempfiles after use */ + +#define ZAPFILE(x) if(x) remove(x) + + /* I/O descriptors */ + +Biobuf* faction; /* file for saving actions */ +Biobuf* fdefine; /* file for #defines */ +Biobuf* fdebug; /* y.debug for strings for debugging */ +Biobuf* ftable; /* y.tab.c file */ +Biobuf* ftemp; /* tempfile to pass 2 */ +Biobuf* finput; /* input file */ +Biobuf* foutput; /* y.output file */ + + /* communication variables between various I/O routines */ + +char* infile; /* input file name */ +int numbval; /* value of an input number */ +char tokname[NAMESIZE+4]; /* input token name, slop for runes and 0 */ + + /* structure declarations */ + +typedef +struct +{ + int lset[TBITSET]; +} Lkset; + +typedef +struct +{ + int* pitem; + Lkset* look; +} Item; + +typedef +struct +{ + char* name; + int value; +} Symb; + +typedef +struct +{ + int* pitem; + int flag; + Lkset ws; +} Wset; + + /* storage of names */ + +char cnames[CNAMSZ]; /* place where token and nonterminal names are stored */ +int cnamsz = CNAMSZ; /* size of cnames */ +char* cnamp = cnames; /* place where next name is to be put in */ +int ndefout = 4; /* number of defined symbols output */ +char* tempname; +char* actname; +char ttempname[] = TEMPNAME; +char tactname[] = ACTNAME; +char* parser; +char* yydebug; +int yyarg; +int yyline = 1; + + /* storage of types */ +int ntypes; /* number of types defined */ +char* typeset[NTYPES]; /* pointers to type tags */ + + /* token information */ + +int ntokens = 0 ; /* number of tokens */ +Symb tokset[NTERMS]; +int toklev[NTERMS]; /* vector with the precedence of the terminals */ + + /* nonterminal information */ + +int nnonter = -1; /* the number of nonterminals */ +Symb nontrst[NNONTERM]; +int start; /* start symbol */ + + /* assigned token type values */ +int extval = 0; + +char* ytabc = OFILE; /* name of y.tab.c */ + + /* grammar rule information */ + +int mem0[MEMSIZE] ; /* production storage */ +int* mem = mem0; +int nprod = 1; /* number of productions */ +int* prdptr[NPROD]; /* pointers to descriptions of productions */ +int levprd[NPROD]; /* precedence levels for the productions */ +int rlines[NPROD]; /* line number for this rule */ + + /* state information */ + +int nstate = 0; /* number of states */ +Item* pstate[NSTATES+2]; /* pointers to the descriptions of the states */ +int tystate[NSTATES]; /* contains type information about the states */ +int defact[NSTATES]; /* the default actions of states */ +int tstates[NTERMS]; /* states generated by terminal gotos */ +int ntstates[NNONTERM]; /* states generated by nonterminal gotos */ +int mstates[NSTATES]; /* chain of overflows of term/nonterm generation lists */ +int lastred; /* the number of the last reduction of a state */ + + /* lookahead set information */ + +Lkset lkst[LSETSIZE]; +int nolook; /* flag to turn off lookahead computations */ +int tbitset; /* size of lookahead sets */ +int nlset = 0; /* next lookahead set index */ +int nolook = 0; /* flag to suppress lookahead computations */ +Lkset clset; /* temporary storage for lookahead computations */ + + /* working set information */ + +Wset wsets[WSETSIZE]; +Wset* cwp; + + /* storage for action table */ + +int amem[ACTSIZE]; /* action table storage */ +int* memp = amem; /* next free action table position */ +int indgo[NSTATES]; /* index to the stored goto table */ + + /* temporary vector, indexable by states, terms, or ntokens */ + +int temp1[TEMPSIZE]; /* temporary storage, indexed by terms + ntokens or states */ +int lineno = 1; /* current input line number */ +int fatfl = 1; /* if on, error is fatal */ +int nerrors = 0; /* number of errors */ + + /* statistics collection variables */ + +int zzgoent; +int zzgobest; +int zzacent; +int zzexcp; +int zzclose; +int zzrrconf; +int zzsrconf; + +int* ggreed = lkst[0].lset; +int* pgo = wsets[0].ws.lset; +int* yypgo = &nontrst[0].value; + +int maxspr = 0; /* maximum spread of any entry */ +int maxoff = 0; /* maximum offset into a array */ +int* pmem = mem0; +int* maxa; +int nxdb = 0; +int adb = 0; + + + /* storage for information about the nonterminals */ + +int** pres[NNONTERM+2]; /* vector of pointers to productions yielding each nonterminal */ +Lkset* pfirst[NNONTERM+2]; /* vector of pointers to first sets for each nonterminal */ +int pempty[NNONTERM+1]; /* vector of nonterminals nontrivially deriving e */ + + /* random stuff picked out from between functions */ + +int indebug = 0; +Wset* zzcwp = wsets; +int zzgoent = 0; +int zzgobest = 0; +int zzacent = 0; +int zzexcp = 0; +int zzclose = 0; +int zzsrconf = 0; +int* zzmemsz = mem0; +int zzrrconf = 0; +int pidebug = 0; /* debugging flag for putitem */ +int gsdebug = 0; +int cldebug = 0; /* debugging flag for closure */ +int pkdebug = 0; +int g2debug = 0; + +struct +{ + char* name; + long value; +} resrv[] = +{ + "binary", BINARY, + "left", LEFT, + "nonassoc", BINARY, + "prec", PREC, + "right", RIGHT, + "start", START, + "term", TERM, + "token", TERM, + "type", TYPEDEF, + "union", UNION, + 0, +}; + + /* define functions */ + +void main(int, char**); +void others(void); +char* chcopy(char*, char*); +char* writem(int*); +char* symnam(int); +void summary(void); +void error(char*, ...); +void aryfil(int*, int, int); +int setunion(int*, int*); +void prlook(Lkset*); +void cpres(void); +void cpfir(void); +int state(int); +void putitem(int*, Lkset*); +void cempty(void); +void stagen(void); +void closure(int); +Lkset* flset(Lkset*); +void cleantmp(void); +void intr(void); +void setup(int, char**); +void finact(void); +int defin(int, char*); +void defout(int); +char* cstash(char*); +long gettok(void); +int fdtype(int); +int chfind(int, char*); +void cpyunion(void); +void cpycode(void); +int skipcom(void); +void cpyact(int); +void openup(char*, int, int, int, char*); +void output(void); +int apack(int*, int); +void go2out(void); +void go2gen(int); +void precftn(int, int, int); +void wract(int); +void wrstate(int); +void warray(char*, int*, int); +void hideprod(void); +void callopt(void); +void gin(int); +void stin(int); +int nxti(void); +void osummary(void); +void aoutput(void); +void arout(char*, int*, int); +int gtnm(void); + +void +main(int argc, char *argv[]) +{ + PARSER = unsharp(PARSER); + PARSERS = unsharp(PARSERS); + parser = PARSER; + + setup(argc, argv); /* initialize and read productions */ + tbitset = NWORDS(ntokens); + cpres(); /* make table of which productions yield a given nonterminal */ + cempty(); /* make a table of which nonterminals can match the empty string */ + cpfir(); /* make a table of firsts of nonterminals */ + stagen(); /* generate the states */ + output(); /* write the states and the tables */ + go2out(); + hideprod(); + summary(); + callopt(); + others(); + exits(0); +} + +/* + * put out other arrays, copy the parsers + */ +void +others(void) +{ + int c, i, j; + + finput = Bopen(parser, OREAD); + if(finput == 0) + error("cannot open parser %s: %r", parser); + warray("yyr1", levprd, nprod); + aryfil(temp1, nprod, 0); + PLOOP(1, i) + temp1[i] = prdptr[i+1]-prdptr[i]-2; + warray("yyr2", temp1, nprod); + + aryfil(temp1, nstate, -1000); + TLOOP(i) + for(j=tstates[i]; j!=0; j=mstates[j]) + temp1[j] = i; + NTLOOP(i) + for(j=ntstates[i]; j!=0; j=mstates[j]) + temp1[j] = -i; + warray("yychk", temp1, nstate); + warray("yydef", defact, nstate); + + /* put out token translation tables */ + /* table 1 has 0-256 */ + aryfil(temp1, 256, 0); + c = 0; + TLOOP(i) { + j = tokset[i].value; + if(j >= 0 && j < 256) { + if(temp1[j]) { + print("yacc bug -- cant have 2 different Ts with same value\n"); + print(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name); + nerrors++; + } + temp1[j] = i; + if(j > c) + c = j; + } + } + warray("yytok1", temp1, c+1); + + /* table 2 has PRIVATE-PRIVATE+256 */ + aryfil(temp1, 256, 0); + c = 0; + TLOOP(i) { + j = tokset[i].value - PRIVATE; + if(j >= 0 && j < 256) { + if(temp1[j]) { + print("yacc bug -- cant have 2 different Ts with same value\n"); + print(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name); + nerrors++; + } + temp1[j] = i; + if(j > c) + c = j; + } + } + warray("yytok2", temp1, c+1); + + /* table 3 has everything else */ + Bprint(ftable, "static\tconst\tlong yytok3[] =\n{\n"); + c = 0; + TLOOP(i) { + j = tokset[i].value; + if(j >= 0 && j < 256) + continue; + if(j >= PRIVATE && j < 256+PRIVATE) + continue; + + Bprint(ftable, "%4d,%4d,", j, i); + c++; + if(c%5 == 0) + Bprint(ftable, "\n"); + } + Bprint(ftable, "%4d\n};\n", 0); + + /* copy parser text */ + while((c=Bgetrune(finput)) != Beof) { + if(c == '$') { + if((c = Bgetrune(finput)) != 'A') + Bputrune(ftable, '$'); + else { /* copy actions */ + faction = Bopen(actname, OREAD); + if(faction == 0) + error("cannot reopen action tempfile"); + while((c=Bgetrune(faction)) != Beof) + Bputrune(ftable, c); + Bterm(faction); + ZAPFILE(actname); + c = Bgetrune(finput); + } + } + Bputrune(ftable, c); + } + Bterm(ftable); +} + +/* + * copies string q into p, returning next free char ptr + */ +char* +chcopy(char* p, char* q) +{ + int c; + + while(c = *q) { + if(c == '"') + *p++ = '\\'; + *p++ = c; + q++; + } + *p = 0; + return p; +} + +/* + * creates output string for item pointed to by pp + */ +char* +writem(int *pp) +{ + int i,*p; + static char sarr[ISIZE]; + char* q; + + for(p=pp; *p>0; p++) + ; + p = prdptr[-*p]; + q = chcopy(sarr, nontrst[*p-NTBASE].name); + q = chcopy(q, ": "); + for(;;) { + *q = ' '; + p++; + if(p == pp) + *q = '.'; + q++; + *q = '\0'; + i = *p; + if(i <= 0) + break; + q = chcopy(q, symnam(i)); + if(q > &sarr[ISIZE-30]) + error("item too big"); + } + + /* an item calling for a reduction */ + i = *pp; + if(i < 0 ) { + q = chcopy(q, " ("); + sprint(q, "%d)", -i); + } + return sarr; +} + +/* + * return a pointer to the name of symbol i + */ +char* +symnam(int i) +{ + char* cp; + + cp = (i >= NTBASE)? nontrst[i-NTBASE].name: tokset[i].name; + if(*cp == ' ') + cp++; + return cp; +} + +/* + * output the summary on y.output + */ +void +summary(void) +{ + + if(foutput != 0) { + Bprint(foutput, "\n%d/%d terminals, %d/%d nonterminals\n", + ntokens, NTERMS, nnonter, NNONTERM); + Bprint(foutput, "%d/%d grammar rules, %d/%d states\n", + nprod, NPROD, nstate, NSTATES); + Bprint(foutput, "%d shift/reduce, %d reduce/reduce conflicts reported\n", + zzsrconf, zzrrconf); + Bprint(foutput, "%d/%d working sets used\n", + (int)(zzcwp-wsets), WSETSIZE); + Bprint(foutput, "memory: states,etc. %d/%d, parser %d/%d\n", + (int)(zzmemsz-mem0), MEMSIZE, (int)(memp-amem), ACTSIZE); + Bprint(foutput, "%d/%d distinct lookahead sets\n", nlset, LSETSIZE); + Bprint(foutput, "%d extra closures\n", zzclose - 2*nstate); + Bprint(foutput, "%d shift entries, %d exceptions\n", zzacent, zzexcp); + Bprint(foutput, "%d goto entries\n", zzgoent); + Bprint(foutput, "%d entries saved by goto default\n", zzgobest); + } + if(zzsrconf != 0 || zzrrconf != 0) { + print("\nconflicts: "); + if(zzsrconf) + print("%d shift/reduce", zzsrconf); + if(zzsrconf && zzrrconf) + print(", "); + if(zzrrconf) + print("%d reduce/reduce", zzrrconf); + print("\n"); + } + if(ftemp != 0) { + Bterm(ftemp); + ftemp = 0; + } + if(fdefine != 0) { + Bterm(fdefine); + fdefine = 0; + } +} + +/* + * write out error comment -- NEEDS WORK + */ +void +error(char *s, ...) +{ + va_list arg; + + nerrors++; + fprint(2, "\n fatal error:"); + va_start(arg, s); + vfprint(2, s, arg); + va_end(arg); + fprint(2, ", %s:%d\n", infile, lineno); + if(!fatfl) + return; + summary(); + cleantmp(); + exits("error"); +} + +/* + * set elements 0 through n-1 to c + */ +void +aryfil(int *v, int n, int c) +{ + int i; + + for(i=0; i<n; i++) + v[i] = c; +} + +/* + * set a to the union of a and b + * return 1 if b is not a subset of a, 0 otherwise + */ +int +setunion(int *a, int *b) +{ + int i, x, sub; + + sub = 0; + SETLOOP(i) { + x = *a; + *a |= *b; + if(*a != x) + sub = 1; + a++; + b++; + } + return sub; +} + +void +prlook(Lkset* p) +{ + int j, *pp; + + pp = p->lset; + if(pp == 0) + Bprint(foutput, "\tNULL"); + else { + Bprint(foutput, " { "); + TLOOP(j) + if(BIT(pp,j)) + Bprint(foutput, "%s ", symnam(j)); + Bprint(foutput, "}"); + } +} + +/* + * compute an array with the beginnings of productions yielding given nonterminals + * The array pres points to these lists + * the array pyield has the lists: the total size is only NPROD+1 + */ +void +cpres(void) +{ + int c, j, i, **pmem; + static int *pyield[NPROD]; + + pmem = pyield; + NTLOOP(i) { + c = i+NTBASE; + pres[i] = pmem; + fatfl = 0; /* make undefined symbols nonfatal */ + PLOOP(0, j) + if(*prdptr[j] == c) + *pmem++ = prdptr[j]+1; + if(pres[i] == pmem) + error("nonterminal %s not defined!", nontrst[i].name); + } + pres[i] = pmem; + fatfl = 1; + if(nerrors) { + summary(); + cleantmp(); + exits("error"); + } + if(pmem != &pyield[nprod]) + error("internal Yacc error: pyield %d", pmem-&pyield[nprod]); +} + +/* + * compute an array with the first of nonterminals + */ +void +cpfir(void) +{ + int *p, **s, i, **t, ch, changes; + + zzcwp = &wsets[nnonter]; + NTLOOP(i) { + aryfil(wsets[i].ws.lset, tbitset, 0); + t = pres[i+1]; + /* initially fill the sets */ + for(s=pres[i]; s<t; ++s) + for(p = *s; (ch = *p) > 0; ++p) { + if(ch < NTBASE) { + SETBIT(wsets[i].ws.lset, ch); + break; + } + if(!pempty[ch-NTBASE]) + break; + } + } + + /* now, reflect transitivity */ + changes = 1; + while(changes) { + changes = 0; + NTLOOP(i) { + t = pres[i+1]; + for(s = pres[i]; s < t; ++s) + for(p = *s; (ch = (*p-NTBASE)) >= 0; ++p) { + changes |= setunion(wsets[i].ws.lset, wsets[ch].ws.lset); + if(!pempty[ch]) + break; + } + } + } + + NTLOOP(i) + pfirst[i] = flset(&wsets[i].ws); + if(!indebug) + return; + if(foutput != 0) + NTLOOP(i) { + Bprint(foutput, "\n%s: ", nontrst[i].name); + prlook(pfirst[i]); + Bprint(foutput, " %d\n", pempty[i]); + } +} + +/* + * sorts last state,and sees if it equals earlier ones. returns state number + */ +int +state(int c) +{ + Item *p1, *p2, *k, *l, *q1, *q2; + int size1, size2, i; + + p1 = pstate[nstate]; + p2 = pstate[nstate+1]; + if(p1 == p2) + return 0; /* null state */ + /* sort the items */ + for(k = p2-1; k > p1; k--) /* make k the biggest */ + for(l = k-1; l >= p1; --l) + if(l->pitem > k->pitem) { + int *s; + Lkset *ss; + + s = k->pitem; + k->pitem = l->pitem; + l->pitem = s; + ss = k->look; + k->look = l->look; + l->look = ss; + } + size1 = p2 - p1; /* size of state */ + + for(i = (c>=NTBASE)? ntstates[c-NTBASE]: tstates[c]; i != 0; i = mstates[i]) { + /* get ith state */ + q1 = pstate[i]; + q2 = pstate[i+1]; + size2 = q2 - q1; + if(size1 != size2) + continue; + k = p1; + for(l = q1; l < q2; l++) { + if(l->pitem != k->pitem) + break; + k++; + } + if(l != q2) + continue; + /* found it */ + pstate[nstate+1] = pstate[nstate]; /* delete last state */ + /* fix up lookaheads */ + if(nolook) + return i; + for(l = q1, k = p1; l < q2; ++l, ++k ) { + int s; + + SETLOOP(s) + clset.lset[s] = l->look->lset[s]; + if(setunion(clset.lset, k->look->lset)) { + tystate[i] = MUSTDO; + /* register the new set */ + l->look = flset( &clset ); + } + } + return i; + } + /* state is new */ + if(nolook) + error("yacc state/nolook error"); + pstate[nstate+2] = p2; + if(nstate+1 >= NSTATES) + error("too many states"); + if(c >= NTBASE) { + mstates[nstate] = ntstates[c-NTBASE]; + ntstates[c-NTBASE] = nstate; + } else { + mstates[nstate] = tstates[c]; + tstates[c] = nstate; + } + tystate[nstate] = MUSTDO; + return nstate++; +} + +void +putitem(int *ptr, Lkset *lptr) +{ + Item *j; + + if(pidebug && foutput != 0) + Bprint(foutput, "putitem(%s), state %d\n", writem(ptr), nstate); + j = pstate[nstate+1]; + j->pitem = ptr; + if(!nolook) + j->look = flset(lptr); + pstate[nstate+1] = ++j; + if((int*)j > zzmemsz) { + zzmemsz = (int*)j; + if(zzmemsz >= &mem0[MEMSIZE]) + error("out of state space"); + } +} + +/* + * mark nonterminals which derive the empty string + * also, look for nonterminals which don't derive any token strings + */ +void +cempty(void) +{ + + int i, *p; + + /* first, use the array pempty to detect productions that can never be reduced */ + /* set pempty to WHONOWS */ + aryfil(pempty, nnonter+1, WHOKNOWS); + + /* now, look at productions, marking nonterminals which derive something */ +more: + PLOOP(0, i) { + if(pempty[*prdptr[i] - NTBASE]) + continue; + for(p = prdptr[i]+1; *p >= 0; ++p) + if(*p >= NTBASE && pempty[*p-NTBASE] == WHOKNOWS) + break; + /* production can be derived */ + if(*p < 0) { + pempty[*prdptr[i]-NTBASE] = OK; + goto more; + } + } + + /* now, look at the nonterminals, to see if they are all OK */ + NTLOOP(i) { + /* the added production rises or falls as the start symbol ... */ + if(i == 0) + continue; + if(pempty[i] != OK) { + fatfl = 0; + error("nonterminal %s never derives any token string", nontrst[i].name); + } + } + + if(nerrors) { + summary(); + cleantmp(); + exits("error"); + } + + /* now, compute the pempty array, to see which nonterminals derive the empty string */ + /* set pempty to WHOKNOWS */ + aryfil( pempty, nnonter+1, WHOKNOWS); + + /* loop as long as we keep finding empty nonterminals */ + +again: + PLOOP(1, i) { + /* not known to be empty */ + if(pempty[*prdptr[i]-NTBASE] == WHOKNOWS) { + for(p = prdptr[i]+1; *p >= NTBASE && pempty[*p-NTBASE] == EMPTY ; ++p) + ; + /* we have a nontrivially empty nonterminal */ + if(*p < 0) { + pempty[*prdptr[i]-NTBASE] = EMPTY; + /* got one ... try for another */ + goto again; + } + } + } +} + +/* + * generate the states + */ +void +stagen(void) +{ + + int c, i, j, more; + Wset *p, *q; + + /* initialize */ + nstate = 0; + + /* THIS IS FUNNY from the standpoint of portability + * it represents the magic moment when the mem0 array, which has + * been holding the productions, starts to hold item pointers, of a + * different type... + * someday, alloc should be used to allocate all this stuff... for now, we + * accept that if pointers don't fit in integers, there is a problem... + */ + + pstate[0] = pstate[1] = (Item*)mem; + aryfil(clset.lset, tbitset, 0); + putitem(prdptr[0]+1, &clset); + tystate[0] = MUSTDO; + nstate = 1; + pstate[2] = pstate[1]; + + aryfil(amem, ACTSIZE, 0); + + /* now, the main state generation loop */ + for(more=1; more;) { + more = 0; + SLOOP(i) { + if(tystate[i] != MUSTDO) + continue; + tystate[i] = DONE; + aryfil(temp1, nnonter+1, 0); + /* take state i, close it, and do gotos */ + closure(i); + /* generate goto's */ + WSLOOP(wsets, p) { + if(p->flag) + continue; + p->flag = 1; + c = *(p->pitem); + if(c <= 1) { + if(pstate[i+1]-pstate[i] <= p-wsets) + tystate[i] = MUSTLOOKAHEAD; + continue; + } + /* do a goto on c */ + WSLOOP(p, q) + /* this item contributes to the goto */ + if(c == *(q->pitem)) { + putitem(q->pitem+1, &q->ws); + q->flag = 1; + } + if(c < NTBASE) + state(c); /* register new state */ + else + temp1[c-NTBASE] = state(c); + } + if(gsdebug && foutput != 0) { + Bprint(foutput, "%d: ", i); + NTLOOP(j) + if(temp1[j]) + Bprint(foutput, "%s %d, ", + nontrst[j].name, temp1[j]); + Bprint(foutput, "\n"); + } + indgo[i] = apack(&temp1[1], nnonter-1) - 1; + /* do some more */ + more = 1; + } + } +} + +/* + * generate the closure of state i + */ +void +closure(int i) +{ + + Wset *u, *v; + Item *p, *q; + int c, ch, work, k, *pi, **s, **t; + + zzclose++; + + /* first, copy kernel of state i to wsets */ + cwp = wsets; + ITMLOOP(i, p, q) { + cwp->pitem = p->pitem; + cwp->flag = 1; /* this item must get closed */ + SETLOOP(k) + cwp->ws.lset[k] = p->look->lset[k]; + WSBUMP(cwp); + } + + /* now, go through the loop, closing each item */ + work = 1; + while(work) { + work = 0; + WSLOOP(wsets, u) { + if(u->flag == 0) + continue; + /* dot is before c */ + c = *(u->pitem); + if(c < NTBASE) { + u->flag = 0; + /* only interesting case is where . is before nonterminal */ + continue; + } + + /* compute the lookahead */ + aryfil(clset.lset, tbitset, 0); + + /* find items involving c */ + WSLOOP(u, v) + if(v->flag == 1 && *(pi=v->pitem) == c) { + v->flag = 0; + if(nolook) + continue; + while((ch = *++pi) > 0) { + /* terminal symbol */ + if(ch < NTBASE) { + SETBIT(clset.lset, ch); + break; + } + /* nonterminal symbol */ + setunion(clset.lset, pfirst[ch-NTBASE]->lset); + if(!pempty[ch-NTBASE]) + break; + } + if(ch <= 0) + setunion(clset.lset, v->ws.lset); + } + + /* + * now loop over productions derived from c + * c is now nonterminal number + */ + c -= NTBASE; + t = pres[c+1]; + for(s = pres[c]; s < t; ++s) { + /* + * put these items into the closure + * is the item there + */ + WSLOOP(wsets, v) + /* yes, it is there */ + if(v->pitem == *s) { + if(nolook) + goto nexts; + if(setunion(v->ws.lset, clset.lset)) + v->flag = work = 1; + goto nexts; + } + + /* not there; make a new entry */ + if(cwp-wsets+1 >= WSETSIZE) + error( "working set overflow"); + cwp->pitem = *s; + cwp->flag = 1; + if(!nolook) { + work = 1; + SETLOOP(k) cwp->ws.lset[k] = clset.lset[k]; + } + WSBUMP(cwp); + + nexts:; + } + } + } + + /* have computed closure; flags are reset; return */ + if(cwp > zzcwp) + zzcwp = cwp; + if(cldebug && foutput != 0) { + Bprint(foutput, "\nState %d, nolook = %d\n", i, nolook); + WSLOOP(wsets, u) { + if(u->flag) + Bprint(foutput, "flag set!\n"); + u->flag = 0; + Bprint(foutput, "\t%s", writem(u->pitem)); + prlook(&u->ws); + Bprint(foutput, "\n"); + } + } +} + +/* + * decide if the lookahead set pointed to by p is known + * return pointer to a perminent location for the set + */ +Lkset* +flset(Lkset *p) +{ + Lkset *q; + int *u, *v, *w, j; + + for(q = &lkst[nlset]; q-- > lkst;) { + u = p->lset; + v = q->lset; + w = &v[tbitset]; + while(v < w) + if(*u++ != *v++) + goto more; + /* we have matched */ + return q; + more:; + } + /* add a new one */ + q = &lkst[nlset++]; + if(nlset >= LSETSIZE) + error("too many lookahead sets"); + SETLOOP(j) + q->lset[j] = p->lset[j]; + return q; +} + +void +cleantmp(void) +{ + ZAPFILE(actname); + ZAPFILE(tempname); +} + +void +intr(void) +{ + cleantmp(); + exits("interrupted"); +} + +void +setup(int argc, char *argv[]) +{ + long c, t; + int i, j, fd, lev, ty, ytab, *p; + int vflag, dflag, stem; + char actnm[8], *stemc, *s, dirbuf[128]; + Biobuf *fout; + + ytab = 0; + vflag = 0; + dflag = 0; + stem = 0; + stemc = "y"; + foutput = 0; + fdefine = 0; + fdebug = 0; + ARGBEGIN{ + case 'v': + case 'V': + vflag++; + break; + case 'D': + yydebug = ARGF(); + break; + case 'a': + yyarg = 1; + break; + case 'd': + dflag++; + break; + case 'l': + yyline = 0; + break; + case 'o': + ytab++; + ytabc = ARGF(); + break; + case 's': + stem++; + stemc = ARGF(); + break; + case 'S': + parser = PARSERS; + break; + default: + error("illegal option: %c", ARGC()); + }ARGEND + openup(stemc, dflag, vflag, ytab, ytabc); + fout = dflag?fdefine:ftable; + if(yyarg){ + Bprint(fdefine, "#define\tYYARG\t1\n\n"); + } + if((fd = mkstemp(ttempname)) >= 0){ + tempname = ttempname; + ftemp = Bfdopen(fd, OWRITE); + } + if((fd = mkstemp(tactname)) >= 0){ + actname = tactname; + faction = Bfdopen(fd, OWRITE); + } + if(ftemp == 0 || faction == 0) + error("cannot open temp file"); + if(argc < 1) + error("no input file"); + infile = argv[0]; + if(infile[0] != '/' && getwd(dirbuf, sizeof dirbuf)!=nil){ + i = strlen(infile)+1+strlen(dirbuf)+1+10; + s = malloc(i); + if(s != nil){ + snprint(s, i, "%s/%s", dirbuf, infile); + cleanname(s); + infile = s; + } + } + finput = Bopen(infile, OREAD); + if(finput == 0) + error("cannot open '%s'", argv[0]); + cnamp = cnames; + + defin(0, "$end"); + extval = PRIVATE; /* tokens start in unicode 'private use' */ + defin(0, "error"); + defin(1, "$accept"); + defin(0, "$unk"); + mem = mem0; + i = 0; + + for(t = gettok(); t != MARK && t != ENDFILE;) + switch(t) { + case ';': + t = gettok(); + break; + + case START: + if(gettok() != IDENTIFIER) + error("bad %%start construction"); + start = chfind(1, tokname); + t = gettok(); + continue; + + case TYPEDEF: + if(gettok() != TYPENAME) + error("bad syntax in %%type"); + ty = numbval; + for(;;) { + t = gettok(); + switch(t) { + case IDENTIFIER: + if((t=chfind(1, tokname)) < NTBASE) { + j = TYPE(toklev[t]); + if(j != 0 && j != ty) + error("type redeclaration of token %s", + tokset[t].name); + else + SETTYPE(toklev[t], ty); + } else { + j = nontrst[t-NTBASE].value; + if(j != 0 && j != ty) + error("type redeclaration of nonterminal %s", + nontrst[t-NTBASE].name ); + else + nontrst[t-NTBASE].value = ty; + } + case ',': + continue; + case ';': + t = gettok(); + default: + break; + } + break; + } + continue; + + case UNION: + /* copy the union declaration to the output */ + cpyunion(); + t = gettok(); + continue; + + case LEFT: + case BINARY: + case RIGHT: + i++; + + case TERM: + /* nonzero means new prec. and assoc. */ + lev = t-TERM; + ty = 0; + + /* get identifiers so defined */ + t = gettok(); + + /* there is a type defined */ + if(t == TYPENAME) { + ty = numbval; + t = gettok(); + } + for(;;) { + switch(t) { + case ',': + t = gettok(); + continue; + + case ';': + break; + + case IDENTIFIER: + j = chfind(0, tokname); + if(j >= NTBASE) + error("%s defined earlier as nonterminal", tokname); + if(lev) { + if(ASSOC(toklev[j])) + error("redeclaration of precedence of %s", tokname); + SETASC(toklev[j], lev); + SETPLEV(toklev[j], i); + } + if(ty) { + if(TYPE(toklev[j])) + error("redeclaration of type of %s", tokname); + SETTYPE(toklev[j],ty); + } + t = gettok(); + if(t == NUMBER) { + tokset[j].value = numbval; + if(j < ndefout && j > 3) + error("please define type number of %s earlier", + tokset[j].name); + t = gettok(); + } + continue; + } + break; + } + continue; + + case LCURLY: + defout(0); + cpycode(); + t = gettok(); + continue; + + default: + error("syntax error"); + } + if(t == ENDFILE) + error("unexpected EOF before %%"); + + /* t is MARK */ + if(!yyarg) + Bprint(ftable, "extern int yyerrflag;\n"); + Bprint(ftable, "#ifndef YYMAXDEPTH\n"); + Bprint(ftable, "#define YYMAXDEPTH 150\n"); + Bprint(ftable, "#endif\n" ); + if(!ntypes) { + Bprint(ftable, "#ifndef YYSTYPE\n"); + Bprint(ftable, "#define YYSTYPE int\n"); + Bprint(ftable, "#endif\n"); + } + if(!yyarg){ + Bprint(ftable, "YYSTYPE yylval;\n"); + Bprint(ftable, "YYSTYPE yyval;\n"); + }else{ + if(dflag) + Bprint(ftable, "#include \"%s.%s\"\n\n", stemc, FILED); + Bprint(fout, "struct Yyarg {\n"); + Bprint(fout, "\tint\tyynerrs;\n"); + Bprint(fout, "\tint\tyyerrflag;\n"); + Bprint(fout, "\tvoid*\targ;\n"); + Bprint(fout, "\tYYSTYPE\tyyval;\n"); + Bprint(fout, "\tYYSTYPE\tyylval;\n"); + Bprint(fout, "};\n\n"); + } + prdptr[0] = mem; + + /* added production */ + *mem++ = NTBASE; + + /* if start is 0, we will overwrite with the lhs of the first rule */ + *mem++ = start; + *mem++ = 1; + *mem++ = 0; + prdptr[1] = mem; + while((t=gettok()) == LCURLY) + cpycode(); + if(t != IDENTCOLON) + error("bad syntax on first rule"); + + if(!start) + prdptr[0][1] = chfind(1, tokname); + + /* read rules */ + while(t != MARK && t != ENDFILE) { + /* process a rule */ + rlines[nprod] = lineno; + if(t == '|') + *mem++ = *prdptr[nprod-1]; + else + if(t == IDENTCOLON) { + *mem = chfind(1, tokname); + if(*mem < NTBASE) + error("token illegal on LHS of grammar rule"); + mem++; + } else + error("illegal rule: missing semicolon or | ?"); + /* read rule body */ + t = gettok(); + + more_rule: + while(t == IDENTIFIER) { + *mem = chfind(1, tokname); + if(*mem < NTBASE) + levprd[nprod] = toklev[*mem]; + mem++; + t = gettok(); + } + if(t == PREC) { + if(gettok() != IDENTIFIER) + error("illegal %%prec syntax"); + j = chfind(2, tokname); + if(j >= NTBASE) + error("nonterminal %s illegal after %%prec", + nontrst[j-NTBASE].name); + levprd[nprod] = toklev[j]; + t = gettok(); + } + if(t == '=') { + levprd[nprod] |= ACTFLAG; + Bprint(faction, "\ncase %d:", nprod); + cpyact(mem-prdptr[nprod]-1); + Bprint(faction, " break;"); + if((t=gettok()) == IDENTIFIER) { + + /* action within rule... */ + sprint(actnm, "$$%d", nprod); + + /* make it a nonterminal */ + j = chfind(1, actnm); + + /* + * the current rule will become rule number nprod+1 + * move the contents down, and make room for the null + */ + for(p = mem; p >= prdptr[nprod]; --p) + p[2] = *p; + mem += 2; + + /* enter null production for action */ + p = prdptr[nprod]; + *p++ = j; + *p++ = -nprod; + + /* update the production information */ + levprd[nprod+1] = levprd[nprod] & ~ACTFLAG; + levprd[nprod] = ACTFLAG; + if(++nprod >= NPROD) + error("more than %d rules", NPROD); + prdptr[nprod] = p; + + /* make the action appear in the original rule */ + *mem++ = j; + + /* get some more of the rule */ + goto more_rule; + } + } + + while(t == ';') + t = gettok(); + *mem++ = -nprod; + + /* check that default action is reasonable */ + if(ntypes && !(levprd[nprod]&ACTFLAG) && nontrst[*prdptr[nprod]-NTBASE].value) { + + /* no explicit action, LHS has value */ + int tempty; + + tempty = prdptr[nprod][1]; + if(tempty < 0) + error("must return a value, since LHS has a type"); + else + if(tempty >= NTBASE) + tempty = nontrst[tempty-NTBASE].value; + else + tempty = TYPE(toklev[tempty]); + if(tempty != nontrst[*prdptr[nprod]-NTBASE].value) + error("default action causes potential type clash"); + } + nprod++; + if(nprod >= NPROD) + error("more than %d rules", NPROD); + prdptr[nprod] = mem; + levprd[nprod] = 0; + } + + /* end of all rules */ + defout(1); + + finact(); + if(t == MARK) { + Bprint(ftable, "\n"); + if(yyline) + Bprint(ftable, "#line\t%d\t\"%s\"\n", lineno, infile); + while((c=Bgetrune(finput)) != Beof) + Bputrune(ftable, c); + } + Bterm(finput); +} + +/* + * finish action routine + */ +void +finact(void) +{ + + Bterm(faction); + Bprint(ftable, "#define YYEOFCODE %d\n", 1); + Bprint(ftable, "#define YYERRCODE %d\n", 2); +} + +/* + * define s to be a terminal if t=0 + * or a nonterminal if t=1 + */ +int +defin(int nt, char *s) +{ + int val; + Rune rune; + + val = 0; + if(nt) { + nnonter++; + if(nnonter >= NNONTERM) + error("too many nonterminals, limit %d",NNONTERM); + nontrst[nnonter].name = cstash(s); + return NTBASE + nnonter; + } + + /* must be a token */ + ntokens++; + if(ntokens >= NTERMS) + error("too many terminals, limit %d", NTERMS); + tokset[ntokens].name = cstash(s); + + /* establish value for token */ + /* single character literal */ + if(s[0] == ' ') { + val = chartorune(&rune, &s[1]); + if(s[val+1] == 0) { + val = rune; + goto out; + } + } + + /* escape sequence */ + if(s[0] == ' ' && s[1] == '\\') { + if(s[3] == 0) { + /* single character escape sequence */ + switch(s[2]) { + case 'n': val = '\n'; break; + case 'r': val = '\r'; break; + case 'b': val = '\b'; break; + case 't': val = '\t'; break; + case 'f': val = '\f'; break; + case '\'': val = '\''; break; + case '"': val = '"'; break; + case '\\': val = '\\'; break; + default: error("invalid escape"); + } + goto out; + } + + /* \nnn sequence */ + if(s[2] >= '0' && s[2] <= '7') { + if(s[3] < '0' || + s[3] > '7' || + s[4] < '0' || + s[4] > '7' || + s[5] != 0) + error("illegal \\nnn construction"); + val = 64*s[2] + 8*s[3] + s[4] - 73*'0'; + if(val == 0) + error("'\\000' is illegal"); + goto out; + } + error("unknown escape"); + } + val = extval++; + +out: + tokset[ntokens].value = val; + toklev[ntokens] = 0; + return ntokens; +} + +/* + * write out the defines (at the end of the declaration section) + */ +void +defout(int last) +{ + int i, c; + char sar[NAMESIZE+10]; + + for(i=ndefout; i<=ntokens; i++) { + /* non-literals */ + c = tokset[i].name[0]; + if(c != ' ' && c != '$') { + Bprint(ftable, "#define %s %d\n", + tokset[i].name, tokset[i].value); + if(fdefine) + Bprint(fdefine, "#define\t%s\t%d\n", + tokset[i].name, tokset[i].value); + } + } + ndefout = ntokens+1; + if(last && fdebug) { + Bprint(fdebug, "static char* yytoknames[] =\n{\n"); + TLOOP(i) { + if(tokset[i].name) { + chcopy(sar, tokset[i].name); + Bprint(fdebug, "\t\"%s\",\n", sar); + continue; + } + Bprint(fdebug, "\t0,\n"); + } + Bprint(fdebug, "};\n"); + } +} + +char* +cstash(char *s) +{ + char *temp; + + temp = cnamp; + do { + if(cnamp >= &cnames[cnamsz]) + error("too many characters in id's and literals"); + else + *cnamp++ = *s; + } while(*s++); + return temp; +} + +long +gettok(void) +{ + long c; + Rune rune; + int i, base, match, reserve; + static int peekline; + +begin: + reserve = 0; + lineno += peekline; + peekline = 0; + c = Bgetrune(finput); + while(c == ' ' || c == '\n' || c == '\t' || c == '\f') { + if(c == '\n') + lineno++; + c = Bgetrune(finput); + } + + /* skip comment */ + if(c == '/') { + lineno += skipcom(); + goto begin; + } + switch(c) { + case Beof: + return ENDFILE; + + case '{': + Bungetrune(finput); + return '='; + + case '<': + /* get, and look up, a type name (union member name) */ + i = 0; + while((c=Bgetrune(finput)) != '>' && c >= 0 && c != '\n') { + rune = c; + c = runetochar(&tokname[i], &rune); + if(i < NAMESIZE) + i += c; + } + if(c != '>') + error("unterminated < ... > clause"); + tokname[i] = 0; + for(i=1; i<=ntypes; i++) + if(!strcmp(typeset[i], tokname)) { + numbval = i; + return TYPENAME; + } + ntypes++; + numbval = ntypes; + typeset[numbval] = cstash(tokname); + return TYPENAME; + + case '"': + case '\'': + match = c; + tokname[0] = ' '; + i = 1; + for(;;) { + c = Bgetrune(finput); + if(c == '\n' || c <= 0) + error("illegal or missing ' or \"" ); + if(c == '\\') { + tokname[i] = '\\'; + if(i < NAMESIZE) + i++; + c = Bgetrune(finput); + } else + if(c == match) + break; + rune = c; + c = runetochar(&tokname[i], &rune); + if(i < NAMESIZE) + i += c; + } + break; + + case '%': + case '\\': + switch(c = Bgetrune(finput)) { + case '0': return TERM; + case '<': return LEFT; + case '2': return BINARY; + case '>': return RIGHT; + case '%': + case '\\': return MARK; + case '=': return PREC; + case '{': return LCURLY; + default: reserve = 1; + } + + default: + /* number */ + if(isdigit(c)) { + numbval = c-'0'; + base = (c=='0')? 8: 10; + for(c = Bgetrune(finput); isdigit(c); c = Bgetrune(finput)) + numbval = numbval*base + (c-'0'); + Bungetrune(finput); + return NUMBER; + } + if(islower(c) || isupper(c) || c=='_' || c=='.' || c=='$') { + i = 0; + while(islower(c) || isupper(c) || isdigit(c) || + c == '-' || c=='_' || c=='.' || c=='$') { + if(reserve && isupper(c)) + c += 'a'-'A'; + rune = c; + c = runetochar(&tokname[i], &rune); + if(i < NAMESIZE) + i += c; + c = Bgetrune(finput); + } + } else + return c; + Bungetrune(finput); + } + tokname[i] = 0; + + /* find a reserved word */ + if(reserve) { + for(c=0; resrv[c].name; c++) + if(strcmp(tokname, resrv[c].name) == 0) + return resrv[c].value; + error("invalid escape, or illegal reserved word: %s", tokname); + } + + /* look ahead to distinguish IDENTIFIER from IDENTCOLON */ + c = Bgetrune(finput); + while(c == ' ' || c == '\t'|| c == '\n' || c == '\f' || c == '/') { + if(c == '\n') + peekline++; + /* look for comments */ + if(c == '/') + peekline += skipcom(); + c = Bgetrune(finput); + } + if(c == ':') + return IDENTCOLON; + Bungetrune(finput); + return IDENTIFIER; +} + +/* + * determine the type of a symbol + */ +int +fdtype(int t) +{ + int v; + + if(t >= NTBASE) + v = nontrst[t-NTBASE].value; + else + v = TYPE(toklev[t]); + if(v <= 0) + error("must specify type for %s", (t>=NTBASE)? + nontrst[t-NTBASE].name: tokset[t].name); + return v; +} + +int +chfind(int t, char *s) +{ + int i; + + if(s[0] == ' ') + t = 0; + TLOOP(i) + if(!strcmp(s, tokset[i].name)) + return i; + NTLOOP(i) + if(!strcmp(s, nontrst[i].name)) + return NTBASE+i; + + /* cannot find name */ + if(t > 1) + error("%s should have been defined earlier", s); + return defin(t, s); +} + +/* + * copy the union declaration to the output, and the define file if present + */ +void +cpyunion(void) +{ + long c; + int level; + + Bprint(ftable, "\n"); + if(yyline) + Bprint(ftable, "#line\t%d\t\"%s\"\n", lineno, infile); + Bprint(ftable, "typedef union "); + if(fdefine != 0) + Bprint(fdefine, "\ntypedef union "); + + level = 0; + for(;;) { + if((c=Bgetrune(finput)) == Beof) + error("EOF encountered while processing %%union"); + Bputrune(ftable, c); + if(fdefine != 0) + Bputrune(fdefine, c); + switch(c) { + case '\n': + lineno++; + break; + case '{': + level++; + break; + case '}': + level--; + + /* we are finished copying */ + if(level == 0) { + Bprint(ftable, " YYSTYPE;\n"); + if(fdefine != 0){ + Bprint(fdefine, "\tYYSTYPE;\n"); + if(!yyarg) + Bprint(fdefine, "extern\tYYSTYPE\tyylval;\n"); + } + return; + } + } + } +} + +/* + * copies code between \{ and \} + */ +void +cpycode(void) +{ + long c; + + c = Bgetrune(finput); + if(c == '\n') { + c = Bgetrune(finput); + lineno++; + } + Bprint(ftable, "\n"); + if(yyline) + Bprint(ftable, "#line\t%d\t\"%s\"\n", lineno, infile); + while(c != Beof) { + if(c == '\\') { + if((c=Bgetrune(finput)) == '}') + return; + Bputc(ftable, '\\'); + } + if(c == '%') { + if((c=Bgetrune(finput)) == '}') + return; + Bputc(ftable, '%'); + } + Bputrune(ftable, c); + if(c == '\n') + lineno++; + c = Bgetrune(finput); + } + error("eof before %%}"); +} + +/* + * skip over comments + * skipcom is called after reading a '/' + */ +int +skipcom(void) +{ + long c; + int i; + + /* i is the number of lines skipped */ + i = 0; + if(Bgetrune(finput) != '*') + error("illegal comment"); + c = Bgetrune(finput); + while(c != Beof) { + while(c == '*') + if((c=Bgetrune(finput)) == '/') + return i; + if(c == '\n') + i++; + c = Bgetrune(finput); + } + error("EOF inside comment"); + return 0; +} + +/* + * copy C action to the next ; or closing } + */ +void +cpyact(int offset) +{ + long c; + int brac, match, j, s, fnd, tok; + + Bprint(faction, "\n"); + if(yyline) + Bprint(faction, "#line\t%d\t\"%s\"\n", lineno, infile); + brac = 0; + +loop: + c = Bgetrune(finput); +swt: + switch(c) { + case ';': + if(brac == 0) { + Bputrune(faction, c); + return; + } + goto lcopy; + + case '{': + brac++; + goto lcopy; + + case '$': + s = 1; + tok = -1; + c = Bgetrune(finput); + + /* type description */ + if(c == '<') { + Bungetrune(finput); + if(gettok() != TYPENAME) + error("bad syntax on $<ident> clause"); + tok = numbval; + c = Bgetrune(finput); + } + if(c == '$') { + Bprint(faction, "yyval"); + + /* put out the proper tag... */ + if(ntypes) { + if(tok < 0) + tok = fdtype(*prdptr[nprod]); + Bprint(faction, ".%s", typeset[tok]); + } + goto loop; + } + if(c == '-') { + s = -s; + c = Bgetrune(finput); + } + if(isdigit(c)) { + j = 0; + while(isdigit(c)) { + j = j*10 + (c-'0'); + c = Bgetrune(finput); + } + Bungetrune(finput); + j = j*s - offset; + if(j > 0) + error("Illegal use of $%d", j+offset); + + dollar: + Bprint(faction, "yypt[-%d].yyv", -j); + + /* put out the proper tag */ + if(ntypes) { + if(j+offset <= 0 && tok < 0) + error("must specify type of $%d", j+offset); + if(tok < 0) + tok = fdtype(prdptr[nprod][j+offset]); + Bprint(faction, ".%s", typeset[tok]); + } + goto loop; + } + if(isupper(c) || islower(c) || c == '_' || c == '.') { + int tok; /* tok used oustide for type info */ + + /* look for $name */ + Bungetrune(finput); + if(gettok() != IDENTIFIER) + error("$ must be followed by an identifier"); + tok = chfind(2, tokname); + if((c = Bgetrune(finput)) != '#') { + Bungetrune(finput); + fnd = -1; + } else + if(gettok() != NUMBER) { + error("# must be followed by number"); + fnd = -1; + } else + fnd = numbval; + for(j=1; j<=offset; ++j) + if(tok == prdptr[nprod][j]) { + if(--fnd <= 0) { + j -= offset; + goto dollar; + } + } + error("$name or $name#number not found"); + } + Bputc(faction, '$'); + if(s < 0 ) + Bputc(faction, '-'); + goto swt; + + case '}': + brac--; + if(brac) + goto lcopy; + Bputrune(faction, c); + return; + + case '/': + /* look for comments */ + Bputrune(faction, c); + c = Bgetrune(finput); + if(c != '*') + goto swt; + + /* it really is a comment */ + Bputrune(faction, c); + c = Bgetrune(finput); + while(c >= 0) { + while(c == '*') { + Bputrune(faction, c); + if((c=Bgetrune(finput)) == '/') + goto lcopy; + } + Bputrune(faction, c); + if(c == '\n') + lineno++; + c = Bgetrune(finput); + } + error("EOF inside comment"); + + case '\'': + /* character constant */ + match = '\''; + goto string; + + case '"': + /* character string */ + match = '"'; + + string: + Bputrune(faction, c); + while(c = Bgetrune(finput)) { + if(c == '\\') { + Bputrune(faction, c); + c = Bgetrune(finput); + if(c == '\n') + lineno++; + } else + if(c == match) + goto lcopy; + if(c == '\n') + error("newline in string or char. const."); + Bputrune(faction, c); + } + error("EOF in string or character constant"); + + case Beof: + error("action does not terminate"); + + case '\n': + lineno++; + goto lcopy; + } + +lcopy: + Bputrune(faction, c); + goto loop; +} + +void +openup(char *stem, int dflag, int vflag, int ytab, char *ytabc) +{ + char buf[256]; + + if(vflag) { + sprint(buf, "%s.%s", stem, FILEU); + foutput = Bopen(buf, OWRITE); + if(foutput == 0) + error("cannot open %s", buf); + } + if(yydebug) { + sprint(buf, "%s.%s", stem, FILEDEBUG); + if((fdebug = Bopen(buf, OWRITE)) == 0) + error("can't open %s", buf); + } + if(dflag) { + sprint(buf, "%s.%s", stem, FILED); + fdefine = Bopen(buf, OWRITE); + if(fdefine == 0) + error("can't create %s", buf); + } + if(ytab == 0) + sprint(buf, "%s.%s", stem, OFILE); + else + strcpy(buf, ytabc); + ftable = Bopen(buf, OWRITE); + if(ftable == 0) + error("cannot open table file %s", buf); +} + +/* + * print the output for the states + */ +void +output(void) +{ + int i, k, c; + Wset *u, *v; + + Bprint(ftable, "static\tconst\tshort yyexca[] =\n{"); + if(fdebug) + Bprint(fdebug, "static\tconst\tchar* yystates[] =\n{\n"); + + /* output the stuff for state i */ + SLOOP(i) { + nolook = tystate[i]!=MUSTLOOKAHEAD; + closure(i); + + /* output actions */ + nolook = 1; + aryfil(temp1, ntokens+nnonter+1, 0); + WSLOOP(wsets, u) { + c = *(u->pitem); + if(c > 1 && c < NTBASE && temp1[c] == 0) { + WSLOOP(u, v) + if(c == *(v->pitem)) + putitem(v->pitem+1, (Lkset*)0); + temp1[c] = state(c); + } else + if(c > NTBASE && temp1[(c -= NTBASE) + ntokens] == 0) + temp1[c+ntokens] = amem[indgo[i]+c]; + } + if(i == 1) + temp1[1] = ACCEPTCODE; + + /* now, we have the shifts; look at the reductions */ + lastred = 0; + WSLOOP(wsets, u) { + c = *u->pitem; + + /* reduction */ + if(c <= 0) { + lastred = -c; + TLOOP(k) + if(BIT(u->ws.lset, k)) { + if(temp1[k] == 0) + temp1[k] = c; + else + if(temp1[k] < 0) { /* reduce/reduce conflict */ + if(foutput) + Bprint(foutput, + "\n%d: reduce/reduce conflict" + " (red'ns %d and %d ) on %s", + i, -temp1[k], lastred, + symnam(k)); + if(-temp1[k] > lastred) + temp1[k] = -lastred; + zzrrconf++; + } else + /* potential shift/reduce conflict */ + precftn( lastred, k, i ); + } + } + } + wract(i); + } + + if(fdebug) + Bprint(fdebug, "};\n"); + Bprint(ftable, "};\n"); + Bprint(ftable, "#define YYNPROD %d\n", nprod); + Bprint(ftable, "#define YYPRIVATE %d\n", PRIVATE); + if(yydebug) + Bprint(ftable, "#define yydebug %s\n", yydebug); +} + +/* + * pack state i from temp1 into amem + */ +int +apack(int *p, int n) +{ + int *pp, *qq, *rr, off, *q, *r; + + /* we don't need to worry about checking because + * we will only look at entries known to be there... + * eliminate leading and trailing 0's + */ + + q = p+n; + for(pp = p, off = 0; *pp == 0 && pp <= q; ++pp, --off) + ; + /* no actions */ + if(pp > q) + return 0; + p = pp; + + /* now, find a place for the elements from p to q, inclusive */ + r = &amem[ACTSIZE-1]; + for(rr = amem; rr <= r; rr++, off++) { + for(qq = rr, pp = p; pp <= q; pp++, qq++) + if(*pp != 0) + if(*pp != *qq && *qq != 0) + goto nextk; + + /* we have found an acceptable k */ + if(pkdebug && foutput != 0) + Bprint(foutput, "off = %d, k = %d\n", off, (int)(rr-amem)); + for(qq = rr, pp = p; pp <= q; pp++, qq++) + if(*pp) { + if(qq > r) + error("action table overflow"); + if(qq > memp) + memp = qq; + *qq = *pp; + } + if(pkdebug && foutput != 0) + for(pp = amem; pp <= memp; pp += 10) { + Bprint(foutput, "\t"); + for(qq = pp; qq <= pp+9; qq++) + Bprint(foutput, "%d ", *qq); + Bprint(foutput, "\n"); + } + return(off); + nextk:; + } + error("no space in action table"); + return 0; +} + +/* + * output the gotos for the nontermninals + */ +void +go2out(void) +{ + int i, j, k, best, count, cbest, times; + + /* mark begining of gotos */ + Bprint(ftemp, "$\n"); + for(i = 1; i <= nnonter; i++) { + go2gen(i); + + /* find the best one to make default */ + best = -1; + times = 0; + + /* is j the most frequent */ + for(j = 0; j <= nstate; j++) { + if(tystate[j] == 0) + continue; + if(tystate[j] == best) + continue; + + /* is tystate[j] the most frequent */ + count = 0; + cbest = tystate[j]; + for(k = j; k <= nstate; k++) + if(tystate[k] == cbest) + count++; + if(count > times) { + best = cbest; + times = count; + } + } + + /* best is now the default entry */ + zzgobest += times-1; + for(j = 0; j <= nstate; j++) + if(tystate[j] != 0 && tystate[j] != best) { + Bprint(ftemp, "%d,%d,", j, tystate[j]); + zzgoent++; + } + + /* now, the default */ + if(best == -1) + best = 0; + zzgoent++; + Bprint(ftemp, "%d\n", best); + } +} + +/* + * output the gotos for nonterminal c + */ +void +go2gen(int c) +{ + int i, work, cc; + Item *p, *q; + + + /* first, find nonterminals with gotos on c */ + aryfil(temp1, nnonter+1, 0); + temp1[c] = 1; + work = 1; + while(work) { + work = 0; + PLOOP(0, i) + + /* cc is a nonterminal */ + if((cc=prdptr[i][1]-NTBASE) >= 0) + /* cc has a goto on c */ + if(temp1[cc] != 0) { + + /* thus, the left side of production i does too */ + cc = *prdptr[i]-NTBASE; + if(temp1[cc] == 0) { + work = 1; + temp1[cc] = 1; + } + } + } + + /* now, we have temp1[c] = 1 if a goto on c in closure of cc */ + if(g2debug && foutput != 0) { + Bprint(foutput, "%s: gotos on ", nontrst[c].name); + NTLOOP(i) + if(temp1[i]) + Bprint(foutput, "%s ", nontrst[i].name); + Bprint(foutput, "\n"); + } + + /* now, go through and put gotos into tystate */ + aryfil(tystate, nstate, 0); + SLOOP(i) + ITMLOOP(i, p, q) + if((cc = *p->pitem) >= NTBASE) + /* goto on c is possible */ + if(temp1[cc-NTBASE]) { + tystate[i] = amem[indgo[i]+c]; + break; + } +} + +/* + * decide a shift/reduce conflict by precedence. + * r is a rule number, t a token number + * the conflict is in state s + * temp1[t] is changed to reflect the action + */ +void +precftn(int r, int t, int s) +{ + int lp, lt, action; + + lp = levprd[r]; + lt = toklev[t]; + if(PLEVEL(lt) == 0 || PLEVEL(lp) == 0) { + + /* conflict */ + if(foutput != 0) + Bprint(foutput, + "\n%d: shift/reduce conflict (shift %d(%d), red'n %d(%d)) on %s", + s, temp1[t], PLEVEL(lt), r, PLEVEL(lp), symnam(t)); + zzsrconf++; + return; + } + if(PLEVEL(lt) == PLEVEL(lp)) + action = ASSOC(lt); + else + if(PLEVEL(lt) > PLEVEL(lp)) + action = RASC; /* shift */ + else + action = LASC; /* reduce */ + switch(action) { + case BASC: /* error action */ + temp1[t] = ERRCODE; + break; + case LASC: /* reduce */ + temp1[t] = -r; + break; + } +} + +/* + * output state i + * temp1 has the actions, lastred the default + */ +void +wract(int i) +{ + int p, p0, p1, ntimes, tred, count, j, flag; + + /* find the best choice for lastred */ + lastred = 0; + ntimes = 0; + TLOOP(j) { + if(temp1[j] >= 0) + continue; + if(temp1[j]+lastred == 0) + continue; + /* count the number of appearances of temp1[j] */ + count = 0; + tred = -temp1[j]; + levprd[tred] |= REDFLAG; + TLOOP(p) + if(temp1[p]+tred == 0) + count++; + if(count > ntimes) { + lastred = tred; + ntimes = count; + } + } + + /* + * for error recovery, arrange that, if there is a shift on the + * error recovery token, `error', that the default be the error action + */ + if(temp1[2] > 0) + lastred = 0; + + /* clear out entries in temp1 which equal lastred */ + TLOOP(p) + if(temp1[p]+lastred == 0) + temp1[p] = 0; + + wrstate(i); + defact[i] = lastred; + flag = 0; + TLOOP(p0) + if((p1=temp1[p0]) != 0) { + if(p1 < 0) { + p1 = -p1; + goto exc; + } + if(p1 == ACCEPTCODE) { + p1 = -1; + goto exc; + } + if(p1 == ERRCODE) { + p1 = 0; + exc: + if(flag++ == 0) + Bprint(ftable, "-1, %d,\n", i); + Bprint(ftable, "\t%d, %d,\n", p0, p1); + zzexcp++; + continue; + } + Bprint(ftemp, "%d,%d,", p0, p1); + zzacent++; + } + if(flag) { + defact[i] = -2; + Bprint(ftable, "\t-2, %d,\n", lastred); + } + Bprint(ftemp, "\n"); +} + +/* + * writes state i + */ +void +wrstate(int i) +{ + int j0, j1; + Item *pp, *qq; + Wset *u; + + if(fdebug) { + if(lastred) { + Bprint(fdebug, " 0, /*%d*/\n", i); + } else { + Bprint(fdebug, " \""); + ITMLOOP(i, pp, qq) + Bprint(fdebug, "%s\\n", writem(pp->pitem)); + if(tystate[i] == MUSTLOOKAHEAD) + WSLOOP(wsets + (pstate[i+1] - pstate[i]), u) + if(*u->pitem < 0) + Bprint(fdebug, "%s\\n", writem(u->pitem)); + Bprint(fdebug, "\", /*%d*/\n", i); + } + } + if(foutput == 0) + return; + Bprint(foutput, "\nstate %d\n", i); + ITMLOOP(i, pp, qq) + Bprint(foutput, "\t%s\n", writem(pp->pitem)); + if(tystate[i] == MUSTLOOKAHEAD) + /* print out empty productions in closure */ + WSLOOP(wsets+(pstate[i+1]-pstate[i]), u) + if(*u->pitem < 0) + Bprint(foutput, "\t%s\n", writem(u->pitem)); + + /* check for state equal to another */ + TLOOP(j0) + if((j1=temp1[j0]) != 0) { + Bprint(foutput, "\n\t%s ", symnam(j0)); + /* shift, error, or accept */ + if(j1 > 0) { + if(j1 == ACCEPTCODE) + Bprint(foutput, "accept"); + else + if(j1 == ERRCODE) + Bprint(foutput, "error"); + else + Bprint(foutput, "shift %d", j1); + } else + Bprint(foutput, "reduce %d (src line %d)", -j1, rlines[-j1]); + } + + /* output the final production */ + if(lastred) + Bprint(foutput, "\n\t. reduce %d (src line %d)\n\n", + lastred, rlines[lastred]); + else + Bprint(foutput, "\n\t. error\n\n"); + + /* now, output nonterminal actions */ + j1 = ntokens; + for(j0 = 1; j0 <= nnonter; j0++) { + j1++; + if(temp1[j1]) + Bprint(foutput, "\t%s goto %d\n", symnam(j0+NTBASE), temp1[j1]); + } +} + +void +warray(char *s, int *v, int n) +{ + int i; + + Bprint(ftable, "static\tconst\tshort %s[] =\n{", s); + for(i=0;;) { + if(i%10 == 0) + Bprint(ftable, "\n"); + Bprint(ftable, "%4d", v[i]); + i++; + if(i >= n) { + Bprint(ftable, "\n};\n"); + break; + } + Bprint(ftable, ","); + } +} + +/* + * in order to free up the mem and amem arrays for the optimizer, + * and still be able to output yyr1, etc., after the sizes of + * the action array is known, we hide the nonterminals + * derived by productions in levprd. + */ + +void +hideprod(void) +{ + int i, j; + + j = 0; + levprd[0] = 0; + PLOOP(1, i) { + if(!(levprd[i] & REDFLAG)) { + j++; + if(foutput != 0) + Bprint(foutput, "Rule not reduced: %s\n", writem(prdptr[i])); + } + levprd[i] = *prdptr[i] - NTBASE; + } + if(j) + print("%d rules never reduced\n", j); +} + +void +callopt(void) +{ + int i, *p, j, k, *q; + + /* read the arrays from tempfile and set parameters */ + finput = Bopen(tempname, OREAD); + if(finput == 0) + error("optimizer cannot open tempfile"); + + pgo[0] = 0; + temp1[0] = 0; + nstate = 0; + nnonter = 0; + for(;;) { + switch(gtnm()) { + case '\n': + nstate++; + pmem--; + temp1[nstate] = pmem - mem0; + case ',': + continue; + case '$': + break; + default: + error("bad tempfile %s", tempname); + } + break; + } + + pmem--; + temp1[nstate] = yypgo[0] = pmem - mem0; + for(;;) { + switch(gtnm()) { + case '\n': + nnonter++; + yypgo[nnonter] = pmem-mem0; + case ',': + continue; + case -1: + break; + default: + error("bad tempfile"); + } + break; + } + pmem--; + yypgo[nnonter--] = pmem - mem0; + for(i = 0; i < nstate; i++) { + k = 32000; + j = 0; + q = mem0 + temp1[i+1]; + for(p = mem0 + temp1[i]; p < q ; p += 2) { + if(*p > j) + j = *p; + if(*p < k) + k = *p; + } + /* nontrivial situation */ + if(k <= j) { + /* j is now the range */ +/* j -= k; *//* call scj */ + if(k > maxoff) + maxoff = k; + } + tystate[i] = (temp1[i+1]-temp1[i]) + 2*j; + if(j > maxspr) + maxspr = j; + } + + /* initialize ggreed table */ + for(i = 1; i <= nnonter; i++) { + ggreed[i] = 1; + j = 0; + + /* minimum entry index is always 0 */ + q = mem0 + yypgo[i+1] - 1; + for(p = mem0+yypgo[i]; p < q ; p += 2) { + ggreed[i] += 2; + if(*p > j) + j = *p; + } + ggreed[i] = ggreed[i] + 2*j; + if(j > maxoff) + maxoff = j; + } + + /* now, prepare to put the shift actions into the amem array */ + for(i = 0; i < ACTSIZE; i++) + amem[i] = 0; + maxa = amem; + for(i = 0; i < nstate; i++) { + if(tystate[i] == 0 && adb > 1) + Bprint(ftable, "State %d: null\n", i); + indgo[i] = YYFLAG1; + } + while((i = nxti()) != NOMORE) + if(i >= 0) + stin(i); + else + gin(-i); + + /* print amem array */ + if(adb > 2 ) + for(p = amem; p <= maxa; p += 10) { + Bprint(ftable, "%4d ", (int)(p-amem)); + for(i = 0; i < 10; ++i) + Bprint(ftable, "%4d ", p[i]); + Bprint(ftable, "\n"); + } + + /* write out the output appropriate to the language */ + aoutput(); + osummary(); + ZAPFILE(tempname); +} + +void +gin(int i) +{ + int *p, *r, *s, *q1, *q2; + + /* enter gotos on nonterminal i into array amem */ + ggreed[i] = 0; + + q2 = mem0+ yypgo[i+1] - 1; + q1 = mem0 + yypgo[i]; + + /* now, find amem place for it */ + for(p = amem; p < &amem[ACTSIZE]; p++) { + if(*p) + continue; + for(r = q1; r < q2; r += 2) { + s = p + *r + 1; + if(*s) + goto nextgp; + if(s > maxa) + if((maxa = s) > &amem[ACTSIZE]) + error("a array overflow"); + } + /* we have found amem spot */ + *p = *q2; + if(p > maxa) + if((maxa = p) > &amem[ACTSIZE]) + error("a array overflow"); + for(r = q1; r < q2; r += 2) { + s = p + *r + 1; + *s = r[1]; + } + pgo[i] = p-amem; + if(adb > 1) + Bprint(ftable, "Nonterminal %d, entry at %d\n", i, pgo[i]); + return; + + nextgp:; + } + error("cannot place goto %d\n", i); +} + +void +stin(int i) +{ + int *r, *s, n, flag, j, *q1, *q2; + + tystate[i] = 0; + + /* enter state i into the amem array */ + q2 = mem0+temp1[i+1]; + q1 = mem0+temp1[i]; + /* find an acceptable place */ + for(n = -maxoff; n < ACTSIZE; n++) { + flag = 0; + for(r = q1; r < q2; r += 2) { + if((s = *r + n + amem) < amem) + goto nextn; + if(*s == 0) + flag++; + else + if(*s != r[1]) + goto nextn; + } + + /* check that the position equals another only if the states are identical */ + for(j=0; j<nstate; j++) { + if(indgo[j] == n) { + + /* we have some disagreement */ + if(flag) + goto nextn; + if(temp1[j+1]+temp1[i] == temp1[j]+temp1[i+1]) { + + /* states are equal */ + indgo[i] = n; + if(adb > 1) + Bprint(ftable, + "State %d: entry at %d equals state %d\n", + i, n, j); + return; + } + + /* we have some disagreement */ + goto nextn; + } + } + + for(r = q1; r < q2; r += 2) { + if((s = *r+n+amem) >= &amem[ACTSIZE]) + error("out of space in optimizer a array"); + if(s > maxa) + maxa = s; + if(*s != 0 && *s != r[1]) + error("clobber of a array, pos'n %d, by %d", s-amem, r[1]); + *s = r[1]; + } + indgo[i] = n; + if(adb > 1) + Bprint(ftable, "State %d: entry at %d\n", i, indgo[i]); + return; + nextn:; + } + error("Error; failure to place state %d\n", i); +} + +/* + * finds the next i + */ +int +nxti(void) +{ + int i, max, maxi; + + max = 0; + maxi = 0; + for(i = 1; i <= nnonter; i++) + if(ggreed[i] >= max) { + max = ggreed[i]; + maxi = -i; + } + for(i = 0; i < nstate; ++i) + if(tystate[i] >= max) { + max = tystate[i]; + maxi = i; + } + if(nxdb) + Bprint(ftable, "nxti = %d, max = %d\n", maxi, max); + if(max == 0) + return NOMORE; + return maxi; +} + +/* + * write summary + */ +void +osummary(void) +{ + + int i, *p; + + if(foutput == 0) + return; + i = 0; + for(p = maxa; p >= amem; p--) + if(*p == 0) + i++; + + Bprint(foutput, "Optimizer space used: input %d/%d, output %d/%d\n", + (int)(pmem-mem0+1), MEMSIZE, (int)(maxa-amem+1), ACTSIZE); + Bprint(foutput, "%d table entries, %d zero\n", (int)(maxa-amem+1), i); + Bprint(foutput, "maximum spread: %d, maximum offset: %d\n", maxspr, maxoff); +} + +/* + * this version is for C + * write out the optimized parser + */ +void +aoutput(void) +{ + Bprint(ftable, "#define\tYYLAST\t%d\n", (int)(maxa-amem+1)); + arout("yyact", amem, (maxa-amem)+1); + arout("yypact", indgo, nstate); + arout("yypgo", pgo, nnonter+1); +} + +void +arout(char *s, int *v, int n) +{ + int i; + + Bprint(ftable, "static\tconst\tshort %s[] =\n{", s); + for(i = 0; i < n;) { + if(i%10 == 0) + Bprint(ftable, "\n"); + Bprint(ftable, "%4d", v[i]); + i++; + if(i == n) + Bprint(ftable, "\n};\n"); + else + Bprint(ftable, ","); + } +} + +/* + * read and convert an integer from the standard input + * return the terminating character + * blanks, tabs, and newlines are ignored + */ +int +gtnm(void) +{ + int sign, val, c; + + sign = 0; + val = 0; + while((c=Bgetrune(finput)) != Beof) { + if(isdigit(c)) { + val = val*10 + c-'0'; + continue; + } + if(c == '-') { + sign = 1; + continue; + } + break; + } + if(sign) + val = -val; + *pmem++ = val; + if(pmem >= &mem0[MEMSIZE]) + error("out of space"); + return c; +} diff --git a/yacc/yaccpar b/yacc/yaccpar @@ -0,0 +1,276 @@ +#define YYFLAG -1000 +#define YYERROR goto yyerrlab +#define YYACCEPT return(0) +#define YYABORT return(1) +#define yyclearin yychar = -1 +#define yyerrok yyerrflag = 0 + +#ifdef yydebug +#include "y.debug" +#else +#define yydebug 0 +static const char* yytoknames[1]; /* for debugging */ +static const char* yystates[1]; /* for debugging */ +#endif + +/* parser for yacc output */ +#ifdef YYARG +#define yynerrs yyarg->yynerrs +#define yyerrflag yyarg->yyerrflag +#define yyval yyarg->yyval +#define yylval yyarg->yylval +#else +int yynerrs = 0; /* number of errors */ +int yyerrflag = 0; /* error recovery flag */ +#endif + +extern int fprint(int, char*, ...); +extern int sprint(char*, char*, ...); + +static const char* +yytokname(int yyc) +{ + static char x[10]; + + if(yyc > 0 && yyc <= sizeof(yytoknames)/sizeof(yytoknames[0])) + if(yytoknames[yyc-1]) + return yytoknames[yyc-1]; + sprint(x, "<%d>", yyc); + return x; +} + +static const char* +yystatname(int yys) +{ + static char x[10]; + + if(yys >= 0 && yys < sizeof(yystates)/sizeof(yystates[0])) + if(yystates[yys]) + return yystates[yys]; + sprint(x, "<%d>\n", yys); + return x; +} + +static long +#ifdef YYARG +yylex1(struct Yyarg *yyarg) +#else +yylex1(void) +#endif +{ + long yychar; + const long *t3p; + int c; + +#ifdef YYARG + yychar = yylex(yyarg); +#else + yychar = yylex(); +#endif + if(yychar <= 0) { + c = yytok1[0]; + goto out; + } + if(yychar < sizeof(yytok1)/sizeof(yytok1[0])) { + c = yytok1[yychar]; + goto out; + } + if(yychar >= YYPRIVATE) + if(yychar < YYPRIVATE+sizeof(yytok2)/sizeof(yytok2[0])) { + c = yytok2[yychar-YYPRIVATE]; + goto out; + } + for(t3p=yytok3;; t3p+=2) { + c = t3p[0]; + if(c == yychar) { + c = t3p[1]; + goto out; + } + if(c == 0) + break; + } + c = 0; + +out: + if(c == 0) + c = yytok2[1]; /* unknown char */ + if(yydebug >= 3) + fprint(2, "lex %.4lux %s\n", yychar, yytokname(c)); + return c; +} + +int +#ifdef YYARG +yyparse(struct Yyarg *yyarg) +#else +yyparse(void) +#endif +{ + struct + { + YYSTYPE yyv; + int yys; + } yys[YYMAXDEPTH], *yyp, *yypt; + const short *yyxi; + int yyj, yym, yystate, yyn, yyg; + long yychar; +#ifndef YYARG + YYSTYPE save1, save2; + int save3, save4; + + save1 = yylval; + save2 = yyval; + save3 = yynerrs; + save4 = yyerrflag; +#endif + + yystate = 0; + yychar = -1; + yynerrs = 0; + yyerrflag = 0; + yyp = &yys[-1]; + goto yystack; + +ret0: + yyn = 0; + goto ret; + +ret1: + yyn = 1; + goto ret; + +ret: +#ifndef YYARG + yylval = save1; + yyval = save2; + yynerrs = save3; + yyerrflag = save4; +#endif + return yyn; + +yystack: + /* put a state and value onto the stack */ + if(yydebug >= 4) + fprint(2, "char %s in %s", yytokname(yychar), yystatname(yystate)); + + yyp++; + if(yyp >= &yys[YYMAXDEPTH]) { + yyerror("yacc stack overflow"); + goto ret1; + } + yyp->yys = yystate; + yyp->yyv = yyval; + +yynewstate: + yyn = yypact[yystate]; + if(yyn <= YYFLAG) + goto yydefault; /* simple state */ + if(yychar < 0) +#ifdef YYARG + yychar = yylex1(yyarg); +#else + yychar = yylex1(); +#endif + yyn += yychar; + if(yyn < 0 || yyn >= YYLAST) + goto yydefault; + yyn = yyact[yyn]; + if(yychk[yyn] == yychar) { /* valid shift */ + yychar = -1; + yyval = yylval; + yystate = yyn; + if(yyerrflag > 0) + yyerrflag--; + goto yystack; + } + +yydefault: + /* default state action */ + yyn = yydef[yystate]; + if(yyn == -2) { + if(yychar < 0) +#ifdef YYARG + yychar = yylex1(yyarg); +#else + yychar = yylex1(); +#endif + + /* look through exception table */ + for(yyxi=yyexca;; yyxi+=2) + if(yyxi[0] == -1 && yyxi[1] == yystate) + break; + for(yyxi += 2;; yyxi += 2) { + yyn = yyxi[0]; + if(yyn < 0 || yyn == yychar) + break; + } + yyn = yyxi[1]; + if(yyn < 0) + goto ret0; + } + if(yyn == 0) { + /* error ... attempt to resume parsing */ + switch(yyerrflag) { + case 0: /* brand new error */ + yyerror("syntax error"); + if(yydebug >= 1) { + fprint(2, "%s", yystatname(yystate)); + fprint(2, "saw %s\n", yytokname(yychar)); + } + goto yyerrlab; + yyerrlab: + yynerrs++; + + case 1: + case 2: /* incompletely recovered error ... try again */ + yyerrflag = 3; + + /* find a state where "error" is a legal shift action */ + while(yyp >= yys) { + yyn = yypact[yyp->yys] + YYERRCODE; + if(yyn >= 0 && yyn < YYLAST) { + yystate = yyact[yyn]; /* simulate a shift of "error" */ + if(yychk[yystate] == YYERRCODE) + goto yystack; + } + + /* the current yyp has no shift onn "error", pop stack */ + if(yydebug >= 2) + fprint(2, "error recovery pops state %d, uncovers %d\n", + yyp->yys, (yyp-1)->yys ); + yyp--; + } + /* there is no state on the stack with an error shift ... abort */ + goto ret1; + + case 3: /* no shift yet; clobber input char */ + if(yydebug >= 2) + fprint(2, "error recovery discards %s\n", yytokname(yychar)); + if(yychar == YYEOFCODE) + goto ret1; + yychar = -1; + goto yynewstate; /* try again in the same state */ + } + } + + /* reduction by production yyn */ + if(yydebug >= 2) + fprint(2, "reduce %d in:\n\t%s", yyn, yystatname(yystate)); + + yypt = yyp; + yyp -= yyr2[yyn]; + yyval = (yyp+1)->yyv; + yym = yyn; + + /* consult goto table to find next state */ + yyn = yyr1[yyn]; + yyg = yypgo[yyn]; + yyj = yyg + yyp->yys + 1; + + if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn) + yystate = yyact[yyg]; + switch(yym) { + $A + } + goto yystack; /* stack new state and value */ +} diff --git a/yacc/yaccpars b/yacc/yaccpars @@ -0,0 +1,273 @@ +#define YYFLAG -1000 +#define YYERROR goto yyerrlab +#define YYACCEPT return(0) +#define YYABORT return(1) +#define yyclearin yychar = -1 +#define yyerrok yyerrflag = 0 + +#ifdef yydebug +#include "y.debug" +#else +#define yydebug 0 +static const char* yytoknames[1]; /* for debugging */ +static const char* yystates[1]; /* for debugging */ +#endif + +/* parser for yacc output */ +#ifdef YYARG +#define yynerrs yyarg->yynerrs +#define yyerrflag yyarg->yyerrflag +#define yyval yyarg->yyval +#define yylval yyarg->yylval +#else +int yynerrs = 0; /* number of errors */ +int yyerrflag = 0; /* error recovery flag */ +#endif + +static const char* +yytokname(int yyc) +{ + static char x[10]; + + if(yyc > 0 && yyc <= sizeof(yytoknames)/sizeof(yytoknames[0])) + if(yytoknames[yyc-1]) + return yytoknames[yyc-1]; + sprintf(x, "<%d>", yyc); + return x; +} + +static const char* +yystatname(int yys) +{ + static char x[10]; + + if(yys >= 0 && yys < sizeof(yystates)/sizeof(yystates[0])) + if(yystates[yys]) + return yystates[yys]; + sprintf(x, "<%d>\n", yys); + return x; +} + +static long +#ifdef YYARG +yylex1(struct Yyarg *yyarg) +#else +yylex1(void) +#endif +{ + long yychar; + const long *t3p; + int c; + +#ifdef YYARG + yychar = yylex(yyarg); +#else + yychar = yylex(); +#endif + if(yychar <= 0) { + c = yytok1[0]; + goto out; + } + if(yychar < sizeof(yytok1)/sizeof(yytok1[0])) { + c = yytok1[yychar]; + goto out; + } + if(yychar >= YYPRIVATE) + if(yychar < YYPRIVATE+sizeof(yytok2)/sizeof(yytok2[0])) { + c = yytok2[yychar-YYPRIVATE]; + goto out; + } + for(t3p=yytok3;; t3p+=2) { + c = t3p[0]; + if(c == yychar) { + c = t3p[1]; + goto out; + } + if(c == 0) + break; + } + c = 0; + +out: + if(c == 0) + c = yytok2[1]; /* unknown char */ + if(yydebug >= 3) + printf("lex %.4lX %s\n", yychar, yytokname(c)); + return c; +} + +int +#ifdef YYARG +yyparse(struct Yyarg *yyarg) +#else +yyparse(void) +#endif +{ + struct + { + YYSTYPE yyv; + int yys; + } yys[YYMAXDEPTH], *yyp, *yypt; + const short *yyxi; + int yyj, yym, yystate, yyn, yyg; + long yychar; +#ifndef YYARG + YYSTYPE save1, save2; + int save3, save4; + + save1 = yylval; + save2 = yyval; + save3 = yynerrs; + save4 = yyerrflag; +#endif + + yystate = 0; + yychar = -1; + yynerrs = 0; + yyerrflag = 0; + yyp = &yys[-1]; + goto yystack; + +ret0: + yyn = 0; + goto ret; + +ret1: + yyn = 1; + goto ret; + +ret: +#ifndef YYARG + yylval = save1; + yyval = save2; + yynerrs = save3; + yyerrflag = save4; +#endif + return yyn; + +yystack: + /* put a state and value onto the stack */ + if(yydebug >= 4) + printf("char %s in %s", yytokname(yychar), yystatname(yystate)); + + yyp++; + if(yyp >= &yys[YYMAXDEPTH]) { + yyerror("yacc stack overflow"); + goto ret1; + } + yyp->yys = yystate; + yyp->yyv = yyval; + +yynewstate: + yyn = yypact[yystate]; + if(yyn <= YYFLAG) + goto yydefault; /* simple state */ + if(yychar < 0) +#ifdef YYARG + yychar = yylex1(yyarg); +#else + yychar = yylex1(); +#endif + yyn += yychar; + if(yyn < 0 || yyn >= YYLAST) + goto yydefault; + yyn = yyact[yyn]; + if(yychk[yyn] == yychar) { /* valid shift */ + yychar = -1; + yyval = yylval; + yystate = yyn; + if(yyerrflag > 0) + yyerrflag--; + goto yystack; + } + +yydefault: + /* default state action */ + yyn = yydef[yystate]; + if(yyn == -2) { + if(yychar < 0) +#ifdef YYARG + yychar = yylex1(yyarg); +#else + yychar = yylex1(); +#endif + + /* look through exception table */ + for(yyxi=yyexca;; yyxi+=2) + if(yyxi[0] == -1 && yyxi[1] == yystate) + break; + for(yyxi += 2;; yyxi += 2) { + yyn = yyxi[0]; + if(yyn < 0 || yyn == yychar) + break; + } + yyn = yyxi[1]; + if(yyn < 0) + goto ret0; + } + if(yyn == 0) { + /* error ... attempt to resume parsing */ + switch(yyerrflag) { + case 0: /* brand new error */ + yyerror("syntax error"); + if(yydebug >= 1) { + printf("%s", yystatname(yystate)); + printf("saw %s\n", yytokname(yychar)); + } + goto yyerrlab; + yyerrlab: + yynerrs++; + + case 1: + case 2: /* incompletely recovered error ... try again */ + yyerrflag = 3; + + /* find a state where "error" is a legal shift action */ + while(yyp >= yys) { + yyn = yypact[yyp->yys] + YYERRCODE; + if(yyn >= 0 && yyn < YYLAST) { + yystate = yyact[yyn]; /* simulate a shift of "error" */ + if(yychk[yystate] == YYERRCODE) + goto yystack; + } + + /* the current yyp has no shift onn "error", pop stack */ + if(yydebug >= 2) + printf("error recovery pops state %d, uncovers %d\n", + yyp->yys, (yyp-1)->yys ); + yyp--; + } + /* there is no state on the stack with an error shift ... abort */ + goto ret1; + + case 3: /* no shift yet; clobber input char */ + if(yydebug >= YYEOFCODE) + printf("error recovery discards %s\n", yytokname(yychar)); + if(yychar == YYEOFCODE) + goto ret1; + yychar = -1; + goto yynewstate; /* try again in the same state */ + } + } + + /* reduction by production yyn */ + if(yydebug >= 2) + printf("reduce %d in:\n\t%s", yyn, yystatname(yystate)); + + yypt = yyp; + yyp -= yyr2[yyn]; + yyval = (yyp+1)->yyv; + yym = yyn; + + /* consult goto table to find next state */ + yyn = yyr1[yyn]; + yyg = yypgo[yyn]; + yyj = yyg + yyp->yys + 1; + + if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn) + yystate = yyact[yyg]; + switch(yym) { + $A + } + goto yystack; /* stack new state and value */ +}