mk-standalone

standalone mk from 9base
git clone git@git.evenfri.pw:mk-standalone.git
Log | Files | Refs | README

commit 9384ccbfd1afa8ea83ceda4918905e2969e0001a
Author: evenfri <evenfri256@gmail.com>
Date:   Sat, 18 Jul 2026 18:17:14 +0800

initial commit

Diffstat:
A.gitignore | 6++++++
AREADME.md | 1+
Aconfig.mk | 25+++++++++++++++++++++++++
Alib9.log | 176+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/LICENSE | 19+++++++++++++++++++
Alib9/Makefile | 231+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/_exits.c | 10++++++++++
Alib9/_p9dialparse.c | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/_p9dir.c | 243+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/_p9translate.c | 46++++++++++++++++++++++++++++++++++++++++++++++
Alib9/announce.c | 152+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/argv0.c | 9+++++++++
Alib9/atexit.c | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/atnotify.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/atoi.c | 9+++++++++
Alib9/atol.c | 9+++++++++
Alib9/atoll.c | 9+++++++++
Alib9/auth.h | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/await.c | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio.h | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/_lib9.h | 12++++++++++++
Alib9/bio/bbuffered.c | 20++++++++++++++++++++
Alib9/bio/bcat.c | 46++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/bfildes.c | 9+++++++++
Alib9/bio/bflush.c | 33+++++++++++++++++++++++++++++++++
Alib9/bio/bgetc.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/bgetd.c | 36++++++++++++++++++++++++++++++++++++
Alib9/bio/bgetrune.c | 47+++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/binit.c | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/boffset.c | 25+++++++++++++++++++++++++
Alib9/bio/bprint.c | 14++++++++++++++
Alib9/bio/bputc.c | 20++++++++++++++++++++
Alib9/bio/bputrune.c | 23+++++++++++++++++++++++
Alib9/bio/brdline.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/brdstr.c | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/bread.c | 45+++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/bseek.c | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/bio/bvprint.c | 38++++++++++++++++++++++++++++++++++++++
Alib9/bio/bwrite.c | 38++++++++++++++++++++++++++++++++++++++
Alib9/bio/lib9.std.h | 26++++++++++++++++++++++++++
Alib9/cistrcmp.c | 26++++++++++++++++++++++++++
Alib9/cistrncmp.c | 28++++++++++++++++++++++++++++
Alib9/cistrstr.c | 23+++++++++++++++++++++++
Alib9/cleanname.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/convD2M.c | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/convM2D.c | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/convM2S.c | 352+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/convS2M.c | 435+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/create.c | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/crypt.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/ctime.c | 181+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/date.c | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/debugmalloc.c | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/dial.c | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/dirfstat.c | 29+++++++++++++++++++++++++++++
Alib9/dirfwstat.c | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/dirmodefmt.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/dirread.c | 212+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/dirstat.c | 32++++++++++++++++++++++++++++++++
Alib9/dirwstat.c | 31+++++++++++++++++++++++++++++++
Alib9/dup.c | 12++++++++++++
Alib9/encodefmt.c | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/errstr.c | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/exec.c | 9+++++++++
Alib9/execl.c | 29+++++++++++++++++++++++++++++
Alib9/exitcode.c | 9+++++++++
Alib9/fcall.h | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fcallfmt.c | 253+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt.h | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/LICENSE | 22++++++++++++++++++++++
Alib9/fmt/NOTICE | 19+++++++++++++++++++
Alib9/fmt/README | 19+++++++++++++++++++
Alib9/fmt/charstod.c | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/dofmt.c | 617+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/dorfmt.c | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/errfmt.c | 16++++++++++++++++
Alib9/fmt/fltfmt.c | 668+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/fmt.c | 220+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/fmtdef.h | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/fmtfd.c | 36++++++++++++++++++++++++++++++++++++
Alib9/fmt/fmtfdflush.c | 22++++++++++++++++++++++
Alib9/fmt/fmtlocale.c | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/fmtlock.c | 15+++++++++++++++
Alib9/fmt/fmtnull.c | 33+++++++++++++++++++++++++++++++++
Alib9/fmt/fmtprint.c | 36++++++++++++++++++++++++++++++++++++
Alib9/fmt/fmtquote.c | 259+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/fmtrune.c | 28++++++++++++++++++++++++++++
Alib9/fmt/fmtstr.c | 16++++++++++++++++
Alib9/fmt/fmtvprint.c | 37+++++++++++++++++++++++++++++++++++++
Alib9/fmt/fprint.c | 17+++++++++++++++++
Alib9/fmt/nan.h | 4++++
Alib9/fmt/nan64.c | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/plan9.h | 38++++++++++++++++++++++++++++++++++++++
Alib9/fmt/pow10.c | 45+++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/print.c | 17+++++++++++++++++
Alib9/fmt/runefmtstr.c | 16++++++++++++++++
Alib9/fmt/runeseprint.c | 18++++++++++++++++++
Alib9/fmt/runesmprint.c | 18++++++++++++++++++
Alib9/fmt/runesnprint.c | 19+++++++++++++++++++
Alib9/fmt/runesprint.c | 18++++++++++++++++++
Alib9/fmt/runevseprint.c | 29+++++++++++++++++++++++++++++
Alib9/fmt/runevsmprint.c | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/runevsnprint.c | 28++++++++++++++++++++++++++++
Alib9/fmt/seprint.c | 17+++++++++++++++++
Alib9/fmt/smprint.c | 17+++++++++++++++++
Alib9/fmt/snprint.c | 18++++++++++++++++++
Alib9/fmt/sprint.c | 30++++++++++++++++++++++++++++++
Alib9/fmt/strtod.c | 520+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/strtod.h | 4++++
Alib9/fmt/test.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/test2.c | 9+++++++++
Alib9/fmt/test3.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/vfprint.c | 21+++++++++++++++++++++
Alib9/fmt/vseprint.c | 28++++++++++++++++++++++++++++
Alib9/fmt/vsmprint.c | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/fmt/vsnprint.c | 28++++++++++++++++++++++++++++
Alib9/fmtlock2.c | 16++++++++++++++++
Alib9/fork.c | 22++++++++++++++++++++++
Alib9/get9root.c | 18++++++++++++++++++
Alib9/getcallerpc-386.c | 7+++++++
Alib9/getcallerpc-PowerMacintosh.c | 7+++++++
Alib9/getcallerpc-arm.c | 8++++++++
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 | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/getuser.c | 16++++++++++++++++
Alib9/getwd.c | 10++++++++++
Alib9/jmp-FreeBSD.s | 3+++
Alib9/jmp.c | 17+++++++++++++++++
Alib9/lib9.a | 0
Alib9/lib9.h | 2++
Alib9/libc.h | 948+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/lnrand.c | 18++++++++++++++++++
Alib9/lock.c | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/lrand.c | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/main.c | 13+++++++++++++
Alib9/malloc.c | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/malloctag.c | 15+++++++++++++++
Alib9/mallocz.c | 15+++++++++++++++
Alib9/nan.c | 27+++++++++++++++++++++++++++
Alib9/nan.h | 4++++
Alib9/needsrcquote.c | 12++++++++++++
Alib9/needstack.c | 8++++++++
Alib9/netcrypt.c | 18++++++++++++++++++
Alib9/netmkaddr.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/notify.c | 273+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/nrand.c | 17+++++++++++++++++
Alib9/nulldir.c | 9+++++++++
Alib9/open.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/opentemp.c | 20++++++++++++++++++++
Alib9/pin.c | 11+++++++++++
Alib9/pipe.c | 15+++++++++++++++
Alib9/post9p.c | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/postnote.c | 34++++++++++++++++++++++++++++++++++
Alib9/priv.c | 32++++++++++++++++++++++++++++++++
Alib9/qlock.c | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/quote.c | 136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/rand.c | 7+++++++
Alib9/read9pmsg.c | 31+++++++++++++++++++++++++++++++
Alib9/readcons.c | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/readn.c | 21+++++++++++++++++++++
Alib9/regex/README | 7+++++++
Alib9/regex/cvt | 11+++++++++++
Alib9/regex/lib9.h | 2++
Alib9/regex/lib9.std.h | 10++++++++++
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 | 212+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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 | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/searchpath.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/sec/libsec.h | 366+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/sec/md5.c | 147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/sec/md5block.c | 267+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/sec/md5pickle.c | 39+++++++++++++++++++++++++++++++++++++++
Alib9/sec/os.h | 2++
Alib9/sec/sha1.c | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/sec/sha1block.c | 187+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/sec/sha1pickle.c | 38++++++++++++++++++++++++++++++++++++++
Alib9/seek.c | 8++++++++
Alib9/sendfd.c | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/sleep.c | 47+++++++++++++++++++++++++++++++++++++++++++++++
Alib9/st4nkO6D | 1+
Alib9/strdup.c | 17+++++++++++++++++
Alib9/strecpy.c | 16++++++++++++++++
Alib9/sysfatal.c | 21+++++++++++++++++++++
Alib9/syslog.c | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/sysname.c | 30++++++++++++++++++++++++++++++
Alib9/test.c | 8++++++++
Alib9/testfltfmt.c | 183+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/testfmt.c | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/testfork.c | 21+++++++++++++++++++++
Alib9/testprint.c | 14++++++++++++++
Alib9/time.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/tm2sec.c | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/tokenize.c | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/truerand.c | 27+++++++++++++++++++++++++++
Alib9/u.h | 190+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/u16.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/u32.c | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/u64.c | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/udp.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/unsharp.c | 44++++++++++++++++++++++++++++++++++++++++++++
Alib9/utf.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/utf/LICENSE | 13+++++++++++++
Alib9/utf/NOTICE | 13+++++++++++++
Alib9/utf/README | 13+++++++++++++
Alib9/utf/plan9.h | 29+++++++++++++++++++++++++++++
Alib9/utf/rune.c | 217+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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/utfecpy.c | 37+++++++++++++++++++++++++++++++++++++
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++++++++++++++++++++
Alib9/write.c | 23+++++++++++++++++++++++
Alib9/zoneinfo.c | 215+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib9/zoneinfo.h | 19+++++++++++++++++++
Amk.log | 28++++++++++++++++++++++++++++
Amk/Makefile | 17+++++++++++++++++
Amk/NOTICE | 27+++++++++++++++++++++++++++
Amk/README | 7+++++++
Amk/arc.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/archive.c | 253+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/bufblock.c | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/env.c | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/file.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/fns.h | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/graph.c | 279+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/job.c | 33+++++++++++++++++++++++++++++++++
Amk/lex.c | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/main.c | 287+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/match.c | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Amk/mk | 0
Amk/mk.1 | 691+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/mk.c | 234+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/mk.h | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/mkfile.test | 12++++++++++++
Amk/parse.c | 318+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/rc.c | 194+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/recipe.c | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/rule.c | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/run.c | 296+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/sh.c | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/shell.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/shprint.c | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/symtab.c | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/sys.h | 5+++++
Amk/sys.std.h | 27+++++++++++++++++++++++++++
Amk/unix.c | 341+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/var.c | 41+++++++++++++++++++++++++++++++++++++++++
Amk/varsub.c | 252+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amk/word.c | 189+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Astd.mk | 35+++++++++++++++++++++++++++++++++++
Ayacc.mk | 40++++++++++++++++++++++++++++++++++++++++
284 files changed, 24356 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,6 @@ +*.o +*.so +*.elf +*.bin +*.tar.xz +build/ diff --git a/README.md b/README.md @@ -0,0 +1 @@ +# mk-standalone diff --git a/config.mk b/config.mk @@ -0,0 +1,25 @@ +# Customize to fit your system + +# paths +PREFIX = /usr/local/plan9 +MANPREFIX = ${PREFIX}/share/man + +VERSION = 7 +OBJTYPE = 386 +#OBJTYPE = arm +#OBJTYPE = x86_64 +#OBJTYPE     = sun4u + +# Linux/BSD +CFLAGS += -Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -c -I. -DPREFIX="\"${PREFIX}\"" +CFLAGS += -c -I. -DPLAN9PORT -DPREFIX="\"${PREFIX}\"" +LDFLAGS += -static + +# Solaris +#CFLAGS = -fast -xtarget=ultra -D__sun__ -c -I. -DPREFIX="\"${PREFIX}\"" +#LDFLAGS = -dn + +# compiler +AR = ar rc +CC = cc +YACC = ../yacc/9yacc diff --git a/lib9.log b/lib9.log @@ -0,0 +1,176 @@ +make: Entering directory '/mnt/develop/mk/lib9' +CC sec/sha1block.c +CC sec/sha1.c +CC sec/sha1pickle.c +CC sec/md5block.c +CC sec/md5.c +CC sec/md5pickle.c +CC fmt/dofmt.c +CC fmt/fltfmt.c +CC fmt/fmt.c +CC fmt/fmtfd.c +CC fmt/fmtfdflush.c +CC fmt/fmtlocale.c +CC fmtlock2.c +CC fmt/fmtnull.c +CC fmt/fmtprint.c +CC fmt/fmtquote.c +CC fmt/fmtrune.c +CC fmt/fmtstr.c +CC fmt/fmtvprint.c +CC fmt/fprint.c +CC fmt/nan64.c +CC fmt/print.c +CC fmt/runefmtstr.c +CC fmt/runeseprint.c +CC fmt/runesmprint.c +CC fmt/runesnprint.c +CC fmt/runesprint.c +CC fmt/runevseprint.c +CC fmt/runevsmprint.c +CC fmt/runevsnprint.c +CC fmt/seprint.c +CC fmt/smprint.c +CC fmt/snprint.c +CC fmt/sprint.c +CC fmt/strtod.c +CC fmt/vfprint.c +CC fmt/vseprint.c +CC fmt/vsmprint.c +CC fmt/vsnprint.c +CC fmt/charstod.c +CC fmt/pow10.c +CC utf/rune.c +CC utf/runestrcat.c +CC utf/runestrchr.c +CC utf/runestrcmp.c +CC utf/runestrcpy.c +CC utf/runestrdup.c +CC utf/runestrlen.c +CC utf/runestrecpy.c +CC utf/runestrncat.c +CC utf/runestrncmp.c +CC utf/runestrncpy.c +CC utf/runestrrchr.c +CC utf/runestrstr.c +CC utf/runetype.c +CC utf/utfecpy.c +CC utf/utflen.c +CC utf/utfnlen.c +CC utf/utfrrune.c +CC utf/utfrune.c +CC utf/utfutf.c +CC bio/bbuffered.c +CC bio/bfildes.c +CC bio/bflush.c +CC bio/bgetc.c +CC bio/bgetrune.c +CC bio/bgetd.c +CC bio/binit.c +CC bio/boffset.c +CC bio/bprint.c +CC bio/bputc.c +CC bio/bputrune.c +CC bio/brdline.c +CC bio/brdstr.c +CC bio/bread.c +CC bio/bseek.c +CC bio/bvprint.c +CC bio/bwrite.c +CC regex/regcomp.c +CC regex/regerror.c +CC regex/regexec.c +CC regex/regsub.c +CC regex/regaux.c +CC regex/rregexec.c +CC regex/rregsub.c +CC _exits.c +CC _p9dialparse.c +CC _p9dir.c +CC announce.c +CC argv0.c +CC atexit.c +CC atoi.c +CC atol.c +CC atoll.c +CC atnotify.c +CC await.c +CC cistrcmp.c +CC cistrncmp.c +CC cistrstr.c +CC cleanname.c +CC create.c +CC ctime.c +CC dial.c +CC dirfstat.c +CC dirfwstat.c +CC dirmodefmt.c +CC dirread.c +CC dirstat.c +CC dirwstat.c +CC dup.c +CC encodefmt.c +CC errstr.c +CC exec.c +CC execl.c +CC exitcode.c +CC fcallfmt.c +CC get9root.c +CC getcallerpc-386.c +CC getenv.c +CC getfields.c +CC getnetconn.c +CC getns.c +CC getuser.c +CC getwd.c +CC jmp.c +CC lrand.c +CC lnrand.c +CC main.c +CC malloc.c +CC malloctag.c +CC mallocz.c +CC nan.c +CC needsrcquote.c +CC needstack.c +CC netmkaddr.c +CC notify.c +CC nrand.c +CC nulldir.c +CC open.c +CC opentemp.c +CC pin.c +CC pipe.c +CC post9p.c +CC postnote.c +CC qlock.c +CC quote.c +CC rand.c +CC read9pmsg.c +CC readcons.c +CC readn.c +CC rfork.c +CC searchpath.c +CC seek.c +CC sendfd.c +CC sleep.c +CC strdup.c +CC strecpy.c +CC sysfatal.c +CC syslog.c +CC sysname.c +CC time.c +CC tm2sec.c +CC tokenize.c +CC truerand.c +CC u16.c +CC u32.c +CC u64.c +CC unsharp.c +CC wait.c +CC waitpid.c +CC write.c +CC zoneinfo.c +AR lib9 +built lib9 +make: Leaving directory '/mnt/develop/mk/lib9' 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,231 @@ +# lib9 - unix port from plan9 lib9 + +include ../config.mk + +LIB=lib9.a +TARG=lib9 + +# following objects are not compiled for several reasons +# crypt.o +# netcrypt.o +# convD2M.o +# convM2D.o +# convM2S.o +# convS2M.o + +SECFILES=\ + sec/sha1block.o\ + sec/sha1.o\ + sec/sha1pickle.o\ + sec/md5block.o\ + sec/md5.o\ + sec/md5pickle.o\ + +NUM=\ + fmt/charstod.o\ + fmt/pow10.o\ + + +FMTOFILES=\ + fmt/dofmt.o\ + fmt/fltfmt.o\ + fmt/fmt.o\ + fmt/fmtfd.o\ + fmt/fmtfdflush.o\ + fmt/fmtlocale.o\ + fmtlock2.o\ + fmt/fmtnull.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\ + $(NUM)\ + +UTFOFILES=\ + 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\ + +BIOFILES=\ + bio/bbuffered.o\ + bio/bfildes.o\ + bio/bflush.o\ + bio/bgetc.o\ + bio/bgetrune.o\ + bio/bgetd.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\ + +REGEXFILES=\ + regex/regcomp.o\ + regex/regerror.o\ + regex/regexec.o\ + regex/regsub.o\ + regex/regaux.o\ + regex/rregexec.o\ + regex/rregsub.o\ + +LIB9OFILES=\ + _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\ + create.o\ + ctime.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\ + exitcode.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\ + pin.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\ + tm2sec.o\ + tokenize.o\ + truerand.o\ + u16.o\ + u32.o\ + u64.o\ + unsharp.o\ + wait.o\ + waitpid.o\ + write.o\ + zoneinfo.o\ + +OFILES=\ + $(SECFILES)\ + $(FMTOFILES)\ + $(UTFOFILES)\ + $(BIOFILES)\ + $(REGEXFILES)\ + $(LIB9OFILES) + +all: ${LIB} + @echo built lib9 + +install: + @mkdir -p ${DESTDIR}${PREFIX}/lib + @cp -f ${LIB} ${DESTDIR}${PREFIX}/lib/ + +uninstall: + rm -f ${DESTDIR}${PREFIX}/lib/${LIB} + +${LIB}: ${OFILES} + @echo AR ${TARG} + @${AR} ${LIB} ${OFILES} + +.c.o: + @echo CC $< + @${CC} -o $@ ${CFLAGS} -Isec -I${PREFIX}/include $< + +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 == 0 || *s == 0) + _exit(0); + _exit(exitcode(s)); +} diff --git a/lib9/_p9dialparse.c b/lib9/_p9dialparse.c @@ -0,0 +1,184 @@ +#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, + "udp", "dns", 53, + "tcp", "dns", 53, +}; + +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; + } + memmove(pip, addr, 4); + 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; + + *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,243 @@ +#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(__APPLE__) +#define _HAVESTGEN +#include <sys/disk.h> +static vlong +disksize(int fd, struct stat *st) +{ + u64int bc; + u32int bs; + + bs = 0; + bc = 0; + ioctl(fd, DKIOCGETBLOCKSIZE, &bs); + ioctl(fd, DKIOCGETBLOCKCOUNT, &bc); + if(bs >0 && bc > 0) + return bc*bs; + return 0; +} + +#elif defined(__FreeBSD__) +#define _HAVESTGEN +#include <sys/disk.h> +#include <sys/disklabel.h> +#include <sys/ioctl.h> +static vlong +disksize(int fd, struct stat *st) +{ + off_t mediasize; + + if(ioctl(fd, DIOCGMEDIASIZE, &mediasize) >= 0) + return mediasize; + return 0; +} + +#elif defined(__OpenBSD__) +#define _HAVESTGEN +#include <sys/disklabel.h> +#include <sys/ioctl.h> +#include <sys/dkio.h> +static vlong +disksize(int fd, struct stat *st) +{ + struct disklabel lab; + int n; + + if(!S_ISCHR(st->st_mode)) + return 0; + if(ioctl(fd, DIOCGDINFO, &lab) < 0) + return 0; + n = minor(st->st_rdev)&7; + if(n >= lab.d_npartitions) + return 0; + return (vlong)lab.d_partitions[n].p_size * lab.d_secsize; +} + +#elif 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, struct stat *st) +{ + 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 l*512; + if(ioctl(fd, HDIO_GETGEO, &geo) >= 0) + return (vlong)geo.heads*geo.sectors*geo.cylinders*512; + return 0; +} + +#else +static vlong +disksize(int fd, struct stat *st) +{ + return 0; +} +#endif + +int _p9usepwlibrary = 1; +/* + * 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 if(_p9usepwlibrary){ + p = getpwuid(st->st_uid); + uid = st->st_uid; + } + if(p == nil || st->st_uid != uid || p->pw_uid != uid){ + 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 if(_p9usepwlibrary){ + g = getgrgid(st->st_gid); + gid = st->st_gid; + } + if(g == nil || st->st_gid != gid || g->gr_gid != gid){ + 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 = st->st_ino; + /* + * do not include st->st_dev in path, because + * automounters give the same file system different + * st_dev values for successive mounts, causing + * spurious write warnings in acme and sam. + d->qid.path |= (uvlong)st->st_dev<<32; + */ +#ifdef _HAVESTGEN + d->qid.vers = st->st_gen; +#endif + if(d->qid.vers == 0) + d->qid.vers = st->st_mtime + st->st_ctime; + d->mode = st->st_mode&0777; + d->atime = st->st_atime; + d->mtime = st->st_mtime; + d->length = st->st_size; + + if(S_ISLNK(lst->st_mode)){ /* yes, lst not st */ + d->mode |= DMSYMLINK; + d->length = lst->st_size; + } + else if(S_ISDIR(st->st_mode)){ + d->length = 0; + d->mode |= DMDIR; + d->qid.type = QTDIR; + } + else if(S_ISFIFO(st->st_mode)) + d->mode |= DMNAMEDPIPE; + else if(S_ISSOCK(st->st_mode)) + d->mode |= DMSOCKET; + else if(S_ISBLK(st->st_mode)){ + d->mode |= DMDEVICE; + d->qid.path = ('b'<<16)|st->st_rdev; + } + else if(S_ISCHR(st->st_mode)){ + d->mode |= DMDEVICE; + d->qid.path = ('c'<<16)|st->st_rdev; + } + /* fetch real size for disks */ + if(S_ISBLK(lst->st_mode) || S_ISCHR(lst->st_mode)){ + if((fd = open(name, O_RDONLY)) >= 0){ + d->length = disksize(fd, st); + close(fd); + } + } + } + + 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 = PREFIX; + } + + 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,56 @@ +#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)(); + } + if(s == 0 || *s == 0) + exit(0); + exit(exitcode(s)); +} 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/auth.h b/lib9/auth.h @@ -0,0 +1,169 @@ +#ifndef __AUTH_H__ +#define __AUTH_H__ 1 + +#ifdef __cplusplus +extern "C" { +#endif +/* +#pragma src "/sys/src/libauth" +#pragma lib "libauth.a" +*/ +AUTOLIB(auth) + +/* + * Interface for typical callers. + */ + +typedef struct AuthInfo AuthInfo; +typedef struct Chalstate Chalstate; +typedef struct Chapreply Chapreply; +typedef struct MSchapreply MSchapreply; +typedef struct UserPasswd UserPasswd; +typedef struct AuthRpc AuthRpc; + +struct CFid; + +enum +{ + MAXCHLEN= 256, /* max challenge length */ + MAXNAMELEN= 256, /* maximum name length */ + MD5LEN= 16, + + ARok = 0, /* rpc return values */ + ARdone, + ARerror, + ARneedkey, + ARbadkey, + ARwritenext, + ARtoosmall, + ARtoobig, + ARrpcfailure, + ARphase, + + AuthRpcMax = 4096 +}; + +struct AuthRpc +{ + int afd; + struct CFid *afid; + char ibuf[AuthRpcMax]; + char obuf[AuthRpcMax]; + char *arg; + uint narg; +}; + +struct AuthInfo +{ + char *cuid; /* caller id */ + char *suid; /* server id */ + char *cap; /* capability (only valid on server side) */ + int nsecret; /* length of secret */ + uchar *secret; /* secret */ +}; + +struct Chalstate +{ + char *user; + char chal[MAXCHLEN]; + int nchal; + void *resp; + int nresp; + +/* for implementation only */ + AuthRpc *rpc; /* to factotum */ + char userbuf[MAXNAMELEN]; /* temp space if needed */ + int userinchal; /* user was sent to obtain challenge */ +}; + +struct Chapreply /* for protocol "chap" */ +{ + uchar id; + char resp[MD5LEN]; +}; + +struct MSchapreply /* for protocol "mschap" */ +{ + char LMresp[24]; /* Lan Manager response */ + char NTresp[24]; /* NT response */ +}; + +struct UserPasswd +{ + char *user; + char *passwd; +}; + +extern int newns(char*, char*); +extern int addns(char*, char*); + +extern int noworld(char*); +extern int amount(int, char*, int, char*); + +/* these two may get generalized away -rsc */ +extern int login(char*, char*, char*); +extern int httpauth(char*, char*); + +typedef struct Attr Attr; +enum { + AttrNameval, /* name=val -- when matching, must have name=val */ + AttrQuery, /* name? -- when matching, must be present */ + AttrDefault /* name:=val -- when matching, if present must match INTERNAL */ +}; +struct Attr +{ + int type; + Attr *next; + char *name; + char *val; +}; + +typedef int AuthGetkey(char*); + +int _attrfmt(Fmt*); +Attr *_copyattr(Attr*); +Attr *_delattr(Attr*, char*); +Attr *_findattr(Attr*, char*); +void _freeattr(Attr*); +Attr *_mkattr(int, char*, char*, Attr*); +Attr *_parseattr(char*); +char *_strfindattr(Attr*, char*); +/* +#pragma varargck type "A" Attr* +*/ + +extern AuthInfo* fauth_proxy(int, AuthRpc *rpc, AuthGetkey *getkey, char *params); +extern AuthInfo* auth_proxy(int fd, AuthGetkey *getkey, char *fmt, ...); +extern AuthInfo* fsfauth_proxy(struct CFid*, AuthRpc *rpc, AuthGetkey *getkey, char *params); +extern AuthInfo* fsauth_proxy(struct CFid*, AuthGetkey *getkey, char *fmt, ...); +extern int auth_getkey(char*); +extern int (*amount_getkey)(char*); +extern void auth_freeAI(AuthInfo *ai); +extern int auth_chuid(AuthInfo *ai, char *ns); +extern Chalstate *auth_challenge(char*, ...); +extern AuthInfo* auth_response(Chalstate*); +extern int auth_respond(void*, uint, char*, uint, void*, uint, AuthGetkey *getkey, char*, ...); +extern void auth_freechal(Chalstate*); +extern AuthInfo* auth_userpasswd(char *user, char *passwd); +extern UserPasswd* auth_getuserpasswd(AuthGetkey *getkey, char*, ...); +extern AuthInfo* auth_getinfo(AuthRpc *rpc); +extern AuthRpc* auth_allocrpc(void); +extern Attr* auth_attr(AuthRpc *rpc); +extern void auth_freerpc(AuthRpc *rpc); +extern uint auth_rpc(AuthRpc *rpc, char *verb, void *a, int n); +extern int auth_wep(char*, char*, ...); + +extern struct CFsys* fsamount(int fd, char *aname); +extern struct CFsys* nsamount(char *name, char *aname); + + +/* +#pragma varargck argpos auth_proxy 3 +#pragma varargck argpos auth_challenge 1 +#pragma varargck argpos auth_respond 3 +#pragma varargck argpos auth_getuserpasswd 2 +*/ +#ifdef __cplusplus +} +#endif +#endif 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,91 @@ +#ifndef _BIO_H_ +#define _BIO_H_ 1 +#if defined(__cplusplus) +extern "C" { +#endif + +#ifdef AUTOLIB +AUTOLIB(bio) +#endif + +#include <fcntl.h> /* for O_RDONLY, O_WRONLY */ + +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 */ + long long 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*); +long long 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); +long long Bseek(Biobuf*, long long, 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,20 @@ +#include "lib9.h" +#include <bio.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,33 @@ +#include "lib9.h" +#include <bio.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,53 @@ +#include "lib9.h" +#include <bio.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,36 @@ +#include "lib9.h" +#include <bio.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[UTFmax]; + + 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,153 @@ +#include "lib9.h" +#include <bio.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, mode); + if(f < 0) + return 0; + break; + + case OWRITE: + f = create(name, mode, 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,25 @@ +#include "lib9.h" +#include <bio.h> + +vlong +Boffset(Biobuf *bp) +{ + vlong 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[UTFmax]; + 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,94 @@ +#include "lib9.h" +#include <bio.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,111 @@ +#include "lib9.h" +#include <bio.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,45 @@ +#include "lib9.h" +#include <bio.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,60 @@ +#include "lib9.h" +#include <bio.h> + +long long +Bseek(Biobuf *bp, long long offset, int base) +{ + vlong 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 = seek(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,38 @@ +#include "lib9.h" +#include <bio.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; + fmtlocaleinit(&f, nil, nil, nil); + n = fmtvprint(&f, fmt, arg); + bp->ocount = (char*)f.to - (char*)f.stop; + if(n == 0) + n = f.nfmt; + return n; +} diff --git a/lib9/bio/bwrite.c b/lib9/bio/bwrite.c @@ -0,0 +1,38 @@ +#include "lib9.h" +#include <bio.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,26 @@ +#define _FILE_OFFSET_BITS 64 +#define _LARGEFILE64_SOURCE + +#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; + +#define seek(fd, offset, whence) lseek(fd, offset, whence) +#define create(name, mode, perm) creat(name, perm) + 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,132 @@ +#include <u.h> +#include <libc.h> +#include <fcall.h> + +uint +sizeD2Mu(Dir *d, int dotu) +{ + char *sv[5]; + int i, ns, nstr, fixlen; + + sv[0] = d->name; + sv[1] = d->uid; + sv[2] = d->gid; + sv[3] = d->muid; + + fixlen = STATFIXLEN; + nstr = 4; + if(dotu){ + fixlen = STATFIXLENU; + sv[4] = d->ext; + nstr = 5; + } + + ns = 0; + for(i = 0; i < nstr; i++) + if(sv[i]) + ns += strlen(sv[i]); + + return fixlen + ns; +} + +uint +sizeD2M(Dir *d) +{ + return sizeD2Mu(d, 0); +} + +uint +convD2Mu(Dir *d, uchar *buf, uint nbuf, int dotu) +{ + uchar *p, *ebuf; + char *sv[5]; + int i, ns, nsv[5], ss, nstr, fixlen; + + 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; + + fixlen = STATFIXLEN; + nstr = 4; + if(dotu){ + fixlen = STATFIXLENU; + sv[4] = d->ext; + nstr = 5; + } + + ns = 0; + for(i = 0; i < nstr; i++){ + if(sv[i]) + nsv[i] = strlen(sv[i]); + else + nsv[i] = 0; + ns += nsv[i]; + } + + ss = fixlen + 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 < nstr; 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(dotu){ + PBIT32(p, d->uidnum); + p += BIT32SZ; + PBIT32(p, d->gidnum); + p += BIT32SZ; + PBIT32(p, d->muidnum); + p += BIT32SZ; + } + + if(ss != p - buf) + return 0; + + return p - buf; +} + +uint +convD2M(Dir *d, uchar *buf, uint nbuf) +{ + return convD2Mu(d, buf, nbuf, 0); +} diff --git a/lib9/convM2D.c b/lib9/convM2D.c @@ -0,0 +1,130 @@ +#include <u.h> +#include <libc.h> +#include <fcall.h> + +int +statchecku(uchar *buf, uint nbuf, int dotu) +{ + uchar *ebuf; + int i, nstr; + + ebuf = buf + nbuf; + + if(nbuf < STATFIXLEN || nbuf != BIT16SZ + GBIT16(buf)) + return -1; + + buf += STATFIXLEN - 4 * BIT16SZ; + + nstr = 4; + if(dotu) + nstr = 5; + for(i = 0; i < nstr; i++){ + if(buf + BIT16SZ > ebuf) + return -1; + buf += BIT16SZ + GBIT16(buf); + } + + if(dotu) + buf += 3*BIT32SZ; + + if(buf != ebuf) + return -1; + + return 0; +} + +int +statcheck(uchar *buf, uint nbuf) +{ + return statchecku(buf, nbuf, 0); +} + +static char nullstring[] = ""; + +uint +convM2Du(uchar *buf, uint nbuf, Dir *d, char *strs, int dotu) +{ + uchar *p, *ebuf; + char *sv[5]; + int i, ns, nstr; + + 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; + + nstr = 4; + if(dotu) + nstr = 5; + for(i = 0; i < nstr; 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(dotu){ + if(p + BIT32SZ*3 > ebuf) + return 0; + d->uidnum = GBIT32(p); + p += BIT32SZ; + d->gidnum = GBIT32(p); + p += BIT32SZ; + d->muidnum = GBIT32(p); + p += BIT32SZ; + } + + if(strs){ + d->name = sv[0]; + d->uid = sv[1]; + d->gid = sv[2]; + d->muid = sv[3]; + d->ext = nullstring; + if(dotu) + d->ext = sv[4]; + }else{ + d->name = nullstring; + d->uid = nullstring; + d->gid = nullstring; + d->muid = nullstring; + d->ext = nullstring; + } + + return p - buf; +} + +uint +convM2D(uchar *buf, uint nbuf, Dir *d, char *strs) +{ + return convM2Du(buf, nbuf, d, strs, 0); +} diff --git a/lib9/convM2S.c b/lib9/convM2S.c @@ -0,0 +1,352 @@ +#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 +convM2Su(uchar *ap, uint nap, Fcall *f, int dotu) +{ + 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; + f->uidnum = NOUID; + if(dotu){ + if(p+BIT32SZ > ep) + return 0; + f->uidnum = GBIT32(p); + p += BIT32SZ; + } + 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; + f->uidnum = NOUID; + if(dotu){ + if(p+BIT32SZ > ep) + return 0; + f->uidnum = GBIT32(p); + p += BIT32SZ; + } + 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; + if(dotu) + p = gstring(p, ep, &f->extension); + 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); + f->errornum = 0; + if(dotu){ + if(p+BIT32SZ > ep) + return 0; + f->errornum = GBIT32(p); + p += BIT32SZ; + } + 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; +} + +uint +convM2S(uchar *ap, uint nap, Fcall *f) +{ + return convM2Su(ap, nap, f, 0); +} diff --git a/lib9/convS2M.c b/lib9/convS2M.c @@ -0,0 +1,435 @@ +#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 +sizeS2Mu(Fcall *f, int dotu) +{ + 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); + if(dotu) + n += BIT32SZ; + break; + + case Tattach: + n += BIT32SZ; + n += BIT32SZ; + n += stringsz(f->uname); + n += stringsz(f->aname); + if(dotu) + n += BIT32SZ; + 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; + if(dotu) + n += stringsz(f->extension); + 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); + if(dotu) + n += BIT32SZ; + 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 +sizeS2M(Fcall *f) +{ + return sizeS2Mu(f, 0); +} + +uint +convS2Mu(Fcall *f, uchar *ap, uint nap, int dotu) +{ + uchar *p; + uint i, size; + + size = sizeS2Mu(f, dotu); + 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); + if(dotu){ + f->uidnum = NOUID; + PBIT32(p, f->uidnum); + p += BIT32SZ; + } + 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); + if(dotu){ + f->uidnum = NOUID; + PBIT32(p, f->uidnum); + p += BIT32SZ; + } + 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; + if(dotu) + p = pstring(p, f->extension); + 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); + if(dotu){ + PBIT32(p, f->errornum); + p += BIT32SZ; + } + 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; +} + +uint +convS2M(Fcall *f, uchar *ap, uint nap) +{ + return convS2Mu(f, ap, nap, 0); +} 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/crypt.c b/lib9/crypt.c @@ -0,0 +1,68 @@ +/* + * Data Encryption Standard + * D.P.Mitchell 83/06/08. + * + * block_cipher(key, block, decrypting) + * + * these routines use the non-standard 7 byte format + * for DES keys. + */ +#include <u.h> +#include <libc.h> +#include <auth.h> +#include <libsec.h> + +/* + * destructively encrypt the buffer, which + * must be at least 8 characters long. + */ +int +encrypt(void *key, void *vbuf, int n) +{ + ulong ekey[32]; + uchar *buf; + int i, r; + + if(n < 8) + return 0; + key_setup(key, ekey); + buf = vbuf; + n--; + r = n % 7; + n /= 7; + for(i = 0; i < n; i++){ + block_cipher(ekey, buf, 0); + buf += 7; + } + if(r) + block_cipher(ekey, buf - 7 + r, 0); + return 1; +} + +/* + * destructively decrypt the buffer, which + * must be at least 8 characters long. + */ +int +decrypt(void *key, void *vbuf, int n) +{ + ulong ekey[128]; + uchar *buf; + int i, r; + + if(n < 8) + return 0; + key_setup(key, ekey); + buf = vbuf; + n--; + r = n % 7; + n /= 7; + buf += n * 7; + if(r) + block_cipher(ekey, buf - 7 + r, 1); + for(i = 0; i < n; i++){ + buf -= 7; + block_cipher(ekey, buf, 1); + } + return 1; +} diff --git a/lib9/ctime.c b/lib9/ctime.c @@ -0,0 +1,181 @@ +/* + * This routine converts time as follows. + * The epoch is 0000 Jan 1 1970 GMT. + * The argument time is in seconds since then. + * The localtime(t) entry returns a pointer to an array + * containing + * + * seconds (0-59) + * minutes (0-59) + * hours (0-23) + * day of month (1-31) + * month (0-11) + * year-1970 + * weekday (0-6, Sun is 0) + * day of the year + * daylight savings flag + * + * The routine gets the daylight savings time from the environment. + * + * asctime(tvec)) + * where tvec is produced by localtime + * returns a ptr to a character string + * that has the ascii time in the form + * + * \\ + * Thu Jan 01 00:00:00 GMT 1970n0 + * 012345678901234567890123456789 + * 0 1 2 + * + * ctime(t) just calls localtime, then asctime. + */ + +#include <u.h> +#include <libc.h> + +#include "zoneinfo.h" + +static char dmsize[12] = +{ + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +#define dysize ctimedysize +static int dysize(int); +static void ct_numb(char*, int); + +char* +ctime(long t) +{ + return asctime(localtime(t)); +} + +Tm* +localtime(long tim) +{ + Tinfo ti; + Tm *ct; + + if (zonelookuptinfo(&ti, tim)!=-1) { + ct = gmtime(tim+ti.tzoff); + strncpy(ct->zone, ti.zone, sizeof ct->zone); + ct->zone[sizeof ct->zone-1] = 0; + ct->tzoff = ti.tzoff; + return ct; + } + return gmtime(tim); +} + +Tm* +gmtime(long tim) +{ + int d0, d1; + long hms, day; + static Tm xtime; + + /* + * break initial number into days + */ + hms = tim % 86400L; + day = tim / 86400L; + if(hms < 0) { + hms += 86400L; + day -= 1; + } + + /* + * generate hours:minutes:seconds + */ + xtime.sec = hms % 60; + d1 = hms / 60; + xtime.min = d1 % 60; + d1 /= 60; + xtime.hour = d1; + + /* + * day is the day number. + * generate day of the week. + * The addend is 4 mod 7 (1/1/1970 was Thursday) + */ + + xtime.wday = (day + 7340036L) % 7; + + /* + * year number + */ + if(day >= 0) + for(d1 = 1970; day >= dysize(d1); d1++) + day -= dysize(d1); + else + for (d1 = 1970; day < 0; d1--) + day += dysize(d1-1); + xtime.year = d1-1900; + xtime.yday = d0 = day; + + /* + * generate month + */ + + if(dysize(d1) == 366) + dmsize[1] = 29; + for(d1 = 0; d0 >= dmsize[d1]; d1++) + d0 -= dmsize[d1]; + dmsize[1] = 28; + xtime.mday = d0 + 1; + xtime.mon = d1; + strcpy(xtime.zone, "GMT"); + return &xtime; +} + +char* +asctime(Tm *t) +{ + 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; + 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; +} + +static +int +dysize(int y) +{ + + if(y%4 == 0 && (y%100 != 0 || y%400 == 0)) + return 366; + return 365; +} + +static +void +ct_numb(char *cp, int n) +{ + + cp[0] = ' '; + if(n >= 10) + cp[0] = (n/10)%10 + '0'; + cp[1] = n%10 + '0'; +} + 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,149 @@ +#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, "unix") != 0 && host == 0){ + werrstr("invalid dial address 0.0.0.0 (aka *)"); + 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); + + 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); + } + + n = 1; + setsockopt(s, SOL_SOCKET, SO_BROADCAST, &n, sizeof n); + if(host != 0){ + memset(&sa, 0, sizeof sa); + memmove(&sa.sin_addr, &host, 4); + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + 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; + } + /* Allow regular files in addition to Unix sockets. */ + if((s = open(unix, ORDWR)) >= 0) + return s; + 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,66 @@ +#define NOPLAN9DEFINES +#include <u.h> +#include <libc.h> +#include <sys/time.h> +#include <sys/stat.h> + +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__linux__) +/* do nothing -- futimes exists and is fine */ + +# if defined(__UCLIBC__) +/* use futimesat */ +static int +futimes(int fd, struct timeval *tv) +{ + return futimesat(fd, 0, tv); +} + +# endif +#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; + } + if(~dir->length != 0){ + if(ftruncate(fd, dir->length) < 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,212 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> +#include <sys/stat.h> +#include <dirent.h> + +#if defined (__linux__) +# include <sys/syscall.h> +# if defined (_LARGEFILE64_SOURCE) +# define getdents SYS_getdents64 +# else +# define getdents SYS_getdents +# endif +#endif + +extern int _p9dir(struct stat*, struct stat*, char*, Dir*, char**, char*); + +#if defined(__linux__) +static int +mygetdents(int fd, struct dirent *buf, int n) { + return syscall (getdents, fd, (void*) buf, n); +} +#elif defined(__APPLE__) || defined(__FreeBSD__) +static int +mygetdents(int fd, struct dirent *buf, int n) +{ + long off; + return getdirentries(fd, (void*)buf, n, &off); +} +#elif defined(__OpenBSD__) +#include <sys/param.h> +# if OpenBSD < 201405 /* for OpenBSD 5.4 and earlier */ +static int +mygetdents(int fd, struct dirent *buf, int n) +{ + off_t off; + return getdirentries(fd, (void*)buf, n, &off); +} +# else +static int +mygetdents(int fd, struct dirent *buf, int n) +{ + return getdents(fd, (void*)buf, n); +} +# endif +#elif defined(__sun__) || defined(__NetBSD__) +static int +mygetdents(int fd, struct dirent *buf, int n) +{ + return getdents(fd, (void*)buf, n); +} +#elif defined(__AIX__) +static int +mygetdents(int fd, struct dirent *buf, int n) +{ + return getdirent(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,31 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> +#include <sys/time.h> +#include <utime.h> +#include <sys/stat.h> + +int +dirwstat(char *file, Dir *dir) +{ + int ret; + struct utimbuf ub; + + /* BUG handle more */ + ret = 0; + if(~dir->mode != 0){ + if(chmod(file, dir->mode) < 0) + ret = -1; + } + if(~dir->mtime != 0){ + ub.actime = dir->mtime; + ub.modtime = dir->mtime; + if(utime(file, &ub) < 0) + ret = -1; + } + if(~dir->length != 0){ + if(truncate(file, dir->length) < 0) + ret = -1; + } + return ret; +} 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,76 @@ +#include <lib9.h> + +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/exitcode.c b/lib9/exitcode.c @@ -0,0 +1,9 @@ +#include <u.h> +#include <libc.h> + +int +exitcode(char *s) +{ + return 1; +} + diff --git a/lib9/fcall.h b/lib9/fcall.h @@ -0,0 +1,149 @@ +#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 */ + + /* 9P2000.u extensions */ + int errornum; /* Rerror */ + int uidnum; /* Tattach, Tauth */ + char *extension; /* Tcreate */ +} Fcall; + + +#define GBIT8(p) ((p)[0]) +#define GBIT16(p) ((p)[0]|((p)[1]<<8)) +#define GBIT32(p) ((u32int)((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))) +#define GBIT64(p) ((u32int)((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 STATFIXLENU (STATFIXLEN+BIT16SZ+3*BIT32SZ) /* for 9P2000.u */ + +#define NOTAG (ushort)~0U /* Dummy tag */ +#define NOFID (u32int)~0U /* Dummy fid */ +#define NOUID (-1) /* Dummy uid */ +#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*); + +uint convM2Su(uchar*, uint, Fcall*, int); +uint convS2Mu(Fcall*, uchar*, uint, int); +uint sizeS2Mu(Fcall*, int); + +int statchecku(uchar *abuf, uint nbuf, int); +uint convM2Du(uchar*, uint, Dir*, char*, int); +uint convD2Mu(Dir*, uchar*, uint, int); +uint sizeD2Mu(Dir*, int); + +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->stat == nil || 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->stat == nil || 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,116 @@ +#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 */ + Rune r; /* % format Rune */ + int width; + int prec; + unsigned long flags; + char *decimal; /* decimal point; cannot be "" */ + + /* For %'d */ + char *thousands; /* separator for thousands */ + + /* + * Each char is an integer indicating #digits before next separator. Values: + * \xFF: no more grouping (or \x7F; defined to be CHAR_MAX in POSIX) + * \x00: repeat previous indefinitely + * \x**: count that many + */ + char *grouping; /* descriptor of separator placement */ +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtApost = FmtSign << 1, + FmtZero = FmtApost << 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 fmtnullinit(Fmt*); +void fmtlocaleinit(Fmt*, char*, char*, char*); +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,22 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson, + * with contributions from Mike Burrows and Sean Dorward. + * + * Copyright (c) 2002-2006 by Lucent Technologies. + * Portions Copyright (c) 2004 Google Inc. + * + * 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 + * NOR GOOGLE INC 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@swtch.com>. + diff --git a/lib9/fmt/NOTICE b/lib9/fmt/NOTICE @@ -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/README b/lib9/fmt/README @@ -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,73 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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,617 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +/* Copyright (c) 2004 Google Inc.; see LICENSE */ + +#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 characters (if FmtWidth is set) + */ +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; + fl = f->flags; + w = 0; + if(fl & FmtWidth) + w = f->width; + 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; + fl = f->flags; + w = 0; + if(fl & FmtWidth) + w = f->width; + 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; + + 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){ +#ifdef PLAN9PORT + Rune r; + i = 0; + for(j=0; j<f->prec && s[i]; j++) + i += chartorune(&r, s+i); +#else + /* ANSI requires precision in bytes, not Runes */ + for(i=0; i<f->prec; i++) + if(s[i] == 0) + break; + j = utfnlen(s, i); /* won't print partial at end */ +#endif + 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[140], *p, *conv; + /* 140: for 64 bits of binary + 3-byte sep every 4 digits */ + uvlong vu; + ulong u; + int neg, base, i, n, fl, w, isv; + int ndig, len, excess, bytelen; + char *grouping; + char *thousands; + + neg = 0; + fl = f->flags; + isv = 0; + vu = 0; + u = 0; +#ifndef PLAN9PORT + /* + * Unsigned verbs for ANSI C + */ + switch(f->r){ + case 'o': + case 'p': + case 'u': + case 'x': + case 'X': + 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"; + grouping = "\4"; /* for hex, octal etc. (undefined by spec but nice) */ + thousands = f->thousands; + switch(f->r){ + case 'd': + case 'i': + case 'u': + base = 10; + grouping = f->grouping; + break; + case 'X': + conv = "0123456789ABCDEF"; + /* fall through */ + case 'x': + base = 16; + thousands = ":"; + break; + case 'b': + base = 2; + thousands = ":"; + 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; /* in runes */ + excess = 0; /* number of bytes > number runes */ + ndig = 0; + len = utflen(thousands); + bytelen = strlen(thousands); + if(isv){ + while(vu){ + i = vu % base; + vu /= base; + if((fl & FmtComma) && n % 4 == 3){ + *p-- = ','; + n++; + } + if((fl & FmtApost) && __needsep(&ndig, &grouping)){ + n += len; + excess += bytelen - len; + p -= bytelen; + memmove(p+1, thousands, bytelen); + } + *p-- = conv[i]; + n++; + } + }else{ + while(u){ + i = u % base; + u /= base; + if((fl & FmtComma) && n % 4 == 3){ + *p-- = ','; + n++; + } + if((fl & FmtApost) && __needsep(&ndig, &grouping)){ + n += len; + excess += bytelen - len; + p -= bytelen; + memmove(p+1, thousands, bytelen); + } + *p-- = conv[i]; + n++; + } + } + if(n == 0){ + /* + * "The result of converting a zero value with + * a precision of zero is no characters." - ANSI + * + * "For o conversion, # increases the precision, if and only if + * necessary, to force the first digit of the result to be a zero + * (if the value and precision are both 0, a single 0 is printed)." - ANSI + */ + if(!(fl & FmtPrec) || f->prec != 0 || (f->r == 'o' && (fl & FmtSharp))){ + *p-- = '0'; + n = 1; + if(fl & FmtApost) + __needsep(&ndig, &grouping); + } + + /* + * Zero values don't get 0x. + */ + if(f->r == 'x' || f->r == 'X') + fl &= ~FmtSharp; + } + for(w = f->prec; n < w && p > buf+3; n++){ + if((fl & FmtApost) && __needsep(&ndig, &grouping)){ + n += len; + excess += bytelen - len; + p -= bytelen; + memmove(p+1, thousands, bytelen); + } + *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))){ + w = 0; + if(fl & FmtWidth) + w = f->width; + for(; n < w && p > buf+3; n++){ + if((fl & FmtApost) && __needsep(&ndig, &grouping)){ + n += len; + excess += bytelen - len; + p -= bytelen; + memmove(p+1, thousands, bytelen); + } + *p-- = '0'; + } + f->flags &= ~FmtWidth; + } + 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 + excess); +} + +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 |= FmtApost; + 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[2+UTFmax]; + int n; + + x[0] = '%'; + n = 1 + runetochar(x+1, &f->r); + x[n++] = '%'; + f->prec = n; + __fmtcpy(f, (const void*)x, n, n); + return 0; +} diff --git a/lib9/fmt/dorfmt.c b/lib9/fmt/dorfmt.c @@ -0,0 +1,50 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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 */ + +/* BUG: THIS FILE IS NOT UPDATED TO THE NEW SPEC */ +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 = (Rune*)f->to; + rs = (Rune*)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 = (char*)f->to; + s = (char*)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 = (Rune*)__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,16 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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,668 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include <stdio.h> +#include <math.h> +#include <float.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <stdarg.h> +#include <fmt.h> +#include <assert.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" +#include "nan.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 npows10 ((int)(sizeof(pows10)/sizeof(pows10[0]))) +#define pow10(x) fmtpow10(x) + +static double +pow10(int n) +{ + double d; + int neg; + + neg = 0; + if(n < 0){ + neg = 1; + n = -n; + } + + if(n < npows10) + d = pows10[n]; + else{ + d = pows10[npows10-1]; + for(;;){ + n -= npows10 - 1; + if(n < npows10){ + d *= pows10[n]; + break; + } + d *= pows10[npows10 - 1]; + } + } + if(neg) + return 1./d; + return d; +} + +/* + * add 1 to the decimal integer string a of length n. + * if 99999 overflows into 10000, return 1 to tell caller + * to move the virtual decimal point. + */ +static int +xadd1(char *a, int n) +{ + char *b; + int c; + + if(n < 0 || n > NSIGNIF) + return 0; + for(b = a+n-1; b >= a; b--) { + c = *b + 1; + if(c <= '9') { + *b = c; + return 0; + } + *b = '0'; + } + /* + * need to overflow adding digit. + * shift number down and insert 1 at beginning. + * decimal is known to be 0s or we wouldn't + * have gotten this far. (e.g., 99999+1 => 00000) + */ + a[0] = '1'; + return 1; +} + +/* + * subtract 1 from the decimal integer string a. + * if 10000 underflows into 09999, make it 99999 + * and return 1 to tell caller to move the virtual + * decimal point. this way, xsub1 is inverse of xadd1. + */ +static int +xsub1(char *a, int n) +{ + char *b; + int c; + + if(n < 0 || n > NSIGNIF) + return 0; + for(b = a+n-1; b >= a; b--) { + c = *b - 1; + if(c >= '0') { + if(c == '0' && b == a) { + /* + * just zeroed the top digit; shift everyone up. + * decimal is known to be 9s or we wouldn't + * have gotten this far. (e.g., 10000-1 => 09999) + */ + *b = '9'; + return 1; + } + *b = c; + return 0; + } + *b = '9'; + } + /* + * can't get here. the number a is always normalized + * so that it has a nonzero first digit. + */ + abort(); +} + +/* + * format exponent like sprintf(p, "e%+02d", e) + */ +static void +xfmtexp(char *p, int e, int ucase) +{ + char se[9]; + int i; + + *p++ = ucase ? 'E' : 'e'; + if(e < 0) { + *p++ = '-'; + e = -e; + } else + *p++ = '+'; + i = 0; + while(e) { + se[i++] = e % 10 + '0'; + e /= 10; + } + while(i < 2) + se[i++] = '0'; + while(i > 0) + *p++ = se[--i]; + *p++ = '\0'; +} + +/* + * compute decimal integer m, exp such that: + * f = m*10^exp + * m is as short as possible with losing exactness + * assumes special cases (NaN, +Inf, -Inf) have been handled. + */ +static void +xdtoa(double f, char *s, int *exp, int *neg, int *ns) +{ + int c, d, e2, e, ee, i, ndigit, oerrno; + char tmp[NSIGNIF+10]; + double g; + + oerrno = errno; /* in case strtod smashes errno */ + + /* + * make f non-negative. + */ + *neg = 0; + if(f < 0) { + f = -f; + *neg = 1; + } + + /* + * must handle zero specially. + */ + if(f == 0){ + *exp = 0; + s[0] = '0'; + s[1] = '\0'; + *ns = 1; + return; + } + + /* + * find g,e such that f = g*10^e. + * guess 10-exponent using 2-exponent, then fine tune. + */ + frexp(f, &e2); + e = (int)(e2 * .301029995664); + g = f * pow10(-e); + while(g < 1) { + e--; + g = f * pow10(-e); + } + while(g >= 10) { + e++; + g = f * pow10(-e); + } + + /* + * convert NSIGNIF digits as a first approximation. + */ + for(i=0; i<NSIGNIF; i++) { + d = (int)g; + s[i] = d+'0'; + g = (g-d) * 10; + } + s[i] = 0; + + /* + * adjust e because s is 314159... not 3.14159... + */ + e -= NSIGNIF-1; + xfmtexp(s+NSIGNIF, e, 0); + + /* + * adjust conversion until strtod(s) == f exactly. + */ + for(i=0; i<10; i++) { + g = fmtstrtod(s, nil); + if(f > g) { + if(xadd1(s, NSIGNIF)) { + /* gained a digit */ + e--; + xfmtexp(s+NSIGNIF, e, 0); + } + continue; + } + if(f < g) { + if(xsub1(s, NSIGNIF)) { + /* lost a digit */ + e++; + xfmtexp(s+NSIGNIF, e, 0); + } + continue; + } + break; + } + + /* + * play with the decimal to try to simplify. + */ + + /* + * bump last few digits up to 9 if we can + */ + for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) { + c = s[i]; + if(c != '9') { + s[i] = '9'; + g = fmtstrtod(s, nil); + if(g != f) { + s[i] = c; + break; + } + } + } + + /* + * add 1 in hopes of turning 9s to 0s + */ + if(s[NSIGNIF-1] == '9') { + strcpy(tmp, s); + ee = e; + if(xadd1(tmp, NSIGNIF)) { + ee--; + xfmtexp(tmp+NSIGNIF, ee, 0); + } + g = fmtstrtod(tmp, nil); + if(g == f) { + strcpy(s, tmp); + e = ee; + } + } + + /* + * bump last few digits down to 0 as we can. + */ + for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) { + c = s[i]; + if(c != '0') { + s[i] = '0'; + g = fmtstrtod(s, nil); + if(g != f) { + s[i] = c; + break; + } + } + } + + /* + * remove trailing zeros. + */ + ndigit = NSIGNIF; + while(ndigit > 1 && s[ndigit-1] == '0'){ + e++; + --ndigit; + } + s[ndigit] = 0; + *exp = e; + *ns = ndigit; + errno = oerrno; +} + +#ifdef PLAN9PORT +static char *special[] = { "NaN", "NaN", "+Inf", "+Inf", "-Inf", "-Inf" }; +#else +static char *special[] = { "nan", "NAN", "inf", "INF", "-inf", "-INF" }; +#endif + +int +__efgfmt(Fmt *fmt) +{ + char buf[NSIGNIF+10], *dot, *digits, *p, *s, suf[10], *t; + double f; + int c, chr, dotwid, e, exp, fl, ndigits, neg, newndigits; + int pad, point, prec, realchr, sign, sufwid, ucase, wid, z1, z2; + Rune r, *rs, *rt; + + if(fmt->flags&FmtLong) + f = va_arg(fmt->args, long double); + else + f = va_arg(fmt->args, double); + + /* + * extract formatting flags + */ + fl = fmt->flags; + fmt->flags = 0; + prec = FDEFLT; + if(fl & FmtPrec) + prec = fmt->prec; + chr = fmt->r; + ucase = 0; + switch(chr) { + case 'A': + case 'E': + case 'F': + case 'G': + chr += 'a'-'A'; + ucase = 1; + break; + } + + /* + * pick off special numbers. + */ + if(__isNaN(f)) { + s = special[0+ucase]; + special: + fmt->flags = fl & (FmtWidth|FmtLeft); + return __fmtcpy(fmt, s, strlen(s), strlen(s)); + } + if(__isInf(f, 1)) { + s = special[2+ucase]; + goto special; + } + if(__isInf(f, -1)) { + s = special[4+ucase]; + goto special; + } + + /* + * get exact representation. + */ + digits = buf; + xdtoa(f, digits, &exp, &neg, &ndigits); + + /* + * get locale's decimal point. + */ + dot = fmt->decimal; + if(dot == nil) + dot = "."; + dotwid = utflen(dot); + + /* + * now the formatting fun begins. + * compute parameters for actual fmt: + * + * pad: number of spaces to insert before/after field. + * z1: number of zeros to insert before digits + * z2: number of zeros to insert after digits + * point: number of digits to print before decimal point + * ndigits: number of digits to use from digits[] + * suf: trailing suffix, like "e-5" + */ + realchr = chr; + switch(chr){ + case 'g': + /* + * convert to at most prec significant digits. (prec=0 means 1) + */ + if(prec == 0) + prec = 1; + if(ndigits > prec) { + if(digits[prec] >= '5' && xadd1(digits, prec)) + exp++; + exp += ndigits-prec; + ndigits = prec; + } + + /* + * extra rules for %g (implemented below): + * trailing zeros removed after decimal unless FmtSharp. + * decimal point only if digit follows. + */ + + /* fall through to %e */ + default: + case 'e': + /* + * one significant digit before decimal, no leading zeros. + */ + point = 1; + z1 = 0; + + /* + * decimal point is after ndigits digits right now. + * slide to be after first. + */ + e = exp + (ndigits-1); + + /* + * if this is %g, check exponent and convert prec + */ + if(realchr == 'g') { + if(-4 <= e && e < prec) + goto casef; + prec--; /* one digit before decimal; rest after */ + } + + /* + * compute trailing zero padding or truncate digits. + */ + if(1+prec >= ndigits) + z2 = 1+prec - ndigits; + else { + /* + * truncate digits + */ + assert(realchr != 'g'); + newndigits = 1+prec; + if(digits[newndigits] >= '5' && xadd1(digits, newndigits)) { + /* + * had 999e4, now have 100e5 + */ + e++; + } + ndigits = newndigits; + z2 = 0; + } + xfmtexp(suf, e, ucase); + sufwid = strlen(suf); + break; + + casef: + case 'f': + /* + * determine where digits go with respect to decimal point + */ + if(ndigits+exp > 0) { + point = ndigits+exp; + z1 = 0; + } else { + point = 1; + z1 = 1 + -(ndigits+exp); + } + + /* + * %g specifies prec = number of significant digits + * convert to number of digits after decimal point + */ + if(realchr == 'g') + prec += z1 - point; + + /* + * compute trailing zero padding or truncate digits. + */ + if(point+prec >= z1+ndigits) + z2 = point+prec - (z1+ndigits); + else { + /* + * truncate digits + */ + assert(realchr != 'g'); + newndigits = point+prec - z1; + if(newndigits < 0) { + z1 += newndigits; + newndigits = 0; + } else if(newndigits == 0) { + /* perhaps round up */ + if(digits[0] >= '5'){ + digits[0] = '1'; + newndigits = 1; + goto newdigit; + } + } else if(digits[newndigits] >= '5' && xadd1(digits, newndigits)) { + /* + * digits was 999, is now 100; make it 1000 + */ + digits[newndigits++] = '0'; + newdigit: + /* + * account for new digit + */ + if(z1) /* 0.099 => 0.100 or 0.99 => 1.00*/ + z1--; + else /* 9.99 => 10.00 */ + point++; + } + z2 = 0; + ndigits = newndigits; + } + sufwid = 0; + break; + } + + /* + * if %g is given without FmtSharp, remove trailing zeros. + * must do after truncation, so that e.g. print %.3g 1.001 + * produces 1, not 1.00. sorry, but them's the rules. + */ + if(realchr == 'g' && !(fl & FmtSharp)) { + if(z1+ndigits+z2 >= point) { + if(z1+ndigits < point) + z2 = point - (z1+ndigits); + else{ + z2 = 0; + while(z1+ndigits > point && digits[ndigits-1] == '0') + ndigits--; + } + } + } + + /* + * compute width of all digits and decimal point and suffix if any + */ + wid = z1+ndigits+z2; + if(wid > point) + wid += dotwid; + else if(wid == point){ + if(fl & FmtSharp) + wid += dotwid; + else + point++; /* do not print any decimal point */ + } + wid += sufwid; + + /* + * determine sign + */ + sign = 0; + if(neg) + sign = '-'; + else if(fl & FmtSign) + sign = '+'; + else if(fl & FmtSpace) + sign = ' '; + if(sign) + wid++; + + /* + * compute padding + */ + pad = 0; + if((fl & FmtWidth) && fmt->width > wid) + pad = fmt->width - wid; + if(pad && !(fl & FmtLeft) && (fl & FmtZero)){ + z1 += pad; + point += pad; + pad = 0; + } + + /* + * format the actual field. too bad about doing this twice. + */ + if(fmt->runes){ + if(pad && !(fl & FmtLeft) && __rfmtpad(fmt, pad) < 0) + return -1; + rt = (Rune*)fmt->to; + rs = (Rune*)fmt->stop; + if(sign) + FMTRCHAR(fmt, rt, rs, sign); + while(z1>0 || ndigits>0 || z2>0) { + if(z1 > 0){ + z1--; + c = '0'; + }else if(ndigits > 0){ + ndigits--; + c = *digits++; + }else{ + z2--; + c = '0'; + } + FMTRCHAR(fmt, rt, rs, c); + if(--point == 0) { + for(p = dot; *p; ){ + p += chartorune(&r, p); + FMTRCHAR(fmt, rt, rs, r); + } + } + } + fmt->nfmt += rt - (Rune*)fmt->to; + fmt->to = rt; + if(sufwid && __fmtcpy(fmt, suf, sufwid, sufwid) < 0) + return -1; + if(pad && (fl & FmtLeft) && __rfmtpad(fmt, pad) < 0) + return -1; + }else{ + if(pad && !(fl & FmtLeft) && __fmtpad(fmt, pad) < 0) + return -1; + t = (char*)fmt->to; + s = (char*)fmt->stop; + if(sign) + FMTCHAR(fmt, t, s, sign); + while(z1>0 || ndigits>0 || z2>0) { + if(z1 > 0){ + z1--; + c = '0'; + }else if(ndigits > 0){ + ndigits--; + c = *digits++; + }else{ + z2--; + c = '0'; + } + FMTCHAR(fmt, t, s, c); + if(--point == 0) + for(p=dot; *p; p++) + FMTCHAR(fmt, t, s, *p); + } + fmt->nfmt += t - (char*)fmt->to; + fmt->to = t; + if(sufwid && __fmtcpy(fmt, suf, sufwid, sufwid) < 0) + return -1; + if(pad && (fl & FmtLeft) && __fmtpad(fmt, pad) < 0) + return -1; + } + return 0; +} + diff --git a/lib9/fmt/fmt.c b/lib9/fmt/fmt.c @@ -0,0 +1,220 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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 */ +}; + +static struct +{ + /* lock by calling __fmtlock, __fmtunlock */ + int nfmt; + Convfmt fmt[Maxfmt]; +} fmtalloc; + +static Convfmt knownfmt[] = { + ' ', __flagfmt, + '#', __flagfmt, + '%', __percentfmt, + '\'', __flagfmt, + '+', __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,105 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ + +/* + * 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 __needsep(int*, char**); +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 = (char*)__fmtflush(f, t, 1);\ + if(t != nil)\ + s = (char*)f->stop;\ + else\ + return -1;\ + }\ + *t++ = c;\ + }while(0) + +#define FMTRCHAR(f, t, s, c)\ + do{\ + if(t + 1 > (Rune*)s){\ + t = (Rune*)__fmtflush(f, t, sizeof(Rune));\ + if(t != nil)\ + s = (Rune*)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 = (char*)__fmtflush(f, t, _runelen);\ + if(t != nil)\ + s = (char*)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,36 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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*)(uintptr_t)fd; + f->flags = 0; + f->nfmt = 0; + fmtlocaleinit(f, nil, nil, nil); + return 0; +} diff --git a/lib9/fmt/fmtfdflush.c b/lib9/fmt/fmtfdflush.c @@ -0,0 +1,22 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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((uintptr)f->farg, f->start, n) != n) + return 0; + f->to = f->start; + return 1; +} diff --git a/lib9/fmt/fmtlocale.c b/lib9/fmt/fmtlocale.c @@ -0,0 +1,55 @@ +/* Copyright (c) 2004 Google Inc.; see LICENSE */ + +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +/* + * Fill in the internationalization stuff in the State structure. + * For nil arguments, provide the sensible defaults: + * decimal is a period + * thousands separator is a comma + * thousands are marked every three digits + */ +void +fmtlocaleinit(Fmt *f, char *decimal, char *thousands, char *grouping) +{ + if(decimal == nil || decimal[0] == '\0') + decimal = "."; + if(thousands == nil) + thousands = ","; + if(grouping == nil) + grouping = "\3"; + f->decimal = decimal; + f->thousands = thousands; + f->grouping = grouping; +} + +/* + * We are about to emit a digit in e.g. %'d. If that digit would + * overflow a thousands (e.g.) grouping, tell the caller to emit + * the thousands separator. Always advance the digit counter + * and pointer into the grouping descriptor. + */ +int +__needsep(int *ndig, char **grouping) +{ + int group; + + (*ndig)++; + group = *(unsigned char*)*grouping; + /* CHAR_MAX means no further grouping. \0 means we got the empty string */ + if(group == 0xFF || group == 0x7f || group == 0x00) + return 0; + if(*ndig > group){ + /* if we're at end of string, continue with this grouping; else advance */ + if((*grouping)[1] != '\0') + (*grouping)++; + *ndig = 1; + return 1; + } + return 0; +} + diff --git a/lib9/fmt/fmtlock.c b/lib9/fmt/fmtlock.c @@ -0,0 +1,15 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include <stdarg.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +void +__fmtlock(void) +{ +} + +void +__fmtunlock(void) +{ +} diff --git a/lib9/fmt/fmtnull.c b/lib9/fmt/fmtnull.c @@ -0,0 +1,33 @@ +/* Copyright (c) 2004 Google Inc.; see LICENSE */ +#include <stdarg.h> +#include <string.h> +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +/* + * Absorb output without using resources. + */ +static Rune nullbuf[32]; + +static int +__fmtnullflush(Fmt *f) +{ + f->to = nullbuf; + f->nfmt = 0; + return 0; +} + +int +fmtnullinit(Fmt *f) +{ + memset(f, 0, sizeof *f); + f->runes = 1; + f->start = nullbuf; + f->to = nullbuf; + f->stop = nullbuf+nelem(nullbuf); + f->flush = __fmtnullflush; + fmtlocaleinit(f, nil, nil, nil); + return 0; +} + diff --git a/lib9/fmt/fmtprint.c b/lib9/fmt/fmtprint.c @@ -0,0 +1,36 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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,259 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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++; + +#ifndef PLAN9PORT + /* ANSI requires precision in bytes, not Runes. */ + nin-= w-1; /* and then n-- in the loop */ +#endif + } +} + +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; + + fl = f->flags; + w = 0; + if(fl & FmtWidth) + w = f->width; + 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,28 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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,16 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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'; + f->to = f->start; + return (char*)f->start; +} diff --git a/lib9/fmt/fmtvprint.c b/lib9/fmt/fmtvprint.c @@ -0,0 +1,37 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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,17 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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/nan.h b/lib9/fmt/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/fmt/nan64.c b/lib9/fmt/nan64.c @@ -0,0 +1,76 @@ +/* + * 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 <assert.h> +#include "fmt.h" +#include "fmtdef.h" + +static uvlong uvnan = ((uvlong)0x7FF00000<<32)|0x00000001; +static uvlong uvinf = ((uvlong)0x7FF00000<<32)|0x00000000; +static uvlong uvneginf = ((uvlong)0xFFF00000<<32)|0x00000000; + +/* gcc sees through the obvious casts. */ +static uvlong +d2u(double d) +{ + union { + uvlong v; + double d; + } u; + assert(sizeof(u.d) == sizeof(u.v)); + u.d = d; + return u.v; +} + +static double +u2d(uvlong v) +{ + union { + uvlong v; + double d; + } u; + assert(sizeof(u.d) == sizeof(u.v)); + u.v = v; + return u.d; +} + +double +__NaN(void) +{ + return u2d(uvnan); +} + +int +__isNaN(double d) +{ + uvlong x; + + x = d2u(d); + /* IEEE 754: exponent bits 0x7FF and non-zero mantissa */ + return (x&uvinf) == uvinf && (x&~uvneginf) != 0; +} + +double +__Inf(int sign) +{ + return u2d(sign < 0 ? uvneginf : uvinf); +} + +int +__isInf(double d, int sign) +{ + uvlong x; + + x = d2u(d); + 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,38 @@ +#include <inttypes.h> + +/* + * 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 +#define uintptr _fmtuintptr + +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; +typedef uintptr_t uintptr; + +/* + * 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/pow10.c b/lib9/fmt/pow10.c @@ -0,0 +1,45 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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,17 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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,16 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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'; + f->to = f->start; + return f->start; +} diff --git a/lib9/fmt/runeseprint.c b/lib9/fmt/runeseprint.c @@ -0,0 +1,18 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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,18 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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,19 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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,18 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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,29 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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); + fmtlocaleinit(&f, nil, nil, nil); + 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,86 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +/* + * 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 = (uintptr)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*)(uintptr)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*)(uintptr)n; + f->nfmt = 0; + fmtlocaleinit(f, nil, nil, nil); + 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,28 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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); + fmtlocaleinit(&f, nil, nil, nil); + 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,17 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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,17 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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,18 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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,30 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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. + * + * careful! gcc-4.2 assumes buf+len < buf can never be true and + * optimizes the test away. casting to uintptr works around this bug. + */ + if((uintptr)buf+len < (uintptr)buf) + len = -(uintptr)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,520 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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]) != '\0'; 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++) != '\0') { + 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,53 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +/* Copyright (c) 2004 Google Inc.; see LICENSE */ + +#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); + + /* test %4$d formats */ + print("%3$d %4$06d %2$d %1$d\n", 444, 333, 111, 222); + print("%3$d %4$06d %2$d %1$d\n", 444, 333, 111, 222); + print("%3$d %4$*5$06d %2$d %1$d\n", 444, 333, 111, 222, 20); + print("%3$hd %4$*5$06d %2$d %1$d\n", 444, 333, (short)111, 222, 20); + print("%3$lld %4$*5$06d %2$d %1$d\n", 444, 333, 111LL, 222, 20); + + /* test %'d formats */ + print("%'d %'d %'d\n", 1, 2222, 33333333); + print("%'019d\n", 0); + print("%08d %08d %08d\n", 1, 2222, 33333333); + print("%'08d %'08d %'08d\n", 1, 2222, 33333333); + print("%'x %'X %'b\n", 0x11111111, 0xabcd1234, 12345); + print("%'lld %'lld %'lld\n", 1LL, 222222222LL, 3333333333333LL); + print("%019lld %019lld %019lld\n", 1LL, 222222222LL, 3333333333333LL); + print("%'019lld %'019lld %'019lld\n", 1LL, 222222222LL, 3333333333333LL); + print("%'020lld %'020lld %'020lld\n", 1LL, 222222222LL, 3333333333333LL); + print("%'llx %'llX %'llb\n", 0x111111111111LL, 0xabcd12345678LL, 112342345LL); + 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,21 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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,28 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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); + fmtlocaleinit(&f, nil, nil, nil); + 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,83 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +/* + * 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 = (uintptr)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*)(uintptr)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*)(uintptr)n; + f->nfmt = 0; + fmtlocaleinit(f, nil, nil, nil); + 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,28 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#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); + fmtlocaleinit(&f, nil, nil, nil); + 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 = PREFIX; + 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-arm.c b/lib9/getcallerpc-arm.c @@ -0,0 +1,8 @@ +#include <lib9.h> + +ulong +getcallerpc(void *x) +{ + return ((ulong*)x)[-2]; +} + 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 u; + 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 u; + 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,97 @@ +#include <u.h> +#include <libc.h> +#include <ctype.h> + +static int +isme(char *uid) +{ + int n; + char *p; + + n = strtol(uid, &p, 10); + if(*p == 0 && p > uid) + return n == getuid(); + return strcmp(getuser(), uid) == 0; +} +/* + * 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){ +#ifdef __APPLE__ + // Might be running native GUI on OS X. + disp = strdup(":0.0"); + if(disp == nil) + return nil; +#else + werrstr("$DISPLAY not set"); + return nil; +#endif + } + + /* canonicalize: xxx:0.0 => xxx:0 */ + p = strrchr(disp, ':'); + if(p){ + p++; + while(isdigit((uchar)*p)) + p++; + if(strcmp(p, ".0") == 0) + *p = 0; + } + + /* turn /tmp/launch/:0 into _tmp_launch_:0 (OS X 10.5) */ + for(p=disp; *p; p++) + if(*p == '/') + *p = '_'; + + 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 || !isme(d->uid)){ + 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.a b/lib9/lib9.a Binary files differ. diff --git a/lib9/lib9.h b/lib9/lib9.h @@ -0,0 +1,2 @@ +#include <u.h> +#include <libc.h> diff --git a/lib9/libc.h b/lib9/libc.h @@ -0,0 +1,948 @@ +/* + * 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 + +#include <utf.h> +#include <fmt.h> + +/* + * 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 = 4, + Runesync = 0x80, + Runeself = 0x80, + Runeerror = 0xFFFD, + Runemax = 0x10FFFF, +}; +*/ + +/* + * 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 +/* Clear some declarations to avoid conflicts in edge situation. + * uClibc based systems have this issue. + */ +#undef gmtime +#undef localtime +#undef asctime +#undef ctime +#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 int exitcode(char*); +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*, int); +/* 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); + +extern void (*_pin)(void); +extern void (*_unpin)(void); + +#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 +#undef longjmp +#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 QTTMP 0x04 /* type bit for non-backed-up file */ +#define QTSYMLINK 0x02 /* type bit for symbolic link */ +#define QTFILE 0x00 /* type bits for 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 DMTMP 0x04000000 /* mode bit for non-backed-up file */ +#define DMSYMLINK 0x02000000 /* mode bit for symbolic link (Unix, 9P2000.u) */ +#define DMDEVICE 0x00800000 /* mode bit for device file (Unix, 9P2000.u) */ +#define DMNAMEDPIPE 0x00200000 /* mode bit for named pipe (Unix, 9P2000.u) */ +#define DMSOCKET 0x00100000 /* mode bit for socket (Unix, 9P2000.u) */ +#define DMSETUID 0x00080000 /* mode bit for setuid (Unix, 9P2000.u) */ +#define DMSETGID 0x00040000 /* mode bit for setgid (Unix, 9P2000.u) */ + +#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 */ + + /* 9P2000.u extensions */ + uint uidnum; /* numeric uid */ + uint gidnum; /* numeric gid */ + uint muidnum; /* numeric muid */ + char *ext; /* extended info */ +} 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 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 long p9write(int, void*, long); +/* 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 +#define write p9write +#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*, char*); +extern int chattyfuse; + +/* external names that we don't want to step on */ +#ifndef NOPLAN9DEFINES +#define main p9main +#endif + +#ifdef VARARGCK +#pragma varargck type "lld" vlong +#pragma varargck type "llx" vlong +#pragma varargck type "lld" uvlong +#pragma varargck type "llx" uvlong +#pragma varargck type "ld" long +#pragma varargck type "lx" long +#pragma varargck type "ld" ulong +#pragma varargck type "lx" ulong +#pragma varargck type "d" int +#pragma varargck type "x" int +#pragma varargck type "c" int +#pragma varargck type "C" int +#pragma varargck type "d" uint +#pragma varargck type "x" uint +#pragma varargck type "c" uint +#pragma varargck type "C" uint +#pragma varargck type "f" double +#pragma varargck type "e" double +#pragma varargck type "g" double +#pragma varargck type "lf" long double +#pragma varargck type "le" long double +#pragma varargck type "lg" long double +#pragma varargck type "s" char* +#pragma varargck type "q" char* +#pragma varargck type "S" Rune* +#pragma varargck type "Q" Rune* +#pragma varargck type "r" void +#pragma varargck type "%" void +#pragma varargck type "n" int* +#pragma varargck type "p" void* +#pragma varargck type "<" void* +#pragma varargck type "[" void* +#pragma varargck type "H" void* +#pragma varargck type "lH" void* + +#pragma varargck flag ' ' +#pragma varargck flag '#' +#pragma varargck flag '+' +#pragma varargck flag ',' +#pragma varargck flag '-' +#pragma varargck flag 'u' + +#pragma varargck argpos fmtprint 2 +#pragma varargck argpos fprint 2 +#pragma varargck argpos print 1 +#pragma varargck argpos runeseprint 3 +#pragma varargck argpos runesmprint 1 +#pragma varargck argpos runesnprint 3 +#pragma varargck argpos runesprint 2 +#pragma varargck argpos seprint 3 +#pragma varargck argpos smprint 1 +#pragma varargck argpos snprint 3 +#pragma varargck argpos sprint 2 +#pragma varargck argpos sysfatal 1 +#pragma varargck argpos p9syslog 3 +#pragma varargck argpos werrstr 1 +#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) ((void)(x)) +# endif +#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,15 @@ +#include <lib9.h> + +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 "fmt/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/netcrypt.c b/lib9/netcrypt.c @@ -0,0 +1,18 @@ +#include <u.h> +#include <libc.h> +#include <auth.h> + +int +netcrypt(void *key, void *chal) +{ + uchar buf[8], *p; + + strncpy((char*)buf, chal, 7); + buf[7] = '\0'; + for(p = buf; *p && *p != '\n'; p++) + ; + *p = '\0'; + encrypt(key, buf, 8); + sprint(chal, "%.2ux%.2ux%.2ux%.2ux", buf[0], buf[1], buf[2], buf[3]); + return 1; +} diff --git a/lib9/netmkaddr.c b/lib9/netmkaddr.c @@ -0,0 +1,62 @@ +#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) + defnet = "net"; + /* allow unix sockets to omit unix! prefix */ + if(access(linear, 0) >= 0){ + snprint(addr, sizeof(addr), "unix!%s", linear); + return addr; + } + /* allow host:service in deference to Unix convention */ + if((cp = strchr(linear, ':')) != nil){ + snprint(addr, sizeof(addr), "%s!%.*s!%s", + defnet, utfnlen(linear, cp-linear), + linear, cp+1); + return addr; + } + 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; + + /* + * if the network is unix, no service + */ + if(strncmp(linear, "unix!", 5) == 0) + 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,273 @@ +/* + * 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, + NoNotify = 1<<2, +}; + +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|NoNotify, +/* SIGTTIN, Restart|Ignore, */ +/* SIGTTOU, Restart|Ignore, */ + SIGXCPU, 0, + SIGXFSZ, 0, + SIGVTALRM, 0, + SIGUSR1, 0, + SIGUSR2, 0, +#ifdef SIGWINCH + SIGWINCH, Restart|Ignore|NoNotify, +#endif +#ifdef SIGINFO + SIGINFO, Restart|Ignore|NoNotify, +#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, !(sig->flags&NoNotify)); + } +} + diff --git a/lib9/nrand.c b/lib9/nrand.c @@ -0,0 +1,17 @@ +#include <lib9.h> + +#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 = d->ext = ""; +} 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,20 @@ +#include <u.h> +#include <libc.h> + +int +opentemp(char *template, int mode) +{ + int fd, fd1; + + fd = mkstemp(template); + if(fd < 0) + return -1; + if((fd1 = open(template, mode)) < 0){ + remove(template); + close(fd); + return -1; + } + close(fd); + return fd1; +} + diff --git a/lib9/pin.c b/lib9/pin.c @@ -0,0 +1,11 @@ +#include <u.h> +#include <libc.h> + +static void +nop(void) +{ +} + +void (*_pin)(void) = nop; +void (*_unpin)(void) = nop; + 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/post9p.c b/lib9/post9p.c @@ -0,0 +1,83 @@ +#include <u.h> +#include <libc.h> + +int chattyfuse; + +int +post9pservice(int fd, char *name, char *mtpt) +{ + int i, pid; + char *ns, *addr; + Waitmsg *w; + + if(name == nil && mtpt == nil){ + close(fd); + werrstr("nothing to do"); + return -1; + } + + if(name){ + if(strchr(name, '!')) /* assume is already network address */ + addr = strdup(name); + else{ + if((ns = getns()) == nil) + return -1; + addr = smprint("unix!%s/%s", ns, name); + free(ns); + } + if(addr == nil) + return -1; + switch(pid = 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", addr, (char*)0); + fprint(2, "exec 9pserve: %r\n"); + _exits("exec"); + } + close(fd); + w = waitfor(pid); + if(w == nil) + return -1; + if(w->msg && w->msg[0]){ + free(w); + werrstr("9pserve failed"); + return -1; + } + free(w); + if(mtpt){ + /* reopen */ + if((fd = dial(addr, nil, nil, nil)) < 0){ + werrstr("cannot reopen for mount: %r"); + return -1; + } + } + free(addr); + } + if(mtpt){ + switch(pid = rfork(RFFDG|RFPROC|RFNOWAIT)){ + case -1: + return -1; + case 0: + dup(fd, 0); + for(i=3; i<20; i++) + close(i); + + /* Try v9fs on Linux, which will mount 9P directly. */ + execlp("mount9p", "mount9p", "-", mtpt, (char*)0); + + if(chattyfuse) + execlp("9pfuse", "9pfuse", "-D", "-", mtpt, (char*)0); + else + execlp("9pfuse", "9pfuse", "-", mtpt, (char*)0); + fprint(2, "exec 9pfuse: %r\n"); + _exits("exec"); + } + close(fd); + } + 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,7 @@ +#include <lib9.h> + +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,101 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> +#include <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,21 @@ +#include <lib9.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/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: + inst->u1.right = (void*)((char*)inst->u1.right + diff); + break; + case CCLASS: + case NCCLASS: + inst->u1.right = (void*)((char*)inst->u1.right + diff); + cl = inst->u1.cp; + cl->end = (void*)((char*)cl->end + diff); + break; + } + inst->u2.left = (void*)((char*)inst->u2.left + diff); + } + npp->startinst = (void*)((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+1; + 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,212 @@ +#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; + Rune *p; + + 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: + p = runestrchr(s, j->startchar); + if(p == 0 || p == j->reol) + return match; + s = p; + break; + case BOL: + if(s == bol) + break; + p = runestrchr(s, '\n'); + if(p == 0 || s == j->reol) + return match; + s = p+1; + 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,128 @@ +#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; + extern char **environ; + + if((flags&(RFPROC|RFFDG|RFMEM)) == (RFPROC|RFFDG)){ + /* check other flags before we commit */ + flags &= ~(RFPROC|RFFDG|RFENVG); + n = (flags & ~(RFNOTEG|RFNAMEG|RFNOWAIT|RFCENVG)); + 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&RFCENVG) + if(environ) + *environ = nil; + } + if(flags&RFPROC){ + werrstr("cannot use rfork for shared memory -- use libthread"); + 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/sec/libsec.h b/lib9/sec/libsec.h @@ -0,0 +1,366 @@ +#ifndef _LIBSEC_H_ +#define _LIBSEC_H_ 1 +#if defined(__cplusplus) +extern "C" { +#endif +/* +#pragma lib "libsec.a" +#pragma src "/sys/src/libsec" +*/ + +AUTOLIB(sec) + +#ifndef _MPINT +typedef struct mpint mpint; +#endif + +/*******************************************************/ +/* AES definitions */ +/*******************************************************/ + +enum +{ + AESbsize= 16, + AESmaxkey= 32, + AESmaxrounds= 14 +}; + +typedef struct AESstate AESstate; +struct AESstate +{ + ulong setup; + int rounds; + int keybytes; + uchar key[AESmaxkey]; /* unexpanded key */ + u32int ekey[4*(AESmaxrounds + 1)]; /* encryption key */ + u32int dkey[4*(AESmaxrounds + 1)]; /* decryption key */ + uchar ivec[AESbsize]; /* initialization vector */ +}; + +void setupAESstate(AESstate *s, uchar key[], int keybytes, uchar *ivec); +void aesCBCencrypt(uchar *p, int len, AESstate *s); +void aesCBCdecrypt(uchar *p, int len, AESstate *s); + +/*******************************************************/ +/* Blowfish Definitions */ +/*******************************************************/ + +enum +{ + BFbsize = 8, + BFrounds = 16 +}; + +/* 16-round Blowfish */ +typedef struct BFstate BFstate; +struct BFstate +{ + ulong setup; + + uchar key[56]; + uchar ivec[8]; + + u32int pbox[BFrounds+2]; + u32int sbox[1024]; +}; + +void setupBFstate(BFstate *s, uchar key[], int keybytes, uchar *ivec); +void bfCBCencrypt(uchar*, int, BFstate*); +void bfCBCdecrypt(uchar*, int, BFstate*); +void bfECBencrypt(uchar*, int, BFstate*); +void bfECBdecrypt(uchar*, int, BFstate*); + +/*******************************************************/ +/* DES definitions */ +/*******************************************************/ + +enum +{ + DESbsize= 8 +}; + +/* single des */ +typedef struct DESstate DESstate; +struct DESstate +{ + ulong setup; + uchar key[8]; /* unexpanded key */ + ulong expanded[32]; /* expanded key */ + uchar ivec[8]; /* initialization vector */ +}; + +void setupDESstate(DESstate *s, uchar key[8], uchar *ivec); +void des_key_setup(uchar[8], ulong[32]); +void block_cipher(ulong*, uchar*, int); +void desCBCencrypt(uchar*, int, DESstate*); +void desCBCdecrypt(uchar*, int, DESstate*); +void desECBencrypt(uchar*, int, DESstate*); +void desECBdecrypt(uchar*, int, DESstate*); + +/* for backward compatibility with 7 byte DES key format */ +void des56to64(uchar *k56, uchar *k64); +void des64to56(uchar *k64, uchar *k56); +void key_setup(uchar[7], ulong[32]); + +/* triple des encrypt/decrypt orderings */ +enum { + DES3E= 0, + DES3D= 1, + DES3EEE= 0, + DES3EDE= 2, + DES3DED= 5, + DES3DDD= 7 +}; + +typedef struct DES3state DES3state; +struct DES3state +{ + ulong setup; + uchar key[3][8]; /* unexpanded key */ + ulong expanded[3][32]; /* expanded key */ + uchar ivec[8]; /* initialization vector */ +}; + +void setupDES3state(DES3state *s, uchar key[3][8], uchar *ivec); +void triple_block_cipher(ulong keys[3][32], uchar*, int); +void des3CBCencrypt(uchar*, int, DES3state*); +void des3CBCdecrypt(uchar*, int, DES3state*); +void des3ECBencrypt(uchar*, int, DES3state*); +void des3ECBdecrypt(uchar*, int, DES3state*); + +/*******************************************************/ +/* digests */ +/*******************************************************/ + +enum +{ + SHA1dlen= 20, /* SHA digest length */ + MD4dlen= 16, /* MD4 digest length */ + MD5dlen= 16 /* MD5 digest length */ +}; + +typedef struct DigestState DigestState; +struct DigestState +{ + ulong len; + u32int state[5]; + uchar buf[128]; + int blen; + char malloced; + char seeded; +}; +typedef struct DigestState SHAstate; /* obsolete name */ +typedef struct DigestState SHA1state; +typedef struct DigestState MD5state; +typedef struct DigestState MD4state; + +DigestState* md4(uchar*, ulong, uchar*, DigestState*); +DigestState* md5(uchar*, ulong, uchar*, DigestState*); +DigestState* sha1(uchar*, ulong, uchar*, DigestState*); +DigestState* hmac_md5(uchar*, ulong, uchar*, ulong, uchar*, DigestState*); +DigestState* hmac_sha1(uchar*, ulong, uchar*, ulong, uchar*, DigestState*); +char* sha1pickle(SHA1state*); +SHA1state* sha1unpickle(char*); + +/*******************************************************/ +/* random number generation */ +/*******************************************************/ +void genrandom(uchar *buf, int nbytes); +void prng(uchar *buf, int nbytes); +ulong fastrand(void); +ulong nfastrand(ulong); + +/*******************************************************/ +/* primes */ +/*******************************************************/ +void genprime(mpint *p, int n, int accuracy); /* generate an n bit probable prime */ +void gensafeprime(mpint *p, mpint *alpha, int n, int accuracy); /* prime and generator */ +void genstrongprime(mpint *p, int n, int accuracy); /* generate an n bit strong prime */ +void DSAprimes(mpint *q, mpint *p, uchar seed[SHA1dlen]); +int probably_prime(mpint *n, int nrep); /* miller-rabin test */ +int smallprimetest(mpint *p); /* returns -1 if not prime, 0 otherwise */ + +/*******************************************************/ +/* rc4 */ +/*******************************************************/ +typedef struct RC4state RC4state; +struct RC4state +{ + uchar state[256]; + uchar x; + uchar y; +}; + +void setupRC4state(RC4state*, uchar*, int); +void rc4(RC4state*, uchar*, int); +void rc4skip(RC4state*, int); +void rc4back(RC4state*, int); + +/*******************************************************/ +/* rsa */ +/*******************************************************/ +typedef struct RSApub RSApub; +typedef struct RSApriv RSApriv; +typedef struct PEMChain PEMChain; + +/* public/encryption key */ +struct RSApub +{ + mpint *n; /* modulus */ + mpint *ek; /* exp (encryption key) */ +}; + +/* private/decryption key */ +struct RSApriv +{ + RSApub pub; + + mpint *dk; /* exp (decryption key) */ + + /* precomputed values to help with chinese remainder theorem calc */ + mpint *p; + mpint *q; + mpint *kp; /* dk mod p-1 */ + mpint *kq; /* dk mod q-1 */ + mpint *c2; /* (inv p) mod q */ +}; + +struct PEMChain +{ + PEMChain *next; + uchar *pem; + int pemlen; +}; + +RSApriv* rsagen(int nlen, int elen, int rounds); +mpint* rsaencrypt(RSApub *k, mpint *in, mpint *out); +mpint* rsadecrypt(RSApriv *k, mpint *in, mpint *out); +RSApub* rsapuballoc(void); +void rsapubfree(RSApub*); +RSApriv* rsaprivalloc(void); +void rsaprivfree(RSApriv*); +RSApub* rsaprivtopub(RSApriv*); +RSApub* X509toRSApub(uchar*, int, char*, int); +RSApriv* asn1toRSApriv(uchar*, int); +uchar* decodepem(char *s, char *type, int *len, char**); +PEMChain* decodepemchain(char *s, char *type); +uchar* X509gen(RSApriv *priv, char *subj, ulong valid[2], int *certlen); +RSApriv* rsafill(mpint *n, mpint *ek, mpint *dk, mpint *p, mpint *q); +uchar* X509req(RSApriv *priv, char *subj, int *certlen); + +/*******************************************************/ +/* elgamal */ +/*******************************************************/ +typedef struct EGpub EGpub; +typedef struct EGpriv EGpriv; +typedef struct EGsig EGsig; + +/* public/encryption key */ +struct EGpub +{ + mpint *p; /* modulus */ + mpint *alpha; /* generator */ + mpint *key; /* (encryption key) alpha**secret mod p */ +}; + +/* private/decryption key */ +struct EGpriv +{ + EGpub pub; + mpint *secret; /* (decryption key) */ +}; + +/* signature */ +struct EGsig +{ + mpint *r, *s; +}; + +EGpriv* eggen(int nlen, int rounds); +mpint* egencrypt(EGpub *k, mpint *in, mpint *out); +mpint* egdecrypt(EGpriv *k, mpint *in, mpint *out); +EGsig* egsign(EGpriv *k, mpint *m); +int egverify(EGpub *k, EGsig *sig, mpint *m); +EGpub* egpuballoc(void); +void egpubfree(EGpub*); +EGpriv* egprivalloc(void); +void egprivfree(EGpriv*); +EGsig* egsigalloc(void); +void egsigfree(EGsig*); +EGpub* egprivtopub(EGpriv*); + +/*******************************************************/ +/* dsa */ +/*******************************************************/ +typedef struct DSApub DSApub; +typedef struct DSApriv DSApriv; +typedef struct DSAsig DSAsig; + +/* public/encryption key */ +struct DSApub +{ + mpint *p; /* modulus */ + mpint *q; /* group order, q divides p-1 */ + mpint *alpha; /* group generator */ + mpint *key; /* (encryption key) alpha**secret mod p */ +}; + +/* private/decryption key */ +struct DSApriv +{ + DSApub pub; + mpint *secret; /* (decryption key) */ +}; + +/* signature */ +struct DSAsig +{ + mpint *r, *s; +}; + +DSApriv* dsagen(DSApub *opub); +DSAsig* dsasign(DSApriv *k, mpint *m); +int dsaverify(DSApub *k, DSAsig *sig, mpint *m); +DSApub* dsapuballoc(void); +void dsapubfree(DSApub*); +DSApriv* dsaprivalloc(void); +void dsaprivfree(DSApriv*); +DSAsig* dsasigalloc(void); +void dsasigfree(DSAsig*); +DSApub* dsaprivtopub(DSApriv*); +DSApriv* asn1toDSApriv(uchar*, int); + +/*******************************************************/ +/* TLS */ +/*******************************************************/ +typedef struct Thumbprint{ + struct Thumbprint *next; + uchar sha1[SHA1dlen]; +} Thumbprint; + +typedef struct TLSconn{ + char dir[40]; /* connection directory */ + uchar *cert; /* certificate (local on input, remote on output) */ + uchar *sessionID; + int certlen, sessionIDlen; + int (*trace)(char*fmt, ...); + PEMChain *chain; +} TLSconn; + +/* tlshand.c */ +extern int tlsClient(int fd, TLSconn *c); +extern int tlsServer(int fd, TLSconn *c); + +/* thumb.c */ +extern Thumbprint* initThumbprints(char *ok, char *crl); +extern void freeThumbprints(Thumbprint *ok); +extern int okThumbprint(uchar *sha1, Thumbprint *ok); + +/* readcert.c */ +extern uchar *readcert(char *filename, int *pcertlen); +PEMChain *readcertchain(char *filename); + +#if defined(__cplusplus) +} +#endif +#endif diff --git a/lib9/sec/md5.c b/lib9/sec/md5.c @@ -0,0 +1,147 @@ +#include "os.h" +#include <libsec.h> + +/* + * rfc1321 requires that I include this. The code is new. The constants + * all come from the rfc (hence the copyright). We trade a table for the + * macros in rfc. The total size is a lot less. -- presotto + * + * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + * rights reserved. + * + * License to copy and use this software is granted provided that it + * is identified as the "RSA Data Security, Inc. MD5 Message-Digest + * Algorithm" in all material mentioning or referencing this software + * or this function. + * + * License is also granted to make and use derivative works provided + * that such works are identified as "derived from the RSA Data + * Security, Inc. MD5 Message-Digest Algorithm" in all material + * mentioning or referencing the derived work. + * + * RSA Data Security, Inc. makes no representations concerning either + * the merchantability of this software or the suitability of this + * software forany particular purpose. It is provided "as is" + * without express or implied warranty of any kind. + * These notices must be retained in any copies of any part of this + * documentation and/or software. + */ + +static void encode(uchar*, u32int*, ulong); + +extern void _md5block(uchar*, ulong, u32int*); + +MD5state* +md5(uchar *p, ulong len, uchar *digest, MD5state *s) +{ + u32int x[16]; + uchar buf[128]; + int i; + uchar *e; + + if(s == nil){ + s = malloc(sizeof(*s)); + if(s == nil) + return nil; + memset(s, 0, sizeof(*s)); + s->malloced = 1; + } + + if(s->seeded == 0){ + /* seed the state, these constants would look nicer big-endian */ + s->state[0] = 0x67452301; + s->state[1] = 0xefcdab89; + s->state[2] = 0x98badcfe; + s->state[3] = 0x10325476; + s->seeded = 1; + } + + /* fill out the partial 64 byte block from previous calls */ + if(s->blen){ + i = 64 - s->blen; + if(len < i) + i = len; + memmove(s->buf + s->blen, p, i); + len -= i; + s->blen += i; + p += i; + if(s->blen == 64){ + _md5block(s->buf, s->blen, s->state); + s->len += s->blen; + s->blen = 0; + } + } + + /* do 64 byte blocks */ + i = len & ~0x3f; + if(i){ + _md5block(p, i, s->state); + s->len += i; + len -= i; + p += i; + } + + /* save the left overs if not last call */ + if(digest == 0){ + if(len){ + memmove(s->buf, p, len); + s->blen += len; + } + return s; + } + + /* + * this is the last time through, pad what's left with 0x80, + * 0's, and the input count to create a multiple of 64 bytes + */ + if(s->blen){ + p = s->buf; + len = s->blen; + } else { + memmove(buf, p, len); + p = buf; + } + s->len += len; + e = p + len; + if(len < 56) + i = 56 - len; + else + i = 120 - len; + memset(e, 0, i); + *e = 0x80; + len += i; + + /* append the count */ + x[0] = s->len<<3; + x[1] = s->len>>29; + encode(p+len, x, 8); + + /* digest the last part */ + _md5block(p, len+8, s->state); + s->len += len; + + /* return result and free state */ + encode(digest, s->state, MD5dlen); + if(s->malloced == 1) + free(s); + return nil; +} + +/* + * encodes input (u32int) into output (uchar). Assumes len is + * a multiple of 4. + */ +static void +encode(uchar *output, u32int *input, ulong len) +{ + u32int x; + uchar *e; + + for(e = output + len; output < e;) { + x = *input++; + *output++ = x; + *output++ = x >> 8; + *output++ = x >> 16; + *output++ = x >> 24; + } +} diff --git a/lib9/sec/md5block.c b/lib9/sec/md5block.c @@ -0,0 +1,267 @@ +#include "os.h" +#include <libsec.h> + +/* + * rfc1321 requires that I include this. The code is new. The constants + * all come from the rfc (hence the copyright). We trade a table for the + * macros in rfc. The total size is a lot less. -- presotto + * + * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + * rights reserved. + * + * License to copy and use this software is granted provided that it + * is identified as the "RSA Data Security, Inc. MD5 Message-Digest + * Algorithm" in all material mentioning or referencing this software + * or this function. + * + * License is also granted to make and use derivative works provided + * that such works are identified as "derived from the RSA Data + * Security, Inc. MD5 Message-Digest Algorithm" in all material + * mentioning or referencing the derived work. + * + * RSA Data Security, Inc. makes no representations concerning either + * the merchantability of this software or the suitability of this + * software forany particular purpose. It is provided "as is" + * without express or implied warranty of any kind. + * These notices must be retained in any copies of any part of this + * documentation and/or software. + */ + +/* + * Rotate ammounts used in the algorithm + */ +enum +{ + S11= 7, + S12= 12, + S13= 17, + S14= 22, + + S21= 5, + S22= 9, + S23= 14, + S24= 20, + + S31= 4, + S32= 11, + S33= 16, + S34= 23, + + S41= 6, + S42= 10, + S43= 15, + S44= 21, +}; + +static u32int md5tab[] = +{ + /* round 1 */ +/*[0]*/ 0xd76aa478, + 0xe8c7b756, + 0x242070db, + 0xc1bdceee, + 0xf57c0faf, + 0x4787c62a, + 0xa8304613, + 0xfd469501, + 0x698098d8, + 0x8b44f7af, + 0xffff5bb1, + 0x895cd7be, + 0x6b901122, + 0xfd987193, + 0xa679438e, + 0x49b40821, + + /* round 2 */ +/*[16]*/0xf61e2562, + 0xc040b340, + 0x265e5a51, + 0xe9b6c7aa, + 0xd62f105d, + 0x2441453, + 0xd8a1e681, + 0xe7d3fbc8, + 0x21e1cde6, + 0xc33707d6, + 0xf4d50d87, + 0x455a14ed, + 0xa9e3e905, + 0xfcefa3f8, + 0x676f02d9, + 0x8d2a4c8a, + + /* round 3 */ +/*[32]*/0xfffa3942, + 0x8771f681, + 0x6d9d6122, + 0xfde5380c, + 0xa4beea44, + 0x4bdecfa9, + 0xf6bb4b60, + 0xbebfbc70, + 0x289b7ec6, + 0xeaa127fa, + 0xd4ef3085, + 0x4881d05, + 0xd9d4d039, + 0xe6db99e5, + 0x1fa27cf8, + 0xc4ac5665, + + /* round 4 */ +/*[48]*/0xf4292244, + 0x432aff97, + 0xab9423a7, + 0xfc93a039, + 0x655b59c3, + 0x8f0ccc92, + 0xffeff47d, + 0x85845dd1, + 0x6fa87e4f, + 0xfe2ce6e0, + 0xa3014314, + 0x4e0811a1, + 0xf7537e82, + 0xbd3af235, + 0x2ad7d2bb, + 0xeb86d391, +}; + +static void decode(u32int*, uchar*, ulong); +extern void _md5block(uchar *p, ulong len, u32int *s); + +void +_md5block(uchar *p, ulong len, u32int *s) +{ + u32int a, b, c, d, sh; + u32int *t; + uchar *end; + u32int x[16]; + + for(end = p+len; p < end; p += 64){ + a = s[0]; + b = s[1]; + c = s[2]; + d = s[3]; + + decode(x, p, 64); + + t = md5tab; + sh = 0; + for(; sh != 16; t += 4){ + a += ((c ^ d) & b) ^ d; + a += x[sh] + t[0]; + a = (a << S11) | (a >> (32 - S11)); + a += b; + + d += ((b ^ c) & a) ^ c; + d += x[sh + 1] + t[1]; + d = (d << S12) | (d >> (32 - S12)); + d += a; + + c += ((a ^ b) & d) ^ b; + c += x[sh + 2] + t[2]; + c = (c << S13) | (c >> (32 - S13)); + c += d; + + b += ((d ^ a) & c) ^ a; + b += x[sh + 3] + t[3]; + b = (b << S14) | (b >> (32 - S14)); + b += c; + + sh += 4; + } + sh = 1; + for(; sh != 1+20*4; t += 4){ + a += ((b ^ c) & d) ^ c; + a += x[sh & 0xf] + t[0]; + a = (a << S21) | (a >> (32 - S21)); + a += b; + + d += ((a ^ b) & c) ^ b; + d += x[(sh + 5) & 0xf] + t[1]; + d = (d << S22) | (d >> (32 - S22)); + d += a; + + c += ((d ^ a) & b) ^ a; + c += x[(sh + 10) & 0xf] + t[2]; + c = (c << S23) | (c >> (32 - S23)); + c += d; + + b += ((c ^ d) & a) ^ d; + b += x[(sh + 15) & 0xf] + t[3]; + b = (b << S24) | (b >> (32 - S24)); + b += c; + + sh += 20; + } + sh = 5; + for(; sh != 5+12*4; t += 4){ + a += b ^ c ^ d; + a += x[sh & 0xf] + t[0]; + a = (a << S31) | (a >> (32 - S31)); + a += b; + + d += a ^ b ^ c; + d += x[(sh + 3) & 0xf] + t[1]; + d = (d << S32) | (d >> (32 - S32)); + d += a; + + c += d ^ a ^ b; + c += x[(sh + 6) & 0xf] + t[2]; + c = (c << S33) | (c >> (32 - S33)); + c += d; + + b += c ^ d ^ a; + b += x[(sh + 9) & 0xf] + t[3]; + b = (b << S34) | (b >> (32 - S34)); + b += c; + + sh += 12; + } + sh = 0; + for(; sh != 28*4; t += 4){ + a += c ^ (b | ~d); + a += x[sh & 0xf] + t[0]; + a = (a << S41) | (a >> (32 - S41)); + a += b; + + d += b ^ (a | ~c); + d += x[(sh + 7) & 0xf] + t[1]; + d = (d << S42) | (d >> (32 - S42)); + d += a; + + c += a ^ (d | ~b); + c += x[(sh + 14) & 0xf] + t[2]; + c = (c << S43) | (c >> (32 - S43)); + c += d; + + b += d ^ (c | ~a); + b += x[(sh + 21) & 0xf] + t[3]; + b = (b << S44) | (b >> (32 - S44)); + b += c; + + sh += 28; + } + + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + } +} + +/* + * decodes input (uchar) into output (u32int). Assumes len is + * a multiple of 4. + */ +static void +decode(u32int *output, uchar *input, ulong len) +{ + uchar *e; + + for(e = input+len; input < e; input += 4) + *output++ = input[0] | (input[1] << 8) | + (input[2] << 16) | (input[3] << 24); +} diff --git a/lib9/sec/md5pickle.c b/lib9/sec/md5pickle.c @@ -0,0 +1,39 @@ +#include "os.h" +#include <libsec.h> + +char* +md5pickle(MD5state *s) +{ + char *p; + int m, n; + + m = 17+4*9+4*((s->blen+3)/3); + p = malloc(m); + if(p == nil) + return p; + n = sprint(p, "%16.16llux %8.8ux %8.8ux %8.8ux %8.8ux ", + s->len, + s->state[0], s->state[1], s->state[2], + s->state[3]); + enc64(p+n, m-n, s->buf, s->blen); + return p; +} + +MD5state* +md5unpickle(char *p) +{ + MD5state *s; + + s = malloc(sizeof(*s)); + if(s == nil) + return nil; + s->len = strtoull(p, &p, 16); + s->state[0] = strtoul(p, &p, 16); + s->state[1] = strtoul(p, &p, 16); + s->state[2] = strtoul(p, &p, 16); + s->state[3] = strtoul(p, &p, 16); + s->blen = dec64(s->buf, sizeof(s->buf), p, strlen(p)); + s->malloced = 1; + s->seeded = 1; + return s; +} diff --git a/lib9/sec/os.h b/lib9/sec/os.h @@ -0,0 +1,2 @@ +#include <u.h> +#include <libc.h> diff --git a/lib9/sec/sha1.c b/lib9/sec/sha1.c @@ -0,0 +1,127 @@ +#include "os.h" +#include <libsec.h> + +static void encode(uchar*, u32int*, ulong); + +extern void _sha1block(uchar*, ulong, u32int*); + +/* + * we require len to be a multiple of 64 for all but + * the last call. There must be room in the input buffer + * to pad. + */ +SHA1state* +sha1(uchar *p, ulong len, uchar *digest, SHA1state *s) +{ + uchar buf[128]; + u32int x[16]; + int i; + uchar *e; + + if(s == nil){ + s = malloc(sizeof(*s)); + if(s == nil) + return nil; + memset(s, 0, sizeof(*s)); + s->malloced = 1; + } + + if(s->seeded == 0){ + /* seed the state, these constants would look nicer big-endian */ + s->state[0] = 0x67452301; + s->state[1] = 0xefcdab89; + s->state[2] = 0x98badcfe; + s->state[3] = 0x10325476; + s->state[4] = 0xc3d2e1f0; + s->seeded = 1; + } + + /* fill out the partial 64 byte block from previous calls */ + if(s->blen){ + i = 64 - s->blen; + if(len < i) + i = len; + memmove(s->buf + s->blen, p, i); + len -= i; + s->blen += i; + p += i; + if(s->blen == 64){ + _sha1block(s->buf, s->blen, s->state); + s->len += s->blen; + s->blen = 0; + } + } + + /* do 64 byte blocks */ + i = len & ~0x3f; + if(i){ + _sha1block(p, i, s->state); + s->len += i; + len -= i; + p += i; + } + + /* save the left overs if not last call */ + if(digest == 0){ + if(len){ + memmove(s->buf, p, len); + s->blen += len; + } + return s; + } + + /* + * this is the last time through, pad what's left with 0x80, + * 0's, and the input count to create a multiple of 64 bytes + */ + if(s->blen){ + p = s->buf; + len = s->blen; + } else { + memmove(buf, p, len); + p = buf; + } + s->len += len; + e = p + len; + if(len < 56) + i = 56 - len; + else + i = 120 - len; + memset(e, 0, i); + *e = 0x80; + len += i; + + /* append the count */ + x[0] = s->len>>29; + x[1] = s->len<<3; + encode(p+len, x, 8); + + /* digest the last part */ + _sha1block(p, len+8, s->state); + s->len += len+8; + + /* return result and free state */ + encode(digest, s->state, SHA1dlen); + if(s->malloced == 1) + free(s); + return nil; +} + +/* + * encodes input (ulong) into output (uchar). Assumes len is + * a multiple of 4. + */ +static void +encode(uchar *output, u32int *input, ulong len) +{ + u32int x; + uchar *e; + + for(e = output + len; output < e;) { + x = *input++; + *output++ = x >> 24; + *output++ = x >> 16; + *output++ = x >> 8; + *output++ = x; + } +} diff --git a/lib9/sec/sha1block.c b/lib9/sec/sha1block.c @@ -0,0 +1,187 @@ +#include "os.h" + +void +_sha1block(uchar *p, ulong len, u32int *s) +{ + u32int a, b, c, d, e, x; + uchar *end; + u32int *wp, *wend; + u32int w[80]; + + /* at this point, we have a multiple of 64 bytes */ + for(end = p+len; p < end;){ + a = s[0]; + b = s[1]; + c = s[2]; + d = s[3]; + e = s[4]; + + wend = w + 15; + for(wp = w; wp < wend; wp += 5){ + wp[0] = (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; + e += ((a<<5) | (a>>27)) + wp[0]; + e += 0x5a827999 + (((c^d)&b)^d); + b = (b<<30)|(b>>2); + + wp[1] = (p[4]<<24) | (p[5]<<16) | (p[6]<<8) | p[7]; + d += ((e<<5) | (e>>27)) + wp[1]; + d += 0x5a827999 + (((b^c)&a)^c); + a = (a<<30)|(a>>2); + + wp[2] = (p[8]<<24) | (p[9]<<16) | (p[10]<<8) | p[11]; + c += ((d<<5) | (d>>27)) + wp[2]; + c += 0x5a827999 + (((a^b)&e)^b); + e = (e<<30)|(e>>2); + + wp[3] = (p[12]<<24) | (p[13]<<16) | (p[14]<<8) | p[15]; + b += ((c<<5) | (c>>27)) + wp[3]; + b += 0x5a827999 + (((e^a)&d)^a); + d = (d<<30)|(d>>2); + + wp[4] = (p[16]<<24) | (p[17]<<16) | (p[18]<<8) | p[19]; + a += ((b<<5) | (b>>27)) + wp[4]; + a += 0x5a827999 + (((d^e)&c)^e); + c = (c<<30)|(c>>2); + + p += 20; + } + + wp[0] = (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; + e += ((a<<5) | (a>>27)) + wp[0]; + e += 0x5a827999 + (((c^d)&b)^d); + b = (b<<30)|(b>>2); + + x = wp[-2] ^ wp[-7] ^ wp[-13] ^ wp[-15]; + wp[1] = (x<<1) | (x>>31); + d += ((e<<5) | (e>>27)) + wp[1]; + d += 0x5a827999 + (((b^c)&a)^c); + a = (a<<30)|(a>>2); + + x = wp[-1] ^ wp[-6] ^ wp[-12] ^ wp[-14]; + wp[2] = (x<<1) | (x>>31); + c += ((d<<5) | (d>>27)) + wp[2]; + c += 0x5a827999 + (((a^b)&e)^b); + e = (e<<30)|(e>>2); + + x = wp[0] ^ wp[-5] ^ wp[-11] ^ wp[-13]; + wp[3] = (x<<1) | (x>>31); + b += ((c<<5) | (c>>27)) + wp[3]; + b += 0x5a827999 + (((e^a)&d)^a); + d = (d<<30)|(d>>2); + + x = wp[1] ^ wp[-4] ^ wp[-10] ^ wp[-12]; + wp[4] = (x<<1) | (x>>31); + a += ((b<<5) | (b>>27)) + wp[4]; + a += 0x5a827999 + (((d^e)&c)^e); + c = (c<<30)|(c>>2); + + wp += 5; + p += 4; + + wend = w + 40; + for(; wp < wend; wp += 5){ + x = wp[-3] ^ wp[-8] ^ wp[-14] ^ wp[-16]; + wp[0] = (x<<1) | (x>>31); + e += ((a<<5) | (a>>27)) + wp[0]; + e += 0x6ed9eba1 + (b^c^d); + b = (b<<30)|(b>>2); + + x = wp[-2] ^ wp[-7] ^ wp[-13] ^ wp[-15]; + wp[1] = (x<<1) | (x>>31); + d += ((e<<5) | (e>>27)) + wp[1]; + d += 0x6ed9eba1 + (a^b^c); + a = (a<<30)|(a>>2); + + x = wp[-1] ^ wp[-6] ^ wp[-12] ^ wp[-14]; + wp[2] = (x<<1) | (x>>31); + c += ((d<<5) | (d>>27)) + wp[2]; + c += 0x6ed9eba1 + (e^a^b); + e = (e<<30)|(e>>2); + + x = wp[0] ^ wp[-5] ^ wp[-11] ^ wp[-13]; + wp[3] = (x<<1) | (x>>31); + b += ((c<<5) | (c>>27)) + wp[3]; + b += 0x6ed9eba1 + (d^e^a); + d = (d<<30)|(d>>2); + + x = wp[1] ^ wp[-4] ^ wp[-10] ^ wp[-12]; + wp[4] = (x<<1) | (x>>31); + a += ((b<<5) | (b>>27)) + wp[4]; + a += 0x6ed9eba1 + (c^d^e); + c = (c<<30)|(c>>2); + } + + wend = w + 60; + for(; wp < wend; wp += 5){ + x = wp[-3] ^ wp[-8] ^ wp[-14] ^ wp[-16]; + wp[0] = (x<<1) | (x>>31); + e += ((a<<5) | (a>>27)) + wp[0]; + e += 0x8f1bbcdc + ((b&c)|((b|c)&d)); + b = (b<<30)|(b>>2); + + x = wp[-2] ^ wp[-7] ^ wp[-13] ^ wp[-15]; + wp[1] = (x<<1) | (x>>31); + d += ((e<<5) | (e>>27)) + wp[1]; + d += 0x8f1bbcdc + ((a&b)|((a|b)&c)); + a = (a<<30)|(a>>2); + + x = wp[-1] ^ wp[-6] ^ wp[-12] ^ wp[-14]; + wp[2] = (x<<1) | (x>>31); + c += ((d<<5) | (d>>27)) + wp[2]; + c += 0x8f1bbcdc + ((e&a)|((e|a)&b)); + e = (e<<30)|(e>>2); + + x = wp[0] ^ wp[-5] ^ wp[-11] ^ wp[-13]; + wp[3] = (x<<1) | (x>>31); + b += ((c<<5) | (c>>27)) + wp[3]; + b += 0x8f1bbcdc + ((d&e)|((d|e)&a)); + d = (d<<30)|(d>>2); + + x = wp[1] ^ wp[-4] ^ wp[-10] ^ wp[-12]; + wp[4] = (x<<1) | (x>>31); + a += ((b<<5) | (b>>27)) + wp[4]; + a += 0x8f1bbcdc + ((c&d)|((c|d)&e)); + c = (c<<30)|(c>>2); + } + + wend = w + 80; + for(; wp < wend; wp += 5){ + x = wp[-3] ^ wp[-8] ^ wp[-14] ^ wp[-16]; + wp[0] = (x<<1) | (x>>31); + e += ((a<<5) | (a>>27)) + wp[0]; + e += 0xca62c1d6 + (b^c^d); + b = (b<<30)|(b>>2); + + x = wp[-2] ^ wp[-7] ^ wp[-13] ^ wp[-15]; + wp[1] = (x<<1) | (x>>31); + d += ((e<<5) | (e>>27)) + wp[1]; + d += 0xca62c1d6 + (a^b^c); + a = (a<<30)|(a>>2); + + x = wp[-1] ^ wp[-6] ^ wp[-12] ^ wp[-14]; + wp[2] = (x<<1) | (x>>31); + c += ((d<<5) | (d>>27)) + wp[2]; + c += 0xca62c1d6 + (e^a^b); + e = (e<<30)|(e>>2); + + x = wp[0] ^ wp[-5] ^ wp[-11] ^ wp[-13]; + wp[3] = (x<<1) | (x>>31); + b += ((c<<5) | (c>>27)) + wp[3]; + b += 0xca62c1d6 + (d^e^a); + d = (d<<30)|(d>>2); + + x = wp[1] ^ wp[-4] ^ wp[-10] ^ wp[-12]; + wp[4] = (x<<1) | (x>>31); + a += ((b<<5) | (b>>27)) + wp[4]; + a += 0xca62c1d6 + (c^d^e); + c = (c<<30)|(c>>2); + } + + /* save state */ + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + s[4] += e; + } +} diff --git a/lib9/sec/sha1pickle.c b/lib9/sec/sha1pickle.c @@ -0,0 +1,38 @@ +#include "os.h" +#include <libsec.h> + +char* +sha1pickle(SHA1state *s) +{ + char *p; + int m, n; + + m = 5*9+4*((s->blen+3)/3); + p = malloc(m); + if(p == nil) + return p; + n = sprint(p, "%8.8ux %8.8ux %8.8ux %8.8ux %8.8ux ", + s->state[0], s->state[1], s->state[2], + s->state[3], s->state[4]); + enc64(p+n, m-n, s->buf, s->blen); + return p; +} + +SHA1state* +sha1unpickle(char *p) +{ + SHA1state *s; + + s = malloc(sizeof(*s)); + if(s == nil) + return nil; + s->state[0] = strtoul(p, &p, 16); + s->state[1] = strtoul(p, &p, 16); + s->state[2] = strtoul(p, &p, 16); + s->state[3] = strtoul(p, &p, 16); + s->state[4] = strtoul(p, &p, 16); + s->blen = dec64(s->buf, sizeof(s->buf), p, strlen(p)); + s->malloced = 1; + s->seeded = 1; + return s; +} 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,88 @@ +#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; + memmove(CMSG_DATA(cmsg), &fd, sizeof(int)); + + 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; + if(n == 0){ + werrstr("unexpected EOF"); + return -1; + } + cmsg = CMSG_FIRSTHDR(&msg); + memmove(&fd, CMSG_DATA(cmsg), sizeof(int)); + return fd; +} diff --git a/lib9/sleep.c b/lib9/sleep.c @@ -0,0 +1,47 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <sys/param.h> +#include <sys/time.h> +#include <sched.h> +#include <libc.h> + +#if defined(__NetBSD__) || (defined(__OpenBSD__) && OpenBSD <= 200611) +#if !defined(sched_yield) +# define sched_yield() \ + do{ struct timespec ts; \ + ts.tv_sec = 0; \ + ts.tv_nsec = 0; \ + nanosleep(&ts, 0); \ + }while(0) +#endif +#endif + +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,21 @@ +#include <lib9.h> + +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/test.c b/lib9/test.c @@ -0,0 +1,8 @@ +#include <lib9.h> + +int +main(int argc, char **argv) +{ + werrstr("hello world"); + print("%r\n"); +} diff --git a/lib9/testfltfmt.c b/lib9/testfltfmt.c @@ -0,0 +1,183 @@ +#include <u.h> +#include <libc.h> +#include <stdio.h> + +/* + * try all combination of flags and float conversions + * with some different widths & precisions + */ + +#define Njust 2 +#define Nplus 3 +#define Nalt 2 +#define Nzero 2 +#define Nspec 5 +#define Nwidth 5 +#define Nprec 5 + +static double fmtvals[] = { + 3.1415925535897932e15, + 3.1415925535897932e14, + 3.1415925535897932e13, + 3.1415925535897932e12, + 3.1415925535897932e11, + 3.1415925535897932e10, + 3.1415925535897932e9, + 3.1415925535897932e8, + 3.1415925535897932e7, + 3.1415925535897932e6, + 3.1415925535897932e5, + 3.1415925535897932e4, + 3.1415925535897932e3, + 3.1415925535897932e2, + 3.1415925535897932e1, + 3.1415925535897932e0, + 3.1415925535897932e-1, + 3.1415925535897932e-2, + 3.1415925535897932e-3, + 3.1415925535897932e-4, + 3.1415925535897932e-5, + 3.1415925535897932e-6, + 3.1415925535897932e-7, + 3.1415925535897932e-8, + 3.1415925535897932e-9, + 3.1415925535897932e-10, + 3.1415925535897932e-11, + 3.1415925535897932e-12, + 3.1415925535897932e-13, + 3.1415925535897932e-14, + 3.1415925535897932e-15, +}; + +/* + * are the numbers close? + * used to compare long numbers where the last few digits are garbage + * due to precision problems + */ +static int +numclose(char *num1, char *num2) +{ + int ndig; + double d1, d2; + enum { MAXDIG = 15 }; + + d1 = fmtstrtod(num1, 0); + d2 = fmtstrtod(num2, 0); + if(d1 != d2) + return 0; + + ndig = 0; + while (*num1) { + if (*num1 >= '0' && *num1 <= '9') { + ndig++; + if (ndig > MAXDIG) { + if (!(*num2 >= '0' && *num2 <= '9')) { + return 0; + } + } else if (*num1 != *num2) { + return 0; + } + } else if (*num1 != *num2) { + return 0; + } else if (*num1 == 'e' || *num1 == 'E') { + ndig = 0; + } + num1++; + num2++; + } + if (*num1 || !num2) + return 0; + return 1; +} + +static void +doit(int just, int plus, int alt, int zero, int width, int prec, int spec) +{ + char format[256]; + char *p; + const char *s; + int i; + + p = format; + *p++ = '%'; + if (just > 0) + *p++ = "-"[just - 1]; + if (plus > 0) + *p++ = "+ "[plus - 1]; + if (alt > 0) + *p++ = "#"[alt - 1]; + if (zero > 0) + *p++ = "0"[zero - 1]; + + s = ""; + switch (width) { + case 1: s = "1"; break; + case 2: s = "5"; break; + case 3: s = "10"; break; + case 4: s = "15"; break; + } + strcpy(p, s); + + s = ""; + switch (prec) { + case 1: s = ".0"; break; + case 2: s = ".2"; break; + case 3: s = ".5"; break; + case 4: s = ".15"; break; + } + strcat(p, s); + + p = strchr(p, '\0'); + *p++ = "efgEG"[spec]; + *p = '\0'; + + for (i = 0; i < sizeof(fmtvals) / sizeof(fmtvals[0]); i++) { + char ref[1024], buf[1024]; + Rune rbuf[1024]; + double d1, d2; + + sprintf(ref, format, fmtvals[i]); + snprint(buf, sizeof(buf), format, fmtvals[i]); + if (strcmp(ref, buf) != 0 + && !numclose(ref, buf)) { + d1 = fmtstrtod(ref, 0); + d2 = fmtstrtod(buf, 0); + fprintf(stderr, "%s: ref='%s'%s fmt='%s'%s\n", + format, + ref, d1==fmtvals[i] ? "" : " (ref is inexact!)", + buf, d2==fmtvals[i] ? "" : " (fmt is inexact!)"); + // exits("oops"); + } + + /* Check again with output to rune string */ + runesnprint(rbuf, 1024, format, fmtvals[i]); + snprint(buf, sizeof(buf), "%S", rbuf); + if (strcmp(ref, buf) != 0 + && !numclose(ref, buf)) { + d1 = fmtstrtod(ref, 0); + d2 = fmtstrtod(buf, 0); + fprintf(stderr, "%s: ref='%s'%s fmt='%s'%s\n", + format, + ref, d1==fmtvals[i] ? "" : " (ref is inexact!)", + buf, d2==fmtvals[i] ? "" : " (fmt is inexact!)"); + // exits("oops"); + } + } +} + +void +main(int argc, char **argv) +{ + int just, plus, alt, zero, width, prec, spec; + + for (just = 0; just < Njust; just++) + for (plus = 0; plus < Nplus; plus++) + for (alt = 0; alt < Nalt; alt++) + for (zero = 0; zero < Nzero; zero++) + for (width = 0; width < Nwidth; width++) + for (prec = 0; prec < Nprec; prec++) + for (spec = 0; spec < Nspec; spec++) + doit(just, plus, alt, zero, width, prec, spec); + + exits(0); +} diff --git a/lib9/testfmt.c b/lib9/testfmt.c @@ -0,0 +1,148 @@ +#include <u.h> +#include <libc.h> +#include <stdio.h> + +int failed; + +/* Consume argument and ignore it */ +int +Zflag(Fmt* f) +{ + if(va_arg(f->args, int)) + ; + return 1; /* it's a flag */ +} + +void +verify(char *s, char *t) +{ + if(strcmp(s, t) != 0){ + failed = 1; + fprintf(stderr, "error: (%s) != (%s)\n", s, t); + } + free(s); +} + +Rune lightsmiley = 0x263a; +Rune darksmiley = 0x263b; + +/* Test printer that loads unusual decimal point and separator */ +char* +mysmprint(char *fmt, ...) +{ + Fmt f; + + if(fmtstrinit(&f) < 0) + return 0; + va_start(f.args, fmt); + f.decimal = smprint("%C", lightsmiley); + f.thousands = smprint("%C", darksmiley); + f.grouping = "\1\2\3\4"; + if(dofmt(&f, fmt) < 0) + return 0; + va_end(f.args); + return fmtstrflush(&f); +} + +double near1[] = { + 0.5, + 0.95, + 0.995, + 0.9995, + 0.99995, + 0.999995, + 0.9999995, + 0.99999995, + 0.999999995, +}; + +void +main(int argc, char **argv) +{ + int i, j; + + quotefmtinstall(); + fmtinstall('Z', Zflag); + fmtinstall(L'\x263a', Zflag); +#ifdef PLAN9PORT +{ extern int __ifmt(Fmt*); + fmtinstall('i', __ifmt); +} +#endif + + verify(smprint("hello world"), "hello world"); +#ifdef PLAN9PORT + verify(smprint("x: %ux", 0x87654321), "x: 87654321"); +#else + verify(smprint("x: %x", 0x87654321), "x: 87654321"); +#endif + verify(smprint("d: %d", 0x87654321), "d: -2023406815"); + verify(smprint("s: %s", "hi there"), "s: hi there"); + verify(smprint("q: %q", "hi i'm here"), "q: 'hi i''m here'"); + verify(smprint("c: %c", '!'), "c: !"); + verify(smprint("g: %g %g %g", 3.14159, 3.14159e10, 3.14159e-10), "g: 3.14159 3.14159e+10 3.14159e-10"); + verify(smprint("e: %e %e %e", 3.14159, 3.14159e10, 3.14159e-10), "e: 3.141590e+00 3.141590e+10 3.141590e-10"); + verify(smprint("f: %f %f %f", 3.14159, 3.14159e10, 3.14159e-10), "f: 3.141590 31415900000.000000 0.000000"); + verify(smprint("smiley: %C", (Rune)0x263a), "smiley: \xe2\x98\xba"); + verify(smprint("%g %.18g", 2e25, 2e25), "2e+25 2e+25"); + verify(smprint("%2.18g", 1.0), " 1"); + verify(smprint("%f", 3.1415927/4), "0.785398"); + verify(smprint("%d", 23), "23"); + verify(smprint("%i", 23), "23"); + verify(smprint("%Zi", 1234, 23), "23"); + + /* ANSI and their wacky corner cases */ + verify(smprint("%.0d", 0), ""); + verify(smprint("%.0o", 0), ""); + verify(smprint("%.0x", 0), ""); + verify(smprint("%#.0o", 0), "0"); + verify(smprint("%#.0x", 0), ""); + + /* difficult floating point tests that many libraries get wrong */ + verify(smprint("%.100f", 1.0), "1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + verify(smprint("%.100g", 1.0), "1"); + verify(smprint("%0100f", 1.0), "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.000000"); + for(i=1; i<9; i++) + for(j=0; j<=i; j++) + verify(smprint("%.*g", j, near1[i]), "1"); + + /* test $ reorderings */ + verify(smprint("%3$d %4$06d %2$d %1$d", 444, 333, 111, 222), "111 000222 333 444"); + verify(smprint("%3$Zd %5$06d %2$d %1$d", 444, 333, 555, 111, 222), "111 000222 333 444"); + verify(smprint("%3$d %4$*5$06d %2$d %1$d", 444, 333, 111, 222, 20), "111 000222 333 444"); + verify(smprint("%3$hd %4$*5$06d %2$d %1$d", 444, 333, (short)111, 222, 20), "111 000222 333 444"); + verify(smprint("%3$\xe2\x98\xba""d %5$06d %2$d %1$d", 444, 333, 555, 111, 222), "111 000222 333 444"); + + /* test %'d formats */ + verify(smprint("%'d %'d %'d", 1, 2222, 33333333), "1 2,222 33,333,333"); + verify(smprint("%'019d", 0), "000,000,000,000,000"); + verify(smprint("%'08d %'08d %'08d", 1, 2222, 33333333), "0,000,001 0,002,222 33,333,333"); +#ifdef PLAN9PORT + verify(smprint("%'ux %'uX %'ub", 0x11111111, 0xabcd1234, 12345), "1111:1111 ABCD:1234 11:0000:0011:1001"); +#else + verify(smprint("%'x %'X %'b", 0x11111111, 0xabcd1234, 12345), "1111:1111 ABCD:1234 11:0000:0011:1001"); +#endif + verify(smprint("%'lld %'lld %'lld", 1LL, 222222222LL, 3333333333333LL), "1 222,222,222 3,333,333,333,333"); + verify(smprint("%'019lld %'019lld %'019lld", 1LL, 222222222LL, 3333333333333LL), "000,000,000,000,001 000,000,222,222,222 003,333,333,333,333"); +#ifdef PLAN9PORT + verify(smprint("%'llux %'lluX %'llub", 0x111111111111LL, 0xabcd12345678LL, 112342345LL), "1111:1111:1111 ABCD:1234:5678 110:1011:0010:0011:0101:0100:1001"); +#else + verify(smprint("%'llx %'llX %'llb", 0x111111111111LL, 0xabcd12345678LL, 112342345LL), "1111:1111:1111 ABCD:1234:5678 110:1011:0010:0011:0101:0100:1001"); +#endif + + /* test %'d with custom (utf-8!) separators */ + /* x and b still use : */ + verify(mysmprint("%'d %'d %'d", 1, 2222, 33333333), "1 2\xe2\x98\xbb""22\xe2\x98\xbb""2 33\xe2\x98\xbb""333\xe2\x98\xbb""33\xe2\x98\xbb""3"); +#ifdef PLAN9PORT + verify(mysmprint("%'ux %'uX %'ub", 0x11111111, 0xabcd1234, 12345), "1111:1111 ABCD:1234 11:0000:0011:1001"); +#else + verify(mysmprint("%'x %'X %'b", 0x11111111, 0xabcd1234, 12345), "1111:1111 ABCD:1234 11:0000:0011:1001"); +#endif + verify(mysmprint("%'lld %'lld %'lld", 1LL, 222222222LL, 3333333333333LL), "1 222\xe2\x98\xbb""222\xe2\x98\xbb""22\xe2\x98\xbb""2 333\xe2\x98\xbb""3333\xe2\x98\xbb""333\xe2\x98\xbb""33\xe2\x98\xbb""3"); + verify(mysmprint("%'llx %'llX %'llb", 0x111111111111LL, 0xabcd12345678LL, 112342345LL), "1111:1111:1111 ABCD:1234:5678 110:1011:0010:0011:0101:0100:1001"); + verify(mysmprint("%.4f", 3.14159), "3\xe2\x98\xba""1416"); + + if(failed) + sysfatal("tests failed"); + exits(0); +} 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/testprint.c b/lib9/testprint.c @@ -0,0 +1,14 @@ +#include <u.h> +#include <libc.h> + +void +main(int argc, char **argv) +{ + char c; + + c = argv[1][strlen(argv[1])-1]; + if(c == 'f' || c == 'e' || c == 'g' || c == 'F' || c == 'E' || c == 'G') + print(argv[1], atof(argv[2])); + else if(c == 'x' || c == 'u' || c == 'd' || c == 'c' || c == 'C' || c == 'X') + print(argv[1], atoi(argv[2])); +} 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/tm2sec.c b/lib9/tm2sec.c @@ -0,0 +1,110 @@ +#include <u.h> +#include <libc.h> + +#include "zoneinfo.h" + +#define SEC2MIN 60L +#define SEC2HOUR (60L*SEC2MIN) +#define SEC2DAY (24L*SEC2HOUR) + +/* + * days per month plus days/year + */ +static int dmsize[] = +{ + 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; +static int ldmsize[] = +{ + 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * return the days/month for the given year + */ +static int * +yrsize(int y) +{ + if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0)) + return ldmsize; + else + return dmsize; +} + +/* + * compute seconds since Jan 1 1970 GMT + * and convert to our timezone. + */ +long +tm2sec(Tm *tm) +{ + Tinfo ti0, ti1, *ti; + long secs; + int i, yday, year, *d2m; + + secs = 0; + + /* + * seconds per year + */ + year = tm->year + 1900; + for(i = 1970; i < year; i++){ + d2m = yrsize(i); + secs += d2m[0] * SEC2DAY; + } + + /* + * if mday is set, use mon and mday to compute yday + */ + if(tm->mday){ + yday = 0; + d2m = yrsize(year); + for(i=0; i<tm->mon; i++) + yday += d2m[i+1]; + yday += tm->mday-1; + }else{ + yday = tm->yday; + } + secs += yday * SEC2DAY; + + /* + * hours, minutes, seconds + */ + secs += tm->hour * SEC2HOUR; + secs += tm->min * SEC2MIN; + secs += tm->sec; + + /* + * Assume the local time zone if zone is not GMT + */ + if(strcmp(tm->zone, "GMT") != 0) { + i = zonelookuptinfo(&ti0, secs); + ti = &ti0; + if (i != -1) + if (ti->tzoff!=0) { + /* + * to what local time period `secs' belongs? + */ + if (ti->tzoff>0) { + /* + * east of GMT; check previous local time transition + */ + if (ti->t+ti->tzoff > secs) + if (zonetinfo(&ti1, i-1)!=-1) + ti = &ti1; + } else + /* + * west of GMT; check next local time transition + */ + if (zonetinfo(&ti1, i+1)) + if (ti1.t+ti->tzoff < secs) + ti = &ti1; +// fprint(2, "tt: %ld+%d %ld\n", (long)ti->t, ti->tzoff, (long)secs); + secs -= ti->tzoff; + } + } + + if(secs < 0) + secs = 0; + return secs; +} 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,27 @@ +#include <u.h> +#include <libc.h> + +ulong +truerand(void) +{ + int i, n; + uchar buf[sizeof(ulong)]; + ulong x; + 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 %s: %r", randfile); + 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); + memmove(&x, buf, sizeof x); + return x; +} diff --git a/lib9/u.h b/lib9/u.h @@ -0,0 +1,190 @@ +#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__) || defined(__SunOS5_9__) || defined(__SunOS5_10__) + /* NOT USING #define __MAKECONTEXT_V2_SOURCE 1 / * SunOS */ +# else + /* What's left? */ +# 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 +#if defined(__FreeBSD__) +# include <sys/cdefs.h> + /* for strtoll */ +# undef __ISO_C_VISIBLE +# define __ISO_C_VISIBLE 1999 +# undef __LONG_LONG_SUPPORTED +# define __LONG_LONG_SUPPORTED +#endif +#if defined(__AIX__) +# define _XOPEN_SOURCE 1 +#endif +#if defined(__APPLE__) +# define _DARWIN_NO_64_BIT_INODE /* Snow Leopard */ +#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 <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 intptr_t intptr; +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; +#define AUTOFRAMEWORK(x) static int __p9l_autoframework_ ## x = 1; + +/* + * Gcc is too smart for its own good. + */ +#if defined(__GNUC__) +# undef strcmp /* causes way too many warnings */ +# if __GNUC__ >= 4 || (__GNUC__==3 && !defined(__APPLE_CC__)) +# undef AUTOLIB +# define AUTOLIB(x) int __p9l_autolib_ ## x __attribute__ ((weak)); +# undef AUTOFRAMEWORK +# define AUTOFRAMEWORK(x) int __p9l_autoframework_ ## x __attribute__ ((weak)); +# else +# undef AUTOLIB +# define AUTOLIB(x) static int __p9l_autolib_ ## x __attribute__ ((unused)); +# undef AUTOFRAMEWORK +# define AUTOFRAMEWORK(x) static int __p9l_autoframework_ ## 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,54 @@ +#ifndef _UTF_H_ +#define _UTF_H_ 1 +#if defined(__cplusplus) +extern "C" { +#endif + +typedef unsigned int Rune; /* 32 bits */ + +enum +{ + UTFmax = 4, /* 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 */ + Runemax = 0x10FFFF /* maximum rune value */ +}; + +/* 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/NOTICE b/lib9/utf/NOTICE @@ -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/README b/lib9/utf/README @@ -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/rune.c b/lib9/utf/rune.c @@ -0,0 +1,217 @@ +/* + * 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, + Bit5 = 2, + + 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 */ + T5 = ((1<<(Bit5+1))-1) ^ 0xFF, /* 1111 1000 */ + + Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0000 0000 0111 1111 */ + Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0000 0000 0111 1111 1111 */ + Rune3 = (1<<(Bit3+2*Bitx))-1, /* 0000 0000 1111 1111 1111 1111 */ + Rune4 = (1<<(Bit4+3*Bitx))-1, /* 0011 1111 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, c3; + 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; + } + + /* + * four character sequence + * 10000-10FFFF => T4 Tx Tx Tx + */ + if(UTFmax >= 4) { + c3 = *(uchar*)(str+3) ^ Tx; + if(c3 & Testx) + goto bad; + if(c < T5) { + l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4; + if(l <= Rune3) + goto bad; + if(l > Runemax) + goto bad; + *rune = l; + return 4; + } + } + + /* + * 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 + * 00080-007FF => T2 Tx + */ + if(c <= Rune2) { + str[0] = T2 | (c >> 1*Bitx); + str[1] = Tx | (c & Maskx); + return 2; + } + + /* + * three character sequence + * 00800-0FFFF => T3 Tx Tx + */ + if(c > Runemax) + c = Runeerror; + if(c <= Rune3) { + str[0] = T3 | (c >> 2*Bitx); + str[1] = Tx | ((c >> 1*Bitx) & Maskx); + str[2] = Tx | (c & Maskx); + return 3; + } + + /* + * four character sequence + * 010000-1FFFFF => T4 Tx Tx Tx + */ + str[0] = T4 | (c >> 3*Bitx); + str[1] = Tx | ((c >> 2*Bitx) & Maskx); + str[2] = Tx | ((c >> 1*Bitx) & Maskx); + str[3] = Tx | (c & Maskx); + return 4; +} + +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 + if(c <= Rune3 || c > Runemax) + nb += 3; + else + nb += 4; + } + return nb; +} + +int +fullrune(char *str, int n) +{ + int c; + + if(n <= 0) + return 0; + c = *(uchar*)str; + if(c < Tx) + return 1; + if(c < T3) + return n >= 2; + if(UTFmax == 3 || c < T4) + return n >= 3; + return n >= 4; +} 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/utfecpy.c b/lib9/utf/utfecpy.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. + */ +#define _BSD_SOURCE 1 /* memccpy */ +#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/lib9/write.c b/lib9/write.c @@ -0,0 +1,23 @@ +#include <u.h> +#define NOPLAN9DEFINES +#include <libc.h> + +long +p9write(int f, void *av, long n) +{ + char *a; + long m, t; + + a = av; + t = 0; + while(t < n){ + m = write(f, a+t, n-t); + if(m <= 0){ + if(t == 0) + return m; + break; + } + t += m; + } + return t; +} diff --git a/lib9/zoneinfo.c b/lib9/zoneinfo.c @@ -0,0 +1,215 @@ +#include <u.h> +#include <libc.h> + +/* + * Access local time entries of zoneinfo files. + * Formats 0 and 2 are supported, and 4-byte timestamps + * + * Copyright © 2008 M. Teichgräber + * Contributed under the terms of the Lucent Public License 1.02. + */ +#include "zoneinfo.h" + +static +struct Zoneinfo +{ + int timecnt; /* # of transition times */ + int typecnt; /* # of local time types */ + int charcnt; /* # of characters of time zone abbreviation strings */ + + uchar *ptime; + uchar *ptype; + uchar *ptt; + uchar *pzone; +} z; + +static uchar *tzdata; + +static +uchar* +readtzfile(char *file) +{ + uchar *p; + int fd; + Dir *d; + + fd = open(file, OREAD); + if (fd<0) + return nil; + d = dirfstat(fd); + if (d==nil) + return nil; + p = malloc(d->length); + if (p!=nil) + readn(fd, p, d->length); + free(d); + close(fd); + return p; +} +static char *zonefile; +void +tzfile(char *f) +{ + if (tzdata!=nil) { + free(tzdata); + tzdata = nil; + } + z.timecnt = 0; + zonefile = f; +} + +static +long +get4(uchar *p) +{ + return (p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; +} + +enum { + TTinfosz = 4+1+1, +}; + +static +int +parsehead(void) +{ + uchar *p; + int ver; + + ver = tzdata[4]; + if (ver!=0) + if (ver!='2') + return -1; + + p = tzdata + 4 + 1 + 15; + + z.timecnt = get4(p+3*4); + z.typecnt = get4(p+4*4); + if (z.typecnt==0) + return -1; + z.charcnt = get4(p+5*4); + z.ptime = p+6*4; + z.ptype = z.ptime + z.timecnt*4; + z.ptt = z.ptype + z.timecnt; + z.pzone = z.ptt + z.typecnt*TTinfosz; + return 0; +} + +static +void +ttinfo(Tinfo *ti, int tti) +{ + uchar *p; + int i; + + i = z.ptype[tti]; + assert(i<z.typecnt); + p = z.ptt + i*TTinfosz; + ti->tzoff = get4(p); + ti->dlflag = p[4]; + assert(p[5]<z.charcnt); + ti->zone = (char*)z.pzone + p[5]; +} + +static +void +readtimezone(void) +{ + char *tmp; + + z.timecnt = 0; + switch (zonefile==nil) { + default: + if ((tmp=getenv("timezone"))!=nil) { + tzdata = readtzfile(tmp); + free(tmp); + break; + } + zonefile = "/etc/localtime"; + /* fall through */ + case 0: + tzdata = readtzfile(zonefile); + } + if (tzdata==nil) + return; + + if (strncmp("TZif", (char*)tzdata, 4)!=0) + goto errfree; + + if (parsehead()==-1) { + errfree: + free(tzdata); + tzdata = nil; + z.timecnt = 0; + return; + } +} + +static +tlong +gett4(uchar *p) +{ + long l; + + l = get4(p); + if (l<0) + return 0; + return l; +} +int +zonetinfo(Tinfo *ti, int i) +{ + if (tzdata==nil) + readtimezone(); + if (i<0 || i>=z.timecnt) + return -1; + ti->t = gett4(z.ptime + 4*i); + ttinfo(ti, i); + return i; +} + +int +zonelookuptinfo(Tinfo *ti, tlong t) +{ + uchar *p; + int i; + tlong oldtt, tt; + + if (tzdata==nil) + readtimezone(); + oldtt = 0; + p = z.ptime; + for (i=0; i<z.timecnt; i++) { + tt = gett4(p); + if (t<tt) + break; + oldtt = tt; + p += 4; + } + if (i>0) { + ttinfo(ti, i-1); + ti->t = oldtt; +// fprint(2, "t:%ld off:%d dflag:%d %s\n", (long)ti->t, ti->tzoff, ti->dlflag, ti->zone); + return i-1; + } + return -1; +} + +void +zonedump(int fd) +{ + int i; + uchar *p; + tlong t; + Tinfo ti; + + if (tzdata==nil) + readtimezone(); + p = z.ptime; + for (i=0; i<z.timecnt; i++) { + t = gett4(p); + ttinfo(&ti, i); + fprint(fd, "%ld\t%d\t%d\t%s\n", (long)t, ti.tzoff, ti.dlflag, ti.zone); + p += 4; + } +} diff --git a/lib9/zoneinfo.h b/lib9/zoneinfo.h @@ -0,0 +1,19 @@ +#define zonetinfo _p9zonetinfo +#define zonedump _p9zonedump +#define zonelookuptinfo _p9zonelookuptinfo + +typedef long tlong; + +typedef +struct Tinfo +{ + long t; + int tzoff; + int dlflag; + char *zone; +} Tinfo; + +extern int zonelookuptinfo(Tinfo*, tlong); +extern int zonetinfo(Tinfo*, int); +extern void zonedump(int fd); + diff --git a/mk.log b/mk.log @@ -0,0 +1,28 @@ +make: Entering directory '/mnt/develop/mk/mk' +CC arc.c +CC archive.c +CC bufblock.c +CC env.c +CC file.c +CC graph.c +CC job.c +CC lex.c +CC main.c +CC match.c +CC mk.c +CC parse.c +CC recipe.c +CC rc.c +CC rule.c +CC run.c +CC sh.c +CC shell.c +CC shprint.c +CC symtab.c +CC var.c +CC varsub.c +CC word.c +CC unix.c +LD mk +built mk +make: Leaving directory '/mnt/develop/mk/mk' diff --git a/mk/Makefile b/mk/Makefile @@ -0,0 +1,17 @@ +# mk - mk unix port from plan9 +# Depends on ../lib9 + +TARG = mk + +OFILES = arc.o archive.o bufblock.o env.o file.o graph.o job.o lex.o \ + main.o match.o mk.o parse.o recipe.o rc.o rule.o run.o sh.o \ + shell.o shprint.o symtab.o var.o varsub.o word.o unix.o +MANFILES = mk.1 + +include ../std.mk + +post-install: + +pre-uninstall: + +post-install: diff --git a/mk/NOTICE b/mk/NOTICE @@ -0,0 +1,27 @@ +Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +Portions Copyright © 1995-1997 C H Forsyth (forsyth@caldo.demon.co.uk). All rights reserved. +Portions Copyright © 1997-1999 Vita Nuova Limited. All rights reserved. +Portions Copyright © 2000-2002 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved. + +Under a licence agreement with Lucent Technologies Inc. effective 1st March 2000, +Vita Nuova Holdings Limited has the right to determine (within a specified scope) +the form and content of sublicences for this software. + +Vita Nuova Holdings Limited now makes this software available as Free +Software under the terms of the `GNU General Public LIcense, Version 2' +(see the file LICENCE or http://www.fsf.org/copyleft/gpl.html for +the full terms and conditions). One of the conditions of that licence +is that you must keep intact all notices that refer to that licence and to the absence of +of any warranty: for this software, note that includes this NOTICE file in particular. + +This suite of programs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +`GNU General Public License' for more details. + +This copyright NOTICE applies to all files in this directory and +subdirectories, unless another copyright notice appears in a given +file or subdirectory. If you take code from this software to use in +other programs, you must somehow include with it an appropriate +copyright notice that includes the copyright notice and the other +notices above. diff --git a/mk/README b/mk/README @@ -0,0 +1,7 @@ +This is a Unix port of mk, +originally done for the Inferno operating system. + +Russ Cox repackaged this to build as a standalone +Unix program. Send comments about packaging to +Russ Cox <rsc@post.harvard.edu> + diff --git a/mk/arc.c b/mk/arc.c @@ -0,0 +1,52 @@ +#include "mk.h" + +Arc * +newarc(Node *n, Rule *r, char *stem, Resub *match) +{ + Arc *a; + + a = (Arc *)Malloc(sizeof(Arc)); + a->n = n; + a->r = r; + a->stem = strdup(stem); + rcopy(a->match, match, NREGEXP); + a->next = 0; + a->flag = 0; + a->prog = r->prog; + return(a); +} + +void +dumpa(char *s, Arc *a) +{ + char buf[1024]; + + Bprint(&bout, "%sArc@%p: n=%p r=%p flag=0x%x stem='%s'", + s, a, a->n, a->r, a->flag, a->stem); + if(a->prog) + Bprint(&bout, " prog='%s'", a->prog); + Bprint(&bout, "\n"); + + if(a->n){ + snprint(buf, sizeof(buf), "%s ", (*s == ' ')? s:""); + dumpn(buf, a->n); + } +} + +void +nrep(void) +{ + Symtab *sym; + Word *w; + + sym = symlook("NREP", S_VAR, 0); + if(sym){ + w = sym->u.ptr; + if (w && w->s && *w->s) + nreps = atoi(w->s); + } + if(nreps < 1) + nreps = 1; + if(DEBUG(D_GRAPH)) + Bprint(&bout, "nreps = %d\n", nreps); +} diff --git a/mk/archive.c b/mk/archive.c @@ -0,0 +1,253 @@ +#include "mk.h" +#define ARMAG "!<arch>\n" +#define SARMAG 8 + +#define ARFMAG "`\n" +#define SARNAME 16 + +struct ar_hdr +{ + char name[SARNAME]; + char date[12]; + char uid[6]; + char gid[6]; + char mode[8]; + char size[10]; + char fmag[2]; +}; +#define SAR_HDR (SARNAME+44) + +static int dolong = 1; + +static void atimes(char *); +static char *split(char*, char**); + +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; +} +long +atimeof(int force, char *name) +{ + Symtab *sym; + long t; + char *archive, *member, buf[512]; + + archive = split(name, &member); + if(archive == 0) + Exit(); + + t = mtime(archive); + sym = symlook(archive, S_AGG, 0); + if(sym){ + if(force || (t > sym->u.value)){ + atimes(archive); + sym->u.value = t; + } + } + else{ + atimes(archive); + /* mark the aggegate as having been done */ + symlook(strdup(archive), S_AGG, "")->u.value = t; + } + /* truncate long member name to sizeof of name field in archive header */ + if(dolong) + snprint(buf, sizeof(buf), "%s(%s)", archive, member); + else + snprint(buf, sizeof(buf), "%s(%.*s)", archive, SARNAME, member); + sym = symlook(buf, S_TIME, 0); + if (sym) + return sym->u.value; + return 0; +} + +void +atouch(char *name) +{ + char *archive, *member; + int fd, i; + struct ar_hdr h; + long t; + + archive = split(name, &member); + if(archive == 0) + Exit(); + + fd = open(archive, ORDWR); + if(fd < 0){ + fd = create(archive, OWRITE, 0666); + if(fd < 0){ + fprint(2, "create %s: %r\n", archive); + Exit(); + } + write(fd, ARMAG, SARMAG); + } + if(symlook(name, S_TIME, 0)){ + /* hoon off and change it in situ */ + LSEEK(fd, SARMAG, 0); + while(read(fd, (char *)&h, sizeof(h)) == sizeof(h)){ + for(i = SARNAME-1; i > 0 && h.name[i] == ' '; i--) + ; + h.name[i+1]=0; + if(strcmp(member, h.name) == 0){ + t = SARNAME-sizeof(h); /* ughgghh */ + LSEEK(fd, t, 1); + fprint(fd, "%-12ld", time(0)); + break; + } + t = atol(h.size); + if(t&01) t++; + LSEEK(fd, t, 1); + } + } + close(fd); +} + +static void +atimes(char *ar) +{ + struct ar_hdr h; + long t; + int fd, i, namelen; + char buf[2048], *p, *strings; + char name[1024]; + Symtab *sym; + + strings = nil; + fd = open(ar, OREAD); + if(fd < 0) + return; + + if(read(fd, buf, SARMAG) != SARMAG){ + close(fd); + return; + } + while(readn(fd, (char *)&h, sizeof(h)) == sizeof(h)){ + t = atol(h.date); + if(t == 0) /* as it sometimes happens; thanks ken */ + t = 1; + namelen = 0; + if(memcmp(h.name, "#1/", 3) == 0){ /* BSD */ + namelen = atoi(h.name+3); + if(namelen >= sizeof name){ + namelen = 0; + goto skip; + } + if(readn(fd, name, namelen) != namelen) + break; + name[namelen] = 0; + }else if(memcmp(h.name, "// ", 2) == 0){ /* GNU */ + /* date, uid, gid, mode all ' ' */ + for(i=2; i<16+12+6+6+8; i++) + if(h.name[i] != ' ') + goto skip; + t = atol(h.size); + if(t&01) + t++; + free(strings); + strings = malloc(t+1); + if(strings){ + if(readn(fd, strings, t) != t){ + free(strings); + strings = nil; + break; + } + strings[t] = 0; + continue; + } + goto skip; + }else if(strings && h.name[0]=='/' && isdigit((uchar)h.name[1])){ + i = strtol(h.name+1, &p, 10); + if(*p != ' ' || i >= strlen(strings)) + goto skip; + p = strings+i; + for(; *p && *p != '/'; p++) + ; + namelen = p-(strings+i); + if(namelen >= sizeof name){ + namelen = 0; + goto skip; + } + memmove(name, strings+i, namelen); + name[namelen] = 0; + namelen = 0; + }else{ + strncpy(name, h.name, sizeof(h.name)); + for(i = sizeof(h.name)-1; i > 0 && name[i] == ' '; i--) + ; + if(name[i] == '/') /* system V bug */ + i--; + name[i+1]=0; + } + snprint(buf, sizeof buf, "%s(%s)", ar, name); + sym = symlook(strdup(buf), S_TIME, (void *)t); + sym->u.value = t; + skip: + t = atol(h.size); + if(t&01) t++; + t -= namelen; + LSEEK(fd, t, 1); + } + close(fd); + free(strings); +} + +static int +type(char *file) +{ + int fd; + char buf[SARMAG]; + + fd = open(file, OREAD); + if(fd < 0){ + if(symlook(file, S_BITCH, 0) == 0){ + if(strlen(file) < 2 || strcmp(file+strlen(file)-2, ".a") != 0) + Bprint(&bout, "%s doesn't exist: assuming it will be an archive\n", file); + symlook(file, S_BITCH, (void *)file); + } + return 1; + } + if(read(fd, buf, SARMAG) != SARMAG){ + close(fd); + return 0; + } + close(fd); + return !strncmp(ARMAG, buf, SARMAG); +} + +static char* +split(char *name, char **member) +{ + char *p, *q; + + p = strdup(name); + q = utfrune(p, '('); + if(q){ + *q++ = 0; + if(member) + *member = q; + q = utfrune(q, ')'); + if (q) + *q = 0; + if(type(p)) + return p; + free(p); + fprint(2, "mk: '%s' is not an archive\n", name); + } + return 0; +} diff --git a/mk/bufblock.c b/mk/bufblock.c @@ -0,0 +1,88 @@ +#include "mk.h" + +static Bufblock *freelist; +#define QUANTA 4096 + +Bufblock * +newbuf(void) +{ + Bufblock *p; + + if (freelist) { + p = freelist; + freelist = freelist->next; + } else { + p = (Bufblock *) Malloc(sizeof(Bufblock)); + p->start = Malloc(QUANTA*sizeof(*p->start)); + p->end = p->start+QUANTA; + } + p->current = p->start; + *p->start = 0; + p->next = 0; + return p; +} + +void +freebuf(Bufblock *p) +{ + p->next = freelist; + freelist = p; +} + +void +growbuf(Bufblock *p) +{ + int n; + Bufblock *f; + char *cp; + + n = p->end-p->start+QUANTA; + /* search the free list for a big buffer */ + for (f = freelist; f; f = f->next) { + if (f->end-f->start >= n) { + memcpy(f->start, p->start, p->end-p->start); + cp = f->start; + f->start = p->start; + p->start = cp; + cp = f->end; + f->end = p->end; + p->end = cp; + f->current = f->start; + break; + } + } + if (!f) { /* not found - grow it */ + p->start = Realloc(p->start, n); + p->end = p->start+n; + } + p->current = p->start+n-QUANTA; +} + +void +bufcpy(Bufblock *buf, char *cp, int n) +{ + + while (n--) + insert(buf, *cp++); +} + +void +insert(Bufblock *buf, int c) +{ + + if (buf->current >= buf->end) + growbuf(buf); + *buf->current++ = c; +} + +void +rinsert(Bufblock *buf, Rune r) +{ + int n; + + n = runelen(r); + if (buf->current+n > buf->end) + growbuf(buf); + runetochar(buf->current, &r); + buf->current += n; +} diff --git a/mk/env.c b/mk/env.c @@ -0,0 +1,149 @@ +#include "mk.h" + +enum { + ENVQUANTA=10 +}; + +Envy *envy; +static int nextv; + +static char *myenv[] = +{ + "target", + "stem", + "prereq", + "pid", + "nproc", + "newprereq", + "alltarget", + "newmember", + "stem0", /* must be in order from here */ + "stem1", + "stem2", + "stem3", + "stem4", + "stem5", + "stem6", + "stem7", + "stem8", + "stem9", + 0 +}; + +void +initenv(void) +{ + char **p; + + for(p = myenv; *p; p++) + symlook(*p, S_INTERNAL, (void *)""); + readenv(); /* o.s. dependent */ +} + +static void +envinsert(char *name, Word *value) +{ + static int envsize; + + if (nextv >= envsize) { + envsize += ENVQUANTA; + envy = (Envy *) Realloc((char *) envy, envsize*sizeof(Envy)); + } + envy[nextv].name = name; + envy[nextv++].values = value; +} + +static void +envupd(char *name, Word *value) +{ + Envy *e; + + for(e = envy; e->name; e++) + if(strcmp(name, e->name) == 0){ + delword(e->values); + e->values = value; + return; + } + e->name = name; + e->values = value; + envinsert(0,0); +} + +static void +ecopy(Symtab *s) +{ + char **p; + + if(symlook(s->name, S_NOEXPORT, 0)) + return; + for(p = myenv; *p; p++) + if(strcmp(*p, s->name) == 0) + return; + envinsert(s->name, s->u.ptr); +} + +void +execinit(void) +{ + char **p; + + nextv = 0; + for(p = myenv; *p; p++) + envinsert(*p, stow("")); + + symtraverse(S_VAR, ecopy); + envinsert(0, 0); +} + +Envy* +buildenv(Job *j, int slot) +{ + char **p, *cp, *qp; + Word *w, *v, **l; + int i; + char buf[256]; + + envupd("target", wdup(j->t)); + if(j->r->attr&REGEXP) + envupd("stem",newword("")); + else + envupd("stem", newword(j->stem)); + envupd("prereq", wdup(j->p)); + sprint(buf, "%d", getpid()); + envupd("pid", newword(buf)); + sprint(buf, "%d", slot); + envupd("nproc", newword(buf)); + envupd("newprereq", wdup(j->np)); + envupd("alltarget", wdup(j->at)); + l = &v; + v = w = wdup(j->np); + while(w){ + cp = strchr(w->s, '('); + if(cp){ + qp = strchr(cp+1, ')'); + if(qp){ + *qp = 0; + strcpy(w->s, cp+1); + l = &w->next; + w = w->next; + continue; + } + } + *l = w->next; + free(w->s); + free(w); + w = *l; + } + envupd("newmember", v); + /* update stem0 -> stem9 */ + for(p = myenv; *p; p++) + if(strcmp(*p, "stem0") == 0) + break; + for(i = 0; *p; i++, p++){ + if((j->r->attr&REGEXP) && j->match[i]) + envupd(*p, newword(j->match[i])); + else + envupd(*p, newword("")); + } + return envy; +} diff --git a/mk/file.c b/mk/file.c @@ -0,0 +1,90 @@ +#include "mk.h" + +/* table-driven version in bootes dump of 12/31/96 */ + +long +mtime(char *name) +{ + return mkmtime(name); +} + +long +timeof(char *name, int force) +{ + Symtab *sym; + long t; + + if(utfrune(name, '(')) + return atimeof(force, name); /* archive */ + + if(force) + return mtime(name); + + + sym = symlook(name, S_TIME, 0); + if (sym) + return sym->u.value; + + t = mtime(name); + if(t == 0) + return 0; + + symlook(name, S_TIME, (void*)t); /* install time in cache */ + return t; +} + +void +touch(char *name) +{ + Bprint(&bout, "touch(%s)\n", name); + if(nflag) + return; + + if(utfrune(name, '(')) + atouch(name); /* archive */ + else if(chgtime(name) < 0) { + fprint(2, "%s: %r\n", name); + Exit(); + } +} + +void +delete(char *name) +{ + if(utfrune(name, '(') == 0) { /* file */ + if(remove(name) < 0) + fprint(2, "remove %s: %r\n", name); + } else + fprint(2, "hoon off; mk can'tdelete archive members\n"); +} + +void +timeinit(char *s) +{ + long t; + char *cp; + Rune r; + int c, n; + + t = time(0); + while (*s) { + cp = s; + do{ + n = chartorune(&r, s); + if (r == ' ' || r == ',' || r == '\n') + break; + s += n; + } while(*s); + c = *s; + *s = 0; + symlook(strdup(cp), S_TIME, (void *)t)->u.value = t; + if (c) + *s++ = c; + while(*s){ + n = chartorune(&r, s); + if(r != ' ' && r != ',' && r != '\n') + break; + s += n; + } + } +} diff --git a/mk/fns.h b/mk/fns.h @@ -0,0 +1,88 @@ +#undef waitfor +#define waitfor mkwaitfor + +void addrule(char*, Word*, char*, Word*, int, int, char*); +void addrules(Word*, Word*, char*, int, int, char*); +void addw(Word*, char*); +void assert(char*, int); +int assline(Biobuf *, Bufblock *); +long atimeof(int,char*); +void atouch(char*); +void bufcpy(Bufblock *, char *, int); +Envy *buildenv(Job*, int); +void catchnotes(void); +int chgtime(char*); +void clrmade(Node*); +void delete(char*); +void delword(Word*); +int dorecipe(Node*); +void dumpa(char*, Arc*); +void dumpj(char*, Job*, int); +void dumpn(char*, Node*); +void dumpr(char*, Rule*); +void dumpv(char*); +void dumpw(char*, Word*); +void execinit(void); +int execsh(char*, char*, Bufblock*, Envy*, Shell*, Word*); +void Exit(void); +void expunge(int, char*); +void freebuf(Bufblock*); +void front(char*); +Node *graph(char*); +void growbuf(Bufblock *); +void initenv(void); +void initshell(void); +void insert(Bufblock *, int); +void ipop(void); +void ipush(void); +void killchildren(char*); +void *Malloc(int); +char *maketmp(int*); +int match(char*, char*, char*, Shell*); +char *membername(char*, int, char*); +void mk(char*); +unsigned long mkmtime(char*); +long mtime(char*); +Arc *newarc(Node*, Rule*, char*, Resub*); +Bufblock *newbuf(void); +Job *newjob(Rule*, Node*, char*, char**, Word*, Word*, Word*, Word*); +Word *newword(char*); +int nextrune(Biobuf*, int); +int nextslot(void); +void nproc(void); +void nrep(void); +int outofdate(Node*, Arc*, int); +void parse(char*, int, int); +int pipecmd(char*, Envy*, int*, Shell*, Word*); +void popshell(void); +void prusage(void); +void pushshell(void); +void rcopy(char**, Resub*, int); +void readenv(void); +void *Realloc(void*, int); +void rinsert(Bufblock *, Rune); +char *rulecnt(void); +void run(Job*); +char *setshell(Word*); +void setvar(char*, void*); +int shargv(Word*, int, char***); +char *shname(char*); +void shprint(char*, Envy*, Bufblock*, Shell*); +Word *stow(char*); +void subst(char*, char*, char*); +void symdel(char*, int); +void syminit(void); +Symtab *symlook(char*, int, void*); +void symstat(void); +void symtraverse(int, void(*)(Symtab*)); +void timeinit(char*); +long timeof(char*, int); +void touch(char*); +void update(int, Node*); +void usage(void); +Word *varsub(char**); +int waitfor(char*); +int waitup(int, int*); +Word *wdup(Word*); +int work(Node*, Node*, Arc*); +char *wtos(Word*, int); diff --git a/mk/graph.c b/mk/graph.c @@ -0,0 +1,279 @@ +#include "mk.h" + +static Node *applyrules(char *, char *); +static void togo(Node *); +static int vacuous(Node *); +static Node *newnode(char *); +static void trace(char *, Arc *); +static void cyclechk(Node *); +static void ambiguous(Node *); +static void attribute(Node *); + +Node * +graph(char *target) +{ + Node *node; + char *cnt; + + cnt = rulecnt(); + node = applyrules(target, cnt); + free(cnt); + cyclechk(node); + node->flags |= PROBABLE; /* make sure it doesn't get deleted */ + vacuous(node); + ambiguous(node); + attribute(node); + return(node); +} + +static Node * +applyrules(char *target, char *cnt) +{ + Symtab *sym; + Node *node; + Rule *r; + Arc head, *a = &head; + Word *w; + char stem[NAMEBLOCK], buf[NAMEBLOCK]; + Resub rmatch[NREGEXP]; + +/* print("applyrules(%lux='%s')\n", target, target); */ + sym = symlook(target, S_NODE, 0); + if(sym) + return sym->u.ptr; + target = strdup(target); + node = newnode(target); + head.n = 0; + head.next = 0; + sym = symlook(target, S_TARGET, 0); + memset((char*)rmatch, 0, sizeof(rmatch)); + for(r = sym? sym->u.ptr:0; r; r = r->chain){ + if(r->attr&META) continue; + if(strcmp(target, r->target)) continue; + if((!r->recipe || !*r->recipe) && (!r->tail || !r->tail->s || !*r->tail->s)) continue; /* no effect; ignore */ + if(cnt[r->rule] >= nreps) continue; + cnt[r->rule]++; + node->flags |= PROBABLE; + +/* if(r->attr&VIR) + * node->flags |= VIRTUAL; + * if(r->attr&NOREC) + * node->flags |= NORECIPE; + * if(r->attr&DEL) + * node->flags |= DELETE; + */ + if(!r->tail || !r->tail->s || !*r->tail->s) { + a->next = newarc((Node *)0, r, "", rmatch); + a = a->next; + } else + for(w = r->tail; w; w = w->next){ + a->next = newarc(applyrules(w->s, cnt), r, "", rmatch); + a = a->next; + } + cnt[r->rule]--; + head.n = node; + } + for(r = metarules; r; r = r->next){ + if((!r->recipe || !*r->recipe) && (!r->tail || !r->tail->s || !*r->tail->s)) continue; /* no effect; ignore */ + if ((r->attr&NOVIRT) && a != &head && (a->r->attr&VIR)) + continue; + if(r->attr&REGEXP){ + stem[0] = 0; + patrule = r; + memset((char*)rmatch, 0, sizeof(rmatch)); + if(regexec(r->pat, node->name, rmatch, NREGEXP) == 0) + continue; + } else { + if(!match(node->name, r->target, stem, r->shellt)) continue; + } + if(cnt[r->rule] >= nreps) continue; + cnt[r->rule]++; + +/* if(r->attr&VIR) + * node->flags |= VIRTUAL; + * if(r->attr&NOREC) + * node->flags |= NORECIPE; + * if(r->attr&DEL) + * node->flags |= DELETE; + */ + + if(!r->tail || !r->tail->s || !*r->tail->s) { + a->next = newarc((Node *)0, r, stem, rmatch); + a = a->next; + } else + for(w = r->tail; w; w = w->next){ + if(r->attr&REGEXP) + regsub(w->s, buf, sizeof buf, rmatch, NREGEXP); + else + subst(stem, w->s, buf); + a->next = newarc(applyrules(buf, cnt), r, stem, rmatch); + a = a->next; + } + cnt[r->rule]--; + } + a->next = node->prereqs; + node->prereqs = head.next; + return(node); +} + +static void +togo(Node *node) +{ + Arc *la, *a; + + /* delete them now */ + la = 0; + for(a = node->prereqs; a; la = a, a = a->next) + if(a->flag&TOGO){ + if(a == node->prereqs) + node->prereqs = a->next; + else + la->next = a->next, a = la; + } +} + +static int +vacuous(Node *node) +{ + Arc *la, *a; + int vac = !(node->flags&PROBABLE); + + if(node->flags&READY) + return(node->flags&VACUOUS); + node->flags |= READY; + for(a = node->prereqs; a; a = a->next) + if(a->n && vacuous(a->n) && (a->r->attr&META)) + a->flag |= TOGO; + else + vac = 0; + /* if a rule generated arcs that DON'T go; no others from that rule go */ + for(a = node->prereqs; a; a = a->next) + if((a->flag&TOGO) == 0) + for(la = node->prereqs; la; la = la->next) + if((la->flag&TOGO) && (la->r == a->r)){ + la->flag &= ~TOGO; + } + togo(node); + if(vac) + node->flags |= VACUOUS; + return(vac); +} + +static Node * +newnode(char *name) +{ + register Node *node; + + node = (Node *)Malloc(sizeof(Node)); + symlook(name, S_NODE, (void *)node); + node->name = name; + node->time = timeof(name, 0); + node->prereqs = 0; + node->flags = node->time? PROBABLE : 0; + node->next = 0; + return(node); +} + +void +dumpn(char *s, Node *n) +{ + char buf[1024]; + Arc *a; + + snprint(buf, sizeof buf, "%s ", (*s == ' ')? s:""); + Bprint(&bout, "%s%s@%ld: time=%ld flags=0x%x next=%ld\n", + s, n->name, n, n->time, n->flags, n->next); + for(a = n->prereqs; a; a = a->next) + dumpa(buf, a); +} + +static void +trace(char *s, Arc *a) +{ + fprint(2, "\t%s", s); + while(a){ + fprint(2, " <-(%s:%d)- %s", a->r->file, a->r->line, + a->n? a->n->name:""); + if(a->n){ + for(a = a->n->prereqs; a; a = a->next) + if(*a->r->recipe) break; + } else + a = 0; + } + fprint(2, "\n"); +} + +static void +cyclechk(Node *n) +{ + Arc *a; + + if((n->flags&CYCLE) && n->prereqs){ + fprint(2, "mk: cycle in graph detected at target %s\n", n->name); + Exit(); + } + n->flags |= CYCLE; + for(a = n->prereqs; a; a = a->next) + if(a->n) + cyclechk(a->n); + n->flags &= ~CYCLE; +} + +static void +ambiguous(Node *n) +{ + Arc *a; + Rule *r = 0; + Arc *la; + int bad = 0; + + la = 0; + for(a = n->prereqs; a; a = a->next){ + if(a->n) + ambiguous(a->n); + if(*a->r->recipe == 0) continue; + if(r == 0) + r = a->r, la = a; + else{ + if(r->recipe != a->r->recipe){ + if((r->attr&META) && !(a->r->attr&META)){ + la->flag |= TOGO; + r = a->r, la = a; + } else if(!(r->attr&META) && (a->r->attr&META)){ + a->flag |= TOGO; + continue; + } + } + if(r->recipe != a->r->recipe){ + if(bad == 0){ + fprint(2, "mk: ambiguous recipes for %s:\n", n->name); + bad = 1; + trace(n->name, la); + } + trace(n->name, a); + } + } + } + if(bad) + Exit(); + togo(n); +} + +static void +attribute(Node *n) +{ + register Arc *a; + + for(a = n->prereqs; a; a = a->next){ + if(a->r->attr&VIR) + n->flags |= VIRTUAL; + if(a->r->attr&NOREC) + n->flags |= NORECIPE; + if(a->r->attr&DEL) + n->flags |= DELETE; + if(a->n) + attribute(a->n); + } + if(n->flags&VIRTUAL) + n->time = 0; +} diff --git a/mk/job.c b/mk/job.c @@ -0,0 +1,33 @@ +#include "mk.h" + +Job * +newjob(Rule *r, Node *nlist, char *stem, char **match, Word *pre, Word *npre, Word *tar, Word *atar) +{ + register Job *j; + + j = (Job *)Malloc(sizeof(Job)); + j->r = r; + j->n = nlist; + j->stem = stem; + j->match = match; + j->p = pre; + j->np = npre; + j->t = tar; + j->at = atar; + j->nproc = -1; + j->next = 0; + return(j); +} + +void +dumpj(char *s, Job *j, int all) +{ + Bprint(&bout, "%s\n", s); + while(j){ + Bprint(&bout, "job@%ld: r=%ld n=%ld stem='%s' nproc=%d\n", + j, j->r, j->n, j->stem, j->nproc); + Bprint(&bout, "\ttarget='%s' alltarget='%s' prereq='%s' nprereq='%s'\n", + wtos(j->t, ' '), wtos(j->at, ' '), wtos(j->p, ' '), wtos(j->np, ' ')); + j = all? j->next : 0; + } +} diff --git a/mk/lex.c b/mk/lex.c @@ -0,0 +1,146 @@ +#include "mk.h" + +static int bquote(Biobuf*, Bufblock*); + +/* + * Assemble a line skipping blank lines, comments, and eliding + * escaped newlines + */ +int +assline(Biobuf *bp, Bufblock *buf) +{ + int c; + int lastc; + + buf->current=buf->start; + while ((c = nextrune(bp, 1)) >= 0){ + switch(c) + { + case '\r': /* consumes CRs for Win95 */ + continue; + case '\n': + if (buf->current != buf->start) { + insert(buf, 0); + return 1; + } + break; /* skip empty lines */ + case '\\': + case '\'': + case '"': + rinsert(buf, c); + if (shellt->escapetoken(bp, buf, 1, c) == 0) + Exit(); + break; + case '`': + if (bquote(bp, buf) == 0) + Exit(); + break; + case '#': + lastc = '#'; + while ((c = Bgetc(bp)) != '\n') { + if (c < 0) + goto eof; + if(c != '\r') + lastc = c; + } + mkinline++; + if (lastc == '\\') + break; /* propagate escaped newlines??*/ + if (buf->current != buf->start) { + insert(buf, 0); + return 1; + } + break; + default: + rinsert(buf, c); + break; + } + } +eof: + insert(buf, 0); + return *buf->start != 0; +} + +/* + * assemble a back-quoted shell command into a buffer + */ +static int +bquote(Biobuf *bp, Bufblock *buf) +{ + int c, line, term; + int start; + + line = mkinline; + while((c = Bgetrune(bp)) == ' ' || c == '\t') + ; + if(c == '{'){ + term = '}'; /* rc style */ + while((c = Bgetrune(bp)) == ' ' || c == '\t') + ; + } else + term = '`'; /* sh style */ + + start = buf->current-buf->start; + for(;c > 0; c = nextrune(bp, 0)){ + if(c == term){ + insert(buf, '\n'); + insert(buf,0); + buf->current = buf->start+start; + execinit(); + execsh(0, buf->current, buf, envy, shellt, shellcmd); + return 1; + } + if(c == '\n') + break; + if(c == '\'' || c == '"' || c == '\\'){ + insert(buf, c); + if(!shellt->escapetoken(bp, buf, 1, c)) + return 0; + continue; + } + rinsert(buf, c); + } + SYNERR(line); + fprint(2, "missing closing %c after `\n", term); + return 0; +} + +/* + * get next character stripping escaped newlines + * the flag specifies whether escaped newlines are to be elided or + * replaced with a blank. + */ +int +nextrune(Biobuf *bp, int elide) +{ + int c, c2; + static int savec; + + if(savec){ + c = savec; + savec = 0; + return c; + } + + for (;;) { + c = Bgetrune(bp); + if (c == '\\') { + c2 = Bgetrune(bp); + if(c2 == '\r'){ + savec = c2; + c2 = Bgetrune(bp); + } + if (c2 == '\n') { + savec = 0; + mkinline++; + if (elide) + continue; + return ' '; + } + Bungetrune(bp); + } + if (c == '\n') + mkinline++; + return c; + } +} diff --git a/mk/main.c b/mk/main.c @@ -0,0 +1,287 @@ +#include "mk.h" + +#define MKFILE "mkfile" + +int debug; +Rule *rules, *metarules; +int nflag = 0; +int tflag = 0; +int iflag = 0; +int kflag = 0; +int aflag = 0; +int uflag = 0; +char *explain = 0; +Word *target1; +int nreps = 1; +Job *jobs; +Biobuf bout; +Rule *patrule; +void badusage(void); +#ifdef PROF +short buf[10000]; +#endif + +int +main(int argc, char **argv) +{ + Word *w; + char *s, *temp; + char *files[256], **f = files, **ff; + int sflag = 0; + int i; + int tfd = -1; + Biobuf tb; + Bufblock *buf; + Bufblock *whatif; + + /* + * start with a copy of the current environment variables + * instead of sharing them + */ + + Binit(&bout, 1, OWRITE); + buf = newbuf(); + whatif = 0; + USED(argc); + for(argv++; *argv && (**argv == '-'); argv++) + { + bufcpy(buf, argv[0], strlen(argv[0])); + insert(buf, ' '); + switch(argv[0][1]) + { + case 'a': + aflag = 1; + break; + case 'd': + if(*(s = &argv[0][2])) + while(*s) switch(*s++) + { + case 'p': debug |= D_PARSE; break; + case 'g': debug |= D_GRAPH; break; + case 'e': debug |= D_EXEC; break; + } + else + debug = 0xFFFF; + break; + case 'e': + explain = &argv[0][2]; + break; + case 'f': + if(*++argv == 0) + badusage(); + *f++ = *argv; + bufcpy(buf, argv[0], strlen(argv[0])); + insert(buf, ' '); + break; + case 'i': + iflag = 1; + break; + case 'k': + kflag = 1; + break; + case 'n': + nflag = 1; + break; + case 's': + sflag = 1; + break; + case 't': + tflag = 1; + break; + case 'u': + uflag = 1; + break; + case 'w': + if(whatif == 0) + whatif = newbuf(); + else + insert(whatif, ' '); + if(argv[0][2]) + bufcpy(whatif, &argv[0][2], strlen(&argv[0][2])); + else { + if(*++argv == 0) + badusage(); + bufcpy(whatif, &argv[0][0], strlen(&argv[0][0])); + } + break; + default: + badusage(); + } + } +#ifdef PROF + { + extern etext(); + monitor(main, etext, buf, sizeof buf, 300); + } +#endif + + if(aflag) + iflag = 1; + usage(); + syminit(); + initshell(); + initenv(); + usage(); + + /* + assignment args become null strings + */ + temp = 0; + for(i = 0; argv[i]; i++) if(utfrune(argv[i], '=')){ + bufcpy(buf, argv[i], strlen(argv[i])); + insert(buf, ' '); + if(tfd < 0){ + temp = maketmp(&tfd); + if(temp == 0) { + fprint(2, "temp file: %r\n"); + Exit(); + } + Binit(&tb, tfd, OWRITE); + } + Bprint(&tb, "%s\n", argv[i]); + *argv[i] = 0; + } + if(tfd >= 0){ + Bflush(&tb); + LSEEK(tfd, 0L, 0); + parse("command line args", tfd, 1); + remove(temp); + } + + if (buf->current != buf->start) { + buf->current--; + insert(buf, 0); + } + symlook("MKFLAGS", S_VAR, (void *) stow(buf->start)); + buf->current = buf->start; + for(i = 0; argv[i]; i++){ + if(*argv[i] == 0) continue; + if(i) + insert(buf, ' '); + bufcpy(buf, argv[i], strlen(argv[i])); + } + insert(buf, 0); + symlook("MKARGS", S_VAR, (void *) stow(buf->start)); + freebuf(buf); + + if(f == files){ + if(access(MKFILE, 4) == 0) + parse(MKFILE, open(MKFILE, 0), 0); + } else + for(ff = files; ff < f; ff++) + parse(*ff, open(*ff, 0), 0); + if(DEBUG(D_PARSE)){ + dumpw("default targets", target1); + dumpr("rules", rules); + dumpr("metarules", metarules); + dumpv("variables"); + } + if(whatif){ + insert(whatif, 0); + timeinit(whatif->start); + freebuf(whatif); + } + execinit(); + /* skip assignment args */ + while(*argv && (**argv == 0)) + argv++; + + catchnotes(); + if(*argv == 0){ + if(target1) + for(w = target1; w; w = w->next) + mk(w->s); + else { + fprint(2, "mk: nothing to mk\n"); + Exit(); + } + } else { + if(sflag){ + for(; *argv; argv++) + if(**argv) + mk(*argv); + } else { + Word *head, *tail, *t; + + /* fake a new rule with all the args as prereqs */ + tail = 0; + t = 0; + for(; *argv; argv++) + if(**argv){ + if(tail == 0) + tail = t = newword(*argv); + else { + t->next = newword(*argv); + t = t->next; + } + } + if(tail->next == 0) + mk(tail->s); + else { + head = newword("command line arguments"); + addrules(head, tail, strdup(""), VIR, mkinline, 0); + mk(head->s); + } + } + } + if(uflag) + prusage(); + exits(0); + return 0; +} + +void +badusage(void) +{ + + fprint(2, "Usage: mk [-f file] [-n] [-a] [-e] [-t] [-k] [-i] [-d[egp]] [targets ...]\n"); + Exit(); +} + +void * +Malloc(int n) +{ + register void *s; + + s = malloc(n); + if(!s) { + fprint(2, "mk: cannot alloc %d bytes\n", n); + Exit(); + } + return(s); +} + +void * +Realloc(void *s, int n) +{ + if(s) + s = realloc(s, n); + else + s = malloc(n); + if(!s) { + fprint(2, "mk: cannot alloc %d bytes\n", n); + Exit(); + } + return(s); +} + +void +assert(char *s, int n) +{ + if(!n){ + fprint(2, "mk: Assertion ``%s'' failed.\n", s); + Exit(); + } +} + +void +regerror(char *s) +{ + if(patrule) + fprint(2, "mk: %s:%d: regular expression error; %s\n", + patrule->file, patrule->line, s); + else + fprint(2, "mk: %s:%d: regular expression error; %s\n", + infile, mkinline, s); + Exit(); +} diff --git a/mk/match.c b/mk/match.c @@ -0,0 +1,49 @@ +#include "mk.h" + +int +match(char *name, char *template, char *stem, Shell *sh) +{ + Rune r; + int n; + + while(*name && *template){ + n = chartorune(&r, template); + if (PERCENT(r)) + break; + while (n--) + if(*name++ != *template++) + return 0; + } + if(!PERCENT(*template)) + return 0; + n = strlen(name)-strlen(template+1); + if (n < 0) + return 0; + if (strcmp(template+1, name+n)) + return 0; + strncpy(stem, name, n); + stem[n] = 0; + if(*template == '&') + return !sh->charin(stem, "./"); + return 1; +} + +void +subst(char *stem, char *template, char *dest) +{ + Rune r; + char *s; + int n; + + while(*template){ + n = chartorune(&r, template); + if (PERCENT(r)) { + template += n; + for (s = stem; *s; s++) + *dest++ = *s; + } else + while (n--) + *dest++ = *template++; + } + *dest = 0; +} diff --git a/mk/mk b/mk/mk Binary files differ. diff --git a/mk/mk.1 b/mk/mk.1 @@ -0,0 +1,691 @@ +.TH MK 1 +.SH NAME +mk \- maintain (make) related files +.SH SYNOPSIS +.B mk +[ +.B -f +.I mkfile +] ... +[ +.I option ... +] +[ +.I target ... +] +.SH DESCRIPTION +.I Mk +uses the dependency rules specified in +.I mkfile +to control the update (usually by compilation) of +.I targets +(usually files) +from the source files upon which they depend. +The +.I mkfile +(default +.LR mkfile ) +contains a +.I rule +for each target that identifies the files and other +targets upon which it depends and an +.IR sh (1) +script, a +.IR recipe , +to update the target. +The script is run if the target does not exist +or if it is older than any of the files it depends on. +.I Mkfile +may also contain +.I meta-rules +that define actions for updating implicit targets. +If no +.I target +is specified, the target of the first rule (not meta-rule) in +.I mkfile +is updated. +.PP +The environment variable +.B $NPROC +determines how many targets may be updated simultaneously; +Some operating systems, e.g., Plan 9, set +.B $NPROC +automatically to the number of CPUs on the current machine. +.PP +Options are: +.TP \w'\fL-d[egp]\ 'u +.B -a +Assume all targets to be out of date. +Thus, everything is updated. +.PD 0 +.TP +.BR -d [ egp ] +Produce debugging output +.RB ( p +is for parsing, +.B g +for graph building, +.B e +for execution). +.TP +.B -e +Explain why each target is made. +.TP +.B -i +Force any missing intermediate targets to be made. +.TP +.B -k +Do as much work as possible in the face of errors. +.TP +.B -n +Print, but do not execute, the commands +needed to update the targets. +.TP +.B -s +Make the command line arguments sequentially rather than in parallel. +.TP +.B -t +Touch (update the modified date of) file targets, without +executing any recipes. +.TP +.BI -w target1 , target2,... +Pretend the modify time for each +.I target +is the current time; useful in conjunction with +.B -n +to learn what updates would be triggered by +modifying the +.IR targets . +.PD +.SS The \fLmkfile\fP +A +.I mkfile +consists of +.I assignments +(described under `Environment') and +.IR rules . +A rule contains +.I targets +and a +.IR tail . +A target is a literal string +and is normally a file name. +The tail contains zero or more +.I prerequisites +and an optional +.IR recipe , +which is an +.B shell +script. +Each line of the recipe must begin with white space. +A rule takes the form +.IP +.EX +target: prereq1 prereq2 + \f2recipe using\fP prereq1, prereq2 \f2to build\fP target +.EE +.PP +When the recipe is executed, +the first character on every line is elided. +.PP +After the colon on the target line, a rule may specify +.IR attributes , +described below. +.PP +A +.I meta-rule +has a target of the form +.IB A % B +where +.I A +and +.I B +are (possibly empty) strings. +A meta-rule acts as a rule for any potential target whose +name matches +.IB A % B +with +.B % +replaced by an arbitrary string, called the +.IR stem . +In interpreting a meta-rule, +the stem is substituted for all occurrences of +.B % +in the prerequisite names. +In the recipe of a meta-rule, the environment variable +.B $stem +contains the string matched by the +.BR % . +For example, a meta-rule to compile a C program using +.IR 9c (1) +might be: +.IP +.EX +%: %.c + 9c -c $stem.c + 9l -o $stem $stem.o +.EE +.PP +Meta-rules may contain an ampersand +.B & +rather than a percent sign +.BR % . +A +.B % +matches a maximal length string of any characters; +an +.B & +matches a maximal length string of any characters except period +or slash. +.PP +The text of the +.I mkfile +is processed as follows. +Lines beginning with +.B < +followed by a file name are replaced by the contents of the named +file. +Lines beginning with +.B "<|" +followed by a file name are replaced by the output +of the execution of the named +file. +Blank lines and comments, which run from unquoted +.B # +characters to the following newline, are deleted. +The character sequence backslash-newline is deleted, +so long lines in +.I mkfile +may be folded. +Non-recipe lines are processed by substituting for +.BI `{ command } +the output of the +.I command +when run by +.IR sh . +References to variables are replaced by the variables' values. +Special characters may be quoted using single quotes +.BR \&'' +as in +.IR sh (1). +.PP +Assignments and rules are distinguished by +the first unquoted occurrence of +.B : +(rule) +or +.B = +(assignment). +.PP +A later rule may modify or override an existing rule under the +following conditions: +.TP +\- +If the targets of the rules exactly match and one rule +contains only a prerequisite clause and no recipe, the +clause is added to the prerequisites of the other rule. +If either or both targets are virtual, the recipe is +always executed. +.TP +\- +If the targets of the rules match exactly and the +prerequisites do not match and both rules +contain recipes, +.I mk +reports an ``ambiguous recipe'' error. +.TP +\- +If the target and prerequisites of both rules match exactly, +the second rule overrides the first. +.SS Environment +Rules may make use of +shell +environment variables. +A legal reference of the form +.B $OBJ +or +.B ${name} +is expanded as in +.IR sh (1). +A reference of the form +.BI ${name: A % B = C\fL%\fID\fL}\fR, +where +.I A, B, C, D +are (possibly empty) strings, +has the value formed by expanding +.B $name +and substituting +.I C +for +.I A +and +.I D +for +.I B +in each word in +.B $name +that matches pattern +.IB A % B\f1. +.PP +Variables can be set by +assignments of the form +.I + var\fL=\fR[\fIattr\fL=\fR]\fIvalue\fR +.br +Blanks in the +.I value +break it into words. +Such variables are exported +to the environment of +recipes as they are executed, unless +.BR U , +the only legal attribute +.IR attr , +is present. +The initial value of a variable is +taken from (in increasing order of precedence) +the default values below, +.I mk's +environment, the +.IR mkfiles , +and any command line assignment as an argument to +.IR mk . +A variable assignment argument overrides the first (but not any subsequent) +assignment to that variable. +.PP +The variable +.B MKFLAGS +contains all the option arguments (arguments starting with +.L - +or containing +.LR = ) +and +.B MKARGS +contains all the targets in the call to +.IR mk . +.PP +The variable +.B MKSHELL +contains the shell command line +.I mk +uses to run recipes. +If the first word of the command ends in +.B rc +or +.BR rcsh , +.I mk +uses +.IR rc (1)'s +quoting rules; otherwise it uses +.IR sh (1)'s. +The +.B MKSHELL +variable is consulted when the mkfile is read, not when it is executed, +so that different shells can be used within a single mkfile: +.IP +.EX +MKSHELL=$PLAN9/bin/rc +use-rc:V: + for(i in a b c) echo $i + +MKSHELL=sh +use-sh:V: + for i in a b c; do echo $i; done +.EE +.LP +Mkfiles included via +.B < +or +.B <| +.RI ( q.v. ) +see their own private copy of +.BR MKSHELL , +which always starts set to +.B sh . +.PP +Dynamic information may be included in the mkfile by using a line of the form +.IP +\fR<|\fIcommand\fR \fIargs\fR +.LP +This runs the command +.I command +with the given arguments +.I args +and pipes its standard output to +.I mk +to be included as part of the mkfile. For instance, the Inferno kernels +use this technique +to run a shell command with an awk script and a configuration +file as arguments in order for +the +.I awk +script to process the file and output a set of variables and their values. +.SS Execution +.PP +During execution, +.I mk +determines which targets must be updated, and in what order, +to build the +.I names +specified on the command line. +It then runs the associated recipes. +.PP +A target is considered up to date if it has no prerequisites or +if all its prerequisites are up to date and it is newer +than all its prerequisites. +Once the recipe for a target has executed, the target is +considered up to date. +.PP +The date stamp +used to determine if a target is up to date is computed +differently for different types of targets. +If a target is +.I virtual +(the target of a rule with the +.B V +attribute), +its date stamp is initially zero; when the target is +updated the date stamp is set to +the most recent date stamp of its prerequisites. +Otherwise, if a target does not exist as a file, +its date stamp is set to the most recent date stamp of its prerequisites, +or zero if it has no prerequisites. +Otherwise, the target is the name of a file and +the target's date stamp is always that file's modification date. +The date stamp is computed when the target is needed in +the execution of a rule; it is not a static value. +.PP +Nonexistent targets that have prerequisites +and are themselves prerequisites are treated specially. +Such a target +.I t +is given the date stamp of its most recent prerequisite +and if this causes all the targets which have +.I t +as a prerequisite to be up to date, +.I t +is considered up to date. +Otherwise, +.I t +is made in the normal fashion. +The +.B -i +flag overrides this special treatment. +.PP +Files may be made in any order that respects +the preceding restrictions. +.PP +A recipe is executed by supplying the recipe as standard input to +the command +.BR /bin/sh . +(Note that unlike +.IR make , +.I mk +feeds the entire recipe to the shell rather than running each line +of the recipe separately.) +The environment is augmented by the following variables: +.TP 14 +.B $alltarget +all the targets of this rule. +.TP +.B $newprereq +the prerequisites that caused this rule to execute. +.TP +.B $newmember +the prerequisites that are members of an aggregate +that caused this rule to execute. +When the prerequisites of a rule are members of an +aggregate, +.B $newprereq +contains the name of the aggregate and out of date +members, while +.B $newmember +contains only the name of the members. +.TP +.B $nproc +the process slot for this recipe. +It satisfies +.RB 0≤ $nproc < $NPROC . +.TP +.B $pid +the process id for the +.I mk +executing the recipe. +.TP +.B $prereq +all the prerequisites for this rule. +.TP +.B $stem +if this is a meta-rule, +.B $stem +is the string that matched +.B % +or +.BR & . +Otherwise, it is empty. +For regular expression meta-rules (see below), the variables +.LR stem0 ", ...," +.L stem9 +are set to the corresponding subexpressions. +.TP +.B $target +the targets for this rule that need to be remade. +.PP +These variables are available only during the execution of a recipe, +not while evaluating the +.IR mkfile . +.PP +Unless the rule has the +.B Q +attribute, +the recipe is printed prior to execution +with recognizable environment variables expanded. +Commands returning error status +cause +.I mk +to terminate. +.PP +Recipes and backquoted +.B rc +commands in places such as assignments +execute in a copy of +.I mk's +environment; changes they make to +environment variables are not visible from +.IR mk . +.PP +Variable substitution in a rule is done when +the rule is read; variable substitution in the recipe is done +when the recipe is executed. For example: +.IP +.EX +bar=a.c +foo: $bar + $CC -o foo $bar +bar=b.c +.EE +.PP +will compile +.B b.c +into +.BR foo , +if +.B a.c +is newer than +.BR foo . +.SS Aggregates +Names of the form +.IR a ( b ) +refer to member +.I b +of the aggregate +.IR a . +Currently, the only aggregates supported are +.I 9ar +(see +.IR 9c (1)) +archives. +.SS Attributes +The colon separating the target from the prerequisites +may be +immediately followed by +.I attributes +and another colon. +The attributes are: +.TP +.B D +If the recipe exits with a non-null status, the target is deleted. +.TP +.B E +Continue execution if the recipe draws errors. +.TP +.B N +If there is no recipe, the target has its time updated. +.TP +.B n +The rule is a meta-rule that cannot be a target of a virtual rule. +Only files match the pattern in the target. +.TP +.B P +The characters after the +.B P +until the terminating +.B : +are taken as a program name. +It will be invoked as +.B "sh -c prog 'arg1' 'arg2'" +and should return a zero exit status +if and only if arg1 is up to date with respect to arg2. +Date stamps are still propagated in the normal way. +.TP +.B Q +The recipe is not printed prior to execution. +.TP +.B R +The rule is a meta-rule using regular expressions. +In the rule, +.B % +has no special meaning. +The target is interpreted as a regular expression as defined in +.IR regexp (7). +The prerequisites may contain references +to subexpressions in form +.BI \e n\f1, +as in the substitute command of +.IR sed (1). +.TP +.B U +The targets are considered to have been updated +even if the recipe did not do so. +.TP +.B V +The targets of this rule are marked as virtual. +They are distinct from files of the same name. +.PD +.SH EXAMPLES +A simple mkfile to compile a program: +.IP +.EX +.ta 8n +8n +8n +8n +8n +8n +8n +</$objtype/mkfile + +prog: a.$O b.$O c.$O + $LD $LDFLAGS -o $target $prereq + +%.$O: %.c + $CC $CFLAGS $stem.c +.EE +.PP +Override flag settings in the mkfile: +.IP +.EX +% mk target 'CFLAGS=-S -w' +.EE +.PP +Maintain a library: +.IP +.EX +libc.a(%.$O):N: %.$O +libc.a: libc.a(abs.$O) libc.a(access.$O) libc.a(alarm.$O) ... + ar r libc.a $newmember +.EE +.PP +String expression variables to derive names from a master list: +.IP +.EX +NAMES=alloc arc bquote builtins expand main match mk var word +OBJ=${NAMES:%=%.$O} +.EE +.PP +Regular expression meta-rules: +.IP +.EX +([^/]*)/(.*)\e.$O:R: \e1/\e2.c + cd $stem1; $CC $CFLAGS $stem2.c +.EE +.PP +A correct way to deal with +.IR yacc (1) +grammars. +The file +.B lex.c +includes the file +.B x.tab.h +rather than +.B y.tab.h +in order to reflect changes in content, not just modification time. +.IP +.EX +lex.$O: x.tab.h +x.tab.h: y.tab.h + cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h +y.tab.c y.tab.h: gram.y + $YACC -d gram.y +.EE +.PP +The above example could also use the +.B P +attribute for the +.B x.tab.h +rule: +.IP +.EX +x.tab.h:Pcmp -s: y.tab.h + cp y.tab.h x.tab.h +.EE +.SH SOURCE +.B \*9/src/cmd/mk +.SH SEE ALSO +.IR sh (1), +.IR regexp (7) +.PP +A. Hume, +``Mk: a Successor to Make'' +(Tenth Edition Research Unix Manuals). +.PP +Andrew G. Hume and Bob Flandrena, +``Maintaining Files on Plan 9 with Mk''. +DOCPREFIX/doc/mk.pdf +.SH HISTORY +Andrew Hume wrote +.I mk +for Tenth Edition Research Unix. +It was later ported to Plan 9. +This software is a port of the Plan 9 version back to Unix. +.SH BUGS +Identical recipes for regular expression meta-rules only have one target. +.PP +Seemingly appropriate input like +.B CFLAGS=-DHZ=60 +is parsed as an erroneous attribute; correct it by inserting +a space after the first +.LR = . +.PP +The recipes printed by +.I mk +before being passed to +the shell +for execution are sometimes erroneously expanded +for printing. Don't trust what's printed; rely +on what the shell +does. diff --git a/mk/mk.c b/mk/mk.c @@ -0,0 +1,234 @@ +#include "mk.h" + +int runerrs; + +void +mk(char *target) +{ + Node *node; + int did = 0; + + nproc(); /* it can be updated dynamically */ + nrep(); /* it can be updated dynamically */ + runerrs = 0; + node = graph(target); + if(DEBUG(D_GRAPH)){ + dumpn("new target\n", node); + Bflush(&bout); + } + clrmade(node); + while(node->flags&NOTMADE){ + if(work(node, (Node *)0, (Arc *)0)) + did = 1; /* found something to do */ + else { + if(waitup(1, (int *)0) > 0){ + if(node->flags&(NOTMADE|BEINGMADE)){ + assert("must be run errors", runerrs); + break; /* nothing more waiting */ + } + } + } + } + if(node->flags&BEINGMADE) + waitup(-1, (int *)0); + while(jobs) + waitup(-2, (int *)0); + assert("target didn't get done", runerrs || (node->flags&MADE)); + if(did == 0) + Bprint(&bout, "mk: '%s' is up to date\n", node->name); +} + +void +clrmade(Node *n) +{ + Arc *a; + + n->flags &= ~(CANPRETEND|PRETENDING); + if(strchr(n->name, '(') ==0 || n->time) + n->flags |= CANPRETEND; + MADESET(n, NOTMADE); + for(a = n->prereqs; a; a = a->next) + if(a->n) + clrmade(a->n); +} + +static void +unpretend(Node *n) +{ + MADESET(n, NOTMADE); + n->flags &= ~(CANPRETEND|PRETENDING); + n->time = 0; +} + +static char* +dir(void) +{ + static char buf[1024]; + + return getcwd(buf, sizeof buf); +} + +int +work(Node *node, Node *p, Arc *parc) +{ + Arc *a, *ra; + int weoutofdate; + int ready; + int did = 0; + + /*print("work(%s) flags=0x%x time=%ld\n", node->name, node->flags, node->time); */ + if(node->flags&BEINGMADE) + return(did); + if((node->flags&MADE) && (node->flags&PRETENDING) && p && outofdate(p, parc, 0)){ + if(explain) + fprint(1, "unpretending %s(%ld) because %s is out of date(%ld)\n", + node->name, node->time, p->name, p->time); + unpretend(node); + } + /* + have a look if we are pretending in case + someone has been unpretended out from underneath us + */ + if(node->flags&MADE){ + if(node->flags&PRETENDING){ + node->time = 0; + }else + return(did); + } + /* consider no prerequsite case */ + if(node->prereqs == 0){ + if(node->time == 0){ + fprint(2, "mk: don't know how to make '%s' in %s\n", node->name, dir()); + if(kflag){ + node->flags |= BEINGMADE; + runerrs++; + } else + Exit(); + } else + MADESET(node, MADE); + return(did); + } + /* + now see if we are out of date or what + */ + ready = 1; + weoutofdate = aflag; + ra = 0; + for(a = node->prereqs; a; a = a->next) + if(a->n){ + did = work(a->n, node, a) || did; + if(a->n->flags&(NOTMADE|BEINGMADE)) + ready = 0; + if(outofdate(node, a, 0)){ + weoutofdate = 1; + if((ra == 0) || (ra->n == 0) + || (ra->n->time < a->n->time)) + ra = a; + } + } else { + if(node->time == 0){ + if(ra == 0) + ra = a; + weoutofdate = 1; + } + } + if(ready == 0) /* can't do anything now */ + return(did); + if(weoutofdate == 0){ + MADESET(node, MADE); + return(did); + } + /* + can we pretend to be made? + */ + if((iflag == 0) && (node->time == 0) && (node->flags&(PRETENDING|CANPRETEND)) + && p && ra->n && !outofdate(p, ra, 0)){ + node->flags &= ~CANPRETEND; + MADESET(node, MADE); + if(explain && ((node->flags&PRETENDING) == 0)) + fprint(1, "pretending %s has time %ld\n", node->name, node->time); + node->flags |= PRETENDING; + return(did); + } + /* + node is out of date and we REALLY do have to do something. + quickly rescan for pretenders + */ + for(a = node->prereqs; a; a = a->next) + if(a->n && (a->n->flags&PRETENDING)){ + if(explain) + Bprint(&bout, "unpretending %s because of %s because of %s\n", + a->n->name, node->name, ra->n? ra->n->name : "rule with no prerequisites"); + + unpretend(a->n); + did = work(a->n, node, a) || did; + ready = 0; + } + if(ready == 0) /* try later unless nothing has happened for -k's sake */ + return(did || work(node, p, parc)); + did = dorecipe(node) || did; + return(did); +} + +void +update(int fake, Node *node) +{ + Arc *a; + + MADESET(node, fake? BEINGMADE : MADE); + if(((node->flags&VIRTUAL) == 0) && (access(node->name, 0) == 0)){ + node->time = timeof(node->name, 1); + node->flags &= ~(CANPRETEND|PRETENDING); + for(a = node->prereqs; a; a = a->next) + if(a->prog) + outofdate(node, a, 1); + } else { + node->time = 1; + for(a = node->prereqs; a; a = a->next) + if(a->n && outofdate(node, a, 1)) + node->time = a->n->time; + } +/* print("----node %s time=%ld flags=0x%x\n", node->name, node->time, node->flags);*/ +} + +static int +pcmp(char *prog, char *p, char *q, Shell *sh, Word *shcmd) +{ + char buf[3*NAMEBLOCK]; + int pid; + + Bflush(&bout); + snprint(buf, sizeof buf, "%s '%s' '%s'\n", prog, p, q); + pid = pipecmd(buf, 0, 0, sh, shcmd); + while(waitup(-3, &pid) >= 0) + ; + return(pid? 2:1); +} + +int +outofdate(Node *node, Arc *arc, int eval) +{ + char buf[3*NAMEBLOCK], *str; + Symtab *sym; + int ret; + + str = 0; + if(arc->prog){ + snprint(buf, sizeof buf, "%s%c%s", node->name, 0377, arc->n->name); + sym = symlook(buf, S_OUTOFDATE, 0); + if(sym == 0 || eval){ + if(sym == 0) + str = strdup(buf); + ret = pcmp(arc->prog, node->name, arc->n->name, arc->r->shellt, arc->r->shellcmd); + if(sym) + sym->u.value = ret; + else + symlook(str, S_OUTOFDATE, (void *)(uintptr)ret); + } else + ret = sym->u.value; + return(ret-1); + } else if(strchr(arc->n->name, '(') && arc->n->time == 0) /* missing archive member */ + return 1; + else + return node->time <= arc->n->time; +} diff --git a/mk/mk.h b/mk/mk.h @@ -0,0 +1,185 @@ +#include "sys.h" + +#undef assert +#define assert mkassert +extern Biobuf bout; + +typedef struct Bufblock +{ + struct Bufblock *next; + char *start; + char *end; + char *current; +} Bufblock; + +typedef struct Word +{ + char *s; + struct Word *next; +} Word; + +typedef struct Envy +{ + char *name; + Word *values; +} Envy; + +extern Envy *envy; + +typedef struct Shell +{ + char *name; + char *termchars; /* used in parse.c to isolate assignment attribute */ + int iws; /* inter-word separator in environment */ + char *(*charin)(char*, char*); /* search for unescaped characters */ + char *(*expandquote)(char*, Rune, Bufblock*); /* extract escaped token */ + int (*escapetoken)(Biobuf*, Bufblock*, int, int); /* input escaped token */ + char *(*copyq)(char*, Rune, Bufblock*); /* check for quoted strings */ + int (*matchname)(char*); /* does name match */ +} Shell; + +typedef struct Rule +{ + char *target; /* one target */ + Word *tail; /* constituents of targets */ + char *recipe; /* do it ! */ + short attr; /* attributes */ + short line; /* source line */ + char *file; /* source file */ + Word *alltargets; /* all the targets */ + int rule; /* rule number */ + Reprog *pat; /* reg exp goo */ + char *prog; /* to use in out of date */ + struct Rule *chain; /* hashed per target */ + struct Rule *next; + Shell *shellt; /* shell to use with this rule */ + Word *shellcmd; +} Rule; + +extern Rule *rules, *metarules, *patrule; + +/* Rule.attr */ +#define META 0x0001 +#define UNUSED 0x0002 +#define UPD 0x0004 +#define QUIET 0x0008 +#define VIR 0x0010 +#define REGEXP 0x0020 +#define NOREC 0x0040 +#define DEL 0x0080 +#define NOVIRT 0x0100 + +#define NREGEXP 10 + +typedef struct Arc +{ + short flag; + struct Node *n; + Rule *r; + char *stem; + char *prog; + char *match[NREGEXP]; + struct Arc *next; +} Arc; + + /* Arc.flag */ +#define TOGO 1 + +typedef struct Node +{ + char *name; + long time; + unsigned short flags; + Arc *prereqs; + struct Node *next; /* list for a rule */ +} Node; + + /* Node.flags */ +#define VIRTUAL 0x0001 +#define CYCLE 0x0002 +#define READY 0x0004 +#define CANPRETEND 0x0008 +#define PRETENDING 0x0010 +#define NOTMADE 0x0020 +#define BEINGMADE 0x0040 +#define MADE 0x0080 +#define MADESET(n,m) n->flags = (n->flags&~(NOTMADE|BEINGMADE|MADE))|(m) +#define PROBABLE 0x0100 +#define VACUOUS 0x0200 +#define NORECIPE 0x0400 +#define DELETE 0x0800 +#define NOMINUSE 0x1000 + +typedef struct Job +{ + Rule *r; /* master rule for job */ + Node *n; /* list of node targets */ + char *stem; + char **match; + Word *p; /* prerequistes */ + Word *np; /* new prerequistes */ + Word *t; /* targets */ + Word *at; /* all targets */ + int nproc; /* slot number */ + struct Job *next; +} Job; +extern Job *jobs; + +typedef struct Symtab +{ + short space; + char *name; + union { + void *ptr; + uintptr value; + } u; + struct Symtab *next; +} Symtab; + +enum { + S_VAR, /* variable -> value */ + S_TARGET, /* target -> rule */ + S_TIME, /* file -> time */ + S_PID, /* pid -> products */ + S_NODE, /* target name -> node */ + S_AGG, /* aggregate -> time */ + S_BITCH, /* bitched about aggregate not there */ + S_NOEXPORT, /* var -> noexport */ + S_OVERRIDE, /* can't override */ + S_OUTOFDATE, /* n1\377n2 -> 2(outofdate) or 1(not outofdate) */ + S_MAKEFILE, /* target -> node */ + S_MAKEVAR, /* dumpable mk variable */ + S_EXPORTED, /* var -> current exported value */ + S_WESET, /* variable; we set in the mkfile */ + S_INTERNAL /* an internal mk variable (e.g., stem, target) */ +}; + +extern int debug; +extern int nflag, tflag, iflag, kflag, aflag, mflag; +extern int mkinline; +extern char *infile; +extern int nreps; +extern char *explain; +extern Shell *shellt; +extern Word *shellcmd; + +extern Shell shshell, rcshell; + +#define SYNERR(l) (fprint(2, "mk: %s:%d: syntax error; ", infile, ((l)>=0)?(l):mkinline)) +#define RERR(r) (fprint(2, "mk: %s:%d: rule error; ", (r)->file, (r)->line)) +#define NAMEBLOCK 1000 +#define BIGBLOCK 20000 + +#define SEP(c) (((c)==' ')||((c)=='\t')||((c)=='\n')) +#define WORDCHR(r) ((r) > ' ' && !utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", (r))) + +#define DEBUG(x) (debug&(x)) +#define D_PARSE 0x01 +#define D_GRAPH 0x02 +#define D_EXEC 0x04 + +#define LSEEK(f,o,p) seek(f,o,p) + +#define PERCENT(ch) (((ch) == '%') || ((ch) == '&')) + +#include "fns.h" diff --git a/mk/mkfile.test b/mk/mkfile.test @@ -0,0 +1,12 @@ +MKSHELL=$PLAN9/bin/rc +use-rc:V: + for(i in a b c) + echo $i + +MKSHELL=/bin/sh +use-sh:V: + for i in a b c + do + echo $i + done + diff --git a/mk/parse.c b/mk/parse.c @@ -0,0 +1,318 @@ +#include "mk.h" + +char *infile; +int mkinline; +static int rhead(char *, Word **, Word **, int *, char **); +static char *rbody(Biobuf*); +extern Word *target1; + +void +parse(char *f, int fd, int varoverride) +{ + int hline; + char *body; + Word *head, *tail; + int attr, set, pid; + char *prog, *p; + int newfd; + Biobuf in; + Bufblock *buf; + char *err; + + if(fd < 0){ + fprint(2, "open %s: %r\n", f); + Exit(); + } + pushshell(); + ipush(); + infile = strdup(f); + mkinline = 1; + Binit(&in, fd, OREAD); + buf = newbuf(); + while(assline(&in, buf)){ + hline = mkinline; + switch(rhead(buf->start, &head, &tail, &attr, &prog)) + { + case '<': + p = wtos(tail, ' '); + if(*p == 0){ + SYNERR(-1); + fprint(2, "missing include file name\n"); + Exit(); + } + newfd = open(p, OREAD); + if(newfd < 0){ + fprint(2, "warning: skipping missing include file %s: %r\n", p); + } else + parse(p, newfd, 0); + break; + case '|': + p = wtos(tail, ' '); + if(*p == 0){ + SYNERR(-1); + fprint(2, "missing include program name\n"); + Exit(); + } + execinit(); + pid=pipecmd(p, envy, &newfd, shellt, shellcmd); + if(newfd < 0){ + fprint(2, "warning: skipping missing program file %s: %r\n", p); + } else + parse(p, newfd, 0); + while(waitup(-3, &pid) >= 0) + ; + if(pid != 0){ + fprint(2, "bad include program status\n"); + Exit(); + } + break; + case ':': + body = rbody(&in); + addrules(head, tail, body, attr, hline, prog); + break; + case '=': + if(head->next){ + SYNERR(-1); + fprint(2, "multiple vars on left side of assignment\n"); + Exit(); + } + if(symlook(head->s, S_OVERRIDE, 0)){ + set = varoverride; + } else { + set = 1; + if(varoverride) + symlook(head->s, S_OVERRIDE, (void *)""); + } + if(set){ +/* +char *cp; +dumpw("tail", tail); +cp = wtos(tail, ' '); print("assign %s to %s\n", head->s, cp); free(cp); +*/ + setvar(head->s, (void *) tail); + symlook(head->s, S_WESET, (void *)""); + if(strcmp(head->s, "MKSHELL") == 0){ + if((err = setshell(tail)) != nil){ + SYNERR(hline); + fprint(2, "%s\n", err); + Exit(); + break; + } + } + } + if(attr) + symlook(head->s, S_NOEXPORT, (void *)""); + break; + default: + SYNERR(hline); + fprint(2, "expected one of :<=\n"); + Exit(); + break; + } + } + close(fd); + freebuf(buf); + ipop(); + popshell(); +} + +void +addrules(Word *head, Word *tail, char *body, int attr, int hline, char *prog) +{ + Word *w; + + assert("addrules args", head && body); + /* tuck away first non-meta rule as default target*/ + if(target1 == 0 && !(attr&REGEXP)){ + for(w = head; w; w = w->next) + if(shellt->charin(w->s, "%&")) + break; + if(w == 0) + target1 = wdup(head); + } + for(w = head; w; w = w->next) + addrule(w->s, tail, body, head, attr, hline, prog); +} + +static int +rhead(char *line, Word **h, Word **t, int *attr, char **prog) +{ + char *p; + char *pp; + int sep; + Rune r; + int n; + Word *w; + + p = shellt->charin(line,":=<"); + if(p == 0) + return('?'); + sep = *p; + *p++ = 0; + if(sep == '<' && *p == '|'){ + sep = '|'; + p++; + } + *attr = 0; + *prog = 0; + if(sep == '='){ + pp = shellt->charin(p, shellt->termchars); /* termchars is shell-dependent */ + if (pp && *pp == '=') { + while (p != pp) { + n = chartorune(&r, p); + switch(r) + { + default: + SYNERR(-1); + fprint(2, "unknown attribute '%c'\n",*p); + Exit(); + case 'U': + *attr = 1; + break; + } + p += n; + } + p++; /* skip trailing '=' */ + } + } + if((sep == ':') && *p && (*p != ' ') && (*p != '\t')){ + while (*p) { + n = chartorune(&r, p); + if (r == ':') + break; + p += n; + switch(r) + { + default: + SYNERR(-1); + fprint(2, "unknown attribute '%c'\n", p[-1]); + Exit(); + case 'D': + *attr |= DEL; + break; + case 'E': + *attr |= NOMINUSE; + break; + case 'n': + *attr |= NOVIRT; + break; + case 'N': + *attr |= NOREC; + break; + case 'P': + pp = utfrune(p, ':'); + if (pp == 0 || *pp == 0) + goto eos; + *pp = 0; + *prog = strdup(p); + *pp = ':'; + p = pp; + break; + case 'Q': + *attr |= QUIET; + break; + case 'R': + *attr |= REGEXP; + break; + case 'U': + *attr |= UPD; + break; + case 'V': + *attr |= VIR; + break; + } + } + if (*p++ != ':') { + eos: + SYNERR(-1); + fprint(2, "missing trailing :\n"); + Exit(); + } + } + *h = w = stow(line); + if(*w->s == 0 && sep != '<' && sep != '|' && sep != 'S') { + SYNERR(mkinline-1); + fprint(2, "no var on left side of assignment/rule\n"); + Exit(); + } + *t = stow(p); + return(sep); +} + +static char * +rbody(Biobuf *in) +{ + Bufblock *buf; + int r, lastr; + char *p; + + lastr = '\n'; + buf = newbuf(); + for(;;){ + r = Bgetrune(in); + if (r < 0) + break; + if (lastr == '\n') { + if (r == '#') + rinsert(buf, r); + else if (r != ' ' && r != '\t') { + Bungetrune(in); + break; + } + } else + rinsert(buf, r); + lastr = r; + if (r == '\n') + mkinline++; + } + insert(buf, 0); + p = strdup(buf->start); + freebuf(buf); + return p; +} + +struct input +{ + char *file; + int line; + struct input *next; +}; +static struct input *inputs = 0; + +void +ipush(void) +{ + struct input *in, *me; + + me = (struct input *)Malloc(sizeof(*me)); + me->file = infile; + me->line = mkinline; + me->next = 0; + if(inputs == 0) + inputs = me; + else { + for(in = inputs; in->next; ) + in = in->next; + in->next = me; + } +} + +void +ipop(void) +{ + struct input *in, *me; + + assert("pop input list", inputs != 0); + if(inputs->next == 0){ + me = inputs; + inputs = 0; + } else { + for(in = inputs; in->next->next; ) + in = in->next; + me = in->next; + in->next = 0; + } + infile = me->file; + mkinline = me->line; + free((char *)me); +} diff --git a/mk/rc.c b/mk/rc.c @@ -0,0 +1,194 @@ +#include "mk.h" + +/* + * This file contains functions that depend on rc's syntax. Most + * of the routines extract strings observing rc's escape conventions + */ + + +/* + * skip a token in single quotes. + */ +static char * +squote(char *cp) +{ + Rune r; + int n; + + while(*cp){ + n = chartorune(&r, cp); + if(r == '\'') { + n += chartorune(&r, cp+n); + if(r != '\'') + return(cp); + } + cp += n; + } + SYNERR(-1); /* should never occur */ + fprint(2, "missing closing '\n"); + return 0; +} + +/* + * search a string for characters in a pattern set + * characters in quotes and variable generators are escaped + */ +char * +rccharin(char *cp, char *pat) +{ + Rune r; + int n, vargen; + + vargen = 0; + while(*cp){ + n = chartorune(&r, cp); + switch(r){ + case '\'': /* skip quoted string */ + cp = squote(cp+1); /* n must = 1 */ + if(!cp) + return 0; + break; + case '$': + if(*(cp+1) == '{') + vargen = 1; + break; + case '}': + if(vargen) + vargen = 0; + else if(utfrune(pat, r)) + return cp; + break; + default: + if(vargen == 0 && utfrune(pat, r)) + return cp; + break; + } + cp += n; + } + if(vargen){ + SYNERR(-1); + fprint(2, "missing closing } in pattern generator\n"); + } + return 0; +} + +/* + * extract an escaped token. Possible escape chars are single-quote, + * double-quote,and backslash. Only the first is valid for rc. the + * others are just inserted into the receiving buffer. + */ +char* +rcexpandquote(char *s, Rune r, Bufblock *b) +{ + if (r != '\'') { + rinsert(b, r); + return s; + } + + while(*s){ + s += chartorune(&r, s); + if(r == '\'') { + if(*s == '\'') + s++; + else + return s; + } + rinsert(b, r); + } + return 0; +} + +/* + * Input an escaped token. Possible escape chars are single-quote, + * double-quote and backslash. Only the first is a valid escape for + * rc; the others are just inserted into the receiving buffer. + */ +int +rcescapetoken(Biobuf *bp, Bufblock *buf, int preserve, int esc) +{ + int c, line; + + if(esc != '\'') + return 1; + + line = mkinline; + while((c = nextrune(bp, 0)) > 0){ + if(c == '\''){ + if(preserve) + rinsert(buf, c); + c = Bgetrune(bp); + if (c < 0) + break; + if(c != '\''){ + Bungetrune(bp); + return 1; + } + } + rinsert(buf, c); + } + SYNERR(line); fprint(2, "missing closing %c\n", esc); + return 0; +} + +/* + * copy a single-quoted string; s points to char after opening quote + */ +static char * +copysingle(char *s, Bufblock *buf) +{ + Rune r; + + while(*s){ + s += chartorune(&r, s); + rinsert(buf, r); + if(r == '\'') + break; + } + return s; +} +/* + * check for quoted strings. backquotes are handled here; single quotes above. + * s points to char after opening quote, q. + */ +char * +rccopyq(char *s, Rune q, Bufblock *buf) +{ + if(q == '\'') /* copy quoted string */ + return copysingle(s, buf); + + if(q != '`') /* not quoted */ + return s; + + while(*s){ /* copy backquoted string */ + s += chartorune(&q, s); + rinsert(buf, q); + if(q == '}') + break; + if(q == '\'') + s = copysingle(s, buf); /* copy quoted string */ + } + return s; +} + +static int +rcmatchname(char *name) +{ + char *p; + + if((p = strrchr(name, '/')) != nil) + name = p+1; + if(name[0] == 'r' && name[1] == 'c') + return 1; + return 0; +} + +Shell rcshell = { + "rc", + "'= \t", + '\1', + rccharin, + rcexpandquote, + rcescapetoken, + rccopyq, + rcmatchname +}; diff --git a/mk/recipe.c b/mk/recipe.c @@ -0,0 +1,117 @@ +#include "mk.h" + +int +dorecipe(Node *node) +{ + char buf[BIGBLOCK]; + register Node *n; + Rule *r = 0; + Arc *a, *aa; + Word head, ahead, lp, ln, *w, *ww, *aw; + Symtab *s; + int did = 0; + + aa = 0; + /* + pick up the rule + */ + for(a = node->prereqs; a; a = a->next) + if(*a->r->recipe) + r = (aa = a)->r; + /* + no recipe? go to buggery! + */ + if(r == 0){ + if(!(node->flags&VIRTUAL) && !(node->flags&NORECIPE)){ + fprint(2, "mk: no recipe to make '%s'\n", node->name); + Exit(); + } + if(strchr(node->name, '(') && node->time == 0) + MADESET(node, MADE); + else + update(0, node); + if(tflag){ + if(!(node->flags&VIRTUAL)) + touch(node->name); + else if(explain) + Bprint(&bout, "no touch of virtual '%s'\n", node->name); + } + return(did); + } + /* + build the node list + */ + node->next = 0; + head.next = 0; + ww = &head; + ahead.next = 0; + aw = &ahead; + if(r->attr&REGEXP){ + ww->next = newword(node->name); + aw->next = newword(node->name); + } else { + for(w = r->alltargets; w; w = w->next){ + if(r->attr&META) + subst(aa->stem, w->s, buf); + else + strcpy(buf, w->s); + aw->next = newword(buf); + aw = aw->next; + if((s = symlook(buf, S_NODE, 0)) == 0) + continue; /* not a node we are interested in */ + n = s->u.ptr; + if(aflag == 0 && n->time) { + for(a = n->prereqs; a; a = a->next) + if(a->n && outofdate(n, a, 0)) + break; + if(a == 0) + continue; + } + ww->next = newword(buf); + ww = ww->next; + if(n == node) continue; + n->next = node->next; + node->next = n; + } + } + for(n = node; n; n = n->next) + if((n->flags&READY) == 0) + return(did); + /* + gather the params for the job + */ + lp.next = ln.next = 0; + for(n = node; n; n = n->next){ + for(a = n->prereqs; a; a = a->next){ + if(a->n){ + addw(&lp, a->n->name); + if(outofdate(n, a, 0)){ + addw(&ln, a->n->name); + if(explain) + fprint(1, "%s(%ld) < %s(%ld)\n", + n->name, n->time, a->n->name, a->n->time); + } + } else { + if(explain) + fprint(1, "%s has no prerequisites\n", + n->name); + } + } + MADESET(n, BEINGMADE); + } + /*print("lt=%s ln=%s lp=%s\n",wtos(head.next, ' '),wtos(ln.next, ' '),wtos(lp.next, ' '));*/ + run(newjob(r, node, aa->stem, aa->match, lp.next, ln.next, head.next, ahead.next)); + return(1); +} + +void +addw(Word *w, char *s) +{ + Word *lw; + + for(lw = w; w = w->next; lw = w){ + if(strcmp(s, w->s) == 0) + return; + } + lw->next = newword(s); +} diff --git a/mk/rule.c b/mk/rule.c @@ -0,0 +1,112 @@ +#include "mk.h" + +static Rule *lr, *lmr; +static int rcmp(Rule *r, char *target, Word *tail); +static int nrules = 0; + +void +addrule(char *head, Word *tail, char *body, Word *ahead, int attr, int hline, char *prog) +{ + Rule *r; + Rule *rr; + Symtab *sym; + int reuse; + + r = 0; + reuse = 0; + if(sym = symlook(head, S_TARGET, 0)){ + for(r = sym->u.ptr; r; r = r->chain) + if(rcmp(r, head, tail) == 0){ + reuse = 1; + break; + } + } + if(r == 0) + r = (Rule *)Malloc(sizeof(Rule)); + r->shellt = shellt; + r->shellcmd = shellcmd; + r->target = head; + r->tail = tail; + r->recipe = body; + r->line = hline; + r->file = infile; + r->attr = attr; + r->alltargets = ahead; + r->prog = prog; + r->rule = nrules++; + if(!reuse){ + rr = symlook(head, S_TARGET, (void *)r)->u.ptr; + if(rr != r){ + r->chain = rr->chain; + rr->chain = r; + } else + r->chain = 0; + } + if(!reuse) + r->next = 0; + if((attr&REGEXP) || shellt->charin(head, "%&")){ + r->attr |= META; + if(reuse) + return; + if(attr&REGEXP){ + patrule = r; + r->pat = regcomp(head); + } + if(metarules == 0) + metarules = lmr = r; + else { + lmr->next = r; + lmr = r; + } + } else { + if(reuse) + return; + r->pat = 0; + if(rules == 0) + rules = lr = r; + else { + lr->next = r; + lr = r; + } + } +} + +void +dumpr(char *s, Rule *r) +{ + if(r == nil) + return; + Bprint(&bout, "%s: start=%ld shelltype=%s shellcmd=%s\n", + s, r, r->shellt->name, wtos(r->shellcmd, ' ')); + for(; r; r = r->next){ + Bprint(&bout, "\tRule %ld: %s[%d] attr=%x next=%ld chain=%ld alltarget='%s'", + r, r->file, r->line, r->attr, r->next, r->chain, wtos(r->alltargets, ' ')); + if(r->prog) + Bprint(&bout, " prog='%s'", r->prog); + Bprint(&bout, "\n\ttarget=%s: %s\n", r->target, wtos(r->tail, ' ')); + Bprint(&bout, "\trecipe@%ld='%s'\n", r->recipe, r->recipe); + } +} + +static int +rcmp(Rule *r, char *target, Word *tail) +{ + Word *w; + + if(strcmp(r->target, target)) + return 1; + for(w = r->tail; w && tail; w = w->next, tail = tail->next) + if(strcmp(w->s, tail->s)) + return 1; + return(w || tail); +} + +char * +rulecnt(void) +{ + char *s; + + s = Malloc(nrules); + memset(s, 0, nrules); + return(s); +} diff --git a/mk/run.c b/mk/run.c @@ -0,0 +1,296 @@ +#include "mk.h" + +typedef struct Event +{ + int pid; + Job *job; +} Event; +static Event *events; +static int nevents, nrunning, nproclimit; + +typedef struct Process +{ + int pid; + int status; + struct Process *b, *f; +} Process; +static Process *phead, *pfree; +static void sched(void); +static void pnew(int, int), pdelete(Process *); + +int pidslot(int); + +void +run(Job *j) +{ + Job *jj; + + if(jobs){ + for(jj = jobs; jj->next; jj = jj->next) + ; + jj->next = j; + } else + jobs = j; + j->next = 0; + /* this code also in waitup after parse redirect */ + if(nrunning < nproclimit) + sched(); +} + +static void +sched(void) +{ + char *flags; + Job *j; + Bufblock *buf; + int slot; + Node *n; + Envy *e; + + if(jobs == 0){ + usage(); + return; + } + j = jobs; + jobs = j->next; + if(DEBUG(D_EXEC)) + fprint(1, "firing up job for target %s\n", wtos(j->t, ' ')); + slot = nextslot(); + events[slot].job = j; + buf = newbuf(); + e = buildenv(j, slot); + shprint(j->r->recipe, e, buf, j->r->shellt); + if(!tflag && (nflag || !(j->r->attr&QUIET))) + Bwrite(&bout, buf->start, (long)strlen(buf->start)); + freebuf(buf); + if(nflag||tflag){ + for(n = j->n; n; n = n->next){ + if(tflag){ + if(!(n->flags&VIRTUAL)) + touch(n->name); + else if(explain) + Bprint(&bout, "no touch of virtual '%s'\n", n->name); + } + n->time = time((long *)0); + MADESET(n, MADE); + } + } else { + if(DEBUG(D_EXEC)) + fprint(1, "recipe='%s'", j->r->recipe);/**/ + Bflush(&bout); + if(j->r->attr&NOMINUSE) + flags = 0; + else + flags = "-e"; + events[slot].pid = execsh(flags, j->r->recipe, 0, e, j->r->shellt, j->r->shellcmd); + usage(); + nrunning++; + if(DEBUG(D_EXEC)) + fprint(1, "pid for target %s = %d\n", wtos(j->t, ' '), events[slot].pid); + } +} + +int +waitup(int echildok, int *retstatus) +{ + Envy *e; + int pid; + int slot; + Symtab *s; + Word *w; + Job *j; + char buf[ERRMAX]; + Bufblock *bp; + int uarg = 0; + int done; + Node *n; + Process *p; + extern int runerrs; + + /* first check against the proces slist */ + if(retstatus) + for(p = phead; p; p = p->f) + if(p->pid == *retstatus){ + *retstatus = p->status; + pdelete(p); + return(-1); + } +again: /* rogue processes */ + pid = waitfor(buf); + if(pid == -1){ + if(echildok > 0) + return(1); + else { + fprint(2, "mk: (waitup %d): %r\n", echildok); + Exit(); + } + } + if(DEBUG(D_EXEC)) + fprint(1, "waitup got pid=%d, status='%s'\n", pid, buf); + if(retstatus && pid == *retstatus){ + *retstatus = buf[0]? 1:0; + return(-1); + } + slot = pidslot(pid); + if(slot < 0){ + if(DEBUG(D_EXEC)) + fprint(2, "mk: wait returned unexpected process %d\n", pid); + pnew(pid, buf[0]? 1:0); + goto again; + } + j = events[slot].job; + usage(); + nrunning--; + events[slot].pid = -1; + if(buf[0]){ + e = buildenv(j, slot); + bp = newbuf(); + shprint(j->r->recipe, e, bp, j->r->shellt); + front(bp->start); + fprint(2, "mk: %s: exit status=%s", bp->start, buf); + freebuf(bp); + for(n = j->n, done = 0; n; n = n->next) + if(n->flags&DELETE){ + if(done++ == 0) + fprint(2, ", deleting"); + fprint(2, " '%s'", n->name); + delete(n->name); + } + fprint(2, "\n"); + if(kflag){ + runerrs++; + uarg = 1; + } else { + jobs = 0; + Exit(); + } + } + for(w = j->t; w; w = w->next){ + if((s = symlook(w->s, S_NODE, 0)) == 0) + continue; /* not interested in this node */ + update(uarg, s->u.ptr); + } + if(nrunning < nproclimit) + sched(); + return(0); +} + +void +nproc(void) +{ + Symtab *sym; + Word *w; + + if(sym = symlook("NPROC", S_VAR, 0)) { + w = sym->u.ptr; + if (w && w->s && w->s[0]) + nproclimit = atoi(w->s); + } + if(nproclimit < 1) + nproclimit = 1; + if(DEBUG(D_EXEC)) + fprint(1, "nprocs = %d\n", nproclimit); + if(nproclimit > nevents){ + if(nevents) + events = (Event *)Realloc((char *)events, nproclimit*sizeof(Event)); + else + events = (Event *)Malloc(nproclimit*sizeof(Event)); + while(nevents < nproclimit) + events[nevents++].pid = 0; + } +} + +int +nextslot(void) +{ + int i; + + for(i = 0; i < nproclimit; i++) + if(events[i].pid <= 0) return i; + assert("out of slots!!", 0); + return 0; /* cyntax */ +} + +int +pidslot(int pid) +{ + int i; + + for(i = 0; i < nevents; i++) + if(events[i].pid == pid) return(i); + if(DEBUG(D_EXEC)) + fprint(2, "mk: wait returned unexpected process %d\n", pid); + return(-1); +} + + +static void +pnew(int pid, int status) +{ + Process *p; + + if(pfree){ + p = pfree; + pfree = p->f; + } else + p = (Process *)Malloc(sizeof(Process)); + p->pid = pid; + p->status = status; + p->f = phead; + phead = p; + if(p->f) + p->f->b = p; + p->b = 0; +} + +static void +pdelete(Process *p) +{ + if(p->f) + p->f->b = p->b; + if(p->b) + p->b->f = p->f; + else + phead = p->f; + p->f = pfree; + pfree = p; +} + +void +killchildren(char *msg) +{ + Process *p; + + kflag = 1; /* to make sure waitup doesn't exit */ + jobs = 0; /* make sure no more get scheduled */ + for(p = phead; p; p = p->f) + expunge(p->pid, msg); + while(waitup(1, (int *)0) == 0) + ; + Bprint(&bout, "mk: %s\n", msg); + Exit(); +} + +static long tslot[1000]; +static long tick; + +void +usage(void) +{ + long t; + + time(&t); + if(tick) + tslot[nrunning] += (t-tick); + tick = t; +} + +void +prusage(void) +{ + int i; + + usage(); + for(i = 0; i <= nevents; i++) + fprint(1, "%d: %ld\n", i, tslot[i]); +} diff --git a/mk/sh.c b/mk/sh.c @@ -0,0 +1,206 @@ +#include "mk.h" + +/* + * This file contains functions that depend on the shell's syntax. Most + * of the routines extract strings observing the shell's escape conventions. + */ + + +/* + * skip a token in quotes. + */ +static char * +squote(char *cp, int c) +{ + Rune r; + int n; + + while(*cp){ + n = chartorune(&r, cp); + if(r == c) + return cp; + if(r == '\\') + n += chartorune(&r, cp+n); + cp += n; + } + SYNERR(-1); /* should never occur */ + fprint(2, "missing closing '\n"); + return 0; +} +/* + * search a string for unescaped characters in a pattern set + */ +static char * +shcharin(char *cp, char *pat) +{ + Rune r; + int n, vargen; + + vargen = 0; + while(*cp){ + n = chartorune(&r, cp); + switch(r){ + case '\\': /* skip escaped char */ + cp += n; + n = chartorune(&r, cp); + break; + case '\'': /* skip quoted string */ + case '"': + cp = squote(cp+1, r); /* n must = 1 */ + if(!cp) + return 0; + break; + case '$': + if(*(cp+1) == '{') + vargen = 1; + break; + case '}': + if(vargen) + vargen = 0; + else if(utfrune(pat, r)) + return cp; + break; + default: + if(vargen == 0 && utfrune(pat, r)) + return cp; + break; + } + cp += n; + } + if(vargen){ + SYNERR(-1); + fprint(2, "missing closing } in pattern generator\n"); + } + return 0; +} + +/* + * extract an escaped token. Possible escape chars are single-quote, + * double-quote,and backslash. + */ +static char* +shexpandquote(char *s, Rune esc, Bufblock *b) +{ + Rune r; + + if (esc == '\\') { + s += chartorune(&r, s); + rinsert(b, r); + return s; + } + + while(*s){ + s += chartorune(&r, s); + if(r == esc) + return s; + if (r == '\\') { + rinsert(b, r); + s += chartorune(&r, s); + } + rinsert(b, r); + } + return 0; +} + +/* + * Input an escaped token. Possible escape chars are single-quote, + * double-quote and backslash. + */ +static int +shescapetoken(Biobuf *bp, Bufblock *buf, int preserve, int esc) +{ + int c, line; + + if(esc == '\\') { + c = Bgetrune(bp); + if(c == '\r') + c = Bgetrune(bp); + if (c == '\n') + mkinline++; + rinsert(buf, c); + return 1; + } + + line = mkinline; + while((c = nextrune(bp, 0)) >= 0){ + if(c == esc){ + if(preserve) + rinsert(buf, c); + return 1; + } + if(c == '\\') { + rinsert(buf, c); + c = Bgetrune(bp); + if(c == '\r') + c = Bgetrune(bp); + if (c < 0) + break; + if (c == '\n') + mkinline++; + } + rinsert(buf, c); + } + SYNERR(line); fprint(2, "missing closing %c\n", esc); + return 0; +} + +/* + * copy a quoted string; s points to char after opening quote + */ +static char * +copysingle(char *s, Rune q, Bufblock *buf) +{ + Rune r; + + while(*s){ + s += chartorune(&r, s); + rinsert(buf, r); + if(r == q) + break; + } + return s; +} +/* + * check for quoted strings. backquotes are handled here; single quotes above. + * s points to char after opening quote, q. + */ +static char * +shcopyq(char *s, Rune q, Bufblock *buf) +{ + if(q == '\'' || q == '"') /* copy quoted string */ + return copysingle(s, q, buf); + + if(q != '`') /* not quoted */ + return s; + + while(*s){ /* copy backquoted string */ + s += chartorune(&q, s); + rinsert(buf, q); + if(q == '`') + break; + if(q == '\'' || q == '"') + s = copysingle(s, q, buf); /* copy quoted string */ + } + return s; +} + +static int +shmatchname(char *name) +{ + USED(name); + + return 1; +} + + +Shell shshell = { + "sh", + "\"'= \t", /*used in parse.c to isolate assignment attribute*/ + ' ', /* inter-word separator in env */ + shcharin, + shexpandquote, + shescapetoken, + shcopyq, + shmatchname +}; + diff --git a/mk/shell.c b/mk/shell.c @@ -0,0 +1,80 @@ +#include "mk.h" + +static Shell *shells[] = { + &rcshell, + &shshell +}; + +Shell *shelldefault = &shshell; + +Shell *shellt; +Word *shellcmd; + +typedef struct Shellstack Shellstack; +struct Shellstack +{ + Shell *t; + Word *w; + Shellstack *next; +}; + +Shellstack *shellstack; + +char* +setshell(Word *w) +{ + int i; + + if(w->s == nil) + return "shell name not found on line"; + + for(i=0; i<nelem(shells); i++) + if(shells[i]->matchname(w->s)) + break; + if(i == nelem(shells)) + return "cannot determine shell type"; + shellt = shells[i]; + shellcmd = w; + return nil; +} + +void +initshell(void) +{ + shellcmd = stow(shelldefault->name); + shellt = shelldefault; + setvar("MKSHELL", shellcmd); +} + +void +pushshell(void) +{ + Shellstack *s; + + /* save */ + s = Malloc(sizeof *s); + s->t = shellt; + s->w = shellcmd; + s->next = shellstack; + shellstack = s; + + initshell(); /* reset to defaults */ +} + +void +popshell(void) +{ + Shellstack *s; + + if(shellstack == nil){ + fprint(2, "internal shellstack error\n"); + Exit(); + } + + s = shellstack; + shellstack = s->next; + shellt = s->t; + shellcmd = s->w; + setvar("MKSHELL", shellcmd); + free(s); +} diff --git a/mk/shprint.c b/mk/shprint.c @@ -0,0 +1,125 @@ +#include "mk.h" + +static char *vexpand(char*, Envy*, Bufblock*); + +#define getfields mkgetfields + +static 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; +} + +void +shprint(char *s, Envy *env, Bufblock *buf, Shell *sh) +{ + int n; + Rune r; + + while(*s) { + n = chartorune(&r, s); + if (r == '$') + s = vexpand(s, env, buf); + else { + rinsert(buf, r); + s += n; + s = sh->copyq(s, r, buf); /*handle quoted strings*/ + } + } + insert(buf, 0); +} + +static char * +mygetenv(char *name, Envy *env) +{ + if (!env) + return 0; + if (symlook(name, S_WESET, 0) == 0 && symlook(name, S_INTERNAL, 0) == 0) + return 0; + /* only resolve internal variables and variables we've set */ + for(; env->name; env++){ + if (strcmp(env->name, name) == 0) + return wtos(env->values, ' '); + } + return 0; +} + +static char * +vexpand(char *w, Envy *env, Bufblock *buf) +{ + char *s, carry, *p, *q; + + assert("vexpand no $", *w == '$'); + p = w+1; /* skip dollar sign */ + if(*p == '{') { + p++; + q = utfrune(p, '}'); + if (!q) + q = strchr(p, 0); + } else + q = shname(p); + carry = *q; + *q = 0; + s = mygetenv(p, env); + *q = carry; + if (carry == '}') + q++; + if (s) { + bufcpy(buf, s, strlen(s)); + free(s); + } else /* copy name intact*/ + bufcpy(buf, w, q-w); + return(q); +} + +void +front(char *s) +{ + char *t, *q; + int i, j; + char *flds[512]; + + q = strdup(s); + i = getfields(q, flds, 512, 0, " \t\n"); + if(i > 5){ + flds[4] = flds[i-1]; + flds[3] = "..."; + i = 5; + } + t = s; + for(j = 0; j < i; j++){ + for(s = flds[j]; *s; *t++ = *s++); + *t++ = ' '; + } + *t = 0; + free(q); +} diff --git a/mk/symtab.c b/mk/symtab.c @@ -0,0 +1,97 @@ +#include "mk.h" + +#define NHASH 4099 +#define HASHMUL 79L /* this is a good value */ +static Symtab *hash[NHASH]; + +void +syminit(void) +{ + Symtab **s, *ss, *next; + + for(s = hash; s < &hash[NHASH]; s++){ + for(ss = *s; ss; ss = next){ + next = ss->next; + free((char *)ss); + } + *s = 0; + } +} + +Symtab * +symlook(char *sym, int space, void *install) +{ + long h; + char *p; + Symtab *s; + + for(p = sym, h = space; *p; h += *p++) + h *= HASHMUL; + if(h < 0) + h = ~h; + h %= NHASH; + for(s = hash[h]; s; s = s->next) + if((s->space == space) && (strcmp(s->name, sym) == 0)) + return(s); + if(install == 0) + return(0); + s = (Symtab *)Malloc(sizeof(Symtab)); + s->space = space; + s->name = sym; + s->u.ptr = install; + s->next = hash[h]; + hash[h] = s; + return(s); +} + +void +symdel(char *sym, int space) +{ + long h; + char *p; + Symtab *s, *ls; + + /* multiple memory leaks */ + + for(p = sym, h = space; *p; h += *p++) + h *= HASHMUL; + if(h < 0) + h = ~h; + h %= NHASH; + for(s = hash[h], ls = 0; s; ls = s, s = s->next) + if((s->space == space) && (strcmp(s->name, sym) == 0)){ + if(ls) + ls->next = s->next; + else + hash[h] = s->next; + free((char *)s); + } +} + +void +symtraverse(int space, void (*fn)(Symtab*)) +{ + Symtab **s, *ss; + + for(s = hash; s < &hash[NHASH]; s++) + for(ss = *s; ss; ss = ss->next) + if(ss->space == space) + (*fn)(ss); +} + +void +symstat(void) +{ + Symtab **s, *ss; + int n; + int l[1000]; + + memset((char *)l, 0, sizeof(l)); + for(s = hash; s < &hash[NHASH]; s++){ + for(ss = *s, n = 0; ss; ss = ss->next) + n++; + l[n]++; + } + for(n = 0; n < 1000; n++) + if(l[n]) Bprint(&bout, "%ld of length %d\n", l[n], n); +} diff --git a/mk/sys.h b/mk/sys.h @@ -0,0 +1,5 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <regexp.h> + diff --git a/mk/sys.std.h b/mk/sys.std.h @@ -0,0 +1,27 @@ +#include <utf.h> +#include <fmt.h> +#include <bio.h> +#include <regexp9.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <ctype.h> +#include <time.h> +#include <stdint.h> + +#define OREAD O_RDONLY +#define OWRITE O_WRONLY +#define ORDWR O_RDWR +#define nil 0 +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#define seek lseek +#define remove unlink +#define exits(x) exit(x && *(char*)x ? 1 : 0) +#define USED(x) if(x){}else +#define create(name, mode, perm) open(name, mode|O_CREAT, perm) +#define ERRMAX 256 + +typedef uintptr_t uintptr; +#define uchar mk_uchar +typedef unsigned char uchar; diff --git a/mk/unix.c b/mk/unix.c @@ -0,0 +1,341 @@ +#define NOPLAN9DEFINES +#include "mk.h" +#include <sys/wait.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/time.h> + +char *shell = "/bin/sh"; +char *shellname = "sh"; + +extern char **environ; + +static void +mkperror(char *s) +{ + fprint(2, "%s: %r\n", s); +} + +void +readenv(void) +{ + char **p, *s; + Word *w; + + for(p = environ; *p; p++){ +/* rsc 5/5/2004 -- This misparses fn#cd={whatever} + s = shname(*p); + if(*s == '=') { + *s = 0; + w = newword(s+1); + } else + w = newword(""); +*/ + s = strchr(*p, '='); + if(s){ + *s = 0; + w = newword(s+1); + } else + w = newword(""); + if (symlook(*p, S_INTERNAL, 0)) + continue; + s = strdup(*p); + setvar(s, (void *)w); + symlook(s, S_EXPORTED, (void*)"")->u.ptr = ""; + } +} + +/* + * done on child side of fork, so parent's env is not affected + * and we don't care about freeing memory because we're going + * to exec immediately after this. + */ +void +exportenv(Envy *e, Shell *sh) +{ + int i; + char **p; + static char buf[16384]; + + p = 0; + for(i = 0; e->name; e++, i++) { + p = (char**) Realloc(p, (i+2)*sizeof(char*)); + if(e->values) + snprint(buf, sizeof buf, "%s=%s", e->name, wtos(e->values, sh->iws)); + else + snprint(buf, sizeof buf, "%s=", e->name); + p[i] = strdup(buf); + } + p[i] = 0; + environ = p; +} + +int +waitfor(char *msg) +{ + int status; + int pid; + + *msg = 0; + pid = wait(&status); + if(pid > 0) { + if(status&0x7f) { + if(status&0x80) + snprint(msg, ERRMAX, "signal %d, core dumped", status&0x7f); + else + snprint(msg, ERRMAX, "signal %d", status&0x7f); + } else if(status&0xff00) + snprint(msg, ERRMAX, "exit(%d)", (status>>8)&0xff); + } + return pid; +} + +void +expunge(int pid, char *msg) +{ + if(strcmp(msg, "interrupt")) + kill(pid, SIGINT); + else + kill(pid, SIGHUP); +} + +int mypid; + +int +shargv(Word *cmd, int extra, char ***pargv) +{ + char **argv; + int i, n; + Word *w; + + n = 0; + for(w=cmd; w; w=w->next) + n++; + + argv = Malloc((n+extra+1)*sizeof(argv[0])); + i = 0; + for(w=cmd; w; w=w->next) + argv[i++] = w->s; + argv[n] = 0; + *pargv = argv; + return n; +} + +int +execsh(char *args, char *cmd, Bufblock *buf, Envy *e, Shell *sh, Word *shellcmd) +{ + char *p, **argv; + int tot, n, pid, in[2], out[2]; + + if(buf && pipe(out) < 0){ + mkperror("pipe"); + Exit(); + } + pid = fork(); + mypid = getpid(); + if(pid < 0){ + mkperror("mk fork"); + Exit(); + } + if(pid == 0){ + if(buf) + close(out[0]); + if(pipe(in) < 0){ + mkperror("pipe"); + Exit(); + } + pid = fork(); + if(pid < 0){ + mkperror("mk fork"); + Exit(); + } + if(pid != 0){ + dup2(in[0], 0); + if(buf){ + dup2(out[1], 1); + close(out[1]); + } + close(in[0]); + close(in[1]); + if (e) + exportenv(e, sh); + n = shargv(shellcmd, 1, &argv); + argv[n++] = args; + argv[n] = 0; + execvp(argv[0], argv); + mkperror(shell); + _exit(1); + } + close(out[1]); + close(in[0]); + if(DEBUG(D_EXEC)) + fprint(1, "starting: %s\n", cmd); + p = cmd+strlen(cmd); + while(cmd < p){ + n = write(in[1], cmd, p-cmd); + if(n < 0) + break; + cmd += n; + } + close(in[1]); + _exit(0); + } + if(buf){ + close(out[1]); + tot = 0; + for(;;){ + if (buf->current >= buf->end) + growbuf(buf); + n = read(out[0], buf->current, buf->end-buf->current); + if(n <= 0) + break; + buf->current += n; + tot += n; + } + if (tot && buf->current[-1] == '\n') + buf->current--; + close(out[0]); + } + return pid; +} + +int +pipecmd(char *cmd, Envy *e, int *fd, Shell *sh, Word *shellcmd) +{ + int pid, pfd[2]; + int n; + char **argv; + + if(DEBUG(D_EXEC)) + fprint(1, "pipecmd='%s'\n", cmd);/**/ + + if(fd && pipe(pfd) < 0){ + mkperror("pipe"); + Exit(); + } + pid = fork(); + if(pid < 0){ + mkperror("mk fork"); + Exit(); + } + if(pid == 0){ + if(fd){ + close(pfd[0]); + dup2(pfd[1], 1); + close(pfd[1]); + } + if(e) + exportenv(e, sh); + n = shargv(shellcmd, 2, &argv); + argv[n++] = "-c"; + argv[n++] = cmd; + argv[n] = 0; + execvp(argv[0], argv); + mkperror(shell); + _exit(1); + } + if(fd){ + close(pfd[1]); + *fd = pfd[0]; + } + return pid; +} + +void +Exit(void) +{ + while(wait(0) >= 0) + ; + exits("error"); +} + +static struct +{ + int sig; + char *msg; +} sigmsgs[] = +{ + SIGALRM, "alarm", + SIGFPE, "sys: fp: fptrap", + SIGPIPE, "sys: write on closed pipe", + SIGILL, "sys: trap: illegal instruction", +/* SIGSEGV, "sys: segmentation violation", */ + 0, 0 +}; + +static void +notifyf(int sig) +{ + int i; + + for(i = 0; sigmsgs[i].msg; i++) + if(sigmsgs[i].sig == sig) + killchildren(sigmsgs[i].msg); + + /* should never happen */ + signal(sig, SIG_DFL); + kill(getpid(), sig); +} + +void +catchnotes(void) +{ + int i; + + for(i = 0; sigmsgs[i].msg; i++) + signal(sigmsgs[i].sig, notifyf); +} + +char* +maketmp(int *pfd) +{ + static char temp[] = "/tmp/mkargXXXXXX"; + static char buf[100]; + int fd; + + strcpy(buf, temp); + fd = mkstemp(buf); + if(fd < 0) + return 0; + *pfd = fd; + return buf; +} + +int +chgtime(char *name) +{ + if(access(name, 0) >= 0) + return utimes(name, 0); + return close(creat(name, 0666)); +} + +void +rcopy(char **to, Resub *match, int n) +{ + int c; + char *p; + + *to = match->s.sp; /* stem0 matches complete target */ + for(to++, match++; --n > 0; to++, match++){ + if(match->s.sp && match->e.ep){ + p = match->e.ep; + c = *p; + *p = 0; + *to = strdup(match->s.sp); + *p = c; + } + else + *to = 0; + } +} + +unsigned long +mkmtime(char *name) +{ + struct stat st; + + if(stat(name, &st) < 0) + return 0; + + return st.st_mtime; +} diff --git a/mk/var.c b/mk/var.c @@ -0,0 +1,41 @@ +#include "mk.h" + +void +setvar(char *name, void *ptr) +{ + symlook(name, S_VAR, ptr)->u.ptr = ptr; + symlook(name, S_MAKEVAR, (void*)""); +} + +static void +print1(Symtab *s) +{ + Word *w; + + Bprint(&bout, "\t%s=", s->name); + for (w = s->u.ptr; w; w = w->next) + Bprint(&bout, "'%s'", w->s); + Bprint(&bout, "\n"); +} + +void +dumpv(char *s) +{ + Bprint(&bout, "%s:\n", s); + symtraverse(S_VAR, print1); +} + +char * +shname(char *a) +{ + Rune r; + int n; + + while (*a) { + n = chartorune(&r, a); + if (!WORDCHR(r)) + break; + a += n; + } + return a; +} diff --git a/mk/varsub.c b/mk/varsub.c @@ -0,0 +1,252 @@ +#include "mk.h" + +static Word *subsub(Word*, char*, char*); +static Word *expandvar(char**); +static Bufblock *varname(char**); +static Word *extractpat(char*, char**, char*, char*); +static int submatch(char*, Word*, Word*, int*, char**); +static Word *varmatch(char *); + +Word * +varsub(char **s) +{ + Bufblock *b; + Word *w; + + if(**s == '{') /* either ${name} or ${name: A%B==C%D}*/ + return expandvar(s); + + b = varname(s); + if(b == 0) + return 0; + + w = varmatch(b->start); + freebuf(b); + return w; +} + +/* + * extract a variable name + */ +static Bufblock* +varname(char **s) +{ + Bufblock *b; + char *cp; + Rune r; + int n; + + b = newbuf(); + cp = *s; + for(;;){ + n = chartorune(&r, cp); + if (!WORDCHR(r)) + break; + rinsert(b, r); + cp += n; + } + if (b->current == b->start){ + SYNERR(-1); + fprint(2, "missing variable name <%s>\n", *s); + freebuf(b); + return 0; + } + *s = cp; + insert(b, 0); + return b; +} + +static Word* +varmatch(char *name) +{ + Word *w; + Symtab *sym; + + sym = symlook(name, S_VAR, 0); + if(sym){ + /* check for at least one non-NULL value */ + for (w = sym->u.ptr; w; w = w->next) + if(w->s && *w->s) + return wdup(w); + } + return 0; +} + +static Word* +expandvar(char **s) +{ + Word *w; + Bufblock *buf; + Symtab *sym; + char *cp, *begin, *end; + + begin = *s; + (*s)++; /* skip the '{' */ + buf = varname(s); + if (buf == 0) + return 0; + cp = *s; + if (*cp == '}') { /* ${name} variant*/ + (*s)++; /* skip the '}' */ + w = varmatch(buf->start); + freebuf(buf); + return w; + } + if (*cp != ':') { + SYNERR(-1); + fprint(2, "bad variable name <%s>\n", buf->start); + freebuf(buf); + return 0; + } + cp++; + end = shellt->charin(cp , "}"); + if(end == 0){ + SYNERR(-1); + fprint(2, "missing '}': %s\n", begin); + Exit(); + } + *end = 0; + *s = end+1; + + sym = symlook(buf->start, S_VAR, 0); + if(sym == 0 || sym->u.ptr == 0) + w = newword(buf->start); + else + w = subsub(sym->u.ptr, cp, end); + freebuf(buf); + return w; +} + +static Word* +extractpat(char *s, char **r, char *term, char *end) +{ + int save; + char *cp; + Word *w; + + cp = shellt->charin(s, term); + if(cp){ + *r = cp; + if(cp == s) + return 0; + save = *cp; + *cp = 0; + w = stow(s); + *cp = save; + } else { + *r = end; + w = stow(s); + } + return w; +} + +static Word* +subsub(Word *v, char *s, char *end) +{ + int nmid; + Word *head, *tail, *w, *h; + Word *a, *b, *c, *d; + Bufblock *buf; + char *cp, *enda; + + a = extractpat(s, &cp, "=%&", end); + b = c = d = 0; + if(PERCENT(*cp)) + b = extractpat(cp+1, &cp, "=", end); + if(*cp == '=') + c = extractpat(cp+1, &cp, "&%", end); + if(PERCENT(*cp)) + d = stow(cp+1); + else if(*cp) + d = stow(cp); + + head = tail = 0; + buf = newbuf(); + for(; v; v = v->next){ + h = w = 0; + if(submatch(v->s, a, b, &nmid, &enda)){ + /* enda points to end of A match in source; + * nmid = number of chars between end of A and start of B + */ + if(c){ + h = w = wdup(c); + while(w->next) + w = w->next; + } + if(PERCENT(*cp) && nmid > 0){ + if(w){ + bufcpy(buf, w->s, strlen(w->s)); + bufcpy(buf, enda, nmid); + insert(buf, 0); + free(w->s); + w->s = strdup(buf->start); + } else { + bufcpy(buf, enda, nmid); + insert(buf, 0); + h = w = newword(buf->start); + } + buf->current = buf->start; + } + if(d && *d->s){ + if(w){ + + bufcpy(buf, w->s, strlen(w->s)); + bufcpy(buf, d->s, strlen(d->s)); + insert(buf, 0); + free(w->s); + w->s = strdup(buf->start); + w->next = wdup(d->next); + while(w->next) + w = w->next; + buf->current = buf->start; + } else + h = w = wdup(d); + } + } + if(w == 0) + h = w = newword(v->s); + + if(head == 0) + head = h; + else + tail->next = h; + tail = w; + } + freebuf(buf); + delword(a); + delword(b); + delword(c); + delword(d); + return head; +} + +static int +submatch(char *s, Word *a, Word *b, int *nmid, char **enda) +{ + Word *w; + int n; + char *end; + + n = 0; + for(w = a; w; w = w->next){ + n = strlen(w->s); + if(strncmp(s, w->s, n) == 0) + break; + } + if(a && w == 0) /* a == NULL matches everything*/ + return 0; + + *enda = s+n; /* pointer to end a A part match */ + *nmid = strlen(s)-n; /* size of remainder of source */ + end = *enda+*nmid; + for(w = b; w; w = w->next){ + n = strlen(w->s); + if(strcmp(w->s, end-n) == 0){ + *nmid -= n; + break; + } + } + if(b && w == 0) /* b == NULL matches everything */ + return 0; + return 1; +} diff --git a/mk/word.c b/mk/word.c @@ -0,0 +1,189 @@ +#include "mk.h" + +static Word *nextword(char**); + +Word* +newword(char *s) +{ + Word *w; + + w = (Word *)Malloc(sizeof(Word)); + w->s = strdup(s); + w->next = 0; + return(w); +} + +Word * +stow(char *s) +{ + Word *head, *w, *new; + + w = head = 0; + while(*s){ + new = nextword(&s); + if(new == 0) + break; + if (w) + w->next = new; + else + head = w = new; + while(w->next) + w = w->next; + + } + if (!head) + head = newword(""); + return(head); +} + +char * +wtos(Word *w, int sep) +{ + Bufblock *buf; + char *cp; + + buf = newbuf(); + for(; w; w = w->next){ + for(cp = w->s; *cp; cp++) + insert(buf, *cp); + if(w->next) + insert(buf, sep); + } + insert(buf, 0); + cp = strdup(buf->start); + freebuf(buf); + return(cp); +} + +Word* +wdup(Word *w) +{ + Word *v, *new, *base; + + v = base = 0; + while(w){ + new = newword(w->s); + if(v) + v->next = new; + else + base = new; + v = new; + w = w->next; + } + return base; +} + +void +delword(Word *w) +{ + Word *v; + + while(v = w){ + w = w->next; + if(v->s) + free(v->s); + free(v); + } +} + +/* + * break out a word from a string handling quotes, executions, + * and variable expansions. + */ +static Word* +nextword(char **s) +{ + Bufblock *b; + Word *head, *tail, *w; + Rune r; + char *cp; + int empty; + + cp = *s; + b = newbuf(); +restart: + head = tail = 0; + while(*cp == ' ' || *cp == '\t') /* leading white space */ + cp++; + empty = 1; + while(*cp){ + cp += chartorune(&r, cp); + switch(r) + { + case ' ': + case '\t': + case '\n': + goto out; + case '\\': + case '\'': + case '"': + empty = 0; + cp = shellt->expandquote(cp, r, b); + if(cp == 0){ + fprint(2, "missing closing quote: %s\n", *s); + Exit(); + } + break; + case '$': + w = varsub(&cp); + if(w == 0){ + if(empty) + goto restart; + break; + } + empty = 0; + if(b->current != b->start){ + bufcpy(b, w->s, strlen(w->s)); + insert(b, 0); + free(w->s); + w->s = strdup(b->start); + b->current = b->start; + } + if(head){ + bufcpy(b, tail->s, strlen(tail->s)); + bufcpy(b, w->s, strlen(w->s)); + insert(b, 0); + free(tail->s); + tail->s = strdup(b->start); + tail->next = w->next; + free(w->s); + free(w); + b->current = b->start; + } else + tail = head = w; + while(tail->next) + tail = tail->next; + break; + default: + empty = 0; + rinsert(b, r); + break; + } + } +out: + *s = cp; + if(b->current != b->start){ + if(head){ + cp = b->current; + bufcpy(b, tail->s, strlen(tail->s)); + bufcpy(b, b->start, cp-b->start); + insert(b, 0); + free(tail->s); + tail->s = strdup(cp); + } else { + insert(b, 0); + head = newword(b->start); + } + } + freebuf(b); + return head; +} + +void +dumpw(char *s, Word *w) +{ + Bprint(&bout, "%s", s); + for(; w; w = w->next) + Bprint(&bout, " '%s'", w->s); + Bputc(&bout, '\n'); +} diff --git a/std.mk b/std.mk @@ -0,0 +1,35 @@ +# std.mk - included by most Makefiles in subdirs + +OFILES ?= ${TARG}.o +MANFILE ?= ${TARG}.1 + +include ../config.mk + +all: ${TARG} +# @strip ${TARG} + @echo built ${TARG} + +install: install-default post-install + +install-default: ${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 ${MANFILE} ${DESTDIR}${MANPREFIX}/man1 + @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILE} + +uninstall: pre-uninstall + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${MANPREFIX}/man1/${MANFILE} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -I../lib9/sec $*.c + +clean: + rm -f ${OFILES} ${TARG} + +${TARG}: ${OFILES} + @echo LD ${TARG} + @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L../lib9 -l9 -lm diff --git a/yacc.mk b/yacc.mk @@ -0,0 +1,40 @@ +# yacc.mk - included by all Makefiles in subdirs which depend on yacc + +include ../config.mk + +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: install-default post-install + +install-default: ${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: pre-uninstall + rm -f ${DESTDIR}${PREFIX}/bin/${TARG} + rm -f ${DESTDIR}${MANPREFIX}/man1/${MANFILES} + +.c.o: + @echo CC $*.c + @${CC} ${CFLAGS} -I../lib9 -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../lib9 -l9 -lm