From c43269a19edbf99608dc7a90808599d4a0ac6b8a Mon Sep 17 00:00:00 2001 From: Giacomo Tesio Date: Wed, 22 Dec 2021 10:35:45 +0100 Subject: [PATCH] minimal plan9port to cross-build Jehanne --- .gitignore | 1 + LICENSE.Plan9Foundation.txt | 21 + README.txt | 27 + build.sh | 12 + cmd/rc/code.c | 496 ++++++ cmd/rc/exec.c | 1001 ++++++++++++ cmd/rc/exec.h | 76 + cmd/rc/fmtquote.c | 162 ++ cmd/rc/fns.h | 68 + cmd/rc/getflags.c | 244 +++ cmd/rc/getflags.h | 7 + cmd/rc/glob.c | 267 ++++ cmd/rc/havefork.c | 298 ++++ cmd/rc/haventfork.c | 211 +++ cmd/rc/here.c | 150 ++ cmd/rc/io.c | 273 ++++ cmd/rc/io.h | 31 + cmd/rc/lex.c | 396 +++++ cmd/rc/mkfile | 38 + cmd/rc/parse.c | 552 +++++++ cmd/rc/pcmd.c | 265 ++++ cmd/rc/pfnc.c | 71 + cmd/rc/plan9ish.c | 604 +++++++ cmd/rc/rc.h | 153 ++ cmd/rc/rc.sh.build | 80 + cmd/rc/simple.c | 505 ++++++ cmd/rc/subr.c | 77 + cmd/rc/syn.y | 91 ++ cmd/rc/trap.c | 37 + cmd/rc/tree.c | 146 ++ cmd/rc/unixcrap.c | 242 +++ cmd/rc/var.c | 173 ++ cmd/yacc.c | 2993 +++++++++++++++++++++++++++++++++++ include/bio.h | 91 ++ include/fmt.h | 116 ++ include/lib9.h | 2 + include/libc.h | 950 +++++++++++ include/u.h | 195 +++ include/utf.h | 54 + lib/bio/_lib9.h | 12 + lib/bio/bbuffered.c | 20 + lib/bio/bcat.c | 46 + lib/bio/bfildes.c | 9 + lib/bio/bflush.c | 33 + lib/bio/bgetc.c | 53 + lib/bio/bgetd.c | 36 + lib/bio/bgetrune.c | 47 + lib/bio/binit.c | 155 ++ lib/bio/boffset.c | 25 + lib/bio/bprint.c | 14 + lib/bio/bputc.c | 20 + lib/bio/bputrune.c | 23 + lib/bio/brdline.c | 94 ++ lib/bio/brdstr.c | 111 ++ lib/bio/bread.c | 45 + lib/bio/bseek.c | 60 + lib/bio/bvprint.c | 38 + lib/bio/bwrite.c | 38 + lib/bio/lib9.std.h | 25 + lib/bio/libbio.sh.build | 47 + lib/bio/mkfile | 31 + lib/bio/portdate | 18 + lib/lib9/LICENSE | 92 ++ lib/lib9/_exits.c | 10 + lib/lib9/_p9dialparse.c | 185 +++ lib/lib9/_p9dir.c | 242 +++ lib/lib9/announce.c | 155 ++ lib/lib9/argv0.c | 9 + lib/lib9/atexit.c | 56 + lib/lib9/atnotify.c | 58 + lib/lib9/atoi.c | 8 + lib/lib9/atol.c | 8 + lib/lib9/atoll.c | 8 + lib/lib9/await.c | 136 ++ lib/lib9/cistrcmp.c | 26 + lib/lib9/cistrncmp.c | 28 + lib/lib9/cistrstr.c | 23 + lib/lib9/cleanname.c | 52 + lib/lib9/convD2M.c | 101 ++ lib/lib9/convM2D.c | 98 ++ lib/lib9/convM2S.c | 326 ++++ lib/lib9/convS2M.c | 399 +++++ lib/lib9/crypt.c | 68 + lib/lib9/ctime.c | 180 +++ lib/lib9/debugmalloc.c | 157 ++ lib/lib9/dial.c | 159 ++ lib/lib9/dirfstat.c | 28 + lib/lib9/dirfwstat.c | 56 + lib/lib9/dirmodefmt.c | 62 + lib/lib9/dirstat.c | 31 + lib/lib9/dirwstat.c | 31 + lib/lib9/dup.c | 12 + lib/lib9/encodefmt.c | 76 + lib/lib9/errstr.c | 80 + lib/lib9/exec.c | 9 + lib/lib9/execl.c | 28 + lib/lib9/exitcode.c | 8 + lib/lib9/fcallfmt.c | 253 +++ lib/lib9/fmt/LICENSE | 22 + lib/lib9/fmt/README | 19 + lib/lib9/fmt/charstod.c | 73 + lib/lib9/fmt/dofmt.c | 617 ++++++++ lib/lib9/fmt/dorfmt.c | 50 + lib/lib9/fmt/errfmt.c | 16 + lib/lib9/fmt/fltfmt.c | 667 ++++++++ lib/lib9/fmt/fmt.c | 229 +++ lib/lib9/fmt/fmtdef.h | 104 ++ lib/lib9/fmt/fmtfd.c | 36 + lib/lib9/fmt/fmtfdflush.c | 22 + lib/lib9/fmt/fmtlocale.c | 54 + lib/lib9/fmt/fmtlock.c | 15 + lib/lib9/fmt/fmtnull.c | 32 + lib/lib9/fmt/fmtprint.c | 35 + lib/lib9/fmt/fmtquote.c | 259 +++ lib/lib9/fmt/fmtrune.c | 28 + lib/lib9/fmt/fmtstr.c | 16 + lib/lib9/fmt/fmtvprint.c | 36 + lib/lib9/fmt/fprint.c | 17 + lib/lib9/fmt/nan.h | 4 + lib/lib9/fmt/nan64.c | 78 + lib/lib9/fmt/plan9.h | 37 + lib/lib9/fmt/portdate | 30 + lib/lib9/fmt/pow10.c | 45 + lib/lib9/fmt/print.c | 17 + lib/lib9/fmt/runefmtstr.c | 16 + lib/lib9/fmt/runeseprint.c | 18 + lib/lib9/fmt/runesmprint.c | 18 + lib/lib9/fmt/runesnprint.c | 18 + lib/lib9/fmt/runesprint.c | 18 + lib/lib9/fmt/runevseprint.c | 28 + lib/lib9/fmt/runevsmprint.c | 86 + lib/lib9/fmt/runevsnprint.c | 28 + lib/lib9/fmt/seprint.c | 17 + lib/lib9/fmt/smprint.c | 17 + lib/lib9/fmt/snprint.c | 17 + lib/lib9/fmt/sprint.c | 30 + lib/lib9/fmt/strtod.c | 520 ++++++ lib/lib9/fmt/strtod.h | 4 + lib/lib9/fmt/test.c | 53 + lib/lib9/fmt/test2.c | 9 + lib/lib9/fmt/test3.c | 52 + lib/lib9/fmt/vfprint.c | 21 + lib/lib9/fmt/vseprint.c | 27 + lib/lib9/fmt/vsmprint.c | 83 + lib/lib9/fmt/vsnprint.c | 28 + lib/lib9/fmtlock2.c | 16 + lib/lib9/fork.c | 22 + lib/lib9/frand.c | 17 + lib/lib9/frexp.c | 9 + lib/lib9/get9root.c | 17 + lib/lib9/getcallerpc.c | 13 + lib/lib9/getenv.c | 20 + lib/lib9/getfields.c | 36 + lib/lib9/getnetconn.c | 160 ++ lib/lib9/getns.c | 97 ++ lib/lib9/getuser.c | 16 + lib/lib9/getwd.c | 10 + lib/lib9/jmp.c | 16 + lib/lib9/lib9.sh.build | 169 ++ lib/lib9/lnrand.c | 18 + lib/lib9/lock.c | 55 + lib/lib9/lrand.c | 81 + lib/lib9/main.c | 13 + lib/lib9/malloc.c | 37 + lib/lib9/malloctag.c | 15 + lib/lib9/mallocz.c | 15 + lib/lib9/mkfile | 196 +++ lib/lib9/nan.c | 27 + lib/lib9/needsrcquote.c | 12 + lib/lib9/needstack.c | 8 + lib/lib9/netcrypt.c | 18 + lib/lib9/netmkaddr.c | 62 + lib/lib9/notify.c | 272 ++++ lib/lib9/nrand.c | 17 + lib/lib9/nulldir.c | 9 + lib/lib9/open.c | 347 ++++ lib/lib9/opentemp.c | 19 + lib/lib9/pin.c | 10 + lib/lib9/pipe.c | 15 + lib/lib9/portdate | 55 + lib/lib9/post9p.c | 83 + lib/lib9/postnote.c | 37 + lib/lib9/priv.c | 31 + lib/lib9/qlock.c | 166 ++ lib/lib9/quote.c | 136 ++ lib/lib9/rand.c | 7 + lib/lib9/read9pmsg.c | 31 + lib/lib9/readcons.c | 104 ++ lib/lib9/readn.c | 21 + lib/lib9/rfork.c | 127 ++ lib/lib9/searchpath.c | 61 + lib/lib9/sendfd.c | 88 + lib/lib9/sleep.c | 47 + lib/lib9/strdup.c | 16 + lib/lib9/strecpy.c | 16 + lib/lib9/sysfatal.c | 20 + lib/lib9/syslog.c | 119 ++ lib/lib9/sysname.c | 30 + lib/lib9/test.c | 8 + lib/lib9/testfltfmt.c | 183 +++ lib/lib9/testfmt.c | 148 ++ lib/lib9/testfork.c | 21 + lib/lib9/testprint.c | 14 + lib/lib9/time.c | 57 + lib/lib9/tm2sec.c | 110 ++ lib/lib9/tokenize.c | 106 ++ lib/lib9/truerand.c | 27 + lib/lib9/u16.c | 52 + lib/lib9/u32.c | 109 ++ lib/lib9/u64.c | 126 ++ lib/lib9/udp.c | 51 + lib/lib9/unsharp.c | 44 + lib/lib9/utf/LICENSE | 13 + lib/lib9/utf/README | 13 + lib/lib9/utf/lib9.h | 16 + lib/lib9/utf/plan9.h | 28 + lib/lib9/utf/portdate | 20 + lib/lib9/utf/rune.c | 217 +++ lib/lib9/utf/runestrcat.c | 25 + lib/lib9/utf/runestrchr.c | 35 + lib/lib9/utf/runestrcmp.c | 35 + lib/lib9/utf/runestrcpy.c | 28 + lib/lib9/utf/runestrdup.c | 30 + lib/lib9/utf/runestrecpy.c | 32 + lib/lib9/utf/runestrlen.c | 24 + lib/lib9/utf/runestrncat.c | 32 + lib/lib9/utf/runestrncmp.c | 37 + lib/lib9/utf/runestrncpy.c | 33 + lib/lib9/utf/runestrrchr.c | 30 + lib/lib9/utf/runestrstr.c | 44 + lib/lib9/utf/runetype.c | 1151 ++++++++++++++ lib/lib9/utf/utfdef.h | 32 + lib/lib9/utf/utfecpy.c | 38 + lib/lib9/utf/utflen.c | 37 + lib/lib9/utf/utfnlen.c | 41 + lib/lib9/utf/utfrrune.c | 45 + lib/lib9/utf/utfrune.c | 44 + lib/lib9/utf/utfutf.c | 41 + lib/lib9/wait.c | 53 + lib/lib9/waitpid.c | 19 + lib/lib9/write.c | 23 + lib/lib9/zoneinfo.c | 213 +++ lib/lib9/zoneinfo.h | 18 + lib/yaccpar | 277 ++++ lib/yaccpars | 274 ++++ 245 files changed, 26213 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.Plan9Foundation.txt create mode 100644 README.txt create mode 100755 build.sh create mode 100644 cmd/rc/code.c create mode 100644 cmd/rc/exec.c create mode 100644 cmd/rc/exec.h create mode 100644 cmd/rc/fmtquote.c create mode 100644 cmd/rc/fns.h create mode 100644 cmd/rc/getflags.c create mode 100644 cmd/rc/getflags.h create mode 100644 cmd/rc/glob.c create mode 100644 cmd/rc/havefork.c create mode 100644 cmd/rc/haventfork.c create mode 100644 cmd/rc/here.c create mode 100644 cmd/rc/io.c create mode 100644 cmd/rc/io.h create mode 100644 cmd/rc/lex.c create mode 100644 cmd/rc/mkfile create mode 100644 cmd/rc/parse.c create mode 100644 cmd/rc/pcmd.c create mode 100644 cmd/rc/pfnc.c create mode 100644 cmd/rc/plan9ish.c create mode 100644 cmd/rc/rc.h create mode 100755 cmd/rc/rc.sh.build create mode 100644 cmd/rc/simple.c create mode 100644 cmd/rc/subr.c create mode 100644 cmd/rc/syn.y create mode 100644 cmd/rc/trap.c create mode 100644 cmd/rc/tree.c create mode 100644 cmd/rc/unixcrap.c create mode 100644 cmd/rc/var.c create mode 100644 cmd/yacc.c create mode 100644 include/bio.h create mode 100644 include/fmt.h create mode 100644 include/lib9.h create mode 100644 include/libc.h create mode 100644 include/u.h create mode 100644 include/utf.h create mode 100644 lib/bio/_lib9.h create mode 100644 lib/bio/bbuffered.c create mode 100644 lib/bio/bcat.c create mode 100644 lib/bio/bfildes.c create mode 100644 lib/bio/bflush.c create mode 100644 lib/bio/bgetc.c create mode 100644 lib/bio/bgetd.c create mode 100644 lib/bio/bgetrune.c create mode 100644 lib/bio/binit.c create mode 100644 lib/bio/boffset.c create mode 100644 lib/bio/bprint.c create mode 100644 lib/bio/bputc.c create mode 100644 lib/bio/bputrune.c create mode 100644 lib/bio/brdline.c create mode 100644 lib/bio/brdstr.c create mode 100644 lib/bio/bread.c create mode 100644 lib/bio/bseek.c create mode 100644 lib/bio/bvprint.c create mode 100644 lib/bio/bwrite.c create mode 100644 lib/bio/lib9.std.h create mode 100755 lib/bio/libbio.sh.build create mode 100644 lib/bio/mkfile create mode 100644 lib/bio/portdate create mode 100644 lib/lib9/LICENSE create mode 100644 lib/lib9/_exits.c create mode 100644 lib/lib9/_p9dialparse.c create mode 100644 lib/lib9/_p9dir.c create mode 100644 lib/lib9/announce.c create mode 100644 lib/lib9/argv0.c create mode 100644 lib/lib9/atexit.c create mode 100644 lib/lib9/atnotify.c create mode 100644 lib/lib9/atoi.c create mode 100644 lib/lib9/atol.c create mode 100644 lib/lib9/atoll.c create mode 100644 lib/lib9/await.c create mode 100644 lib/lib9/cistrcmp.c create mode 100644 lib/lib9/cistrncmp.c create mode 100644 lib/lib9/cistrstr.c create mode 100644 lib/lib9/cleanname.c create mode 100644 lib/lib9/convD2M.c create mode 100644 lib/lib9/convM2D.c create mode 100644 lib/lib9/convM2S.c create mode 100644 lib/lib9/convS2M.c create mode 100644 lib/lib9/crypt.c create mode 100644 lib/lib9/ctime.c create mode 100644 lib/lib9/debugmalloc.c create mode 100644 lib/lib9/dial.c create mode 100644 lib/lib9/dirfstat.c create mode 100644 lib/lib9/dirfwstat.c create mode 100644 lib/lib9/dirmodefmt.c create mode 100644 lib/lib9/dirstat.c create mode 100644 lib/lib9/dirwstat.c create mode 100644 lib/lib9/dup.c create mode 100644 lib/lib9/encodefmt.c create mode 100644 lib/lib9/errstr.c create mode 100644 lib/lib9/exec.c create mode 100644 lib/lib9/execl.c create mode 100644 lib/lib9/exitcode.c create mode 100644 lib/lib9/fcallfmt.c create mode 100644 lib/lib9/fmt/LICENSE create mode 100644 lib/lib9/fmt/README create mode 100644 lib/lib9/fmt/charstod.c create mode 100644 lib/lib9/fmt/dofmt.c create mode 100644 lib/lib9/fmt/dorfmt.c create mode 100644 lib/lib9/fmt/errfmt.c create mode 100644 lib/lib9/fmt/fltfmt.c create mode 100644 lib/lib9/fmt/fmt.c create mode 100644 lib/lib9/fmt/fmtdef.h create mode 100644 lib/lib9/fmt/fmtfd.c create mode 100644 lib/lib9/fmt/fmtfdflush.c create mode 100644 lib/lib9/fmt/fmtlocale.c create mode 100644 lib/lib9/fmt/fmtlock.c create mode 100644 lib/lib9/fmt/fmtnull.c create mode 100644 lib/lib9/fmt/fmtprint.c create mode 100644 lib/lib9/fmt/fmtquote.c create mode 100644 lib/lib9/fmt/fmtrune.c create mode 100644 lib/lib9/fmt/fmtstr.c create mode 100644 lib/lib9/fmt/fmtvprint.c create mode 100644 lib/lib9/fmt/fprint.c create mode 100644 lib/lib9/fmt/nan.h create mode 100644 lib/lib9/fmt/nan64.c create mode 100644 lib/lib9/fmt/plan9.h create mode 100644 lib/lib9/fmt/portdate create mode 100644 lib/lib9/fmt/pow10.c create mode 100644 lib/lib9/fmt/print.c create mode 100644 lib/lib9/fmt/runefmtstr.c create mode 100644 lib/lib9/fmt/runeseprint.c create mode 100644 lib/lib9/fmt/runesmprint.c create mode 100644 lib/lib9/fmt/runesnprint.c create mode 100644 lib/lib9/fmt/runesprint.c create mode 100644 lib/lib9/fmt/runevseprint.c create mode 100644 lib/lib9/fmt/runevsmprint.c create mode 100644 lib/lib9/fmt/runevsnprint.c create mode 100644 lib/lib9/fmt/seprint.c create mode 100644 lib/lib9/fmt/smprint.c create mode 100644 lib/lib9/fmt/snprint.c create mode 100644 lib/lib9/fmt/sprint.c create mode 100644 lib/lib9/fmt/strtod.c create mode 100644 lib/lib9/fmt/strtod.h create mode 100644 lib/lib9/fmt/test.c create mode 100644 lib/lib9/fmt/test2.c create mode 100644 lib/lib9/fmt/test3.c create mode 100644 lib/lib9/fmt/vfprint.c create mode 100644 lib/lib9/fmt/vseprint.c create mode 100644 lib/lib9/fmt/vsmprint.c create mode 100644 lib/lib9/fmt/vsnprint.c create mode 100644 lib/lib9/fmtlock2.c create mode 100644 lib/lib9/fork.c create mode 100644 lib/lib9/frand.c create mode 100644 lib/lib9/frexp.c create mode 100644 lib/lib9/get9root.c create mode 100644 lib/lib9/getcallerpc.c create mode 100644 lib/lib9/getenv.c create mode 100644 lib/lib9/getfields.c create mode 100644 lib/lib9/getnetconn.c create mode 100644 lib/lib9/getns.c create mode 100644 lib/lib9/getuser.c create mode 100644 lib/lib9/getwd.c create mode 100644 lib/lib9/jmp.c create mode 100755 lib/lib9/lib9.sh.build create mode 100644 lib/lib9/lnrand.c create mode 100644 lib/lib9/lock.c create mode 100644 lib/lib9/lrand.c create mode 100644 lib/lib9/main.c create mode 100644 lib/lib9/malloc.c create mode 100644 lib/lib9/malloctag.c create mode 100644 lib/lib9/mallocz.c create mode 100644 lib/lib9/mkfile create mode 100644 lib/lib9/nan.c create mode 100644 lib/lib9/needsrcquote.c create mode 100644 lib/lib9/needstack.c create mode 100644 lib/lib9/netcrypt.c create mode 100644 lib/lib9/netmkaddr.c create mode 100644 lib/lib9/notify.c create mode 100644 lib/lib9/nrand.c create mode 100644 lib/lib9/nulldir.c create mode 100644 lib/lib9/open.c create mode 100644 lib/lib9/opentemp.c create mode 100644 lib/lib9/pin.c create mode 100644 lib/lib9/pipe.c create mode 100644 lib/lib9/portdate create mode 100644 lib/lib9/post9p.c create mode 100644 lib/lib9/postnote.c create mode 100644 lib/lib9/priv.c create mode 100644 lib/lib9/qlock.c create mode 100644 lib/lib9/quote.c create mode 100644 lib/lib9/rand.c create mode 100644 lib/lib9/read9pmsg.c create mode 100644 lib/lib9/readcons.c create mode 100644 lib/lib9/readn.c create mode 100644 lib/lib9/rfork.c create mode 100644 lib/lib9/searchpath.c create mode 100644 lib/lib9/sendfd.c create mode 100644 lib/lib9/sleep.c create mode 100644 lib/lib9/strdup.c create mode 100644 lib/lib9/strecpy.c create mode 100644 lib/lib9/sysfatal.c create mode 100644 lib/lib9/syslog.c create mode 100644 lib/lib9/sysname.c create mode 100644 lib/lib9/test.c create mode 100644 lib/lib9/testfltfmt.c create mode 100644 lib/lib9/testfmt.c create mode 100644 lib/lib9/testfork.c create mode 100644 lib/lib9/testprint.c create mode 100644 lib/lib9/time.c create mode 100644 lib/lib9/tm2sec.c create mode 100644 lib/lib9/tokenize.c create mode 100644 lib/lib9/truerand.c create mode 100644 lib/lib9/u16.c create mode 100644 lib/lib9/u32.c create mode 100644 lib/lib9/u64.c create mode 100644 lib/lib9/udp.c create mode 100644 lib/lib9/unsharp.c create mode 100644 lib/lib9/utf/LICENSE create mode 100644 lib/lib9/utf/README create mode 100644 lib/lib9/utf/lib9.h create mode 100644 lib/lib9/utf/plan9.h create mode 100644 lib/lib9/utf/portdate create mode 100644 lib/lib9/utf/rune.c create mode 100644 lib/lib9/utf/runestrcat.c create mode 100644 lib/lib9/utf/runestrchr.c create mode 100644 lib/lib9/utf/runestrcmp.c create mode 100644 lib/lib9/utf/runestrcpy.c create mode 100644 lib/lib9/utf/runestrdup.c create mode 100644 lib/lib9/utf/runestrecpy.c create mode 100644 lib/lib9/utf/runestrlen.c create mode 100644 lib/lib9/utf/runestrncat.c create mode 100644 lib/lib9/utf/runestrncmp.c create mode 100644 lib/lib9/utf/runestrncpy.c create mode 100644 lib/lib9/utf/runestrrchr.c create mode 100644 lib/lib9/utf/runestrstr.c create mode 100644 lib/lib9/utf/runetype.c create mode 100644 lib/lib9/utf/utfdef.h create mode 100644 lib/lib9/utf/utfecpy.c create mode 100644 lib/lib9/utf/utflen.c create mode 100644 lib/lib9/utf/utfnlen.c create mode 100644 lib/lib9/utf/utfrrune.c create mode 100644 lib/lib9/utf/utfrune.c create mode 100644 lib/lib9/utf/utfutf.c create mode 100644 lib/lib9/wait.c create mode 100644 lib/lib9/waitpid.c create mode 100644 lib/lib9/write.c create mode 100644 lib/lib9/zoneinfo.c create mode 100644 lib/lib9/zoneinfo.h create mode 100644 lib/yaccpar create mode 100644 lib/yaccpars diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5761abc --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.o diff --git a/LICENSE.Plan9Foundation.txt b/LICENSE.Plan9Foundation.txt new file mode 100644 index 0000000..247b17a --- /dev/null +++ b/LICENSE.Plan9Foundation.txt @@ -0,0 +1,21 @@ +Copyright © 2021 Plan 9 Foundation +Portions Copyright © 2005 Russ Cox, MIT + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..b8b4a2c --- /dev/null +++ b/README.txt @@ -0,0 +1,27 @@ +A minimal plan9port fork designed just to cross-build Jehanne +============================================================= + +The sole goal of these tools is to cross-build Jehanne in a way +that can be more easily ported to Jehanne itself. + + +Tools available +--------------- +- lib + - lib9: stripped down lib9 from plan9port + uses $JEHANNE/hacking instead of $PLAN9 to not mess with + more useful plan9port installation + - bio: plan9port libbio + - yaccpar and yaccpars (parts of "yacc runtime") + +- cmd + - yacc (9yacc in $JEHANNE/hacking/bin) + - rc + +Commands will be installed in $JEHANNE/hacking/bin +Libraries will be installed in $JEHANNE/hacking/lib + +WARNING +------- + +Do not use these tools for anything else. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..f2cebbd --- /dev/null +++ b/build.sh @@ -0,0 +1,12 @@ +#!/bin/sh -e + +TARGET=$JEHANNE/hacking/bin/ +TRAMPOLINE=$JEHANNE/hacking/src/trampoline + + +(cd $TRAMPOLINE/lib/lib9/ && ./lib9.sh.build) +(cd $TRAMPOLINE/lib/bio/ && ./libbio.sh.build) +(cd $TRAMPOLINE/cmd/ && \ +cc yacc.c -DPLAN9PORT -I$TRAMPOLINE/include -L$JEHANNE/hacking/lib -O0 -lbio -l9 -o $TARGET/9yacc) +(cp lib/yacc* $TARGET/../lib) +(cd $TRAMPOLINE/cmd/rc/ && ./rc.sh.build) diff --git a/cmd/rc/code.c b/cmd/rc/code.c new file mode 100644 index 0000000..208476a --- /dev/null +++ b/cmd/rc/code.c @@ -0,0 +1,496 @@ +#include "rc.h" +#include "io.h" +#include "exec.h" +#include "fns.h" +#include "getflags.h" +#define c0 t->child[0] +#define c1 t->child[1] +#define c2 t->child[2] +int codep, ncode; +#define emitf(x) ((void)(codep!=ncode || morecode()), codebuf[codep].f = (x), codep++) +#define emiti(x) ((void)(codep!=ncode || morecode()), codebuf[codep].i = (x), codep++) +#define emits(x) ((void)(codep!=ncode || morecode()), codebuf[codep].s = (x), codep++) +void stuffdot(int); +char *fnstr(tree*); +void outcode(tree*, int); +void codeswitch(tree*, int); +int iscase(tree*); +code *codecopy(code*); +void codefree(code*); + +int +morecode(void) +{ + ncode+=100; + codebuf = (code *)realloc((char *)codebuf, ncode*sizeof codebuf[0]); + if(codebuf==0) + panic("Can't realloc %d bytes in morecode!", + ncode*sizeof codebuf[0]); + memset(codebuf+ncode-100, 0, 100*sizeof codebuf[0]); + return 0; +} + +void +stuffdot(int a) +{ + if(a<0 || codep<=a) + panic("Bad address %d in stuffdot", a); + codebuf[a].i = codep; +} + +int +compile(tree *t) +{ + if(flag['D']) { + struct io *s; + s = openstr(); + pfmt(s, "compile: %u\n", t); + write(2, s->strp, strlen(s->strp)); + closeio(s); + if(eflagok) // made it out of rcmain - stop executing commands, just print them + t = nil; + } + + ncode = 100; + codebuf = (code *)emalloc(ncode*sizeof codebuf[0]); + codep = 0; + emiti(0); /* reference count */ + outcode(t, flag['e']?1:0); + if(nerror){ + efree((char *)codebuf); + return 0; + } + readhere(); + emitf(Xreturn); + emitf(0); + return 1; +} + +void +cleanhere(char *f) +{ + emitf(Xdelhere); + emits(strdup(f)); +} + +char* +fnstr(tree *t) +{ + io *f = openstr(); + char *v; + extern char nl; + char svnl = nl; + nl=';'; + pfmt(f, "%t", t); + nl = svnl; + v = f->strp; + f->strp = 0; + closeio(f); + return v; +} + +void +outcode(tree *t, int eflag) +{ + int p, q; + tree *tt; + if(t==0) + return; + if(t->type!=NOT && t->type!=';') + runq->iflast = 0; + switch(t->type){ + default: + pfmt(err, "bad type %d in outcode\n", t->type); + break; + case '$': + emitf(Xmark); + outcode(c0, eflag); + emitf(Xdol); + break; + case '"': + emitf(Xmark); + outcode(c0, eflag); + emitf(Xqdol); + break; + case SUB: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xmark); + outcode(c1, eflag); + emitf(Xsub); + break; + case '&': + emitf(Xasync); + if(havefork){ + p = emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else + emits(fnstr(c0)); + break; + case ';': + outcode(c0, eflag); + outcode(c1, eflag); + break; + case '^': + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xconc); + break; + case '`': + emitf(Xbackq); + if(havefork){ + p = emiti(0); + outcode(c0, 0); + emitf(Xexit); + stuffdot(p); + } else + emits(fnstr(c0)); + break; + case ANDAND: + outcode(c0, 0); + emitf(Xtrue); + p = emiti(0); + outcode(c1, eflag); + stuffdot(p); + break; + case ARGLIST: + outcode(c1, eflag); + outcode(c0, eflag); + break; + case BANG: + outcode(c0, eflag); + emitf(Xbang); + break; + case PCMD: + case BRACE: + outcode(c0, eflag); + break; + case COUNT: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xcount); + break; + case FN: + emitf(Xmark); + outcode(c0, eflag); + if(c1){ + emitf(Xfn); + p = emiti(0); + emits(fnstr(c1)); + outcode(c1, eflag); + emitf(Xunlocal); /* get rid of $* */ + emitf(Xreturn); + stuffdot(p); + } + else + emitf(Xdelfn); + break; + case IF: + outcode(c0, 0); + emitf(Xif); + p = emiti(0); + outcode(c1, eflag); + emitf(Xwastrue); + stuffdot(p); + break; + case NOT: + if(!runq->iflast) + yyerror("`if not' does not follow `if(...)'"); + emitf(Xifnot); + p = emiti(0); + outcode(c0, eflag); + stuffdot(p); + break; + case OROR: + outcode(c0, 0); + emitf(Xfalse); + p = emiti(0); + outcode(c1, eflag); + stuffdot(p); + break; + case PAREN: + outcode(c0, eflag); + break; + case SIMPLE: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xsimple); + if(eflag) + emitf(Xeflag); + break; + case SUBSHELL: + emitf(Xsubshell); + if(havefork){ + p = emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else + emits(fnstr(c0)); + if(eflag) + emitf(Xeflag); + break; + case SWITCH: + codeswitch(t, eflag); + break; + case TWIDDLE: + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xmatch); + if(eflag) + emitf(Xeflag); + break; + case WHILE: + q = codep; + outcode(c0, 0); + if(q==codep) + emitf(Xsettrue); /* empty condition == while(true) */ + emitf(Xtrue); + p = emiti(0); + outcode(c1, eflag); + emitf(Xjump); + emiti(q); + stuffdot(p); + break; + case WORDS: + outcode(c1, eflag); + outcode(c0, eflag); + break; + case FOR: + emitf(Xmark); + if(c1){ + outcode(c1, eflag); + emitf(Xglob); + } + else{ + emitf(Xmark); + emitf(Xword); + emits(strdup("*")); + emitf(Xdol); + } + emitf(Xmark); /* dummy value for Xlocal */ + emitf(Xmark); + outcode(c0, eflag); + emitf(Xlocal); + p = emitf(Xfor); + q = emiti(0); + outcode(c2, eflag); + emitf(Xjump); + emiti(p); + stuffdot(q); + emitf(Xunlocal); + break; + case WORD: + emitf(Xword); + emits(strdup(t->str)); + break; + case DUP: + if(t->rtype==DUPFD){ + emitf(Xdup); + emiti(t->fd0); + emiti(t->fd1); + } + else{ + emitf(Xclose); + emiti(t->fd0); + } + outcode(c1, eflag); + emitf(Xpopredir); + break; + case PIPEFD: + emitf(Xpipefd); + emiti(t->rtype); + if(havefork){ + p = emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else { + emits(fnstr(c0)); + } + break; + case REDIR: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xglob); + switch(t->rtype){ + case APPEND: + emitf(Xappend); + break; + case WRITE: + emitf(Xwrite); + break; + case READ: + case HERE: + emitf(Xread); + break; + case RDWR: + emitf(Xrdwr); + break; + } + emiti(t->fd0); + outcode(c1, eflag); + emitf(Xpopredir); + break; + case '=': + tt = t; + for(;t && t->type=='=';t = c2); + if(t){ + for(t = tt;t->type=='=';t = c2){ + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xlocal); + } + outcode(t, eflag); + for(t = tt; t->type=='='; t = c2) + emitf(Xunlocal); + } + else{ + for(t = tt;t;t = c2){ + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xassign); + } + } + t = tt; /* so tests below will work */ + break; + case PIPE: + emitf(Xpipe); + emiti(t->fd0); + emiti(t->fd1); + if(havefork){ + p = emiti(0); + q = emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else { + emits(fnstr(c0)); + q = emiti(0); + } + outcode(c1, eflag); + emitf(Xreturn); + stuffdot(q); + emitf(Xpipewait); + break; + } + if(t->type!=NOT && t->type!=';') + runq->iflast = t->type==IF; + else if(c0) runq->iflast = c0->type==IF; +} +/* + * switch code looks like this: + * Xmark + * (get switch value) + * Xjump 1f + * out: Xjump leave + * 1: Xmark + * (get case values) + * Xcase 1f + * (commands) + * Xjump out + * 1: Xmark + * (get case values) + * Xcase 1f + * (commands) + * Xjump out + * 1: + * leave: + * Xpopm + */ + +void +codeswitch(tree *t, int eflag) +{ + int leave; /* patch jump address to leave switch */ + int out; /* jump here to leave switch */ + int nextcase; /* patch jump address to next case */ + tree *tt; + if(c1->child[0]==nil + || c1->child[0]->type!=';' + || !iscase(c1->child[0]->child[0])){ + yyerror("case missing in switch"); + return; + } + emitf(Xmark); + outcode(c0, eflag); + emitf(Xjump); + nextcase = emiti(0); + out = emitf(Xjump); + leave = emiti(0); + stuffdot(nextcase); + t = c1->child[0]; + while(t->type==';'){ + tt = c1; + emitf(Xmark); + for(t = c0->child[0];t->type==ARGLIST;t = c0) outcode(c1, eflag); + emitf(Xcase); + nextcase = emiti(0); + t = tt; + for(;;){ + if(t->type==';'){ + if(iscase(c0)) break; + outcode(c0, eflag); + t = c1; + } + else{ + if(!iscase(t)) outcode(t, eflag); + break; + } + } + emitf(Xjump); + emiti(out); + stuffdot(nextcase); + } + stuffdot(leave); + emitf(Xpopm); +} + +int +iscase(tree *t) +{ + if(t->type!=SIMPLE) + return 0; + do t = c0; while(t->type==ARGLIST); + return t->type==WORD && !t->quoted && strcmp(t->str, "case")==0; +} + +code* +codecopy(code *cp) +{ + cp[0].i++; + return cp; +} + +void +codefree(code *cp) +{ + code *p; + if(--cp[0].i!=0) + return; + for(p = cp+1;p->f;p++){ + if(p->f==Xappend || p->f==Xclose || p->f==Xread || p->f==Xwrite + || p->f==Xrdwr + || p->f==Xasync || p->f==Xbackq || p->f==Xcase || p->f==Xfalse + || p->f==Xfor || p->f==Xjump + || p->f==Xsubshell || p->f==Xtrue) p++; + else if(p->f==Xdup || p->f==Xpipefd) p+=2; + else if(p->f==Xpipe) p+=4; + else if(p->f==Xword || p->f==Xdelhere) efree((++p)->s); + else if(p->f==Xfn){ + efree(p[2].s); + p+=2; + } + } + efree((char *)cp); +} diff --git a/cmd/rc/exec.c b/cmd/rc/exec.c new file mode 100644 index 0000000..0320976 --- /dev/null +++ b/cmd/rc/exec.c @@ -0,0 +1,1001 @@ +#include "rc.h" +#include "getflags.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +/* + * Start executing the given code at the given pc with the given redirection + */ +char *argv0="rc"; + +void +start(code *c, int pc, var *local) +{ + struct thread *p = new(struct thread); + + p->code = codecopy(c); + p->pc = pc; + p->argv = 0; + p->redir = p->startredir = runq?runq->redir:0; + p->local = local; + p->cmdfile = 0; + p->cmdfd = 0; + p->eof = 0; + p->iflag = 0; + p->lineno = 1; + p->ret = runq; + runq = p; +} + +word* +newword(char *wd, word *next) +{ + word *p = new(word); + p->word = strdup(wd); + p->next = next; + return p; +} + +void +pushword(char *wd) +{ + if(runq->argv==0) + panic("pushword but no argv!", 0); + runq->argv->words = newword(wd, runq->argv->words); +} + +void +popword(void) +{ + word *p; + if(runq->argv==0) + panic("popword but no argv!", 0); + p = runq->argv->words; + if(p==0) + panic("popword but no word!", 0); + runq->argv->words = p->next; + efree(p->word); + efree((char *)p); +} + +void +freelist(word *w) +{ + word *nw; + while(w){ + nw = w->next; + efree(w->word); + efree((char *)w); + w = nw; + } +} + +void +pushlist(void) +{ + list *p = new(list); + p->next = runq->argv; + p->words = 0; + runq->argv = p; +} + +void +poplist(void) +{ + list *p = runq->argv; + if(p==0) + panic("poplist but no argv", 0); + freelist(p->words); + runq->argv = p->next; + efree((char *)p); +} + +int +count(word *w) +{ + int n; + for(n = 0;w;n++) w = w->next; + return n; +} + +void +pushredir(int type, int from, int to) +{ + redir * rp = new(redir); + rp->type = type; + rp->from = from; + rp->to = to; + rp->next = runq->redir; + runq->redir = rp; +} + +var* +newvar(char *name, var *next) +{ + var *v = new(var); + v->name = name; + v->val = 0; + v->fn = 0; + v->changed = 0; + v->fnchanged = 0; + v->next = next; + v->changefn = 0; + return v; +} +/* + * get command line flags, initialize keywords & traps. + * get values from environment. + * set $pid, $cflag, $* + * fabricate bootstrap code and start it (*=(argv);. /usr/lib/rcmain $*) + * start interpreting code + */ +int +main(int argc, char *argv[]) +{ + code bootstrap[32]; + char num[12], *rcmain; + int i; + + /* needed for rcmain later */ + putenv("PLAN9", unsharp("#9")); + + argc = getflags(argc, argv, "DSYsrdiIlxepvVc:1m:1[command]", 1); + if(argc==-1) + usage("[file [arg ...]]"); + if(argv[0][0]=='-') + flag['l'] = flagset; + if(flag['I']) + flag['i'] = 0; + else if(flag['i']==0 && argc==1 && Isatty(0)) flag['i'] = flagset; + rcmain = flag['m'] ? flag['m'][0] : Rcmain(); + err = openfd(2); + kinit(); + Trapinit(); + Vinit(); + inttoascii(num, mypid = getpid()); + pathinit(); + setvar("pid", newword(num, (word *)0)); + setvar("cflag", flag['c']?newword(flag['c'][0], (word *)0) + :(word *)0); + setvar("rcname", newword(argv[0], (word *)0)); + i = 0; + bootstrap[i++].i = 1; + bootstrap[i++].f = Xmark; + bootstrap[i++].f = Xword; + bootstrap[i++].s="*"; + bootstrap[i++].f = Xassign; + bootstrap[i++].f = Xmark; + bootstrap[i++].f = Xmark; + bootstrap[i++].f = Xword; + bootstrap[i++].s="*"; + bootstrap[i++].f = Xdol; + bootstrap[i++].f = Xword; + bootstrap[i++].s = rcmain; + bootstrap[i++].f = Xword; + bootstrap[i++].s="."; + bootstrap[i++].f = Xsimple; + bootstrap[i++].f = Xexit; + bootstrap[i].i = 0; + start(bootstrap, 1, (var *)0); + /* prime bootstrap argv */ + pushlist(); + argv0 = strdup(argv[0]); + for(i = argc-1;i!=0;--i) pushword(argv[i]); + for(;;){ + if(flag['r']) + pfnc(err, runq); + runq->pc++; + (*runq->code[runq->pc-1].f)(); + if(ntrap) + dotrap(); + } + return 0; /* not reached; silence OS X Lion gcc */ +} +/* + * Opcode routines + * Arguments on stack (...) + * Arguments in line [...] + * Code in line with jump around {...} + * + * Xappend(file)[fd] open file to append + * Xassign(name, val) assign val to name + * Xasync{... Xexit} make thread for {}, no wait + * Xbackq{... Xreturn} make thread for {}, push stdout + * Xbang complement condition + * Xcase(pat, value){...} exec code on match, leave (value) on + * stack + * Xclose[i] close file descriptor + * Xconc(left, right) concatenate, push results + * Xcount(name) push var count + * Xdelfn(name) delete function definition + * Xdeltraps(names) delete named traps + * Xdol(name) get variable value + * Xqdol(name) concatenate variable components + * Xdup[i j] dup file descriptor + * Xexit rc exits with status + * Xfalse{...} execute {} if false + * Xfn(name){... Xreturn} define function + * Xfor(var, list){... Xreturn} for loop + * Xjump[addr] goto + * Xlocal(name, val) create local variable, assign value + * Xmark mark stack + * Xmatch(pat, str) match pattern, set status + * Xpipe[i j]{... Xreturn}{... Xreturn} construct a pipe between 2 new threads, + * wait for both + * Xpipefd[type]{... Xreturn} connect {} to pipe (input or output, + * depending on type), push /dev/fd/?? + * Xpopm(value) pop value from stack + * Xrdwr(file)[fd] open file for reading and writing + * Xread(file)[fd] open file to read + * Xsettraps(names){... Xreturn} define trap functions + * Xshowtraps print trap list + * Xsimple(args) run command and wait + * Xreturn kill thread + * Xsubshell{... Xexit} execute {} in a subshell and wait + * Xtrue{...} execute {} if true + * Xunlocal delete local variable + * Xword[string] push string + * Xwrite(file)[fd] open file to write + */ + +void +Xappend(void) +{ + char *file; + int f; + switch(count(runq->argv->words)){ + default: + Xerror1(">> requires singleton"); + return; + case 0: + Xerror1(">> requires file"); + return; + case 1: + break; + } + file = runq->argv->words->word; + if((f = open(file, 1))<0 && (f = Creat(file))<0){ + pfmt(err, "%s: ", file); + Xerror("can't open"); + return; + } + Seek(f, 0L, 2); + pushredir(ROPEN, f, runq->code[runq->pc].i); + runq->pc++; + poplist(); +} + +void +Xsettrue(void) +{ + setstatus(""); +} + +void +Xbang(void) +{ + setstatus(truestatus()?"false":""); +} + +void +Xclose(void) +{ + pushredir(RCLOSE, runq->code[runq->pc].i, 0); + runq->pc++; +} + +void +Xdup(void) +{ + pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i); + runq->pc+=2; +} + +void +Xeflag(void) +{ + if(eflagok && !truestatus()) Xexit(); +} + +void +Xexit(void) +{ + struct var *trapreq; + struct word *starval; + static int beenhere = 0; + if(getpid()==mypid && !beenhere){ + trapreq = vlook("sigexit"); + if(trapreq->fn){ + beenhere = 1; + --runq->pc; + starval = vlook("*")->val; + start(trapreq->fn, trapreq->pc, (struct var *)0); + runq->local = newvar(strdup("*"), runq->local); + runq->local->val = copywords(starval, (struct word *)0); + runq->local->changed = 1; + runq->redir = runq->startredir = 0; + return; + } + } + Exit(getstatus()); +} + +void +Xfalse(void) +{ + if(truestatus()) runq->pc = runq->code[runq->pc].i; + else runq->pc++; +} +int ifnot; /* dynamic if not flag */ + +void +Xifnot(void) +{ + if(ifnot) + runq->pc++; + else + runq->pc = runq->code[runq->pc].i; +} + +void +Xjump(void) +{ + runq->pc = runq->code[runq->pc].i; +} + +void +Xmark(void) +{ + pushlist(); +} + +void +Xpopm(void) +{ + poplist(); +} + +void +Xread(void) +{ + char *file; + int f; + switch(count(runq->argv->words)){ + default: + Xerror1("< requires singleton\n"); + return; + case 0: + Xerror1("< requires file\n"); + return; + case 1: + break; + } + file = runq->argv->words->word; + if((f = open(file, 0))<0){ + pfmt(err, "%s: ", file); + Xerror("can't open"); + return; + } + pushredir(ROPEN, f, runq->code[runq->pc].i); + runq->pc++; + poplist(); +} + +void +Xrdwr(void) +{ + char *file; + int f; + + switch(count(runq->argv->words)){ + default: + Xerror1("<> requires singleton\n"); + return; + case 0: + Xerror1("<> requires file\n"); + return; + case 1: + break; + } + file = runq->argv->words->word; + if((f = open(file, ORDWR))<0){ + pfmt(err, "%s: ", file); + Xerror("can't open"); + return; + } + pushredir(ROPEN, f, runq->code[runq->pc].i); + runq->pc++; + poplist(); +} + +void +turfredir(void) +{ + while(runq->redir!=runq->startredir) + Xpopredir(); +} + +void +Xpopredir(void) +{ + struct redir *rp = runq->redir; + if(rp==0) + panic("turfredir null!", 0); + runq->redir = rp->next; + if(rp->type==ROPEN) + close(rp->from); + efree((char *)rp); +} + +void +Xreturn(void) +{ + struct thread *p = runq; + turfredir(); + while(p->argv) poplist(); + codefree(p->code); + runq = p->ret; + efree((char *)p); + if(runq==0) + Exit(getstatus()); +} + +void +Xtrue(void) +{ + if(truestatus()) runq->pc++; + else runq->pc = runq->code[runq->pc].i; +} + +void +Xif(void) +{ + ifnot = 1; + if(truestatus()) runq->pc++; + else runq->pc = runq->code[runq->pc].i; +} + +void +Xwastrue(void) +{ + ifnot = 0; +} + +void +Xword(void) +{ + pushword(runq->code[runq->pc++].s); +} + +void +Xwrite(void) +{ + char *file; + int f; + switch(count(runq->argv->words)){ + default: + Xerror1("> requires singleton\n"); + return; + case 0: + Xerror1("> requires file\n"); + return; + case 1: + break; + } + file = runq->argv->words->word; + if((f = Creat(file))<0){ + pfmt(err, "%s: ", file); + Xerror("can't open"); + return; + } + pushredir(ROPEN, f, runq->code[runq->pc].i); + runq->pc++; + poplist(); +} + +char* +list2str(word *words) +{ + char *value, *s, *t; + int len = 0; + word *ap; + for(ap = words;ap;ap = ap->next) + len+=1+strlen(ap->word); + value = emalloc(len+1); + s = value; + for(ap = words;ap;ap = ap->next){ + for(t = ap->word;*t;) *s++=*t++; + *s++=' '; + } + if(s==value) + *s='\0'; + else s[-1]='\0'; + return value; +} + +void +Xmatch(void) +{ + word *p; + char *subject; + subject = list2str(runq->argv->words); + setstatus("no match"); + for(p = runq->argv->next->words;p;p = p->next) + if(match(subject, p->word, '\0')){ + setstatus(""); + break; + } + efree(subject); + poplist(); + poplist(); +} + +void +Xcase(void) +{ + word *p; + char *s; + int ok = 0; + s = list2str(runq->argv->next->words); + for(p = runq->argv->words;p;p = p->next){ + if(match(s, p->word, '\0')){ + ok = 1; + break; + } + } + efree(s); + if(ok) + runq->pc++; + else + runq->pc = runq->code[runq->pc].i; + poplist(); +} + +word* +conclist(word *lp, word *rp, word *tail) +{ + char *buf; + word *v; + if(lp->next || rp->next) + tail = conclist(lp->next==0?lp:lp->next, rp->next==0?rp:rp->next, + tail); + buf = emalloc(strlen(lp->word)+strlen(rp->word)+1); + strcpy(buf, lp->word); + strcat(buf, rp->word); + v = newword(buf, tail); + efree(buf); + return v; +} + +void +Xconc(void) +{ + word *lp = runq->argv->words; + word *rp = runq->argv->next->words; + word *vp = runq->argv->next->next->words; + int lc = count(lp), rc = count(rp); + if(lc!=0 || rc!=0){ + if(lc==0 || rc==0){ + Xerror1("null list in concatenation"); + return; + } + if(lc!=1 && rc!=1 && lc!=rc){ + Xerror1("mismatched list lengths in concatenation"); + return; + } + vp = conclist(lp, rp, vp); + } + poplist(); + poplist(); + runq->argv->words = vp; +} + +void +Xassign(void) +{ + var *v; + if(count(runq->argv->words)!=1){ + Xerror1("variable name not singleton!"); + return; + } + deglob(runq->argv->words->word); + v = vlook(runq->argv->words->word); + poplist(); + globlist(); + freewords(v->val); + v->val = runq->argv->words; + v->changed = 1; + if(v->changefn) + v->changefn(v); + runq->argv->words = 0; + poplist(); +} +/* + * copy arglist a, adding the copy to the front of tail + */ + +word* +copywords(word *a, word *tail) +{ + word *v = 0, **end; + for(end=&v;a;a = a->next,end=&(*end)->next) + *end = newword(a->word, 0); + *end = tail; + return v; +} + +void +Xdol(void) +{ + word *a, *star; + char *s, *t; + int n; + if(count(runq->argv->words)!=1){ + Xerror1("variable name not singleton!"); + return; + } + s = runq->argv->words->word; + deglob(s); + n = 0; + for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0'; + a = runq->argv->next->words; + if(n==0 || *t) + a = copywords(vlook(s)->val, a); + else{ + star = vlook("*")->val; + if(star && 1<=n && n<=count(star)){ + while(--n) star = star->next; + a = newword(star->word, a); + } + } + poplist(); + runq->argv->words = a; +} + +void +Xqdol(void) +{ + word *a, *p; + char *s; + int n; + if(count(runq->argv->words)!=1){ + Xerror1("variable name not singleton!"); + return; + } + s = runq->argv->words->word; + deglob(s); + a = vlook(s)->val; + poplist(); + n = count(a); + if(n==0){ + pushword(""); + return; + } + for(p = a;p;p = p->next) n+=strlen(p->word); + s = emalloc(n); + if(a){ + strcpy(s, a->word); + for(p = a->next;p;p = p->next){ + strcat(s, " "); + strcat(s, p->word); + } + } + else + s[0]='\0'; + pushword(s); + efree(s); +} + +word* +copynwords(word *a, word *tail, int n) +{ + word *v, **end; + + v = 0; + end = &v; + while(n-- > 0){ + *end = newword(a->word, 0); + end = &(*end)->next; + a = a->next; + } + *end = tail; + return v; +} + +word* +subwords(word *val, int len, word *sub, word *a) +{ + int n, m; + char *s; + if(!sub) + return a; + a = subwords(val, len, sub->next, a); + s = sub->word; + deglob(s); + m = 0; + n = 0; + while('0'<=*s && *s<='9') + n = n*10+ *s++ -'0'; + if(*s == '-'){ + if(*++s == 0) + m = len - n; + else{ + while('0'<=*s && *s<='9') + m = m*10+ *s++ -'0'; + m -= n; + } + } + if(n<1 || n>len || m<0) + return a; + if(n+m>len) + m = len-n; + while(--n > 0) + val = val->next; + return copynwords(val, a, m+1); +} + +void +Xsub(void) +{ + word *a, *v; + char *s; + if(count(runq->argv->next->words)!=1){ + Xerror1("variable name not singleton!"); + return; + } + s = runq->argv->next->words->word; + deglob(s); + a = runq->argv->next->next->words; + v = vlook(s)->val; + a = subwords(v, count(v), runq->argv->words, a); + poplist(); + poplist(); + runq->argv->words = a; +} + +void +Xcount(void) +{ + word *a; + char *s, *t; + int n; + char num[12]; + if(count(runq->argv->words)!=1){ + Xerror1("variable name not singleton!"); + return; + } + s = runq->argv->words->word; + deglob(s); + n = 0; + for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0'; + if(n==0 || *t){ + a = vlook(s)->val; + inttoascii(num, count(a)); + } + else{ + a = vlook("*")->val; + inttoascii(num, a && 1<=n && n<=count(a)?1:0); + } + poplist(); + pushword(num); +} + +void +Xlocal(void) +{ + if(count(runq->argv->words)!=1){ + Xerror1("variable name must be singleton\n"); + return; + } + deglob(runq->argv->words->word); + runq->local = newvar(strdup(runq->argv->words->word), runq->local); + runq->local->val = copywords(runq->argv->next->words, (word *)0); + runq->local->changed = 1; + poplist(); + poplist(); +} + +void +Xunlocal(void) +{ + var *v = runq->local, *hid; + if(v==0) + panic("Xunlocal: no locals!", 0); + runq->local = v->next; + hid = vlook(v->name); + hid->changed = 1; + efree(v->name); + freewords(v->val); + efree((char *)v); +} + +void +freewords(word *w) +{ + word *nw; + while(w){ + efree(w->word); + nw = w->next; + efree((char *)w); + w = nw; + } +} + +void +Xfn(void) +{ + var *v; + word *a; + int end; + end = runq->code[runq->pc].i; + for(a = runq->argv->words;a;a = a->next){ + v = gvlook(a->word); + if(v->fn) + codefree(v->fn); + v->fn = codecopy(runq->code); + v->pc = runq->pc+2; + v->fnchanged = 1; + } + runq->pc = end; + poplist(); +} + +void +Xdelfn(void) +{ + var *v; + word *a; + for(a = runq->argv->words;a;a = a->next){ + v = gvlook(a->word); + if(v->fn) + codefree(v->fn); + v->fn = 0; + v->fnchanged = 1; + } + poplist(); +} + +char* +concstatus(char *s, char *t) +{ + static char v[NSTATUS+1]; + int n = strlen(s); + strncpy(v, s, NSTATUS); + if(npid==-1) + setstatus(concstatus(runq->status, getstatus())); + else{ + strncpy(status, getstatus(), NSTATUS); + status[NSTATUS]='\0'; + Waitfor(runq->pid, 1); + runq->pid=-1; + setstatus(concstatus(getstatus(), status)); + } +} + +void +Xrdcmds(void) +{ + struct thread *p = runq; + word *prompt; + flush(err); + nerror = 0; + if(flag['s'] && !truestatus()) + pfmt(err, "status=%v\n", vlook("status")->val); + if(runq->iflag){ + prompt = vlook("prompt")->val; + if(prompt) + promptstr = prompt->word; + else + promptstr="% "; + } + Noerror(); + if((flag['Y'] ? yyparse : parse)()){ + if(!p->iflag || p->eof && !Eintr()){ + if(p->cmdfile) + efree(p->cmdfile); + closeio(p->cmdfd); + Xreturn(); /* should this be omitted? */ + } + else{ + if(Eintr()){ + pchr(err, '\n'); + p->eof = 0; + } + --p->pc; /* go back for next command */ + } + } + else{ + ntrap = 0; /* avoid double-interrupts during blocked writes */ + --p->pc; /* re-execute Xrdcmds after codebuf runs */ + start(codebuf, 1, runq->local); + } + freenodes(); +} + +void +Xerror(char *s) +{ + if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0) + pfmt(err, "rc: %s: %r\n", s); + else + pfmt(err, "rc (%s): %s: %r\n", argv0, s); + flush(err); + setstatus("error"); + while(!runq->iflag) Xreturn(); +} + +void +Xerror1(char *s) +{ + if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0) + pfmt(err, "rc: %s\n", s); + else + pfmt(err, "rc (%s): %s\n", argv0, s); + flush(err); + setstatus("error"); + while(!runq->iflag) Xreturn(); +} + +void +setstatus(char *s) +{ + setvar("status", newword(s, (word *)0)); +} + +char* +getstatus(void) +{ + var *status = vlook("status"); + return status->val?status->val->word:""; +} + +int +truestatus(void) +{ + char *s; + for(s = getstatus();*s;s++) + if(*s!='|' && *s!='0') + return 0; + return 1; +} + +void +Xdelhere(void) +{ + Unlink(runq->code[runq->pc++].s); +} + +void +Xfor(void) +{ + if(runq->argv->words==0){ + poplist(); + runq->pc = runq->code[runq->pc].i; + } + else{ + freelist(runq->local->val); + runq->local->val = runq->argv->words; + runq->local->changed = 1; + runq->argv->words = runq->argv->words->next; + runq->local->val->next = 0; + runq->pc++; + } +} + +void +Xglob(void) +{ + globlist(); +} diff --git a/cmd/rc/exec.h b/cmd/rc/exec.h new file mode 100644 index 0000000..06cfd64 --- /dev/null +++ b/cmd/rc/exec.h @@ -0,0 +1,76 @@ +/* + * Definitions used in the interpreter + */ +extern void Xappend(void), Xasync(void), Xbackq(void), Xbang(void), Xclose(void); +extern void Xconc(void), Xcount(void), Xdelfn(void), Xdol(void), Xqdol(void), Xdup(void); +extern void Xexit(void), Xfalse(void), Xfn(void), Xfor(void), Xglob(void); +extern void Xjump(void), Xmark(void), Xmatch(void), Xpipe(void), Xread(void); +extern void Xrdwr(void); +extern void Xrdfn(void), Xunredir(void), Xstar(void), Xreturn(void), Xsubshell(void); +extern void Xtrue(void), Xword(void), Xwrite(void), Xpipefd(void), Xcase(void); +extern void Xlocal(void), Xunlocal(void), Xassign(void), Xsimple(void), Xpopm(void); +extern void Xrdcmds(void), Xwastrue(void), Xif(void), Xifnot(void), Xpipewait(void); +extern void Xdelhere(void), Xpopredir(void), Xsub(void), Xeflag(void), Xsettrue(void); +extern void Xerror(char*); +extern void Xerror1(char*); +/* + * word lists are in correct order, + * i.e. word0->word1->word2->word3->0 + */ +struct word{ + char *word; + word *next; +}; +struct list{ + word *words; + list *next; +}; +word *newword(char *, word *), *copywords(word *, word *); +struct redir{ + char type; /* what to do */ + short from, to; /* what to do it to */ + struct redir *next; /* what else to do (reverse order) */ +}; +#define NSTATUS ERRMAX /* length of status (from plan 9) */ +/* + * redir types + */ +#define ROPEN 1 /* dup2(from, to); close(from); */ +#define RDUP 2 /* dup2(from, to); */ +#define RCLOSE 3 /* close(from); */ +struct thread{ + union code *code; /* code for this thread */ + int pc; /* code[pc] is the next instruction */ + struct list *argv; /* argument stack */ + struct redir *redir; /* redirection stack */ + struct redir *startredir; /* redir inheritance point */ + struct var *local; /* list of local variables */ + char *cmdfile; /* file name in Xrdcmd */ + struct io *cmdfd; /* file descriptor for Xrdcmd */ + int iflast; /* static `if not' checking */ + int eof; /* is cmdfd at eof? */ + int iflag; /* interactive? */ + int lineno; /* linenumber */ + int pid; /* process for Xpipewait to wait for */ + char status[NSTATUS]; /* status for Xpipewait */ + tree *treenodes; /* tree nodes created by this process */ + thread *ret; /* who continues when this finishes */ +}; +thread *runq; +code *codecopy(code*); +code *codebuf; /* compiler output */ +int ntrap; /* number of outstanding traps */ +int trap[NSIG]; /* number of outstanding traps per type */ +struct builtin{ + char *name; + void (*fnc)(void); +}; +extern struct builtin Builtin[]; +int eflagok; /* kludge flag so that -e doesn't exit in startup */ +extern int havefork; + +void execcd(void), execwhatis(void), execeval(void), execexec(void); +int execforkexec(void); +void execexit(void), execshift(void); +void execwait(void), execumask(void), execdot(void), execflag(void); +void execfunc(var*), execcmds(io *); diff --git a/cmd/rc/fmtquote.c b/cmd/rc/fmtquote.c new file mode 100644 index 0000000..e6b91e3 --- /dev/null +++ b/cmd/rc/fmtquote.c @@ -0,0 +1,162 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include +#include +#include "fmt.h" +#include "fmtdef.h" + +extern int (*doquote)(int); + +/* + * How many bytes of output UTF will be produced by quoting (if necessary) this string? + * How many runes? How much of the input will be consumed? + * The parameter q is filled in by _quotesetup. + * The string may be UTF or Runes (s or r). + * Return count does not include NUL. + * Terminate the scan at the first of: + * NUL in input + * count exceeded in input + * count exceeded on output + * *ninp is set to number of input bytes accepted. + * nin may be <0 initially, to avoid checking input by count. + */ +void +__quotesetup(char *s, int nin, int nout, Quoteinfo *q, int sharp) +{ + int c; + + q->quoted = 0; + q->nbytesout = 0; + q->nrunesout = 0; + q->nbytesin = 0; + q->nrunesin = 0; + if(sharp || nin==0 || *s=='\0'){ + if(nout < 2) + return; + q->quoted = 1; + q->nbytesout = 2; + q->nrunesout = 2; + } + for(; nin!=0; nin-=1){ + c = *s; + + if(c == '\0') + break; + if(q->nrunesout+1 > nout) + break; + + if((c <= L' ') || (c == L'\'') || (doquote!=nil && doquote(c))){ + if(!q->quoted){ + if(1+q->nrunesout+1+1 > nout) /* no room for quotes */ + break; + q->nrunesout += 2; /* include quotes */ + q->nbytesout += 2; /* include quotes */ + q->quoted = 1; + } + if(c == '\'') { + q->nbytesout++; + q->nrunesout++; /* quotes reproduce as two characters */ + } + } + + /* advance input */ + s++; + q->nbytesin++; + q->nrunesin++; + + /* advance output */ + q->nbytesout++; + q->nrunesout++; + } +} + +static int +qstrfmt(char *sin, Quoteinfo *q, Fmt *f) +{ + int r; + char *t, *s, *m, *me; + ulong fl; + int nc, w; + + m = sin; + me = m + q->nbytesin; + + w = f->width; + fl = f->flags; + if(!(fl & FmtLeft) && __fmtpad(f, w - q->nbytesout) < 0) + return -1; + t = f->to; + s = f->stop; + FMTCHAR(f, t, s, '\''); + for(nc = q->nrunesin; nc > 0; nc--){ + r = *(uchar*)m++; + FMTCHAR(f, t, s, r); + if(r == '\'') + FMTCHAR(f, t, s, r); + } + + FMTCHAR(f, t, s, '\''); + f->nfmt += t - (char *)f->to; + f->to = t; + if(fl & FmtLeft && __fmtpad(f, w - q->nbytesout) < 0) + return -1; + return 0; +} + +int +__quotestrfmt(int runesin, Fmt *f) +{ + int outlen; + char *s; + Quoteinfo q; + + f->flags &= ~FmtPrec; /* ignored for %q %Q, so disable for %s %S in easy case */ + s = va_arg(f->args, char *); + if(!s) + return __fmtcpy(f, "", 5, 5); + + if(f->flush) + outlen = 0x7FFFFFFF; /* if we can flush, no output limit */ + else + outlen = (char*)f->stop - (char*)f->to; + + __quotesetup(s, -1, outlen, &q, f->flags&FmtSharp); + + if(!q.quoted) + return __fmtcpy(f, s, q.nrunesin, q.nbytesin); + return qstrfmt(s, &q, f); +} + +int +quotestrfmt(Fmt *f) +{ + return __quotestrfmt(0, f); +} + +void +quotefmtinstall(void) +{ + fmtinstall('q', quotestrfmt); +} + +int +__needsquotes(char *s, int *quotelenp) +{ + Quoteinfo q; + + __quotesetup(s, -1, 0x7FFFFFFF, &q, 0); + *quotelenp = q.nbytesout; + + return q.quoted; +} diff --git a/cmd/rc/fns.h b/cmd/rc/fns.h new file mode 100644 index 0000000..5721d61 --- /dev/null +++ b/cmd/rc/fns.h @@ -0,0 +1,68 @@ +void Abort(void); +void Closedir(int); +int Creat(char*); +int Dup(int, int); +int Dup1(int); +int Eintr(void); +int Executable(char*); +void Execute(word*, word*); +void Exit(char*); +int ForkExecute(char*, char**, int, int, int); +int Globsize(char*); +int Isatty(int); +void Memcpy(char*, char*, long); +void Noerror(void); +int Opendir(char*); +long Read(int, char*, long); +int Readdir(int, char*, int); +long Seek(int, long, long); +void Trapinit(void); +void Unlink(char*); +void Updenv(void); +void Vinit(void); +int Waitfor(int, int); +long Write(int, char*, long); +void addwaitpid(int); +int advance(void); +int back(int); +void cleanhere(char*); +void codefree(code*); +int compile(tree*); +char * list2str(word*); +int count(word*); +void deglob(char*); +void delwaitpid(int); +void dotrap(void); +void freenodes(void); +void freewords(word*); +void globlist(void); +int havewaitpid(int); +int idchr(int); +void inttoascii(char*, long); +void kinit(void); +int mapfd(int); +int match(char*, char*, int); +int matchfn(char*, char*); +char** mkargv(word*); +void clearwaitpids(void); +void panic(char*, int); +void pathinit(void); +void poplist(void); +void popword(void); +void pprompt(void); +void pushlist(void); +void pushredir(int, int, int); +void pushword(char*); +void readhere(void); +word* searchpath(char*); +void setstatus(char*); +void setvar(char*, word*); +void skipnl(void); +void start(code*, int, var*); +int truestatus(void); +void usage(char*); +int wordchr(int); +void yyerror(char*); +int yylex(void); +int yyparse(void); +int parse(void); diff --git a/cmd/rc/getflags.c b/cmd/rc/getflags.c new file mode 100644 index 0000000..09f6e3a --- /dev/null +++ b/cmd/rc/getflags.c @@ -0,0 +1,244 @@ +/*% cyntax -DTEST % && cc -DTEST -go # % + */ +#include "rc.h" +#include "getflags.h" +#include "fns.h" +char *flagset[] = {""}; +char **flag[NFLAG]; +char cmdline[NCMDLINE+1]; +char *cmdname; +static char *flagarg=""; +static void reverse(char**, char**); +static int scanflag(int, char*); +static void errn(char*, int); +static void errs(char*); +static void errc(int); +static int reason; +#define RESET 1 +#define FEWARGS 2 +#define FLAGSYN 3 +#define BADFLAG 4 +static int badflag; + +int +getflags(int argc, char *argv[], char *flags, int stop) +{ + char *s, *t; + int i, j, c, count; + flagarg = flags; + if(cmdname==0) + cmdname = argv[0]; + s = cmdline; + for(i = 0;i!=argc;i++){ + for(t = argv[i];*t;t++) + if(s!=&cmdline[NCMDLINE]) + *s++=*t; + if(i!=argc-1 && s!=&cmdline[NCMDLINE]) + *s++=' '; + } + *s='\0'; + i = 1; + while(i!=argc){ + if(argv[i][0]!='-' || argv[i][1]=='\0'){ + if(stop) + return argc; + i++; + continue; + } + s = argv[i]+1; + while(*s){ + c=*s++; + count = scanflag(c, flags); + if(count==-1) + return -1; + if(flag[c]){ reason = RESET; badflag = c; return -1; } + if(count==0){ + flag[c] = flagset; + if(*s=='\0'){ + for(j = i+1;j<=argc;j++) + argv[j-1] = argv[j]; + --argc; + } + } + else{ + if(*s=='\0'){ + for(j = i+1;j<=argc;j++) + argv[j-1] = argv[j]; + --argc; + s = argv[i]; + } + if(argc-inext) n++; + list = (char **)emalloc(n*sizeof(char *)); + for(a = left,n = 0;a!=right;a = a->next,n++) list[n] = a->word; + qsort((void *)list, n, sizeof(void *), globcmp); + for(a = left,n = 0;a!=right;a = a->next,n++) a->word = list[n]; + efree((char *)list); +} +/* + * Push names prefixed by globname and suffixed by a match of p onto the astack. + * namep points to the end of the prefix in globname. + */ + +void +globdir(char *p, char *namep) +{ + char *t, *newp; + int f; + /* scan the pattern looking for a component with a metacharacter in it */ + if(*p=='\0'){ + globv = newword(globname, globv); + return; + } + t = namep; + newp = p; + while(*newp){ + if(*newp==GLOB) + break; + *t=*newp++; + if(*t++=='/'){ + namep = t; + p = newp; + } + } + /* If we ran out of pattern, append the name if accessible */ + if(*newp=='\0'){ + *t='\0'; + if(access(globname, 0)==0) + globv = newword(globname, globv); + return; + } + /* read the directory and recur for any entry that matches */ + *namep='\0'; + if((f = Opendir(globname[0]?globname:"."))<0) return; + while(*newp!='/' && *newp!='\0') newp++; + while(Readdir(f, namep, *newp=='/')){ + if(matchfn(namep, p)){ + for(t = namep;*t;t++); + globdir(newp, t); + } + } + Closedir(f); +} +/* + * Push all file names matched by p on the current thread's stack. + * If there are no matches, the list consists of p. + */ + +void +glob(char *p) +{ + word *svglobv = globv; + int globlen = Globsize(p); + if(!globlen){ + deglob(p); + globv = newword(p, globv); + return; + } + globname = emalloc(globlen); + globname[0]='\0'; + globdir(p, globname); + efree(globname); + if(svglobv==globv){ + deglob(p); + globv = newword(p, globv); + } + else + globsort(globv, svglobv); +} +/* + * Do p and q point at equal utf codes + */ + +int +equtf(char *p, char *q) +{ + if(*p!=*q) + return 0; + if(twobyte(*p)) return p[1]==q[1]; + if(threebyte(*p)){ + if(p[1]!=q[1]) + return 0; + if(p[1]=='\0') + return 1; /* broken code at end of string! */ + return p[2]==q[2]; + } + if(fourbyte(*p)){ + if(p[1]!=q[1]) + return 0; + if(p[1]=='\0') + return 1; + if(p[2]!=q[2]) + return 0; + if(p[2]=='\0') + return 1; + return p[3]==q[3]; + } + return 1; +} +/* + * Return a pointer to the next utf code in the string, + * not jumping past nuls in broken utf codes! + */ + +char* +nextutf(char *p) +{ + if(twobyte(*p)) return p[1]=='\0'?p+1:p+2; + if(threebyte(*p)) return p[1]=='\0'?p+1:p[2]=='\0'?p+2:p+3; + if(fourbyte(*p)) return p[1]=='\0'?p+1:p[2]=='\0'?p+2:p[3]=='\0'?p+3:p+4; + return p+1; +} +/* + * Convert the utf code at *p to a unicode value + */ + +int +unicode(char *p) +{ + int u=*p&0xff; + if(twobyte(u)) return ((u&0x1f)<<6)|(p[1]&0x3f); + if(threebyte(u)) return (u<<12)|((p[1]&0x3f)<<6)|(p[2]&0x3f); + if(fourbyte(u)) return (u<<18)|((p[1]&0x3f)<<12)|((p[2]&0x3f)<<6)|(p[3]&0x3f); + return u; +} +/* + * Does the string s match the pattern p + * . and .. are only matched by patterns starting with . + * * matches any sequence of characters + * ? matches any single character + * [...] matches the enclosed list of characters + */ + +int +matchfn(char *s, char *p) +{ + if(s[0]=='.' && (s[1]=='\0' || s[1]=='.' && s[2]=='\0') && p[0]!='.') + return 0; + return match(s, p, '/'); +} + +int +match(char *s, char *p, int stop) +{ + int compl, hit, lo, hi, t, c; + for(;*p!=stop && *p!='\0';s = nextutf(s),p = nextutf(p)){ + if(*p!=GLOB){ + if(!equtf(p, s)) return 0; + } + else switch(*++p){ + case GLOB: + if(*s!=GLOB) + return 0; + break; + case '*': + for(;;){ + if(match(s, nextutf(p), stop)) return 1; + if(!*s) + break; + s = nextutf(s); + } + return 0; + case '?': + if(*s=='\0') + return 0; + break; + case '[': + if(*s=='\0') + return 0; + c = unicode(s); + p++; + compl=*p=='~'; + if(compl) + p++; + hit = 0; + while(*p!=']'){ + if(*p=='\0') + return 0; /* syntax error */ + lo = unicode(p); + p = nextutf(p); + if(*p!='-') + hi = lo; + else{ + p++; + if(*p=='\0') + return 0; /* syntax error */ + hi = unicode(p); + p = nextutf(p); + if(hinext); + glob(gl->word); + } +} + +void +globlist(void) +{ + word *a; + globv = 0; + globlist1(runq->argv->words); + poplist(); + pushlist(); + if(globv){ + for(a = globv;a->next;a = a->next); + a->next = runq->argv->words; + runq->argv->words = globv; + } +} diff --git a/cmd/rc/havefork.c b/cmd/rc/havefork.c new file mode 100644 index 0000000..63f8335 --- /dev/null +++ b/cmd/rc/havefork.c @@ -0,0 +1,298 @@ +#include +#include +#if defined(PLAN9PORT) && defined(__sun__) +# define BSD_COMP /* sigh. for TIOCNOTTY */ +#endif +#include +#include "rc.h" +#include "getflags.h" +#include "exec.h" +#include "io.h" +#include "fns.h" + +int havefork = 1; + +void +Xasync(void) +{ + int null = open("/dev/null", 0); + int tty; + int pid; + char npid[10]; + if(null<0){ + Xerror("Can't open /dev/null\n"); + return; + } + switch(pid = rfork(RFFDG|RFPROC|RFNOTEG)){ + case -1: + close(null); + Xerror("try again"); + break; + case 0: + clearwaitpids(); + /* + * I don't know what the right thing to do here is, + * so this is all experimentally determined. + * If we just dup /dev/null onto 0, then running + * ssh foo & will reopen /dev/tty, try to read a password, + * get a signal, and repeat, in a tight loop, forever. + * Arguably this is a bug in ssh (it behaves the same + * way under bash as under rc) but I'm fixing it here + * anyway. If we dissociate the process from the tty, + * then it won't be able to open /dev/tty ever again. + * The SIG_IGN on SIGTTOU makes writing the tty + * (via fd 1 or 2, for example) succeed even though + * our pgrp is not the terminal's controlling pgrp. + */ + if((tty = open("/dev/tty", OREAD)) >= 0){ + /* + * Should make reads of tty fail, writes succeed. + */ + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + ioctl(tty, TIOCNOTTY); + close(tty); + } + if(isatty(0)) + pushredir(ROPEN, null, 0); + else + close(null); + start(runq->code, runq->pc+1, runq->local); + runq->ret = 0; + break; + default: + addwaitpid(pid); + close(null); + runq->pc = runq->code[runq->pc].i; + inttoascii(npid, pid); + setvar("apid", newword(npid, (word *)0)); + break; + } +} + +void +Xpipe(void) +{ + struct thread *p = runq; + int pc = p->pc, forkid; + int lfd = p->code[pc++].i; + int rfd = p->code[pc++].i; + int pfd[2]; + if(pipe(pfd)<0){ + Xerror("can't get pipe"); + return; + } + switch(forkid = fork()){ + case -1: + Xerror("try again"); + break; + case 0: + clearwaitpids(); + start(p->code, pc+2, runq->local); + runq->ret = 0; + close(pfd[PRD]); + pushredir(ROPEN, pfd[PWR], lfd); + break; + default: + addwaitpid(forkid); + start(p->code, p->code[pc].i, runq->local); + close(pfd[PWR]); + pushredir(ROPEN, pfd[PRD], rfd); + p->pc = p->code[pc+1].i; + p->pid = forkid; + break; + } +} + +/* + * Who should wait for the exit from the fork? + */ +void +Xbackq(void) +{ + struct thread *p = runq; + char wd[8193]; + int c, n; + char *s, *ewd=&wd[8192], *stop, *q; + struct io *f; + var *ifs = vlook("ifs"); + word *v, *nextv; + int pfd[2]; + int pid; + Rune r; + stop = ifs->val?ifs->val->word:""; + if(pipe(pfd)<0){ + Xerror("can't make pipe"); + return; + } + switch(pid = fork()){ + case -1: + Xerror("try again"); + close(pfd[PRD]); + close(pfd[PWR]); + return; + case 0: + clearwaitpids(); + close(pfd[PRD]); + start(runq->code, runq->pc+1, runq->local); + pushredir(ROPEN, pfd[PWR], 1); + return; + default: + addwaitpid(pid); + close(pfd[PWR]); + f = openfd(pfd[PRD]); + s = wd; + v = 0; + while((c = rchr(f))!=EOF){ + if(s != ewd) { + *s++ = c; + for(q=stop; *q; q+=n) { + n = chartorune(&r, q); + if(s-wd >= n && memcmp(s-n, q, n) == 0) { + s -= n; + goto stop; + } + } + continue; + } + stop: + if(s != wd) { + *s = '\0'; + v = newword(wd, v); + } + s = wd; + } + if(s!=wd){ + *s='\0'; + v = newword(wd, v); + } + closeio(f); + Waitfor(pid, 0); + /* v points to reversed arglist -- reverse it onto argv */ + while(v){ + nextv = v->next; + v->next = runq->argv->words; + runq->argv->words = v; + v = nextv; + } + p->pc = p->code[p->pc].i; + return; + } +} + +void +Xpipefd(void) +{ + struct thread *p = runq; + int pc = p->pc, pid; + char name[40]; + int pfd[2]; + struct { int sidefd, mainfd; } fd[2], *r, *w; + + r = &fd[0]; + w = &fd[1]; + switch(p->code[pc].i){ + case READ: + w = nil; + break; + case WRITE: + r = nil; + } + + if(r){ + if(pipe(pfd)<0){ + Xerror("can't get pipe"); + return; + } + r->sidefd = pfd[PWR]; + r->mainfd = pfd[PRD]; + } + if(w){ + if(pipe(pfd)<0){ + Xerror("can't get pipe"); + return; + } + w->sidefd = pfd[PRD]; + w->mainfd = pfd[PWR]; + } + switch(pid = fork()){ + case -1: + Xerror("try again"); + break; + case 0: + clearwaitpids(); + start(p->code, pc+2, runq->local); + if(r){ + close(r->mainfd); + pushredir(ROPEN, r->sidefd, 1); + } + if(w){ + close(w->mainfd); + pushredir(ROPEN, w->sidefd, 0); + } + runq->ret = 0; + break; + default: + addwaitpid(pid); + if(w){ + close(w->sidefd); + pushredir(ROPEN, w->mainfd, w->mainfd); /* so that Xpopredir can close it later */ + strcpy(name, Fdprefix); + inttoascii(name+strlen(name), w->mainfd); + pushword(name); + } + if(r){ + close(r->sidefd); + pushredir(ROPEN, r->mainfd, r->mainfd); + strcpy(name, Fdprefix); + inttoascii(name+strlen(name), r->mainfd); + pushword(name); + } + p->pc = p->code[pc+1].i; + break; + } +} + +void +Xsubshell(void) +{ + int pid; + switch(pid = fork()){ + case -1: + Xerror("try again"); + break; + case 0: + clearwaitpids(); + start(runq->code, runq->pc+1, runq->local); + runq->ret = 0; + break; + default: + addwaitpid(pid); + Waitfor(pid, 1); + runq->pc = runq->code[runq->pc].i; + break; + } +} + +int +execforkexec(void) +{ + int pid; + int n; + char buf[ERRMAX]; + + switch(pid = fork()){ + case -1: + return -1; + case 0: + clearwaitpids(); + pushword("exec"); + execexec(); + strcpy(buf, "can't exec: "); + n = strlen(buf); + errstr(buf+n, ERRMAX-n); + Exit(buf); + } + addwaitpid(pid); + return pid; +} diff --git a/cmd/rc/haventfork.c b/cmd/rc/haventfork.c new file mode 100644 index 0000000..fc8f760 --- /dev/null +++ b/cmd/rc/haventfork.c @@ -0,0 +1,211 @@ +#include "rc.h" +#include "getflags.h" +#include "exec.h" +#include "io.h" +#include "fns.h" + +int havefork = 0; + +static char ** +rcargv(char *s) +{ + int argc; + char **argv; + word *p; + + p = vlook("*")->val; + argv = malloc((count(p)+6)*sizeof(char*)); + argc = 0; + argv[argc++] = argv0; + if(flag['e']) + argv[argc++] = "-Se"; + else + argv[argc++] = "-S"; + argv[argc++] = "-c"; + argv[argc++] = s; + for(p = vlook("*")->val; p; p = p->next) + argv[argc++] = p->word; + argv[argc] = 0; + return argv; +} + +void +Xasync(void) +{ + uint pid; + char buf[20], **argv; + + Updenv(); + + argv = rcargv(runq->code[runq->pc].s); + pid = ForkExecute(argv0, argv, -1, 1, 2); + free(argv); + + if(pid == 0) { + Xerror("proc failed"); + return; + } + + runq->pc++; + sprint(buf, "%d", pid); + setvar("apid", newword(buf, (word *)0)); +} + +void +Xbackq(void) +{ + char wd[8193], **argv; + int c; + char *s, *ewd=&wd[8192], *stop; + struct io *f; + var *ifs = vlook("ifs"); + word *v, *nextv; + int pfd[2]; + int pid; + + stop = ifs->val?ifs->val->word:""; + if(pipe(pfd)<0){ + Xerror("can't make pipe"); + return; + } + + Updenv(); + + argv = rcargv(runq->code[runq->pc].s); + pid = ForkExecute(argv0, argv, -1, pfd[1], 2); + free(argv); + + close(pfd[1]); + + if(pid == 0) { + Xerror("proc failed"); + close(pfd[0]); + return; + } + + f = openfd(pfd[0]); + s = wd; + v = 0; + while((c=rchr(f))!=EOF){ + if(strchr(stop, c) || s==ewd){ + if(s!=wd){ + *s='\0'; + v=newword(wd, v); + s=wd; + } + } + else *s++=c; + } + if(s!=wd){ + *s='\0'; + v=newword(wd, v); + } + closeio(f); + Waitfor(pid, 1); + /* v points to reversed arglist -- reverse it onto argv */ + while(v){ + nextv=v->next; + v->next=runq->argv->words; + runq->argv->words=v; + v=nextv; + } + runq->pc++; +} + +void +Xpipe(void) +{ + thread *p=runq; + int pc=p->pc, pid; + int rfd=p->code[pc+1].i; + int pfd[2]; + char **argv; + + if(pipe(pfd)<0){ + Xerror1("can't get pipe"); + return; + } + + Updenv(); + + argv = rcargv(runq->code[pc+2].s); + pid = ForkExecute(argv0, argv, 0, pfd[1], 2); + free(argv); + close(pfd[1]); + + if(pid == 0) { + Xerror("proc failed"); + close(pfd[0]); + return; + } + + start(p->code, pc+4, runq->local); + pushredir(ROPEN, pfd[0], rfd); + p->pc=p->code[pc+3].i; + p->pid=pid; +} + +void +Xpipefd(void) +{ + Abort(); +} + +void +Xsubshell(void) +{ + char **argv; + int pid; + + Updenv(); + + argv = rcargv(runq->code[runq->pc].s); + pid = ForkExecute(argv0, argv, -1, 1, 2); + free(argv); + + if(pid < 0) { + Xerror("proc failed"); + return; + } + + Waitfor(pid, 1); + runq->pc++; +} + +/* + * start a process running the cmd on the stack and return its pid. + */ +int +execforkexec(void) +{ + char **argv; + char file[1024]; + int nc; + word *path; + int pid; + + if(runq->argv->words==0) + return -1; + argv = mkargv(runq->argv->words); + + for(path = searchpath(runq->argv->words->word);path;path = path->next){ + nc = strlen(path->word); + if(ncword); + if(file[0]){ + strcat(file, "/"); + nc++; + } + if(nc+strlen(argv[1])= 0){ + free(argv); + return pid; + } + } + } + } + free(argv); + return -1; +} diff --git a/cmd/rc/here.c b/cmd/rc/here.c new file mode 100644 index 0000000..17c6245 --- /dev/null +++ b/cmd/rc/here.c @@ -0,0 +1,150 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +struct here *here, **ehere; +int ser = 0; +char tmp[]="/tmp/here0000.0000"; +char hex[]="0123456789abcdef"; +void psubst(io*, char*); +void pstrs(io*, word*); + +void +hexnum(char *p, int n) +{ + *p++=hex[(n>>12)&0xF]; + *p++=hex[(n>>8)&0xF]; + *p++=hex[(n>>4)&0xF]; + *p = hex[n&0xF]; +} + +tree* +heredoc(tree *tag) +{ + struct here *h = new(struct here); + if(tag->type!=WORD) + yyerror("Bad here tag"); + h->next = 0; + if(here) + *ehere = h; + else + here = h; + ehere=&h->next; + h->tag = tag; + hexnum(&tmp[9], getpid()); + hexnum(&tmp[14], ser++); + h->name = strdup(tmp); + return token(tmp, WORD); +} +/* + * bug: lines longer than NLINE get split -- this can cause spurious + * missubstitution, or a misrecognized EOF marker. + */ +#define NLINE 4096 + +void +readhere(void) +{ + struct here *h, *nexth; + io *f; + char *s, *tag; + int c, subst; + char line[NLINE+1]; + for(h = here;h;h = nexth){ + subst=!h->tag->quoted; + tag = h->tag->str; + c = Creat(h->name); + if(c<0) + yyerror("can't create here document"); + f = openfd(c); + s = line; + pprompt(); + while((c = rchr(runq->cmdfd))!=EOF){ + if(c=='\n' || s==&line[NLINE]){ + *s='\0'; + if(tag && strcmp(line, tag)==0) break; + if(subst) + psubst(f, line); + else pstr(f, line); + s = line; + if(c=='\n'){ + pprompt(); + pchr(f, c); + } + else *s++=c; + } + else *s++=c; + } + flush(f); + closeio(f); + cleanhere(h->name); + nexth = h->next; + efree((char *)h); + } + here = 0; + doprompt = 1; +} + +void +psubst(io *f, char *s) +{ + char *t, *u; + int savec, n; + word *star; + while(*s){ + if(*s!='$'){ + if(0xa0<=(*s&0xff) && (*s&0xff)<=0xf5){ + pchr(f, *s++); + if(*s=='\0') + break; + } + else if(0xf6<=(*s&0xff) && (*s&0xff)<=0xf7){ + pchr(f, *s++); + if(*s=='\0') + break; + pchr(f, *s++); + if(*s=='\0') + break; + } + pchr(f, *s++); + } + else{ + t=++s; + if(*t=='$') + pchr(f, *t++); + else{ + while(*t && idchr(*t)) t++; + savec=*t; + *t='\0'; + n = 0; + for(u = s;*u && '0'<=*u && *u<='9';u++) n = n*10+*u-'0'; + if(n && *u=='\0'){ + star = vlook("*")->val; + if(star && 1<=n && n<=count(star)){ + while(--n) star = star->next; + pstr(f, star->word); + } + } + else + pstrs(f, vlook(s)->val); + *t = savec; + if(savec=='^') + t++; + } + s = t; + } + } +} + +void +pstrs(io *f, word *a) +{ + if(a){ + while(a->next && a->next->word){ + pstr(f, a->word); + pchr(f, ' '); + a = a->next; + } + pstr(f, a->word); + } +} diff --git a/cmd/rc/io.c b/cmd/rc/io.c new file mode 100644 index 0000000..907ba86 --- /dev/null +++ b/cmd/rc/io.c @@ -0,0 +1,273 @@ +#include +#include +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +int pfmtnest = 0; + +void +pfmt(io *f, char *fmt, ...) +{ + va_list ap; + char err[ERRMAX]; + va_start(ap, fmt); + pfmtnest++; + for(;*fmt;fmt++) + if(*fmt!='%') + pchr(f, *fmt); + else switch(*++fmt){ + case '\0': + va_end(ap); + return; + case 'c': + pchr(f, va_arg(ap, int)); + break; + case 'd': + pdec(f, va_arg(ap, int)); + break; + case 'o': + poct(f, va_arg(ap, unsigned)); + break; + case 'p': + pptr(f, va_arg(ap, void*)); + break; + case 'Q': + pquo(f, va_arg(ap, char *)); + break; + case 'q': + pwrd(f, va_arg(ap, char *)); + break; + case 'r': + rerrstr(err, sizeof err); pstr(f, err); + break; + case 's': + pstr(f, va_arg(ap, char *)); + break; + case 't': + pcmd(f, va_arg(ap, tree *)); + break; + case 'u': + pcmdu(f, va_arg(ap, tree *)); + break; + case 'v': + pval(f, va_arg(ap, struct word *)); + break; + default: + pchr(f, *fmt); + break; + } + va_end(ap); + if(--pfmtnest==0) + flush(f); +} + +void +pchr(io *b, int c) +{ + if(b->bufp==b->ebuf) + fullbuf(b, c); + else *b->bufp++=c; +} + +int +rchr(io *b) +{ + if(b->bufp==b->ebuf) + return emptybuf(b); + return *b->bufp++ & 0xFF; +} + +void +pquo(io *f, char *s) +{ + pchr(f, '\''); + for(;*s;s++) + if(*s=='\'') + pfmt(f, "''"); + else pchr(f, *s); + pchr(f, '\''); +} + +void +pwrd(io *f, char *s) +{ + char *t; + for(t = s;*t;t++) if(!wordchr(*t)) break; + if(t==s || *t) + pquo(f, s); + else pstr(f, s); +} + +void +pptr(io *f, void *v) +{ + int n; + uintptr p; + + p = (uintptr)v; + if(sizeof(uintptr) == sizeof(uvlong) && p>>32) + for(n = 60;n>=32;n-=4) pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]); + + for(n = 28;n>=0;n-=4) pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]); +} + +void +pstr(io *f, char *s) +{ + if(s==0) + s="(null)"; + while(*s) pchr(f, *s++); +} + +void +pdec(io *f, int n) +{ + if(n<0){ + if(n!=INT_MIN){ + pchr(f, '-'); + pdec(f, -n); + return; + } + /* n is two's complement minimum integer */ + n = -(INT_MIN+1); + pchr(f, '-'); + pdec(f, n/10); + pchr(f, n%10+'1'); + return; + } + if(n>9) + pdec(f, n/10); + pchr(f, n%10+'0'); +} + +void +poct(io *f, unsigned n) +{ + if(n>7) + poct(f, n>>3); + pchr(f, (n&7)+'0'); +} + +void +pval(io *f, word *a) +{ + if(a){ + while(a->next && a->next->word){ + pwrd(f, a->word); + pchr(f, ' '); + a = a->next; + } + pwrd(f, a->word); + } +} + +int +fullbuf(io *f, int c) +{ + flush(f); + return *f->bufp++=c; +} + +void +flush(io *f) +{ + int n; + char *s; + if(f->strp){ + n = f->ebuf-f->strp; + f->strp = realloc(f->strp, n+101); + if(f->strp==0) + panic("Can't realloc %d bytes in flush!", n+101); + f->bufp = f->strp+n; + f->ebuf = f->bufp+100; + for(s = f->bufp;s<=f->ebuf;s++) *s='\0'; + } + else{ + n = f->bufp-f->buf; + if(n && Write(f->fd, f->buf, n) < 0){ + Write(3, "Write error\n", 12); + if(ntrap) + dotrap(); + } + f->bufp = f->buf; + f->ebuf = f->buf+NBUF; + } +} + +io* +openfd(int fd) +{ + io *f = new(struct io); + f->fd = fd; + f->bufp = f->ebuf = f->buf; + f->strp = 0; + return f; +} + +io* +openstr(void) +{ + io *f = new(struct io); + char *s; + f->fd=-1; + f->bufp = f->strp = emalloc(101); + f->ebuf = f->bufp+100; + for(s = f->bufp;s<=f->ebuf;s++) *s='\0'; + return f; +} +/* + * Open a corebuffer to read. EOF occurs after reading len + * characters from buf. + */ + +io* +opencore(char *s, int len) +{ + io *f = new(struct io); + char *buf = emalloc(len); + f->fd= -1 /*open("/dev/null", 0)*/; + f->bufp = f->strp = buf; + f->ebuf = buf+len; + Memcpy(buf, s, len); + return f; +} + +void +iorewind(io *io) +{ + if(io->fd==-1) + io->bufp = io->strp; + else{ + io->bufp = io->ebuf = io->buf; + Seek(io->fd, 0L, 0); + } +} + +void +closeio(io *io) +{ + if(io->fd>=0) + close(io->fd); + if(io->strp) + efree(io->strp); + efree((char *)io); +} + +int +emptybuf(io *f) +{ + int n; + if(f->fd==-1) + return EOF; +Loop: + errno = 0; + n = Read(f->fd, f->buf, NBUF); + if(n < 0 && errno == EINTR) + goto Loop; + if(n <= 0) + return EOF; + f->bufp = f->buf; + f->ebuf = f->buf+n; + return *f->bufp++&0xff; +} diff --git a/cmd/rc/io.h b/cmd/rc/io.h new file mode 100644 index 0000000..6c75cc5 --- /dev/null +++ b/cmd/rc/io.h @@ -0,0 +1,31 @@ +/* + * on Mac OS X, err is something else, + * and assigning to it causes a bus error. + * what a crappy linker. + */ +#define err rc_err +#define EOF (-1) +#define NBUF 512 +struct io{ + int fd; + char *bufp, *ebuf, *strp, buf[NBUF]; +}; +io *err; +io *openfd(int), *openstr(void), *opencore(char *, int); +int emptybuf(io*); +void pchr(io*, int); +int rchr(io*); +void closeio(io*); +void flush(io*); +int fullbuf(io*, int); +void pdec(io*, int); +void poct(io*, unsigned); +void pptr(io*, void*); +void pquo(io*, char*); +void pwrd(io*, char*); +void pstr(io*, char*); +void pcmd(io*, tree*); +void pcmdu(io*, tree*); +void pval(io*, word*); +void pfnc(io*, thread*); +void pfmt(io*, char*, ...); diff --git a/cmd/rc/lex.c b/cmd/rc/lex.c new file mode 100644 index 0000000..e4410c0 --- /dev/null +++ b/cmd/rc/lex.c @@ -0,0 +1,396 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "getflags.h" +#include "fns.h" +int getnext(void); + +int +wordchr(int c) +{ + return !strchr("\n \t#;&|^$=`'{}()<>", c) && c!=EOF; +} + +int +idchr(int c) +{ + /* + * Formerly: + * return 'a'<=c && c<='z' || 'A'<=c && c<='Z' || '0'<=c && c<='9' + * || c=='_' || c=='*'; + */ + return c>' ' && !strchr("!\"#$%&'()+,-./:;<=>?@[\\]^`{|}~", c); +} +int future = EOF; +int doprompt = 1; +int inquote; +int incomm; +/* + * Look ahead in the input stream + */ + +int +nextc(void) +{ + if(future==EOF) + future = getnext(); + return future; +} +/* + * Consume the lookahead character. + */ + +int +advance(void) +{ + int c = nextc(); + lastc = future; + future = EOF; + return c; +} +/* + * read a character from the input stream + */ + +int +getnext(void) +{ + int c; + static int peekc = EOF; + if(peekc!=EOF){ + c = peekc; + peekc = EOF; + return c; + } + if(runq->eof) + return EOF; + if(doprompt) + pprompt(); + c = rchr(runq->cmdfd); + if(!inquote && c=='\\'){ + c = rchr(runq->cmdfd); + if(c=='\n' && !incomm){ /* don't continue a comment */ + doprompt = 1; + c=' '; + } + else{ + peekc = c; + c='\\'; + } + } + doprompt = doprompt || c=='\n' || c==EOF; + if(c==EOF) + runq->eof++; + else if(flag['V'] || ndot>=2 && flag['v']) pchr(err, c); + return c; +} + +void +pprompt(void) +{ + var *prompt; + if(runq->iflag){ + pstr(err, promptstr); + flush(err); + prompt = vlook("prompt"); + if(prompt->val && prompt->val->next) + promptstr = prompt->val->next->word; + else + promptstr="\t"; + } + runq->lineno++; + doprompt = 0; +} + +int +skipwhite(void) +{ + int c, skipped; + skipped = 0; + for(;;){ + c = nextc(); + /* Why did this used to be if(!inquote && c=='#') ?? */ + if(c=='#'){ + incomm = 1; + skipped = 1; + for(;;){ + c = nextc(); + if(c=='\n' || c==EOF) { + incomm = 0; + break; + } + advance(); + } + } + if(c==' ' || c=='\t') { + skipped = 1; + advance(); + } + else + return skipped; + } +} + +void +skipnl(void) +{ + int c; + for(;;){ + skipwhite(); + c = nextc(); + if(c!='\n') + return; + advance(); + } +} + +int +nextis(int c) +{ + if(nextc()==c){ + advance(); + return 1; + } + return 0; +} + +char* +addtok(char *p, int val) +{ + if(p==0) + return 0; + if(p==&tok[NTOK-1]){ + *p = 0; + yyerror("token buffer too short"); + return 0; + } + *p++=val; + return p; +} + +char* +addutf(char *p, int c) +{ + p = addtok(p, c); + if(twobyte(c)) /* 2-byte escape */ + return addtok(p, advance()); + if(threebyte(c)){ /* 3-byte escape */ + p = addtok(p, advance()); + return addtok(p, advance()); + } + if(fourbyte(c)){ /* 4-byte escape */ + p = addtok(p, advance()); + p = addtok(p, advance()); + return addtok(p, advance()); + } + return p; +} +int lastdol; /* was the last token read '$' or '$#' or '"'? */ +int lastword; /* was the last token read a word or compound word terminator? */ + +int +yylex(void) +{ + int c, d = nextc(); + char *w = tok; + tree *t; + yylval.tree = 0; + /* + * Embarassing sneakiness: if the last token read was a quoted or unquoted + * WORD then we alter the meaning of what follows. If the next character + * is `(', we return SUB (a subscript paren) and consume the `('. Otherwise, + * if the next character is the first character of a simple or compound word, + * we insert a `^' before it. + */ + if(lastword && flag['Y']){ + lastword = 0; + if(d=='('){ + advance(); + strcpy(tok, "("); + return SUB; + } + if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){ + strcpy(tok, "^"); + return '^'; + } + } + inquote = 0; + if(skipwhite() && !flag['Y']) + return ' '; + switch(c = advance()){ + case EOF: + lastdol = 0; + strcpy(tok, "EOF"); + return EOF; + case '$': + lastdol = 1; + if(nextis('#')){ + strcpy(tok, "$#"); + return COUNT; + } + if(nextis('"')){ + strcpy(tok, "$\""); + return '"'; + } + strcpy(tok, "$"); + return '$'; + case '&': + lastdol = 0; + if(nextis('&')){ + if(flag['Y']) + skipnl(); + strcpy(tok, "&&"); + return ANDAND; + } + strcpy(tok, "&"); + return '&'; + case '|': + lastdol = 0; + if(nextis(c)){ + if(flag['Y']) + skipnl(); + strcpy(tok, "||"); + return OROR; + } + case '<': + case '>': + lastdol = 0; + /* + * funny redirection tokens: + * redir: arrow | arrow '[' fd ']' + * arrow: '<' | '<<' | '>' | '>>' | '|' + * fd: digit | digit '=' | digit '=' digit + * digit: '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' + * some possibilities are nonsensical and get a message. + */ + *w++=c; + t = newtree(); + switch(c){ + case '|': + t->type = PIPE; + t->fd0 = 1; + t->fd1 = 0; + break; + case '>': + t->type = REDIR; + if(nextis(c)){ + t->rtype = APPEND; + *w++=c; + } + else t->rtype = WRITE; + t->fd0 = 1; + break; + case '<': + t->type = REDIR; + if(nextis(c)){ + t->rtype = HERE; + *w++=c; + } else if (nextis('>')){ + t->rtype = RDWR; + *w++=c; + } else t->rtype = READ; + t->fd0 = 0; + break; + } + if(nextis('[')){ + *w++='['; + c = advance(); + *w++=c; + if(c<'0' || '9'type==PIPE?"pipe syntax" + :"redirection syntax"); + return EOF; + } + t->fd0 = 0; + do{ + t->fd0 = t->fd0*10+c-'0'; + *w++=c; + c = advance(); + }while('0'<=c && c<='9'); + if(c=='='){ + *w++='='; + if(t->type==REDIR) + t->type = DUP; + c = advance(); + if('0'<=c && c<='9'){ + t->rtype = DUPFD; + t->fd1 = t->fd0; + t->fd0 = 0; + do{ + t->fd0 = t->fd0*10+c-'0'; + *w++=c; + c = advance(); + }while('0'<=c && c<='9'); + } + else{ + if(t->type==PIPE) + goto RedirErr; + t->rtype = CLOSE; + } + } + if(c!=']' + || t->type==DUP && (t->rtype==HERE || t->rtype==APPEND)) + goto RedirErr; + *w++=']'; + } + *w='\0'; + yylval.tree = t; + if(t->type==PIPE && flag['Y']) + skipnl(); + if(t->type==REDIR) { + skipwhite(); + if(nextc() == '{') + t->type = REDIRW; + } + return t->type; + case '\'': + lastdol = 0; + lastword = 1; + inquote = 1; + for(;;){ + c = advance(); + if(c==EOF) + break; + if(c=='\''){ + if(nextc()!='\'') + break; + advance(); + } + w = addutf(w, c); + } + if(w!=0) + *w='\0'; + t = token(tok, WORD); + t->quoted = 1; + yylval.tree = t; + return t->type; + } + if(!wordchr(c)){ + lastdol = 0; + tok[0] = c; + tok[1]='\0'; + return c; + } + for(;;){ + /* next line should have (char)c==GLOB, but ken's compiler is broken */ + if(c=='*' || c=='[' || c=='?' || c==(unsigned char)GLOB) + w = addtok(w, GLOB); + w = addutf(w, c); + c = nextc(); + if(lastdol?!idchr(c):!wordchr(c)) break; + advance(); + } + + lastword = 1; + lastdol = 0; + if(w!=0) + *w='\0'; + t = klook(tok); + if(t->type!=WORD) + lastword = 0; + t->quoted = 0; + yylval.tree = t; + return t->type; +} diff --git a/cmd/rc/mkfile b/cmd/rc/mkfile new file mode 100644 index 0000000..c1e77ae --- /dev/null +++ b/cmd/rc/mkfile @@ -0,0 +1,38 @@ +<$PLAN9/src/mkhdr + +TARG=rc + +OFILES=\ + code.$O\ + exec.$O\ + getflags.$O\ + glob.$O\ + here.$O\ + io.$O\ + lex.$O\ + parse.$O\ + pcmd.$O\ + pfnc.$O\ + simple.$O\ + subr.$O\ + trap.$O\ + tree.$O\ + unixcrap.$O\ + var.$O\ + y.tab.$O\ + plan9ish.$O\ + havefork.$O\ + +HFILES=\ + rc.h\ + x.tab.h\ + io.h\ + exec.h\ + fns.h\ + +YFILES=syn.y + +<$PLAN9/src/mkone + +x.tab.h: y.tab.h + cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h diff --git a/cmd/rc/parse.c b/cmd/rc/parse.c new file mode 100644 index 0000000..dd10219 --- /dev/null +++ b/cmd/rc/parse.c @@ -0,0 +1,552 @@ +#include "rc.h" +#include "io.h" +#include "fns.h" + +static tree* body(int tok, int *ptok); +static tree* brace(int tok); +static tree* cmd(int tok, int *ptok); +static tree* cmd2(int tok, int *ptok); +static tree* cmd3(int tok, int *ptok); +static tree* cmds(int tok, int *ptok, int nlok); +static tree* epilog(int tok, int *ptok); +static int iswordtok(int tok); +static tree* line(int tok, int *ptok); +static tree* paren(int tok); +static tree* yyredir(int tok, int *ptok); +static tree* yyword(int tok, int *ptok, int eqok); +static tree* word1(int tok, int *ptok); +static tree* words(int tok, int *ptok); + +static jmp_buf yyjmp; + +static int +dropnl(int tok) +{ + while(tok == ' ' || tok == '\n') + tok = yylex(); + return tok; +} + +static int +dropsp(int tok) +{ + while(tok == ' ') + tok = yylex(); + return tok; +} + +static void +syntax(int tok) +{ + USED(tok); + yyerror("syntax error"); + longjmp(yyjmp, 1); +} + +int +parse(void) +{ + tree *t; + int tok; + + if(setjmp(yyjmp)) + return 1; + + // rc: { return 1;} + // | line '\n' {return !compile($1);} + + tok = dropsp(yylex()); + if(tok == EOF) + return 1; + t = line(tok, &tok); + if(tok != '\n') + yyerror("missing newline at end of line"); + yylval.tree = t; + return !compile(t); +} + +static tree* +line(int tok, int *ptok) +{ + return cmds(tok, ptok, 0); +} + +static tree* +body(int tok, int *ptok) +{ + return cmds(tok, ptok, 1); +} + +static tree* +cmds(int tok, int *ptok, int nlok) +{ + tree *t, **last, *t2; + + // line: cmd + // | cmdsa line {$$=tree2(';', $1, $2);} + // cmdsa: cmd ';' + // | cmd '&' {$$=tree1('&', $1);} + + // body: cmd + // | cmdsan body {$$=tree2(';', $1, $2);} + // cmdsan: cmdsa + // | cmd '\n' + + t = nil; + last = nil; + for(;;) { + t2 = cmd(tok, &tok); + if(tok == '&') + t2 = tree1('&', t2); + if(t2 != nil) { + // slot into list t + if(last == nil) { + t = t2; + last = &t; + } else { + *last = tree2(';', *last, t2); + last = &(*last)->child[1]; + } + } + if(tok != ';' && tok != '&' && (!nlok || tok != '\n')) + break; + tok = yylex(); + } + *ptok = tok; + return t; +} + +static tree* +brace(int tok) +{ + tree *t; + + // brace: '{' body '}' {$$=tree1(BRACE, $2);} + + tok = dropsp(tok); + if(tok != '{') + syntax(tok); + t = body(yylex(), &tok); + if(tok != '}') + syntax(tok); + return tree1(BRACE, t); +} + +static tree* +paren(int tok) +{ + tree *t; + + // paren: '(' body ')' {$$=tree1(PCMD, $2);} + + tok = dropsp(tok); + if(tok != '(') + syntax(tok); + t = body(yylex(), &tok); + if(tok != ')') + syntax(tok); + return tree1(PCMD, t); +} + +static tree* +epilog(int tok, int *ptok) +{ + tree *t, *r; + + // epilog: {$$=0;} + // | redir epilog {$$=mung2($1, $1->child[0], $2);} + + if(tok != REDIR && tok != DUP) { + *ptok = tok; + return nil; + } + + r = yyredir(tok, &tok); + t = epilog(tok, &tok); + *ptok = tok; + return mung2(r, r->child[0], t); +} + +static tree* +yyredir(int tok, int *ptok) +{ + tree *r, *w; + + // redir: REDIR word {$$=mung1($1, $1->rtype==HERE?heredoc($2):$2);} + // | DUP + + switch(tok) { + default: + syntax(tok); + case DUP: + r = yylval.tree; + *ptok = dropsp(yylex()); + break; + case REDIR: + r = yylval.tree; + w = yyword(yylex(), &tok, 1); + *ptok = dropsp(tok); + r = mung1(r, r->rtype==HERE?heredoc(w):w); + break; + } + return r; +} + +static tree* +cmd(int tok, int *ptok) +{ + int op; + tree *t1, *t2; + + // | cmd ANDAND cmd {$$=tree2(ANDAND, $1, $3);} + // | cmd OROR cmd {$$=tree2(OROR, $1, $3);} + + tok = dropsp(tok); + t1 = cmd2(tok, &tok); + while(tok == ANDAND || tok == OROR) { + op = tok; + t2 = cmd2(dropnl(yylex()), &tok); + t1 = tree2(op, t1, t2); + } + *ptok = tok; + return t1; +} + +static tree* +cmd2(int tok, int *ptok) +{ + tree *t1, *t2, *t3; + + // | cmd PIPE cmd {$$=mung2($2, $1, $3);} + t1 = cmd3(tok, &tok); + while(tok == PIPE) { + t2 = yylval.tree; + t3 = cmd3(dropnl(yylex()), &tok); + t1 = mung2(t2, t1, t3); + } + *ptok = tok; + return t1; +} + +static tree* +cmd3(int tok, int *ptok) +{ + tree *t1, *t2, *t3, *t4; + + tok = dropsp(tok); + switch(tok) { + case ';': + case '&': + case '\n': + *ptok = tok; + return nil; + + case IF: + // | IF paren {skipnl();} cmd {$$=mung2($1, $2, $4);} + // | IF NOT {skipnl();} cmd {$$=mung1($2, $4);} + t1 = yylval.tree; + tok = dropsp(yylex()); + if(tok == NOT) { + t1 = yylval.tree; + t2 = cmd(dropnl(yylex()), ptok); + return mung1(t1, t2); + } + t2 = paren(tok); + t3 = cmd(dropnl(yylex()), ptok); + return mung2(t1, t2, t3); + + case FOR: + // | FOR '(' word IN words ')' {skipnl();} cmd + // {$$=mung3($1, $3, $5 ? $5 : tree1(PAREN, $5), $8);} + // | FOR '(' word ')' {skipnl();} cmd + // {$$=mung3($1, $3, (tree *)0, $6);} + t1 = yylval.tree; + tok = dropsp(yylex()); + if(tok != '(') + syntax(tok); + t2 = yyword(yylex(), &tok, 1); + switch(tok) { + default: + syntax(tok); + case ')': + t3 = nil; + break; + case IN: + t3 = words(yylex(), &tok); + if(t3 == nil) + t3 = tree1(PAREN, nil); + if(tok != ')') + syntax(tok); + break; + } + t4 = cmd(dropnl(yylex()), ptok); + return mung3(t1, t2, t3, t4); + + case WHILE: + // | WHILE paren {skipnl();} cmd + // {$$=mung2($1, $2, $4);} + t1 = yylval.tree; + t2 = paren(yylex()); + t3 = cmd(dropnl(yylex()), ptok); + return mung2(t1, t2, t3); + + case SWITCH: + // | SWITCH word {skipnl();} brace + // {$$=tree2(SWITCH, $2, $4);} + t1 = yyword(yylex(), &tok, 1); + tok = dropnl(tok); // doesn't work in yacc grammar but works here! + t2 = brace(tok); + *ptok = dropsp(yylex()); + return tree2(SWITCH, t1, t2); + // Note: cmd: a && for(x) y && b is a && {for (x) {y && b}}. + return cmd(tok, ptok); + + case FN: + // | FN words brace {$$=tree2(FN, $2, $3);} + // | FN words {$$=tree1(FN, $2);} + t1 = words(yylex(), &tok); + if(tok != '{') { + *ptok = tok; + return tree1(FN, t1); + } + t2 = brace(tok); + *ptok = dropsp(yylex()); + return tree2(FN, t1, t2); + + case TWIDDLE: + // | TWIDDLE word words {$$=mung2($1, $2, $3);} + t1 = yylval.tree; + t2 = yyword(yylex(), &tok, 1); + t3 = words(tok, ptok); + return mung2(t1, t2, t3); + + case BANG: + case SUBSHELL: + // | BANG cmd {$$=mung1($1, $2);} + // | SUBSHELL cmd {$$=mung1($1, $2);} + // Note: cmd2: ! x | y is !{x | y} not {!x} | y. + t1 = yylval.tree; + return mung1(t1, cmd2(yylex(), ptok)); + + case REDIR: + case DUP: + // | redir cmd %prec BANG {$$=mung2($1, $1->child[0], $2);} + // Note: cmd2: {>x echo a | tr a-z A-Z} writes A to x. + t1 = yyredir(tok, &tok); + t2 = cmd2(tok, ptok); + return mung2(t1, t1->child[0], t2); + + case '{': + // | brace epilog {$$=epimung($1, $2);} + t1 = brace(tok); + tok = dropsp(yylex()); + t2 = epilog(tok, ptok); + return epimung(t1, t2); + } + + if(!iswordtok(tok)) { + *ptok = tok; + return nil; + } + + // cmd: ... + // | simple {$$=simplemung($1);} + // | assign cmd %prec BANG {$$=mung3($1, $1->child[0], $1->child[1], $2);} + // assign: first '=' word {$$=tree2('=', $1, $3);} + // Note: first is same as word except for disallowing all the leading keywords, + // but all those keywords have been picked off in the switch above. + // Except NOT, but disallowing that in yacc was likely a mistake anyway: + // there's no ambiguity in not=1 or not x y z. + t1 = yyword(tok, &tok, 0); + if(tok == '=') { + // assignment + // Note: cmd2: {x=1 true | echo $x} echoes 1. + t1 = tree2('=', t1, yyword(yylex(), &tok, 1)); + t2 = cmd2(tok, ptok); + return mung3(t1, t1->child[0], t1->child[1], t2); + } + + // simple: first + // | simple word {$$=tree2(ARGLIST, $1, $2);} + // | simple redir {$$=tree2(ARGLIST, $1, $2);} + for(;;) { + if(tok == REDIR || tok == DUP) { + t1 = tree2(ARGLIST, t1, yyredir(tok, &tok)); + } else if(iswordtok(tok)) { + t1 = tree2(ARGLIST, t1, yyword(tok, &tok, 1)); + } else { + break; + } + } + *ptok = tok; + return simplemung(t1); +} + +static tree* +words(int tok, int *ptok) +{ + tree *t; + + // words: {$$=(tree*)0;} + // | words word {$$=tree2(WORDS, $1, $2);} + + t = nil; + tok = dropsp(tok); + while(iswordtok(tok)) + t = tree2(WORDS, t, yyword(tok, &tok, 1)); + *ptok = tok; + return t; +} + +static tree* +yyword(int tok, int *ptok, int eqok) +{ + tree *t; + + // word: keyword {lastword=1; $1->type=WORD;} + // | comword + // | word '^' word {$$=tree2('^', $1, $3);} + // comword: '$' word {$$=tree1('$', $2);} + // | '$' word SUB words ')' {$$=tree2(SUB, $2, $4);} + // | '"' word {$$=tree1('"', $2);} + // | COUNT word {$$=tree1(COUNT, $2);} + // | WORD + // | '`' brace {$$=tree1('`', $2);} + // | '(' words ')' {$$=tree1(PAREN, $2);} + // | REDIR brace {$$=mung1($1, $2); $$->type=PIPEFD;} + // keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN + // + // factored into: + // + // word: word1 + // | word '^' word1 + // + // word1: keyword | comword + + t = word1(tok, &tok); + if(tok == '=' && !eqok) + goto out; + for(;;) { + if(iswordtok(tok)) { + // No free carats around parens. + if(t->type == PAREN || tok == '(') + syntax(tok); + t = tree2('^', t, word1(tok, &tok)); + continue; + } + tok = dropsp(tok); + if(tok == '^') { + t = tree2('^', t, word1(yylex(), &tok)); + continue; + } + break; + } +out: + *ptok = dropsp(tok); + return t; +} + +static tree* +word1(int tok, int *ptok) +{ + tree *w, *sub, *t; + + tok = dropsp(tok); + switch(tok) { + default: + syntax(tok); + + case WORD: + case FOR: + case IN: + case WHILE: + case IF: + case NOT: + case TWIDDLE: + case BANG: + case SUBSHELL: + case SWITCH: + case FN: + // | WORD + // keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN + t = yylval.tree; + t->type = WORD; + *ptok = yylex(); + return t; + + case '=': + *ptok = yylex(); + return token("=", WORD); + + case '$': + // comword: '$' word1 {$$=tree1('$', $2);} + // | '$' word1 SUB words ')' {$$=tree2(SUB, $2, $4);} + w = word1(yylex(), &tok); + if(tok == '(') { + sub = words(yylex(), &tok); + if(tok != ')') + syntax(tok); + *ptok = yylex(); + return tree2(SUB, w, sub); + } + *ptok = tok; + return tree1('$', w); + + case '"': + // | '"' word1 {$$=tree1('"', $2);} + return tree1('"', word1(yylex(), ptok)); + + case COUNT: + // | COUNT word1 {$$=tree1(COUNT, $2);} + return tree1(COUNT, word1(yylex(), ptok)); + + case '`': + // | '`' brace {$$=tree1('`', $2);} + t = tree1('`', brace(yylex())); + *ptok = yylex(); + return t; + + case '(': + // | '(' words ')' {$$=tree1(PAREN, $2);} + t = tree1(PAREN, words(yylex(), &tok)); + if(tok != ')') + syntax(tok); + *ptok = yylex(); + return t; + + case REDIRW: + // | REDIRW brace {$$=mung1($1, $2); $$->type=PIPEFD;} + t = yylval.tree; + t = mung1(t, brace(yylex())); + t->type = PIPEFD; + *ptok = yylex(); + return t; + } +} + +static int +iswordtok(int tok) +{ + switch(tok) { + case FOR: + case IN: + case WHILE: + case IF: + case NOT: + case TWIDDLE: + case BANG: + case SUBSHELL: + case SWITCH: + case FN: + case '$': + case '"': + case COUNT: + case WORD: + case '`': + case '(': + case REDIRW: + case '=': + return 1; + } + return 0; +} diff --git a/cmd/rc/pcmd.c b/cmd/rc/pcmd.c new file mode 100644 index 0000000..cae8473 --- /dev/null +++ b/cmd/rc/pcmd.c @@ -0,0 +1,265 @@ +#include "rc.h" +#include "io.h" +#include "fns.h" +char nl='\n'; /* change to semicolon for bourne-proofing */ +#define c0 t->child[0] +#define c1 t->child[1] +#define c2 t->child[2] + +void +pdeglob(io *f, char *s) +{ + while(*s){ + if(*s==GLOB) + s++; + pchr(f, *s++); + } +} + +void +pcmd(io *f, tree *t) +{ + if(t==0) + return; + switch(t->type){ + default: pfmt(f, "bad %d %p %p %p", t->type, c0, c1, c2); + break; + case '$': pfmt(f, "$%t", c0); + break; + case '"': pfmt(f, "$\"%t", c0); + break; + case '&': pfmt(f, "%t&", c0); + break; + case '^': pfmt(f, "%t^%t", c0, c1); + break; + case '`': pfmt(f, "`%t", c0); + break; + case ANDAND: pfmt(f, "%t && %t", c0, c1); + break; + case BANG: pfmt(f, "! %t", c0); + break; + case BRACE: pfmt(f, "{%t}", c0); + break; + case COUNT: pfmt(f, "$#%t", c0); + break; + case FN: pfmt(f, "fn %t %t", c0, c1); + break; + case IF: pfmt(f, "if%t%t", c0, c1); + break; + case NOT: pfmt(f, "if not %t", c0); + break; + case OROR: pfmt(f, "%t || %t", c0, c1); + break; + case PCMD: + case PAREN: pfmt(f, "(%t)", c0); + break; + case SUB: pfmt(f, "$%t(%t)", c0, c1); + break; + case SIMPLE: pfmt(f, "%t", c0); + break; + case SUBSHELL: pfmt(f, "@ %t", c0); + break; + case SWITCH: pfmt(f, "switch %t %t", c0, c1); + break; + case TWIDDLE: pfmt(f, "~ %t %t", c0, c1); + break; + case WHILE: pfmt(f, "while %t%t", c0, c1); + break; + case ARGLIST: + if(c0==0) + pfmt(f, "%t", c1); + else if(c1==0) + pfmt(f, "%t", c0); + else + pfmt(f, "%t %t", c0, c1); + break; + case ';': + if(c0){ + if(c1) + pfmt(f, "%t%c%t", c0, nl, c1); + else pfmt(f, "%t", c0); + } + else pfmt(f, "%t", c1); + break; + case WORDS: + if(c0) + pfmt(f, "%t ", c0); + pfmt(f, "%t", c1); + break; + case FOR: + pfmt(f, "for(%t", c0); + if(c1) + pfmt(f, " in %t", c1); + pfmt(f, ")%t", c2); + break; + case WORD: + if(t->quoted) + pfmt(f, "%Q", t->str); + else pdeglob(f, t->str); + break; + case DUP: + if(t->rtype==DUPFD) + pfmt(f, ">[%d=%d]", t->fd1, t->fd0); /* yes, fd1, then fd0; read lex.c */ + else + pfmt(f, ">[%d=]", t->fd0); + pfmt(f, "%t", c1); + break; + case PIPEFD: + case REDIR: + switch(t->rtype){ + case HERE: + pchr(f, '<'); + case READ: + case RDWR: + pchr(f, '<'); + if(t->rtype==RDWR) + pchr(f, '>'); + if(t->fd0!=0) + pfmt(f, "[%d]", t->fd0); + break; + case APPEND: + pchr(f, '>'); + case WRITE: + pchr(f, '>'); + if(t->fd0!=1) + pfmt(f, "[%d]", t->fd0); + break; + } + pfmt(f, "%t", c0); + if(c1) + pfmt(f, " %t", c1); + break; + case '=': + pfmt(f, "%t=%t", c0, c1); + if(c2) + pfmt(f, " %t", c2); + break; + case PIPE: + pfmt(f, "%t|", c0); + if(t->fd1==0){ + if(t->fd0!=1) + pfmt(f, "[%d]", t->fd0); + } + else pfmt(f, "[%d=%d]", t->fd0, t->fd1); + pfmt(f, "%t", c1); + break; + } +} + +void +pcmdu(io *f, tree *t) /* unambiguous */ +{ + if(t==0) { + pfmt(f, ""); + return; + } + + switch(t->type){ + default: pfmt(f, "(bad %d %p %p %p)", t->type, c0, c1, c2); + break; + case '$': pfmt(f, "($ %u)", c0); + break; + case '"': pfmt(f, "($\" %u)", c0); + break; + case '&': pfmt(f, "(& %u)", c0); + break; + case '^': pfmt(f, "(^ %u %u)", c0, c1); + break; + case '`': pfmt(f, "(` %u)", c0); + break; + case ANDAND: pfmt(f, "(&& %u %u)", c0, c1); + break; + case BANG: pfmt(f, "(! %u)", c0); + break; + case BRACE: pfmt(f, "(brace %u)", c0); + break; + case COUNT: pfmt(f, "($# %u)", c0); + break; + case FN: pfmt(f, "(fn %u %u)", c0, c1); + break; + case IF: pfmt(f, "(if %u %u)", c0, c1); + break; + case NOT: pfmt(f, "(if not %u)", c0); + break; + case OROR: pfmt(f, "(|| %u %u)", c0, c1); + break; + case PCMD: + case PAREN: pfmt(f, "(paren %u)", c0); + break; + case SUB: pfmt(f, "($sub %u %u)", c0, c1); + break; + case SIMPLE: pfmt(f, "(simple %u)", c0); + break; + case SUBSHELL: pfmt(f, "(@ %u)", c0); + break; + case SWITCH: pfmt(f, "(switch %u %u)", c0, c1); + break; + case TWIDDLE: pfmt(f, "(~ %u %u)", c0, c1); + break; + case WHILE: pfmt(f, "(while %u %u)", c0, c1); + break; + case ARGLIST: + pfmt(f, "(arglist %u %u)", c0, c1); + break; + case ';': + pfmt(f, "(; %u %u)", c0, c1); + break; + case WORDS: + pfmt(f, "(words %u %u)", c0, c1); + break; + case FOR: + pfmt(f, "(for %u %u %u)", c0, c1, c2); + break; + case WORD: + if(t->quoted) + pfmt(f, "%Q", t->str); + else pdeglob(f, t->str); + break; + case DUP: + if(t->rtype==DUPFD) + pfmt(f, "(>[%d=%d]", t->fd1, t->fd0); /* yes, fd1, then fd0; read lex.c */ + else + pfmt(f, "(>[%d=]", t->fd0); /*)*/ + pfmt(f, " %u)", c1); + break; + case PIPEFD: + case REDIR: + pfmt(f, "("); + switch(t->rtype){ + case HERE: + pchr(f, '<'); + case READ: + case RDWR: + pchr(f, '<'); + if(t->rtype==RDWR) + pchr(f, '>'); + if(t->fd0!=0) + pfmt(f, "[%d]", t->fd0); + break; + case APPEND: + pchr(f, '>'); + case WRITE: + pchr(f, '>'); + if(t->fd0!=1) + pfmt(f, "[%d]", t->fd0); + break; + } + if(t->rtype == HERE) + pfmt(f, "HERE %u)", c1); + else + pfmt(f, "%u %u)", c0, c1); + break; + case '=': + pfmt(f, "(%u=%u %u)", c0, c1, c2); + break; + case PIPE: + pfmt(f, "(|"); + if(t->fd1==0){ + if(t->fd0!=1) + pfmt(f, "[%d]", t->fd0); + } + else pfmt(f, "[%d=%d]", t->fd0, t->fd1); + pfmt(f, " %u %u", c0, c1); + break; + } +} diff --git a/cmd/rc/pfnc.c b/cmd/rc/pfnc.c new file mode 100644 index 0000000..3f2b4c9 --- /dev/null +++ b/cmd/rc/pfnc.c @@ -0,0 +1,71 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +struct{ + void (*f)(void); + char *name; +}fname[] = { + Xappend, "Xappend", + Xasync, "Xasync", + Xbang, "Xbang", + Xclose, "Xclose", + Xdup, "Xdup", + Xeflag, "Xeflag", + Xexit, "Xexit", + Xfalse, "Xfalse", + Xifnot, "Xifnot", + Xjump, "Xjump", + Xmark, "Xmark", + Xpopm, "Xpopm", + Xrdwr, "Xrdwr", + Xread, "Xread", + Xreturn, "Xreturn", + Xtrue, "Xtrue", + Xif, "Xif", + Xwastrue, "Xwastrue", + Xword, "Xword", + Xwrite, "Xwrite", + Xmatch, "Xmatch", + Xcase, "Xcase", + Xconc, "Xconc", + Xassign, "Xassign", + Xdol, "Xdol", + Xcount, "Xcount", + Xlocal, "Xlocal", + Xunlocal, "Xunlocal", + Xfn, "Xfn", + Xdelfn, "Xdelfn", + Xpipe, "Xpipe", + Xpipewait, "Xpipewait", + Xrdcmds, "Xrdcmds", + (void (*)(void))Xerror, "Xerror", + Xbackq, "Xbackq", + Xpipefd, "Xpipefd", + Xsubshell, "Xsubshell", + Xdelhere, "Xdelhere", + Xfor, "Xfor", + Xglob, "Xglob", + Xrdfn, "Xrdfn", + Xsimple, "Xsimple", + Xrdfn, "Xrdfn", + Xqdol, "Xqdol", +0}; + +void +pfnc(io *fd, thread *t) +{ + int i; + void (*fn)(void) = t->code[t->pc].f; + list *a; + pfmt(fd, "pid %d cycle %p %d ", getpid(), t->code, t->pc); + for(i = 0;fname[i].f;i++) if(fname[i].f==fn){ + pstr(fd, fname[i].name); + break; + } + if(!fname[i].f) + pfmt(fd, "%p", fn); + for(a = t->argv;a;a = a->next) pfmt(fd, " (%v)", a->words); + pchr(fd, '\n'); + flush(fd); +} diff --git a/cmd/rc/plan9ish.c b/cmd/rc/plan9ish.c new file mode 100644 index 0000000..d52def7 --- /dev/null +++ b/cmd/rc/plan9ish.c @@ -0,0 +1,604 @@ +/* + * Plan 9 versions of system-specific functions + * By convention, exported routines herein have names beginning with an + * upper case letter. + */ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +#include "getflags.h" +char *Signame[]={ + "sigexit", "sighup", "sigint", "sigquit", + "sigalrm", "sigkill", "sigfpe", "sigterm", + 0 +}; +char *syssigname[]={ + "exit", /* can't happen */ + "hangup", + "interrupt", + "quit", /* can't happen */ + "alarm", + "kill", + "sys: fp: ", + "term", + 0 +}; +char* +Rcmain(void) +{ + return unsharp("#9/rcmain"); +} + +char Fdprefix[]="/dev/fd/"; +long readnb(int, char *, long); +void execfinit(void); +void execbind(void); +void execmount(void); +void execulimit(void); +void execumask(void); +void execrfork(void); +builtin Builtin[]={ + "cd", execcd, + "whatis", execwhatis, + "eval", execeval, + "exec", execexec, /* but with popword first */ + "exit", execexit, + "shift", execshift, + "wait", execwait, + ".", execdot, + "finit", execfinit, + "flag", execflag, + "ulimit", execulimit, + "umask", execumask, + "rfork", execrfork, + 0 +}; + +void +execrfork(void) +{ + int arg; + char *s; + + switch(count(runq->argv->words)){ + case 1: + arg = RFENVG|RFNOTEG|RFNAMEG; + break; + case 2: + arg = 0; + for(s = runq->argv->words->next->word;*s;s++) switch(*s){ + default: + goto Usage; + case 'n': + arg|=RFNAMEG; break; + case 'N': + arg|=RFCNAMEG; + break; + case 'e': + /* arg|=RFENVG; */ break; + case 'E': + arg|=RFCENVG; break; + case 's': + arg|=RFNOTEG; break; + case 'f': + arg|=RFFDG; break; + case 'F': + arg|=RFCFDG; break; + } + break; + default: + Usage: + pfmt(err, "Usage: %s [nNeEsfF]\n", runq->argv->words->word); + setstatus("rfork usage"); + poplist(); + return; + } + if(rfork(arg)==-1){ + pfmt(err, "rc: %s failed\n", runq->argv->words->word); + setstatus("rfork failed"); + } + else + setstatus(""); + poplist(); +} + + + +#define SEP '\1' +char **environp; +struct word *enval(s) +register char *s; +{ + register char *t, c; + register struct word *v; + for(t=s;*t && *t!=SEP;t++); + c=*t; + *t='\0'; + v=newword(s, c=='\0'?(struct word *)0:enval(t+1)); + *t=c; + return v; +} +void Vinit(void){ + extern char **environ; + register char *s; + register char **env=environ; + environp=env; + for(;*env;env++){ + for(s=*env;*s && *s!='(' && *s!='=';s++); + switch(*s){ + case '\0': + /* pfmt(err, "rc: odd environment %q?\n", *env); */ + break; + case '=': + *s='\0'; + setvar(*env, enval(s+1)); + *s='='; + break; + case '(': /* ignore functions for now */ + break; + } + } +} +char **envp; +void Xrdfn(void){ + char *p; + register char *s; + register int len; + for(;*envp;envp++){ + s = *envp; + if(strncmp(s, "fn#", 3) == 0){ + p = strchr(s, '='); + if(p == nil) + continue; + *p = ' '; + s[2] = ' '; + len = strlen(s); + execcmds(opencore(s, len)); + s[len] = '\0'; + return; + } +#if 0 + for(s=*envp;*s && *s!='(' && *s!='=';s++); + switch(*s){ + case '\0': + pfmt(err, "environment %q?\n", *envp); + break; + case '=': /* ignore variables */ + break; + case '(': /* Bourne again */ + s=*envp+3; + envp++; + len=strlen(s); + s[len]='\n'; + execcmds(opencore(s, len+1)); + s[len]='\0'; + return; + } +#endif + } + Xreturn(); +} +union code rdfns[4]; +void execfinit(void){ + static int first=1; + if(first){ + rdfns[0].i=1; + rdfns[1].f=Xrdfn; + rdfns[2].f=Xjump; + rdfns[3].i=1; + first=0; + } + Xpopm(); + envp=environp; + start(rdfns, 1, runq->local); +} +extern int mapfd(int); +int Waitfor(int pid, int unused0){ + thread *p; + Waitmsg *w; + char errbuf[ERRMAX]; + + if(pid >= 0 && !havewaitpid(pid)) + return 0; + while((w = wait()) != nil){ + delwaitpid(w->pid); + if(w->pid==pid){ + if(strncmp(w->msg, "signal: ", 8) == 0) + fprint(mapfd(2), "%d: %s\n", w->pid, w->msg); + setstatus(w->msg); + free(w); + return 0; + } + if(runq->iflag && strncmp(w->msg, "signal: ", 8) == 0) + fprint(2, "%d: %s\n", w->pid, w->msg); + for(p=runq->ret;p;p=p->ret) + if(p->pid==w->pid){ + p->pid=-1; + strcpy(p->status, w->msg); + } + free(w); + } + + rerrstr(errbuf, sizeof errbuf); + if(strcmp(errbuf, "interrupted")==0) return -1; + return 0; +} +char **mkargv(word *a) +{ + char **argv=(char **)emalloc((count(a)+2)*sizeof(char *)); + char **argp=argv+1; /* leave one at front for runcoms */ + for(;a;a=a->next) *argp++=a->word; + *argp=0; + return argv; +} +/* +void addenv(var *v) +{ + char envname[256]; + word *w; + int f; + io *fd; + if(v->changed){ + v->changed=0; + snprint(envname, sizeof envname, "/env/%s", v->name); + if((f=Creat(envname))<0) + pfmt(err, "rc: can't open %s: %r\n", envname); + else{ + for(w=v->val;w;w=w->next) + write(f, w->word, strlen(w->word)+1L); + close(f); + } + } + if(v->fnchanged){ + v->fnchanged=0; + snprint(envname, sizeof envname, "/env/fn#%s", v->name); + if((f=Creat(envname))<0) + pfmt(err, "rc: can't open %s: %r\n", envname); + else{ + if(v->fn){ + fd=openfd(f); + pfmt(fd, "fn %s %s\n", v->name, v->fn[v->pc-1].s); + closeio(fd); + } + close(f); + } + } +} +void updenvlocal(var *v) +{ + if(v){ + updenvlocal(v->next); + addenv(v); + } +} +void Updenv(void){ + var *v, **h; + for(h=gvar;h!=&gvar[NVAR];h++) + for(v=*h;v;v=v->next) + addenv(v); + if(runq) updenvlocal(runq->local); +} +*/ +int +cmpenv(const void *a, const void *b) +{ + return strcmp(*(char**)a, *(char**)b); +} +char **mkenv(){ + register char **env, **ep, *p, *q; + register struct var **h, *v; + register struct word *a; + register int nvar=0, nchr=0, sep; + /* + * Slightly kludgy loops look at locals then globals + */ + for(h=gvar-1;h!=&gvar[NVAR];h++) for(v=h>=gvar?*h:runq->local;v;v=v->next){ + if((v==vlook(v->name)) && v->val){ + nvar++; + nchr+=strlen(v->name)+1; + for(a=v->val;a;a=a->next) + nchr+=strlen(a->word)+1; + } + if(v->fn){ + nvar++; + nchr+=strlen(v->name)+strlen(v->fn[v->pc-1].s)+8; + } + } + env=(char **)emalloc((nvar+1)*sizeof(char *)+nchr); + ep=env; + p=(char *)&env[nvar+1]; + for(h=gvar-1;h!=&gvar[NVAR];h++) for(v=h>=gvar?*h:runq->local;v;v=v->next){ + if((v==vlook(v->name)) && v->val){ + *ep++=p; + q=v->name; + while(*q) *p++=*q++; + sep='='; + for(a=v->val;a;a=a->next){ + *p++=sep; + sep=SEP; + q=a->word; + while(*q) *p++=*q++; + } + *p++='\0'; + } + if(v->fn){ + *ep++=p; +#if 0 + *p++='#'; *p++='('; *p++=')'; /* to fool Bourne */ + *p++='f'; *p++='n'; *p++=' '; + q=v->name; + while(*q) *p++=*q++; + *p++=' '; +#endif + *p++='f'; *p++='n'; *p++='#'; + q=v->name; + while(*q) *p++=*q++; + *p++='='; + q=v->fn[v->pc-1].s; + while(*q) *p++=*q++; + *p++='\n'; + *p++='\0'; + } + } + *ep=0; + qsort((char *)env, nvar, sizeof ep[0], cmpenv); + return env; +} +void Updenv(void){} +void Execute(word *args, word *path) +{ + char **argv=mkargv(args); + char **env=mkenv(); + char file[1024]; + int nc; + Updenv(); + for(;path;path=path->next){ + nc=strlen(path->word); + if(nc<1024){ + strcpy(file, path->word); + if(file[0]){ + strcat(file, "/"); + nc++; + } + if(nc+strlen(argv[1])<1024){ + strcat(file, argv[1]); + execve(file, argv+1, env); + } + else werrstr("command name too long"); + } + } + rerrstr(file, sizeof file); + pfmt(err, "%s: %s\n", argv[1], file); + efree((char *)argv); +} +#define NDIR 256 /* shoud be a better way */ +int Globsize(char *p) +{ + ulong isglob=0, globlen=NDIR+1; + for(;*p;p++){ + if(*p==GLOB){ + p++; + if(*p!=GLOB) isglob++; + globlen+=*p=='*'?NDIR:1; + } + else + globlen++; + } + return isglob?globlen:0; +} +#define NFD 50 +#define NDBUF 32 +struct{ + Dir *dbuf; + int i; + int n; +}dir[NFD]; +int Opendir(char *name) +{ + Dir *db; + int f; + f=open(name, 0); + if(f==-1) + return f; + db = dirfstat(f); + if(db!=nil && (db->mode&DMDIR)){ + if(f=NFD) + return 0; + if(dir[f].i==dir[f].n){ /* read */ + free(dir[f].dbuf); + dir[f].dbuf=0; + n=dirread(f, &dir[f].dbuf); + if(n>=0) + dir[f].n=n; + else + dir[f].n=0; + dir[f].i=0; + } + if(dir[f].i==dir[f].n) + return 0; + strcpy(p, dir[f].dbuf[dir[f].i].name); + dir[f].i++; + return 1; +} +void Closedir(int f){ + if(f>=0 && f=32){ /* rc is probably in a trap loop */ + pfmt(err, "rc: Too many traps (trap %s), aborting\n", s); + abort(); + } + noted(NCONT); +} +void Trapinit(void){ + notify(notifyf); +} +void Unlink(char *name) +{ + remove(name); +} +long Write(int fd, char *buf, long cnt) +{ + return write(fd, buf, (long)cnt); +} +long Read(int fd, char *buf, long cnt) +{ + int i; + + i = readnb(fd, buf, cnt); + if(ntrap) dotrap(); + return i; +} +long Seek(int fd, long cnt, long whence) +{ + return seek(fd, cnt, whence); +} +int Executable(char *file) +{ + Dir *statbuf; + int ret; + + statbuf = dirstat(file); + if(statbuf == nil) return 0; + ret = ((statbuf->mode&0111)!=0 && (statbuf->mode&DMDIR)==0); + free(statbuf); + return ret; +} +int Creat(char *file) +{ + return create(file, 1, 0666L); +} +int Dup(int a, int b){ + return dup(a, b); +} +int Dup1(int a){ + return dup(a, -1); +} +void Exit(char *stat) +{ + Updenv(); + setstatus(stat); + exits(truestatus()?"":getstatus()); +} +int Eintr(void){ + return interrupted; +} +void Noerror(void){ + interrupted=0; +} +int +Isatty(int fd){ + return isatty(fd); +} +void Abort(void){ + pfmt(err, "aborting\n"); + flush(err); + Exit("aborting"); +} +void Memcpy(char *a, char *b, long n) +{ + memmove(a, b, (long)n); +} +void *Malloc(ulong n){ + return malloc(n); +} + +int +exitcode(char *msg) +{ + int n; + + n = atoi(msg); + if(n == 0) + n = 1; + return n; +} + +int *waitpids; +int nwaitpids; + +void +addwaitpid(int pid) +{ + waitpids = realloc(waitpids, (nwaitpids+1)*sizeof waitpids[0]); + if(waitpids == 0) + panic("Can't realloc %d waitpids", nwaitpids+1); + waitpids[nwaitpids++] = pid; +} + +void +delwaitpid(int pid) +{ + int r, w; + + for(r=w=0; r +#include +#undef NSIG +#undef SIGINT +#undef SIGQUIT +#define NSIG 32 +#define SIGINT 2 +#define SIGQUIT 3 +#endif +#ifdef V9 +#include +#include +#endif +#ifdef Sun +#include +#endif +#define YYMAXDEPTH 500 +#ifndef PAREN +#ifndef YYMAJOR +#include "x.tab.h" +#endif +#endif + +#undef pipe /* so that /dev/fd works */ +#define searchpath rcsearchpath /* avoid new libc function */ + +/* some systems define a global "var", "thread" */ +#undef var +#define var rcvar +#undef thread +#define thread rcthread + +typedef struct tree tree; +typedef struct word word; +typedef struct io io; +typedef union code code; +typedef struct var var; +typedef struct list list; +typedef struct redir redir; +typedef struct thread thread; +typedef struct builtin builtin; + +struct tree{ + int type; + int rtype, fd0, fd1; /* details of REDIR PIPE DUP tokens */ + char *str; + int quoted; + int iskw; + tree *child[3]; + tree *next; +}; +tree *newtree(void); +tree *token(char*, int), *klook(char*), *tree1(int, tree*); +tree *tree2(int, tree*, tree*), *tree3(int, tree*, tree*, tree*); +tree *mung1(tree*, tree*), *mung2(tree*, tree*, tree*); +tree *mung3(tree*, tree*, tree*, tree*), *epimung(tree*, tree*); +tree *simplemung(tree*), *heredoc(tree*); +void freetree(tree*); +tree *cmdtree; +/* + * The first word of any code vector is a reference count. + * Always create a new reference to a code vector by calling codecopy(.). + * Always call codefree(.) when deleting a reference. + */ +union code{ + void (*f)(void); + int i; + char *s; +}; +char *promptstr; +#define NTOK 8192 +char tok[NTOK]; +#define APPEND 1 +#define WRITE 2 +#define READ 3 +#define HERE 4 +#define DUPFD 5 +#define CLOSE 6 +#define RDWR 7 +struct var{ + char *name; /* ascii name */ + word *val; /* value */ + int changed; + code *fn; /* pointer to function's code vector */ + int fnchanged; + int pc; /* pc of start of function */ + var *next; /* next on hash or local list */ + void (*changefn)(var*); +}; +var *vlook(char*), *gvlook(char*), *newvar(char*, var*); +#define NVAR 521 +var *gvar[NVAR]; /* hash for globals */ +#define new(type) ((type *)emalloc(sizeof(type))) +char *emalloc(long); +void *Malloc(ulong); +void efree(char*); +#define NOFILE 128 /* should come from */ +struct here{ + tree *tag; + char *name; + struct here *next; +}; +int mypid; +/* + * Glob character escape in strings: + * In a string, GLOB must be followed by *?[ or GLOB. + * GLOB* matches any string + * GLOB? matches any single character + * GLOB[...] matches anything in the brackets + * GLOBGLOB matches GLOB + */ +#define GLOB ((char)0x01) +/* + * onebyte(c), twobyte(c), threebyte(c) + * Is c the first character of a one- two- or three-byte utf sequence? + */ +#define onebyte(c) ((c&0x80)==0x00) +#define twobyte(c) ((c&0xe0)==0xc0) +#define threebyte(c) ((c&0xf0)==0xe0) +#define fourbyte(c) ((c&0xf8)==0xf0) + +char **argp; +char **args; +int nerror; /* number of errors encountered during compilation */ +extern int doprompt; /* is it time for a prompt? */ +/* + * Which fds are the reading/writing end of a pipe? + * Unfortunately, this can vary from system to system. + * 9th edition Unix doesn't care, the following defines + * work on plan 9. + */ +#define PRD 0 +#define PWR 1 +extern char *Rcmain(), Fdprefix[]; +#define register +/* + * How many dot commands have we executed? + * Used to ensure that -v flag doesn't print rcmain. + */ +int ndot; +char *getstatus(void); +int lastc; +int lastword; +int kidpid; diff --git a/cmd/rc/rc.sh.build b/cmd/rc/rc.sh.build new file mode 100755 index 0000000..89c340f --- /dev/null +++ b/cmd/rc/rc.sh.build @@ -0,0 +1,80 @@ +#!/bin/sh + +mkdir -p $JEHANNE/hacking/bin/ +TARGET=rc #$JEHANNE/hacking/bin/rc + +git clean -xdf . + +9yacc -d syn.y +cp y.tab.h x.tab.h + +cc -c \ + -DPLAN9PORT \ + -I$JEHANNE/hacking/src/trampoline/include \ + -O2 \ + -c \ + -Wall \ + -Wno-parentheses \ + -Wno-missing-braces \ + -Wno-switch \ + -Wno-comment \ + -Wno-sign-compare \ + -Wno-unknown-pragmas \ + -Wno-misleading-indentation \ + -Wno-stringop-truncation \ + -Wno-stringop-overflow \ + -Wno-format-truncation \ + -fno-omit-frame-pointer \ + -fsigned-char \ + -fcommon \ + -ggdb \ + code.c \ + exec.c \ + getflags.c \ + glob.c \ + havefork.c \ + here.c \ + io.c \ + plan9ish.c \ + unixcrap.c \ + lex.c \ + parse.c \ + pcmd.c \ + pfnc.c \ + simple.c \ + subr.c \ + trap.c \ + tree.c \ + var.c \ + y.tab.c + + + +gcc -pie -o $TARGET \ + code.o \ + exec.o \ + getflags.o \ + glob.o \ + here.o \ + io.o \ + lex.o \ + parse.o \ + pcmd.o \ + pfnc.o \ + simple.o \ + subr.o \ + trap.o \ + tree.o \ + unixcrap.o \ + var.o \ + y.tab.o \ + plan9ish.o \ + havefork.o \ + -L$JEHANNE/hacking/lib -lutil -lresolv -lpthread -l9 -lm \ + -lutil \ + -lresolv \ + -lpthread \ + -lc + + +#git clean -xdf . diff --git a/cmd/rc/simple.c b/cmd/rc/simple.c new file mode 100644 index 0000000..a7d78f6 --- /dev/null +++ b/cmd/rc/simple.c @@ -0,0 +1,505 @@ +/* + * Maybe `simple' is a misnomer. + */ +#include "rc.h" +#include "getflags.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +/* + * Search through the following code to see if we're just going to exit. + */ +int +exitnext(void){ + union code *c=&runq->code[runq->pc]; + while(c->f==Xpopredir) c++; + return c->f==Xexit; +} + +void +Xsimple(void) +{ + word *a; + thread *p = runq; + var *v; + struct builtin *bp; + int pid; + globlist(); + a = runq->argv->words; + if(a==0){ + Xerror1("empty argument list"); + return; + } + if(flag['x']) + pfmt(err, "%v\n", p->argv->words); /* wrong, should do redirs */ + v = gvlook(a->word); + if(v->fn) + execfunc(v); + else{ + if(strcmp(a->word, "builtin")==0){ + if(count(a)==1){ + pfmt(err, "builtin: empty argument list\n"); + setstatus("empty arg list"); + poplist(); + return; + } + a = a->next; + popword(); + } + for(bp = Builtin;bp->name;bp++) + if(strcmp(a->word, bp->name)==0){ + (*bp->fnc)(); + return; + } + if(exitnext()){ + /* fork and wait is redundant */ + pushword("exec"); + execexec(); + Xexit(); + } + else{ + flush(err); + Updenv(); /* necessary so changes don't go out again */ + if((pid = execforkexec()) < 0){ + Xerror("try again"); + return; + } + + /* interrupts don't get us out */ + poplist(); + while(Waitfor(pid, 1) < 0) + ; + } + } +} +struct word nullpath = { "", 0}; + +void +doredir(redir *rp) +{ + if(rp){ + doredir(rp->next); + switch(rp->type){ + case ROPEN: + if(rp->from!=rp->to){ + Dup(rp->from, rp->to); + close(rp->from); + } + break; + case RDUP: + Dup(rp->from, rp->to); + break; + case RCLOSE: + close(rp->from); + break; + } + } +} + +word* +searchpath(char *w) +{ + word *path; + if(strncmp(w, "/", 1)==0 +/* || strncmp(w, "#", 1)==0 */ + || strncmp(w, "./", 2)==0 + || strncmp(w, "../", 3)==0 + || (path = vlook("path")->val)==0) + path=&nullpath; + return path; +} + +void +execexec(void) +{ + popword(); /* "exec" */ + if(runq->argv->words==0){ + Xerror1("empty argument list"); + return; + } + doredir(runq->redir); + Execute(runq->argv->words, searchpath(runq->argv->words->word)); + poplist(); +} + +void +execfunc(var *func) +{ + word *starval; + popword(); + starval = runq->argv->words; + runq->argv->words = 0; + poplist(); + start(func->fn, func->pc, runq->local); + runq->local = newvar(strdup("*"), runq->local); + runq->local->val = starval; + runq->local->changed = 1; +} + +int +dochdir(char *word) +{ + /* report to /dev/wdir if it exists and we're interactive */ + static int wdirfd = -2; + if(chdir(word)<0) return -1; + if(flag['i']!=0){ + if(wdirfd==-2) /* try only once */ + wdirfd = open("/dev/wdir", OWRITE|OCEXEC); + if(wdirfd>=0) + write(wdirfd, word, strlen(word)); + } + return 1; +} + +void +execcd(void) +{ + word *a = runq->argv->words; + word *cdpath; + char dir[512]; + setstatus("can't cd"); + cdpath = vlook("cdpath")->val; + switch(count(a)){ + default: + pfmt(err, "Usage: cd [directory]\n"); + break; + case 2: + if(a->next->word[0]=='/' || cdpath==0) + cdpath=&nullpath; + for(;cdpath;cdpath = cdpath->next){ + strcpy(dir, cdpath->word); + if(dir[0]) + strcat(dir, "/"); + strcat(dir, a->next->word); + if(dochdir(dir)>=0){ + if(strlen(cdpath->word) + && strcmp(cdpath->word, ".")!=0) + pfmt(err, "%s\n", dir); + setstatus(""); + break; + } + } + if(cdpath==0) + pfmt(err, "Can't cd %s: %r\n", a->next->word); + break; + case 1: + a = vlook("home")->val; + if(count(a)>=1){ + if(dochdir(a->word)>=0) + setstatus(""); + else + pfmt(err, "Can't cd %s: %r\n", a->word); + } + else + pfmt(err, "Can't cd -- $home empty\n"); + break; + } + poplist(); +} + +void +execexit(void) +{ + switch(count(runq->argv->words)){ + default: + pfmt(err, "Usage: exit [status]\nExiting anyway\n"); + case 2: + setstatus(runq->argv->words->next->word); + case 1: Xexit(); + } +} + +void +execshift(void) +{ + int n; + word *a; + var *star; + switch(count(runq->argv->words)){ + default: + pfmt(err, "Usage: shift [n]\n"); + setstatus("shift usage"); + poplist(); + return; + case 2: + n = atoi(runq->argv->words->next->word); + break; + case 1: + n = 1; + break; + } + star = vlook("*"); + for(;n && star->val;--n){ + a = star->val->next; + efree(star->val->word); + efree((char *)star->val); + star->val = a; + star->changed = 1; + } + setstatus(""); + poplist(); +} + +int +octal(char *s) +{ + int n = 0; + while(*s==' ' || *s=='\t' || *s=='\n') s++; + while('0'<=*s && *s<='7') n = n*8+*s++-'0'; + return n; +} + +int +mapfd(int fd) +{ + redir *rp; + for(rp = runq->redir;rp;rp = rp->next){ + switch(rp->type){ + case RCLOSE: + if(rp->from==fd) + fd=-1; + break; + case RDUP: + case ROPEN: + if(rp->to==fd) + fd = rp->from; + break; + } + } + return fd; +} +union code rdcmds[4]; + +void +execcmds(io *f) +{ + static int first = 1; + if(first){ + rdcmds[0].i = 1; + rdcmds[1].f = Xrdcmds; + rdcmds[2].f = Xreturn; + first = 0; + } + start(rdcmds, 1, runq->local); + runq->cmdfd = f; + runq->iflast = 0; +} + +void +execeval(void) +{ + char *cmdline, *s, *t; + int len = 0; + word *ap; + if(count(runq->argv->words)<=1){ + Xerror1("Usage: eval cmd ..."); + return; + } + eflagok = 1; + for(ap = runq->argv->words->next;ap;ap = ap->next) + len+=1+strlen(ap->word); + cmdline = emalloc(len); + s = cmdline; + for(ap = runq->argv->words->next;ap;ap = ap->next){ + for(t = ap->word;*t;) *s++=*t++; + *s++=' '; + } + s[-1]='\n'; + poplist(); + execcmds(opencore(cmdline, len)); + efree(cmdline); +} +union code dotcmds[14]; + +void +execdot(void) +{ + int iflag = 0; + int fd; + list *av; + thread *p = runq; + char *zero; + static int first = 1; + char file[512]; + word *path; + + if(first){ + dotcmds[0].i = 1; + dotcmds[1].f = Xmark; + dotcmds[2].f = Xword; + dotcmds[3].s="0"; + dotcmds[4].f = Xlocal; + dotcmds[5].f = Xmark; + dotcmds[6].f = Xword; + dotcmds[7].s="*"; + dotcmds[8].f = Xlocal; + dotcmds[9].f = Xrdcmds; + dotcmds[10].f = Xunlocal; + dotcmds[11].f = Xunlocal; + dotcmds[12].f = Xreturn; + first = 0; + } + else + eflagok = 1; + popword(); + if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){ + iflag = 1; + popword(); + } + /* get input file */ + if(p->argv->words==0){ + Xerror1("Usage: . [-i] file [arg ...]"); + return; + } + zero = strdup(p->argv->words->word); + popword(); + fd=-1; + for(path = searchpath(zero);path;path = path->next){ + strcpy(file, path->word); + if(file[0]) + strcat(file, "/"); + strcat(file, zero); + if((fd = open(file, 0))>=0) break; + if(strcmp(file, "/dev/stdin")==0){ /* for sun & ucb */ + fd = Dup1(0); + if(fd>=0) + break; + } + } + if(fd<0){ + pfmt(err, "%s: ", zero); + setstatus("can't open"); + Xerror(".: can't open"); + return; + } + /* set up for a new command loop */ + start(dotcmds, 1, (struct var *)0); + pushredir(RCLOSE, fd, 0); + runq->cmdfile = zero; + runq->cmdfd = openfd(fd); + runq->iflag = iflag; + runq->iflast = 0; + /* push $* value */ + pushlist(); + runq->argv->words = p->argv->words; + /* free caller's copy of $* */ + av = p->argv; + p->argv = av->next; + efree((char *)av); + /* push $0 value */ + pushlist(); + pushword(zero); + ndot++; +} + +void +execflag(void) +{ + char *letter, *val; + switch(count(runq->argv->words)){ + case 2: + setstatus(flag[(uchar)runq->argv->words->next->word[0]]?"":"flag not set"); + break; + case 3: + letter = runq->argv->words->next->word; + val = runq->argv->words->next->next->word; + if(strlen(letter)==1){ + if(strcmp(val, "+")==0){ + flag[(uchar)letter[0]] = flagset; + break; + } + if(strcmp(val, "-")==0){ + flag[(uchar)letter[0]] = 0; + break; + } + } + default: + Xerror1("Usage: flag [letter] [+-]"); + return; + } + poplist(); +} + +void +execwhatis(void){ /* mildly wrong -- should fork before writing */ + word *a, *b, *path; + var *v; + struct builtin *bp; + char file[512]; + struct io out[1]; + int found, sep; + a = runq->argv->words->next; + if(a==0){ + Xerror1("Usage: whatis name ..."); + return; + } + setstatus(""); + out->fd = mapfd(1); + out->bufp = out->buf; + out->ebuf = &out->buf[NBUF]; + out->strp = 0; + for(;a;a = a->next){ + v = vlook(a->word); + if(v->val){ + pfmt(out, "%s=", a->word); + if(v->val->next==0) + pfmt(out, "%q\n", v->val->word); + else{ + sep='('; + for(b = v->val;b && b->word;b = b->next){ + pfmt(out, "%c%q", sep, b->word); + sep=' '; + } + pfmt(out, ")\n"); + } + found = 1; + } + else + found = 0; + v = gvlook(a->word); + if(v->fn) + pfmt(out, "fn %s %s\n", v->name, v->fn[v->pc-1].s); + else{ + for(bp = Builtin;bp->name;bp++) + if(strcmp(a->word, bp->name)==0){ + pfmt(out, "builtin %s\n", a->word); + break; + } + if(!bp->name){ + for(path = searchpath(a->word);path;path = path->next){ + strcpy(file, path->word); + if(file[0]) + strcat(file, "/"); + strcat(file, a->word); + if(Executable(file)){ + pfmt(out, "%s\n", file); + break; + } + } + if(!path && !found){ + pfmt(err, "%s: not found\n", a->word); + setstatus("not found"); + } + } + } + } + poplist(); + flush(err); +} + +void +execwait(void) +{ + switch(count(runq->argv->words)){ + default: + Xerror1("Usage: wait [pid]"); + return; + case 2: + Waitfor(atoi(runq->argv->words->next->word), 0); + break; + case 1: + Waitfor(-1, 0); + break; + } + poplist(); +} diff --git a/cmd/rc/subr.c b/cmd/rc/subr.c new file mode 100644 index 0000000..47f7439 --- /dev/null +++ b/cmd/rc/subr.c @@ -0,0 +1,77 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" + +char* +emalloc(long n) +{ + char *p = (char *)Malloc(n); + if(p==0) + panic("Can't malloc %d bytes", n); +/* if(err){ pfmt(err, "malloc %d->%p\n", n, p); flush(err); } /**/ + memset(p, 0, n); + return p; +} + +void +efree(char *p) +{ +/* pfmt(err, "free %p\n", p); flush(err); /**/ + if(p) + free(p); + else pfmt(err, "free 0\n"); +} +extern int lastword, lastdol; + +void +yyerror(char *m) +{ + pfmt(err, "rc: "); + if(runq->cmdfile && !runq->iflag) + pfmt(err, "%s:%d: ", runq->cmdfile, runq->lineno); + else if(runq->cmdfile) + pfmt(err, "%s: ", runq->cmdfile); + else if(!runq->iflag) + pfmt(err, "line %d: ", runq->lineno); + if(tok[0] && tok[0]!='\n') + pfmt(err, "token %q: ", tok); + pfmt(err, "%s\n", m); + flush(err); + lastword = 0; + lastdol = 0; + while(lastc!='\n' && lastc!=EOF) advance(); + nerror++; + setvar("status", newword(m, (word *)0)); +} +char *bp; + +static void +iacvt(int n) +{ + if(n<0){ + *bp++='-'; + n=-n; /* doesn't work for n==-inf */ + } + if(n/10) + iacvt(n/10); + *bp++=n%10+'0'; +} + +void +inttoascii(char *s, long n) +{ + bp = s; + iacvt(n); + *bp='\0'; +} + +void +panic(char *s, int n) +{ + pfmt(err, "rc: "); + pfmt(err, s, n); + pchr(err, '\n'); + flush(err); + Abort(); +} diff --git a/cmd/rc/syn.y b/cmd/rc/syn.y new file mode 100644 index 0000000..5c98ef8 --- /dev/null +++ b/cmd/rc/syn.y @@ -0,0 +1,91 @@ +%term FOR IN WHILE IF NOT TWIDDLE BANG SUBSHELL SWITCH FN +%term WORD REDIR REDIRW DUP PIPE SUB +%term SIMPLE ARGLIST WORDS BRACE PAREN PCMD PIPEFD /* not used in syntax */ +/* operator priorities -- lowest first */ +%left IF WHILE FOR SWITCH ')' NOT +%left ANDAND OROR +%left BANG SUBSHELL +%left PIPE +%left '^' +%right '$' COUNT '"' +%left SUB +%{ +#include "rc.h" +#include "fns.h" +%} +%union{ + struct tree *tree; +}; +%type line paren brace body cmdsa cmdsan assign epilog redir +%type cmd simple first word comword keyword words +%type NOT FOR IN WHILE IF TWIDDLE BANG SUBSHELL SWITCH FN +%type WORD REDIR REDIRW DUP PIPE +%% +rc: { return 1;} +| line '\n' {return !compile($1);} +line: cmd +| cmdsa line {$$=tree2(';', $1, $2);} +body: cmd +| cmdsan body {$$=tree2(';', $1, $2);} +cmdsa: cmd ';' +| cmd '&' {$$=tree1('&', $1);} +cmdsan: cmdsa +| cmd '\n' +brace: '{' body '}' {$$=tree1(BRACE, $2);} +paren: '(' body ')' {$$=tree1(PCMD, $2);} +assign: first '=' word {$$=tree2('=', $1, $3);} +epilog: {$$=0;} +| redir epilog {$$=mung2($1, $1->child[0], $2);} +redir: REDIR word {$$=mung1($1, $1->rtype==HERE?heredoc($2):$2);} +| DUP +cmd: {$$=0;} +| brace epilog {$$=epimung($1, $2);} +| IF paren {skipnl();} cmd + {$$=mung2($1, $2, $4);} +| IF NOT {skipnl();} cmd {$$=mung1($2, $4);} +| FOR '(' word IN words ')' {skipnl();} cmd + /* + * if ``words'' is nil, we need a tree element to distinguish between + * for(i in ) and for(i), the former being a loop over the empty set + * and the latter being the implicit argument loop. so if $5 is nil + * (the empty set), we represent it as "()". don't parenthesize non-nil + * functions, to avoid growing parentheses every time we reread the + * definition. + */ + {$$=mung3($1, $3, $5 ? $5 : tree1(PAREN, $5), $8);} +| FOR '(' word ')' {skipnl();} cmd + {$$=mung3($1, $3, (struct tree *)0, $6);} +| WHILE paren {skipnl();} cmd + {$$=mung2($1, $2, $4);} +| SWITCH word {skipnl();} brace + {$$=tree2(SWITCH, $2, $4);} +| simple {$$=simplemung($1);} +| TWIDDLE word words {$$=mung2($1, $2, $3);} +| cmd ANDAND cmd {$$=tree2(ANDAND, $1, $3);} +| cmd OROR cmd {$$=tree2(OROR, $1, $3);} +| cmd PIPE cmd {$$=mung2($2, $1, $3);} +| redir cmd %prec BANG {$$=mung2($1, $1->child[0], $2);} +| assign cmd %prec BANG {$$=mung3($1, $1->child[0], $1->child[1], $2);} +| BANG cmd {$$=mung1($1, $2);} +| SUBSHELL cmd {$$=mung1($1, $2);} +| FN words brace {$$=tree2(FN, $2, $3);} +| FN words {$$=tree1(FN, $2);} +simple: first +| simple word {$$=tree2(ARGLIST, $1, $2);} +| simple redir {$$=tree2(ARGLIST, $1, $2);} +first: comword +| first '^' word {$$=tree2('^', $1, $3);} +word: keyword {lastword=1; $1->type=WORD;} +| comword +| word '^' word {$$=tree2('^', $1, $3);} +comword: '$' word {$$=tree1('$', $2);} +| '$' word SUB words ')' {$$=tree2(SUB, $2, $4);} +| '"' word {$$=tree1('"', $2);} +| COUNT word {$$=tree1(COUNT, $2);} +| WORD +| '`' brace {$$=tree1('`', $2);} +| '(' words ')' {$$=tree1(PAREN, $2);} +| REDIRW brace {$$=mung1($1, $2); $$->type=PIPEFD;} +keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN +words: {$$=(struct tree*)0;} +| words word {$$=tree2(WORDS, $1, $2);} diff --git a/cmd/rc/trap.c b/cmd/rc/trap.c new file mode 100644 index 0000000..a572cac --- /dev/null +++ b/cmd/rc/trap.c @@ -0,0 +1,37 @@ +#include "rc.h" +#include "exec.h" +#include "fns.h" +#include "io.h" +extern char *Signame[]; + +void +dotrap(void) +{ + int i; + struct var *trapreq; + struct word *starval; + starval = vlook("*")->val; + while(ntrap) for(i = 0;i!=NSIG;i++) while(trap[i]){ + --trap[i]; + --ntrap; + if(getpid()!=mypid) Exit(getstatus()); + trapreq = vlook(Signame[i]); + if(trapreq->fn){ + start(trapreq->fn, trapreq->pc, (struct var *)0); + runq->local = newvar(strdup("*"), runq->local); + runq->local->val = copywords(starval, (struct word *)0); + runq->local->changed = 1; + runq->redir = runq->startredir = 0; + } + else if(i==SIGINT || i==SIGQUIT){ + /* + * run the stack down until we uncover the + * command reading loop. Xreturn will exit + * if there is none (i.e. if this is not + * an interactive rc.) + */ + while(!runq->iflag) Xreturn(); + } + else Exit(getstatus()); + } +} diff --git a/cmd/rc/tree.c b/cmd/rc/tree.c new file mode 100644 index 0000000..2c3ff6b --- /dev/null +++ b/cmd/rc/tree.c @@ -0,0 +1,146 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +tree *treenodes; +/* + * create and clear a new tree node, and add it + * to the node list. + */ + +tree* +newtree(void) +{ + tree *t = new(tree); + t->iskw = 0; + t->str = 0; + t->child[0] = t->child[1] = t->child[2] = 0; + t->next = treenodes; + treenodes = t; + return t; +} + +void +freenodes(void) +{ + tree *t, *u; + for(t = treenodes;t;t = u){ + u = t->next; + if(t->str) + efree(t->str); + efree((char *)t); + } + treenodes = 0; +} + +tree* +tree1(int type, tree *c0) +{ + return tree3(type, c0, (tree *)0, (tree *)0); +} + +tree* +tree2(int type, tree *c0, tree *c1) +{ + return tree3(type, c0, c1, (tree *)0); +} + +tree* +tree3(int type, tree *c0, tree *c1, tree *c2) +{ + tree *t; + if(type==';'){ + if(c0==0) + return c1; + if(c1==0) + return c0; + } + t = newtree(); + t->type = type; + t->child[0] = c0; + t->child[1] = c1; + t->child[2] = c2; + return t; +} + +tree* +mung1(tree *t, tree *c0) +{ + t->child[0] = c0; + return t; +} + +tree* +mung2(tree *t, tree *c0, tree *c1) +{ + t->child[0] = c0; + t->child[1] = c1; + return t; +} + +tree* +mung3(tree *t, tree *c0, tree *c1, tree *c2) +{ + t->child[0] = c0; + t->child[1] = c1; + t->child[2] = c2; + return t; +} + +tree* +epimung(tree *comp, tree *epi) +{ + tree *p; + if(epi==0) + return comp; + for(p = epi;p->child[1];p = p->child[1]); + p->child[1] = comp; + return epi; +} +/* + * Add a SIMPLE node at the root of t and percolate all the redirections + * up to the root. + */ + +tree* +simplemung(tree *t) +{ + tree *u; + struct io *s; + t = tree1(SIMPLE, t); + s = openstr(); + pfmt(s, "%t", t); + t->str = strdup(s->strp); + closeio(s); + for(u = t->child[0];u->type==ARGLIST;u = u->child[0]){ + if(u->child[1]->type==DUP + || u->child[1]->type==REDIR){ + u->child[1]->child[1] = t; + t = u->child[1]; + u->child[1] = 0; + } + } + return t; +} + +tree* +token(char *str, int type) +{ + tree *t = newtree(); + t->type = type; + t->str = strdup(str); + return t; +} + +void +freetree(tree *p) +{ + if(p==0) + return; + freetree(p->child[0]); + freetree(p->child[1]); + freetree(p->child[2]); + if(p->str) + efree(p->str); + efree((char *)p); +} diff --git a/cmd/rc/unixcrap.c b/cmd/rc/unixcrap.c new file mode 100644 index 0000000..0b91563 --- /dev/null +++ b/cmd/rc/unixcrap.c @@ -0,0 +1,242 @@ +#include +#include +#include +#include +#include +#include +#include +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +#include "getflags.h" + +extern char **mkargv(word*); +extern int mapfd(int); + +static char *eargs = "cdflmnstuv"; +static int rlx[] = { + RLIMIT_CORE, + RLIMIT_DATA, + RLIMIT_FSIZE, +#ifdef RLIMIT_MEMLOCK + RLIMIT_MEMLOCK, +#else + 0, +#endif +#ifdef RLIMIT_RSS + RLIMIT_RSS, +#else + 0, +#endif + RLIMIT_NOFILE, + RLIMIT_STACK, + RLIMIT_CPU, +#ifdef RLIMIT_NPROC + RLIMIT_NPROC, +#else + 0, +#endif +#ifdef RLIMIT_RSS + RLIMIT_RSS, +#else + 0, +#endif +}; + +static void +eusage(void) +{ + fprint(mapfd(2), "usage: ulimit [-SHa%s [limit]]\n", eargs); +} + +#define Notset -4 +#define Unlimited -3 +#define Hard -2 +#define Soft -1 + +void +execulimit(void) +{ + rlim_t n; + int fd, argc, sethard, setsoft, limit; + int flag[256]; + char **argv, **oargv, *p; + char *argv0; + struct rlimit rl; + + argv0 = nil; + setstatus(""); + oargv = mkargv(runq->argv->words); + argv = oargv+1; + for(argc=0; argv[argc]; argc++) + ; + + memset(flag, 0, sizeof flag); + ARGBEGIN{ + default: + if(strchr(eargs, ARGC()) == nil){ + eusage(); + return; + } + case 'S': + case 'H': + case 'a': + flag[ARGC()] = 1; + break; + }ARGEND + + if(argc > 1){ + eusage(); + goto out; + } + + fd = mapfd(1); + + sethard = 1; + setsoft = 1; + if(flag['S'] && flag['H']) + ; + else if(flag['S']) + sethard = 0; + else if(flag['H']) + setsoft = 0; + + limit = Notset; + if(argc>0){ + if(strcmp(argv[0], "unlimited") == 0) + limit = Unlimited; + else if(strcmp(argv[0], "hard") == 0) + limit = Hard; + else if(strcmp(argv[0], "soft") == 0) + limit = Soft; + else if((limit = strtol(argv[0], &p, 0)) < 0 || *p != 0){ + eusage(); + goto out; + } + } + if(flag['a']){ + for(p=eargs; *p; p++){ + getrlimit(rlx[p-eargs], &rl); + n = flag['H'] ? rl.rlim_max : rl.rlim_cur; + if(n == RLIM_INFINITY) + fprint(fd, "ulimit -%c unlimited\n", *p); + else + fprint(fd, "ulimit -%c %llud\n", *p, (uvlong)n); + } + goto out; + } + for(p=eargs; *p; p++){ + if(flag[(uchar)*p]){ + n = 0; + getrlimit(rlx[p-eargs], &rl); + switch(limit){ + case Notset: + n = flag['H'] ? rl.rlim_max : rl.rlim_cur; + if(n == RLIM_INFINITY) + fprint(fd, "ulimit -%c unlimited\n", *p); + else + fprint(fd, "ulimit -%c %llud\n", *p, (uvlong)n); + break; + case Hard: + n = rl.rlim_max; + goto set; + case Soft: + n = rl.rlim_cur; + goto set; + case Unlimited: + n = RLIM_INFINITY; + goto set; + default: + n = limit; + set: + if(setsoft) + rl.rlim_cur = n; + if(sethard) + rl.rlim_max = n; + if(setrlimit(rlx[p-eargs], &rl) < 0) + fprint(mapfd(2), "setrlimit: %r\n"); + } + } + } + +out: + free(oargv); + poplist(); + flush(err); +} + +void +execumask(void) +{ + int n, argc; + char **argv, **oargv, *p; + char *argv0; + + argv0 = nil; + setstatus(""); + oargv = mkargv(runq->argv->words); + argv = oargv+1; + for(argc=0; argv[argc]; argc++) + ; + + ARGBEGIN{ + default: + usage: + fprint(mapfd(2), "usage: umask [mode]\n"); + goto out; + }ARGEND + + if(argc > 1) + goto usage; + + if(argc == 1){ + n = strtol(argv[0], &p, 8); + if(*p != 0 || p == argv[0]) + goto usage; + umask(n); + goto out; + } + + n = umask(0); + umask(n); + if(n < 0){ + fprint(mapfd(2), "umask: %r\n"); + goto out; + } + + fprint(mapfd(1), "umask %03o\n", n); + +out: + free(oargv); + poplist(); + flush(err); +} + +/* + * Cope with non-blocking read. + */ +long +readnb(int fd, char *buf, long cnt) +{ + int n, didreset; + int flgs; + + didreset = 0; +again: + n = read(fd, buf, cnt); + if(n == -1) + if(errno == EAGAIN){ + if(!didreset){ + if((flgs = fcntl(fd, F_GETFL, 0)) == -1) + return -1; + flgs &= ~O_NONBLOCK; + if(fcntl(fd, F_SETFL, flgs) == -1) + return -1; + didreset = 1; + } + goto again; + } + + return n; +} diff --git a/cmd/rc/var.c b/cmd/rc/var.c new file mode 100644 index 0000000..2564ba2 --- /dev/null +++ b/cmd/rc/var.c @@ -0,0 +1,173 @@ +#include "rc.h" +#include "exec.h" +#include "fns.h" + +int +hash(char *s, int n) +{ + int h = 0, i = 1; + while(*s) h+=*s++*i++; + h%=n; + return h<0?h+n:h; +} +#define NKW 30 +struct kw{ + char *name; + int type; + struct kw *next; +}*kw[NKW]; + +void +kenter(int type, char *name) +{ + int h = hash(name, NKW); + struct kw *p = new(struct kw); + p->type = type; + p->name = name; + p->next = kw[h]; + kw[h] = p; +} + +void +kinit(void) +{ + kenter(FOR, "for"); + kenter(IN, "in"); + kenter(WHILE, "while"); + kenter(IF, "if"); + kenter(NOT, "not"); + kenter(TWIDDLE, "~"); + kenter(BANG, "!"); + kenter(SUBSHELL, "@"); + kenter(SWITCH, "switch"); + kenter(FN, "fn"); +} + +tree* +klook(char *name) +{ + struct kw *p; + tree *t = token(name, WORD); + for(p = kw[hash(name, NKW)];p;p = p->next) + if(strcmp(p->name, name)==0){ + t->type = p->type; + t->iskw = 1; + break; + } + return t; +} + +var* +gvlook(char *name) +{ + int h = hash(name, NVAR); + var *v; + for(v = gvar[h];v;v = v->next) if(strcmp(v->name, name)==0) return v; + return gvar[h] = newvar(strdup(name), gvar[h]); +} + +var* +vlook(char *name) +{ + var *v; + if(runq) + for(v = runq->local;v;v = v->next) + if(strcmp(v->name, name)==0) return v; + return gvlook(name); +} + +void +_setvar(char *name, word *val, int callfn) +{ + struct var *v = vlook(name); + freewords(v->val); + v->val=val; + v->changed=1; + if(callfn && v->changefn) + v->changefn(v); +} + +void +setvar(char *name, word *val) +{ + _setvar(name, val, 1); +} + +void +bigpath(var *v) +{ + /* convert $PATH to $path */ + char *p, *q; + word **l, *w; + + if(v->val == nil){ + _setvar("path", nil, 0); + return; + } + p = v->val->word; + w = nil; + l = &w; + /* + * Doesn't handle escaped colon nonsense. + */ + if(p[0] == 0) + p = nil; + while(p){ + q = strchr(p, ':'); + if(q) + *q = 0; + *l = newword(p[0] ? p : ".", nil); + l = &(*l)->next; + if(q){ + *q = ':'; + p = q+1; + }else + p = nil; + } + _setvar("path", w, 0); +} + +char* +list2strcolon(word *words) +{ + char *value, *s, *t; + int len = 0; + word *ap; + for(ap = words;ap;ap = ap->next) + len+=1+strlen(ap->word); + value = emalloc(len+1); + s = value; + for(ap = words;ap;ap = ap->next){ + for(t = ap->word;*t;) *s++=*t++; + *s++=':'; + } + if(s==value) + *s='\0'; + else s[-1]='\0'; + return value; +} +void +littlepath(var *v) +{ + /* convert $path to $PATH */ + char *p; + word *w; + + p = list2strcolon(v->val); + w = new(word); + w->word = p; + w->next = nil; + _setvar("PATH", w, 1); /* 1: recompute $path to expose colon problems */ +} + +void +pathinit(void) +{ + var *v; + + v = gvlook("path"); + v->changefn = littlepath; + v = gvlook("PATH"); + v->changefn = bigpath; + bigpath(v); +} diff --git a/cmd/yacc.c b/cmd/yacc.c new file mode 100644 index 0000000..bd3f6aa --- /dev/null +++ b/cmd/yacc.c @@ -0,0 +1,2993 @@ +#include +#include +#include +#include + +#define Bungetrune Bungetc /* ok for now. */ + +/* + * all these are 32 bit + */ +#define TBITSET ((32+NTERMS)/32) /* BOTCH?? +31 */ +#define BIT(a,i) ((a)[(i)>>5] & (1<<((i)&037))) +#define SETBIT(a,i) ((a)[(i)>>5] |= (1<<((i)&037))) +#define NWORDS(n) (((n)+32)/32) + +char *PARSER = "#9/lib/yaccpar"; +char *PARSERS = "#9/lib/yaccpars"; + +#define TEMPNAME "y.tmp.XXXXXX" +#define ACTNAME "y.acts.XXXXXX" +#define OFILE "tab.c" +#define FILEU "output" +#define FILED "tab.h" +#define FILEDEBUG "debug" + +enum +{ +/* + * the following are adjustable + * according to memory size + */ + ACTSIZE = 40000, + MEMSIZE = 40000, + NSTATES = 2000, + NTERMS = 511, + NPROD = 1600, + NNONTERM = 600, + TEMPSIZE = 2000, + CNAMSZ = 10000, + LSETSIZE = 2400, + WSETSIZE = 350, + + NAMESIZE = 50, + NTYPES = 63, + ISIZE = 400, + + PRIVATE = 0xE000, /* unicode private use */ + + /* relationships which must hold: + TBITSET ints must hold NTERMS+1 bits... + WSETSIZE >= NNONTERM + LSETSIZE >= NNONTERM + TEMPSIZE >= NTERMS + NNONTERM + 1 + TEMPSIZE >= NSTATES + */ + + NTBASE = 010000, + ERRCODE = 8190, + ACCEPTCODE = 8191, + + NOASC = 0, /* no assoc. */ + LASC = 1, /* left assoc. */ + RASC = 2, /* right assoc. */ + BASC = 3, /* binary assoc. */ + + /* flags for state generation */ + + DONE = 0, + MUSTDO = 1, + MUSTLOOKAHEAD = 2, + + /* flags for a rule having an action, and being reduced */ + + ACTFLAG = 04, + REDFLAG = 010, + + /* output parser flags */ + YYFLAG1 = -1000, + + /* parse tokens */ + IDENTIFIER = PRIVATE, + MARK, + TERM, + LEFT, + RIGHT, + BINARY, + PREC, + LCURLY, + IDENTCOLON, + NUMBER, + START, + TYPEDEF, + TYPENAME, + UNION, + + ENDFILE = 0, + + EMPTY = 1, + WHOKNOWS = 0, + OK = 1, + NOMORE = -1000 +}; + + /* macros for getting associativity and precedence levels */ + +#define ASSOC(i) ((i)&03) +#define PLEVEL(i) (((i)>>4)&077) +#define TYPE(i) (((i)>>10)&077) + + /* macros for setting associativity and precedence levels */ + +#define SETASC(i,j) i |= j +#define SETPLEV(i,j) i |= (j<<4) +#define SETTYPE(i,j) i |= (j<<10) + + /* looping macros */ + +#define TLOOP(i) for(i=1; i<=ntokens; i++) +#define NTLOOP(i) for(i=0; i<=nnonter; i++) +#define PLOOP(s,i) for(i=s; i= 0 && j < 256) { + if(temp1[j]) { + print("yacc bug -- cant have 2 different Ts with same value\n"); + print(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name); + nerrors++; + } + temp1[j] = i; + if(j > c) + c = j; + } + } + warray("yytok1", temp1, c+1); + + /* table 2 has PRIVATE-PRIVATE+256 */ + aryfil(temp1, 256, 0); + c = 0; + TLOOP(i) { + j = tokset[i].value - PRIVATE; + if(j >= 0 && j < 256) { + if(temp1[j]) { + print("yacc bug -- cant have 2 different Ts with same value\n"); + print(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name); + nerrors++; + } + temp1[j] = i; + if(j > c) + c = j; + } + } + warray("yytok2", temp1, c+1); + + /* table 3 has everything else */ + Bprint(ftable, "static\tconst\tlong yytok3[] =\n{\n"); + c = 0; + TLOOP(i) { + j = tokset[i].value; + if(j >= 0 && j < 256) + continue; + if(j >= PRIVATE && j < 256+PRIVATE) + continue; + + Bprint(ftable, "%4d,%4d,", j, i); + c++; + if(c%5 == 0) + Bprint(ftable, "\n"); + } + Bprint(ftable, "%4d\n};\n", 0); + + /* copy parser text */ + while((c=Bgetrune(finput)) != Beof) { + if(c == '$') { + if((c = Bgetrune(finput)) != 'A') + Bputrune(ftable, '$'); + else { /* copy actions */ + faction = Bopen(actname, OREAD); + if(faction == 0) + error("cannot reopen action tempfile"); + while((c=Bgetrune(faction)) != Beof) + Bputrune(ftable, c); + Bterm(faction); + ZAPFILE(actname); + c = Bgetrune(finput); + } + } + Bputrune(ftable, c); + } + Bterm(ftable); +} + +/* + * copies string q into p, returning next free char ptr + */ +char* +chcopy(char* p, char* q) +{ + int c; + + while(c = *q) { + if(c == '"') + *p++ = '\\'; + *p++ = c; + q++; + } + *p = 0; + return p; +} + +/* + * creates output string for item pointed to by pp + */ +char* +writem(int *pp) +{ + int i,*p; + static char sarr[ISIZE]; + char* q; + + for(p=pp; *p>0; p++) + ; + p = prdptr[-*p]; + q = chcopy(sarr, nontrst[*p-NTBASE].name); + q = chcopy(q, ": "); + for(;;) { + *q = ' '; + p++; + if(p == pp) + *q = '.'; + q++; + *q = '\0'; + i = *p; + if(i <= 0) + break; + q = chcopy(q, symnam(i)); + if(q > &sarr[ISIZE-30]) + error("item too big"); + } + + /* an item calling for a reduction */ + i = *pp; + if(i < 0 ) { + q = chcopy(q, " ("); + sprint(q, "%d)", -i); + } + return sarr; +} + +/* + * return a pointer to the name of symbol i + */ +char* +symnam(int i) +{ + char* cp; + + cp = (i >= NTBASE)? nontrst[i-NTBASE].name: tokset[i].name; + if(*cp == ' ') + cp++; + return cp; +} + +/* + * output the summary on y.output + */ +void +summary(void) +{ + + if(foutput != 0) { + Bprint(foutput, "\n%d/%d terminals, %d/%d nonterminals\n", + ntokens, NTERMS, nnonter, NNONTERM); + Bprint(foutput, "%d/%d grammar rules, %d/%d states\n", + nprod, NPROD, nstate, NSTATES); + Bprint(foutput, "%d shift/reduce, %d reduce/reduce conflicts reported\n", + zzsrconf, zzrrconf); + Bprint(foutput, "%d/%d working sets used\n", + (int)(zzcwp-wsets), WSETSIZE); + Bprint(foutput, "memory: states,etc. %d/%d, parser %d/%d\n", + (int)(zzmemsz-mem0), MEMSIZE, (int)(memp-amem), ACTSIZE); + Bprint(foutput, "%d/%d distinct lookahead sets\n", nlset, LSETSIZE); + Bprint(foutput, "%d extra closures\n", zzclose - 2*nstate); + Bprint(foutput, "%d shift entries, %d exceptions\n", zzacent, zzexcp); + Bprint(foutput, "%d goto entries\n", zzgoent); + Bprint(foutput, "%d entries saved by goto default\n", zzgobest); + } + if(zzsrconf != 0 || zzrrconf != 0) { + print("\nconflicts: "); + if(zzsrconf) + print("%d shift/reduce", zzsrconf); + if(zzsrconf && zzrrconf) + print(", "); + if(zzrrconf) + print("%d reduce/reduce", zzrrconf); + print("\n"); + } + if(ftemp != 0) { + Bterm(ftemp); + ftemp = 0; + } + if(fdefine != 0) { + Bterm(fdefine); + fdefine = 0; + } +} + +/* + * write out error comment -- NEEDS WORK + */ +void +error(char *s, ...) +{ + va_list arg; + + nerrors++; + fprint(2, "\n fatal error:"); + va_start(arg, s); + vfprint(2, s, arg); + va_end(arg); + fprint(2, ", %s:%d\n", infile, lineno); + if(!fatfl) + return; + summary(); + cleantmp(); + exits("error"); +} + +/* + * set elements 0 through n-1 to c + */ +void +aryfil(int *v, int n, int c) +{ + int i; + + for(i=0; ilset; + if(pp == 0) + Bprint(foutput, "\tNULL"); + else { + Bprint(foutput, " { "); + TLOOP(j) + if(BIT(pp,j)) + Bprint(foutput, "%s ", symnam(j)); + Bprint(foutput, "}"); + } +} + +/* + * compute an array with the beginnings of productions yielding given nonterminals + * The array pres points to these lists + * the array pyield has the lists: the total size is only NPROD+1 + */ +void +cpres(void) +{ + int c, j, i, **pmem; + static int *pyield[NPROD]; + + pmem = pyield; + NTLOOP(i) { + c = i+NTBASE; + pres[i] = pmem; + fatfl = 0; /* make undefined symbols nonfatal */ + PLOOP(0, j) + if(*prdptr[j] == c) + *pmem++ = prdptr[j]+1; + if(pres[i] == pmem) + error("nonterminal %s not defined!", nontrst[i].name); + } + pres[i] = pmem; + fatfl = 1; + if(nerrors) { + summary(); + cleantmp(); + exits("error"); + } + if(pmem != &pyield[nprod]) + error("internal Yacc error: pyield %d", pmem-&pyield[nprod]); +} + +/* + * compute an array with the first of nonterminals + */ +void +cpfir(void) +{ + int *p, **s, i, **t, ch, changes; + + zzcwp = &wsets[nnonter]; + NTLOOP(i) { + aryfil(wsets[i].ws.lset, tbitset, 0); + t = pres[i+1]; + /* initially fill the sets */ + for(s=pres[i]; s 0; ++p) { + if(ch < NTBASE) { + SETBIT(wsets[i].ws.lset, ch); + break; + } + if(!pempty[ch-NTBASE]) + break; + } + } + + /* now, reflect transitivity */ + changes = 1; + while(changes) { + changes = 0; + NTLOOP(i) { + t = pres[i+1]; + for(s = pres[i]; s < t; ++s) + for(p = *s; (ch = (*p-NTBASE)) >= 0; ++p) { + changes |= setunion(wsets[i].ws.lset, wsets[ch].ws.lset); + if(!pempty[ch]) + break; + } + } + } + + NTLOOP(i) + pfirst[i] = flset(&wsets[i].ws); + if(!indebug) + return; + if(foutput != 0) + NTLOOP(i) { + Bprint(foutput, "\n%s: ", nontrst[i].name); + prlook(pfirst[i]); + Bprint(foutput, " %d\n", pempty[i]); + } +} + +/* + * sorts last state,and sees if it equals earlier ones. returns state number + */ +int +state(int c) +{ + Item *p1, *p2, *k, *l, *q1, *q2; + int size1, size2, i; + + p1 = pstate[nstate]; + p2 = pstate[nstate+1]; + if(p1 == p2) + return 0; /* null state */ + /* sort the items */ + for(k = p2-1; k > p1; k--) /* make k the biggest */ + for(l = k-1; l >= p1; --l) + if(l->pitem > k->pitem) { + int *s; + Lkset *ss; + + s = k->pitem; + k->pitem = l->pitem; + l->pitem = s; + ss = k->look; + k->look = l->look; + l->look = ss; + } + size1 = p2 - p1; /* size of state */ + + for(i = (c>=NTBASE)? ntstates[c-NTBASE]: tstates[c]; i != 0; i = mstates[i]) { + /* get ith state */ + q1 = pstate[i]; + q2 = pstate[i+1]; + size2 = q2 - q1; + if(size1 != size2) + continue; + k = p1; + for(l = q1; l < q2; l++) { + if(l->pitem != k->pitem) + break; + k++; + } + if(l != q2) + continue; + /* found it */ + pstate[nstate+1] = pstate[nstate]; /* delete last state */ + /* fix up lookaheads */ + if(nolook) + return i; + for(l = q1, k = p1; l < q2; ++l, ++k ) { + int s; + + SETLOOP(s) + clset.lset[s] = l->look->lset[s]; + if(setunion(clset.lset, k->look->lset)) { + tystate[i] = MUSTDO; + /* register the new set */ + l->look = flset( &clset ); + } + } + return i; + } + /* state is new */ + if(nolook) + error("yacc state/nolook error"); + pstate[nstate+2] = p2; + if(nstate+1 >= NSTATES) + error("too many states"); + if(c >= NTBASE) { + mstates[nstate] = ntstates[c-NTBASE]; + ntstates[c-NTBASE] = nstate; + } else { + mstates[nstate] = tstates[c]; + tstates[c] = nstate; + } + tystate[nstate] = MUSTDO; + return nstate++; +} + +void +putitem(int *ptr, Lkset *lptr) +{ + Item *j; + + if(pidebug && foutput != 0) + Bprint(foutput, "putitem(%s), state %d\n", writem(ptr), nstate); + j = pstate[nstate+1]; + j->pitem = ptr; + if(!nolook) + j->look = flset(lptr); + pstate[nstate+1] = ++j; + if((int*)j > zzmemsz) { + zzmemsz = (int*)j; + if(zzmemsz >= &mem0[MEMSIZE]) + error("out of state space"); + } +} + +/* + * mark nonterminals which derive the empty string + * also, look for nonterminals which don't derive any token strings + */ +void +cempty(void) +{ + + int i, *p; + + /* first, use the array pempty to detect productions that can never be reduced */ + /* set pempty to WHONOWS */ + aryfil(pempty, nnonter+1, WHOKNOWS); + + /* now, look at productions, marking nonterminals which derive something */ +more: + PLOOP(0, i) { + if(pempty[*prdptr[i] - NTBASE]) + continue; + for(p = prdptr[i]+1; *p >= 0; ++p) + if(*p >= NTBASE && pempty[*p-NTBASE] == WHOKNOWS) + break; + /* production can be derived */ + if(*p < 0) { + pempty[*prdptr[i]-NTBASE] = OK; + goto more; + } + } + + /* now, look at the nonterminals, to see if they are all OK */ + NTLOOP(i) { + /* the added production rises or falls as the start symbol ... */ + if(i == 0) + continue; + if(pempty[i] != OK) { + fatfl = 0; + error("nonterminal %s never derives any token string", nontrst[i].name); + } + } + + if(nerrors) { + summary(); + cleantmp(); + exits("error"); + } + + /* now, compute the pempty array, to see which nonterminals derive the empty string */ + /* set pempty to WHOKNOWS */ + aryfil( pempty, nnonter+1, WHOKNOWS); + + /* loop as long as we keep finding empty nonterminals */ + +again: + PLOOP(1, i) { + /* not known to be empty */ + if(pempty[*prdptr[i]-NTBASE] == WHOKNOWS) { + for(p = prdptr[i]+1; *p >= NTBASE && pempty[*p-NTBASE] == EMPTY ; ++p) + ; + /* we have a nontrivially empty nonterminal */ + if(*p < 0) { + pempty[*prdptr[i]-NTBASE] = EMPTY; + /* got one ... try for another */ + goto again; + } + } + } +} + +/* + * generate the states + */ +void +stagen(void) +{ + + int c, i, j, more; + Wset *p, *q; + + /* initialize */ + nstate = 0; + + /* THIS IS FUNNY from the standpoint of portability + * it represents the magic moment when the mem0 array, which has + * been holding the productions, starts to hold item pointers, of a + * different type... + * someday, alloc should be used to allocate all this stuff... for now, we + * accept that if pointers don't fit in integers, there is a problem... + */ + + pstate[0] = pstate[1] = (Item*)mem; + aryfil(clset.lset, tbitset, 0); + putitem(prdptr[0]+1, &clset); + tystate[0] = MUSTDO; + nstate = 1; + pstate[2] = pstate[1]; + + aryfil(amem, ACTSIZE, 0); + + /* now, the main state generation loop */ + for(more=1; more;) { + more = 0; + SLOOP(i) { + if(tystate[i] != MUSTDO) + continue; + tystate[i] = DONE; + aryfil(temp1, nnonter+1, 0); + /* take state i, close it, and do gotos */ + closure(i); + /* generate goto's */ + WSLOOP(wsets, p) { + if(p->flag) + continue; + p->flag = 1; + c = *(p->pitem); + if(c <= 1) { + if(pstate[i+1]-pstate[i] <= p-wsets) + tystate[i] = MUSTLOOKAHEAD; + continue; + } + /* do a goto on c */ + WSLOOP(p, q) + /* this item contributes to the goto */ + if(c == *(q->pitem)) { + putitem(q->pitem+1, &q->ws); + q->flag = 1; + } + if(c < NTBASE) + state(c); /* register new state */ + else + temp1[c-NTBASE] = state(c); + } + if(gsdebug && foutput != 0) { + Bprint(foutput, "%d: ", i); + NTLOOP(j) + if(temp1[j]) + Bprint(foutput, "%s %d, ", + nontrst[j].name, temp1[j]); + Bprint(foutput, "\n"); + } + indgo[i] = apack(&temp1[1], nnonter-1) - 1; + /* do some more */ + more = 1; + } + } +} + +/* + * generate the closure of state i + */ +void +closure(int i) +{ + + Wset *u, *v; + Item *p, *q; + int c, ch, work, k, *pi, **s, **t; + + zzclose++; + + /* first, copy kernel of state i to wsets */ + cwp = wsets; + ITMLOOP(i, p, q) { + cwp->pitem = p->pitem; + cwp->flag = 1; /* this item must get closed */ + SETLOOP(k) + cwp->ws.lset[k] = p->look->lset[k]; + WSBUMP(cwp); + } + + /* now, go through the loop, closing each item */ + work = 1; + while(work) { + work = 0; + WSLOOP(wsets, u) { + if(u->flag == 0) + continue; + /* dot is before c */ + c = *(u->pitem); + if(c < NTBASE) { + u->flag = 0; + /* only interesting case is where . is before nonterminal */ + continue; + } + + /* compute the lookahead */ + aryfil(clset.lset, tbitset, 0); + + /* find items involving c */ + WSLOOP(u, v) + if(v->flag == 1 && *(pi=v->pitem) == c) { + v->flag = 0; + if(nolook) + continue; + while((ch = *++pi) > 0) { + /* terminal symbol */ + if(ch < NTBASE) { + SETBIT(clset.lset, ch); + break; + } + /* nonterminal symbol */ + setunion(clset.lset, pfirst[ch-NTBASE]->lset); + if(!pempty[ch-NTBASE]) + break; + } + if(ch <= 0) + setunion(clset.lset, v->ws.lset); + } + + /* + * now loop over productions derived from c + * c is now nonterminal number + */ + c -= NTBASE; + t = pres[c+1]; + for(s = pres[c]; s < t; ++s) { + /* + * put these items into the closure + * is the item there + */ + WSLOOP(wsets, v) + /* yes, it is there */ + if(v->pitem == *s) { + if(nolook) + goto nexts; + if(setunion(v->ws.lset, clset.lset)) + v->flag = work = 1; + goto nexts; + } + + /* not there; make a new entry */ + if(cwp-wsets+1 >= WSETSIZE) + error( "working set overflow"); + cwp->pitem = *s; + cwp->flag = 1; + if(!nolook) { + work = 1; + SETLOOP(k) cwp->ws.lset[k] = clset.lset[k]; + } + WSBUMP(cwp); + + nexts:; + } + } + } + + /* have computed closure; flags are reset; return */ + if(cwp > zzcwp) + zzcwp = cwp; + if(cldebug && foutput != 0) { + Bprint(foutput, "\nState %d, nolook = %d\n", i, nolook); + WSLOOP(wsets, u) { + if(u->flag) + Bprint(foutput, "flag set!\n"); + u->flag = 0; + Bprint(foutput, "\t%s", writem(u->pitem)); + prlook(&u->ws); + Bprint(foutput, "\n"); + } + } +} + +/* + * decide if the lookahead set pointed to by p is known + * return pointer to a perminent location for the set + */ +Lkset* +flset(Lkset *p) +{ + Lkset *q; + int *u, *v, *w, j; + + for(q = &lkst[nlset]; q-- > lkst;) { + u = p->lset; + v = q->lset; + w = &v[tbitset]; + while(v < w) + if(*u++ != *v++) + goto more; + /* we have matched */ + return q; + more:; + } + /* add a new one */ + q = &lkst[nlset++]; + if(nlset >= LSETSIZE) + error("too many lookahead sets"); + SETLOOP(j) + q->lset[j] = p->lset[j]; + return q; +} + +void +cleantmp(void) +{ + ZAPFILE(actname); + ZAPFILE(tempname); +} + +void +intr(void) +{ + cleantmp(); + exits("interrupted"); +} + +void +setup(int argc, char *argv[]) +{ + long c, t; + int i, j, fd, lev, ty, ytab, *p; + int vflag, dflag, stem; + char actnm[8], *stemc, *s, dirbuf[128]; + Biobuf *fout; + + ytab = 0; + vflag = 0; + dflag = 0; + stem = 0; + stemc = "y"; + foutput = 0; + fdefine = 0; + fdebug = 0; + ARGBEGIN{ + case 'v': + case 'V': + vflag++; + break; + case 'D': + yydebug = ARGF(); + break; + case 'a': + yyarg = 1; + break; + case 'd': + dflag++; + break; + case 'l': + yyline = 0; + break; + case 'o': + ytab++; + ytabc = ARGF(); + break; + case 's': + stem++; + stemc = ARGF(); + break; + case 'S': + parser = PARSERS; + break; + default: + error("illegal option: %c", ARGC()); + }ARGEND + openup(stemc, dflag, vflag, ytab, ytabc); + fout = dflag?fdefine:ftable; + if(yyarg){ + Bprint(ftable, "#define\tYYARG\t1\n\n"); + } + if((fd = mkstemp(ttempname)) >= 0){ + tempname = ttempname; + ftemp = Bfdopen(fd, OWRITE); + } + if((fd = mkstemp(tactname)) >= 0){ + actname = tactname; + faction = Bfdopen(fd, OWRITE); + } + if(ftemp == 0 || faction == 0) + error("cannot open temp file"); + if(argc < 1) + error("no input file"); + infile = argv[0]; + if(infile[0] != '/' && getwd(dirbuf, sizeof dirbuf)!=nil){ + i = strlen(infile)+1+strlen(dirbuf)+1+10; + s = malloc(i); + if(s != nil){ + snprint(s, i, "%s/%s", dirbuf, infile); + cleanname(s); + infile = s; + } + } + finput = Bopen(infile, OREAD); + if(finput == 0) + error("cannot open '%s'", argv[0]); + cnamp = cnames; + + defin(0, "$end"); + extval = PRIVATE; /* tokens start in unicode 'private use' */ + defin(0, "error"); + defin(1, "$accept"); + defin(0, "$unk"); + mem = mem0; + i = 0; + + for(t = gettok(); t != MARK && t != ENDFILE;) + switch(t) { + case ';': + t = gettok(); + break; + + case START: + if(gettok() != IDENTIFIER) + error("bad %%start construction"); + start = chfind(1, tokname); + t = gettok(); + continue; + + case TYPEDEF: + if(gettok() != TYPENAME) + error("bad syntax in %%type"); + ty = numbval; + for(;;) { + t = gettok(); + switch(t) { + case IDENTIFIER: + if((t=chfind(1, tokname)) < NTBASE) { + j = TYPE(toklev[t]); + if(j != 0 && j != ty) + error("type redeclaration of token %s", + tokset[t].name); + else + SETTYPE(toklev[t], ty); + } else { + j = nontrst[t-NTBASE].value; + if(j != 0 && j != ty) + error("type redeclaration of nonterminal %s", + nontrst[t-NTBASE].name ); + else + nontrst[t-NTBASE].value = ty; + } + case ',': + continue; + case ';': + t = gettok(); + default: + break; + } + break; + } + continue; + + case UNION: + /* copy the union declaration to the output */ + cpyunion(); + t = gettok(); + continue; + + case LEFT: + case BINARY: + case RIGHT: + i++; + + case TERM: + /* nonzero means new prec. and assoc. */ + lev = t-TERM; + ty = 0; + + /* get identifiers so defined */ + t = gettok(); + + /* there is a type defined */ + if(t == TYPENAME) { + ty = numbval; + t = gettok(); + } + for(;;) { + switch(t) { + case ',': + t = gettok(); + continue; + + case ';': + break; + + case IDENTIFIER: + j = chfind(0, tokname); + if(j >= NTBASE) + error("%s defined earlier as nonterminal", tokname); + if(lev) { + if(ASSOC(toklev[j])) + error("redeclaration of precedence of %s", tokname); + SETASC(toklev[j], lev); + SETPLEV(toklev[j], i); + } + if(ty) { + if(TYPE(toklev[j])) + error("redeclaration of type of %s", tokname); + SETTYPE(toklev[j],ty); + } + t = gettok(); + if(t == NUMBER) { + tokset[j].value = numbval; + if(j < ndefout && j > 3) + error("please define type number of %s earlier", + tokset[j].name); + t = gettok(); + } + continue; + } + break; + } + continue; + + case LCURLY: + defout(0); + cpycode(); + t = gettok(); + continue; + + default: + error("syntax error"); + } + if(t == ENDFILE) + error("unexpected EOF before %%"); + + /* t is MARK */ + if(!yyarg) + Bprint(ftable, "extern int yyerrflag;\n"); + Bprint(ftable, "#ifndef YYMAXDEPTH\n"); + Bprint(ftable, "#define YYMAXDEPTH 150\n"); + Bprint(ftable, "#endif\n" ); + if(!ntypes) { + Bprint(ftable, "#ifndef YYSTYPE\n"); + Bprint(ftable, "#define YYSTYPE int\n"); + Bprint(ftable, "#endif\n"); + } + if(!yyarg){ + Bprint(ftable, "YYSTYPE yylval;\n"); + Bprint(ftable, "YYSTYPE yyval;\n"); + }else{ + if(dflag) + Bprint(ftable, "#include \"%s.%s\"\n\n", stemc, FILED); + Bprint(fout, "struct Yyarg {\n"); + Bprint(fout, "\tint\tyynerrs;\n"); + Bprint(fout, "\tint\tyyerrflag;\n"); + Bprint(fout, "\tvoid*\targ;\n"); + Bprint(fout, "\tYYSTYPE\tyyval;\n"); + Bprint(fout, "\tYYSTYPE\tyylval;\n"); + Bprint(fout, "};\n\n"); + } + prdptr[0] = mem; + + /* added production */ + *mem++ = NTBASE; + + /* if start is 0, we will overwrite with the lhs of the first rule */ + *mem++ = start; + *mem++ = 1; + *mem++ = 0; + prdptr[1] = mem; + while((t=gettok()) == LCURLY) + cpycode(); + if(t != IDENTCOLON) + error("bad syntax on first rule"); + + if(!start) + prdptr[0][1] = chfind(1, tokname); + + /* read rules */ + while(t != MARK && t != ENDFILE) { + /* process a rule */ + rlines[nprod] = lineno; + if(t == '|') + *mem++ = *prdptr[nprod-1]; + else + if(t == IDENTCOLON) { + *mem = chfind(1, tokname); + if(*mem < NTBASE) + error("token illegal on LHS of grammar rule"); + mem++; + } else + error("illegal rule: missing semicolon or | ?"); + /* read rule body */ + t = gettok(); + + more_rule: + while(t == IDENTIFIER) { + *mem = chfind(1, tokname); + if(*mem < NTBASE) + levprd[nprod] = toklev[*mem]; + mem++; + t = gettok(); + } + if(t == PREC) { + if(gettok() != IDENTIFIER) + error("illegal %%prec syntax"); + j = chfind(2, tokname); + if(j >= NTBASE) + error("nonterminal %s illegal after %%prec", + nontrst[j-NTBASE].name); + levprd[nprod] = toklev[j]; + t = gettok(); + } + if(t == '=') { + levprd[nprod] |= ACTFLAG; + Bprint(faction, "\ncase %d:", nprod); + cpyact(mem-prdptr[nprod]-1); + Bprint(faction, " break;"); + if((t=gettok()) == IDENTIFIER) { + + /* action within rule... */ + sprint(actnm, "$$%d", nprod); + + /* make it a nonterminal */ + j = chfind(1, actnm); + + /* + * the current rule will become rule number nprod+1 + * move the contents down, and make room for the null + */ + for(p = mem; p >= prdptr[nprod]; --p) + p[2] = *p; + mem += 2; + + /* enter null production for action */ + p = prdptr[nprod]; + *p++ = j; + *p++ = -nprod; + + /* update the production information */ + levprd[nprod+1] = levprd[nprod] & ~ACTFLAG; + levprd[nprod] = ACTFLAG; + if(++nprod >= NPROD) + error("more than %d rules", NPROD); + prdptr[nprod] = p; + + /* make the action appear in the original rule */ + *mem++ = j; + + /* get some more of the rule */ + goto more_rule; + } + } + + while(t == ';') + t = gettok(); + *mem++ = -nprod; + + /* check that default action is reasonable */ + if(ntypes && !(levprd[nprod]&ACTFLAG) && nontrst[*prdptr[nprod]-NTBASE].value) { + + /* no explicit action, LHS has value */ + int tempty; + + tempty = prdptr[nprod][1]; + if(tempty < 0) + error("must return a value, since LHS has a type"); + else + if(tempty >= NTBASE) + tempty = nontrst[tempty-NTBASE].value; + else + tempty = TYPE(toklev[tempty]); + if(tempty != nontrst[*prdptr[nprod]-NTBASE].value) + error("default action causes potential type clash"); + } + nprod++; + if(nprod >= NPROD) + error("more than %d rules", NPROD); + prdptr[nprod] = mem; + levprd[nprod] = 0; + } + + /* end of all rules */ + defout(1); + + finact(); + if(t == MARK) { + Bprint(ftable, "\n"); + if(yyline) + Bprint(ftable, "#line\t%d\t\"%s\"\n", lineno, infile); + while((c=Bgetrune(finput)) != Beof) + Bputrune(ftable, c); + } + Bterm(finput); +} + +/* + * finish action routine + */ +void +finact(void) +{ + + Bterm(faction); + Bprint(ftable, "#define YYEOFCODE %d\n", 1); + Bprint(ftable, "#define YYERRCODE %d\n", 2); +} + +/* + * define s to be a terminal if t=0 + * or a nonterminal if t=1 + */ +int +defin(int nt, char *s) +{ + int val; + Rune rune; + + val = 0; + if(nt) { + nnonter++; + if(nnonter >= NNONTERM) + error("too many nonterminals, limit %d",NNONTERM); + nontrst[nnonter].name = cstash(s); + return NTBASE + nnonter; + } + + /* must be a token */ + ntokens++; + if(ntokens >= NTERMS) + error("too many terminals, limit %d", NTERMS); + tokset[ntokens].name = cstash(s); + + /* establish value for token */ + /* single character literal */ + if(s[0] == ' ') { + val = chartorune(&rune, &s[1]); + if(s[val+1] == 0) { + val = rune; + goto out; + } + } + + /* escape sequence */ + if(s[0] == ' ' && s[1] == '\\') { + if(s[3] == 0) { + /* single character escape sequence */ + switch(s[2]) { + case 'n': val = '\n'; break; + case 'r': val = '\r'; break; + case 'b': val = '\b'; break; + case 't': val = '\t'; break; + case 'f': val = '\f'; break; + case '\'': val = '\''; break; + case '"': val = '"'; break; + case '\\': val = '\\'; break; + default: error("invalid escape"); + } + goto out; + } + + /* \nnn sequence */ + if(s[2] >= '0' && s[2] <= '7') { + if(s[3] < '0' || + s[3] > '7' || + s[4] < '0' || + s[4] > '7' || + s[5] != 0) + error("illegal \\nnn construction"); + val = 64*s[2] + 8*s[3] + s[4] - 73*'0'; + if(val == 0) + error("'\\000' is illegal"); + goto out; + } + error("unknown escape"); + } + val = extval++; + +out: + tokset[ntokens].value = val; + toklev[ntokens] = 0; + return ntokens; +} + +/* + * write out the defines (at the end of the declaration section) + */ +void +defout(int last) +{ + int i, c; + char sar[NAMESIZE+10]; + + for(i=ndefout; i<=ntokens; i++) { + /* non-literals */ + c = tokset[i].name[0]; + if(c != ' ' && c != '$') { + Bprint(ftable, "#define %s %d\n", + tokset[i].name, tokset[i].value); + if(fdefine) + Bprint(fdefine, "#define\t%s\t%d\n", + tokset[i].name, tokset[i].value); + } + } + ndefout = ntokens+1; + if(last && fdebug) { + Bprint(fdebug, "static char* yytoknames[] =\n{\n"); + TLOOP(i) { + if(tokset[i].name) { + chcopy(sar, tokset[i].name); + Bprint(fdebug, "\t\"%s\",\n", sar); + continue; + } + Bprint(fdebug, "\t0,\n"); + } + Bprint(fdebug, "};\n"); + } +} + +char* +cstash(char *s) +{ + char *temp; + + temp = cnamp; + do { + if(cnamp >= &cnames[cnamsz]) + error("too many characters in id's and literals"); + else + *cnamp++ = *s; + } while(*s++); + return temp; +} + +int +isvalidchar(long i) +{ + return (i & ~0xffUL) == 0; +} + +long +gettok(void) +{ + long c; + Rune rune; + int i, base, match, reserve; + static int peekline; + +begin: + reserve = 0; + lineno += peekline; + peekline = 0; + c = Bgetrune(finput); + while(c == ' ' || c == '\n' || c == '\t' || c == '\f') { + if(c == '\n') + lineno++; + c = Bgetrune(finput); + } + + /* skip comment */ + if(c == '/') { + lineno += skipcom(); + goto begin; + } + switch(c) { + case Beof: + return ENDFILE; + + case '{': + Bungetrune(finput); + return '='; + + case '<': + /* get, and look up, a type name (union member name) */ + i = 0; + while((c=Bgetrune(finput)) != '>' && c >= 0 && c != '\n') { + rune = c; + c = runetochar(&tokname[i], &rune); + if(i < NAMESIZE) + i += c; + } + if(c != '>') + error("unterminated < ... > clause"); + tokname[i] = 0; + for(i=1; i<=ntypes; i++) + if(!strcmp(typeset[i], tokname)) { + numbval = i; + return TYPENAME; + } + ntypes++; + numbval = ntypes; + typeset[numbval] = cstash(tokname); + return TYPENAME; + + case '"': + case '\'': + match = c; + tokname[0] = ' '; + i = 1; + for(;;) { + c = Bgetrune(finput); + if(c == '\n' || c <= 0) + error("illegal or missing ' or \"" ); + if(c == '\\') { + tokname[i] = '\\'; + if(i < NAMESIZE) + i++; + c = Bgetrune(finput); + } else + if(c == match) + break; + rune = c; + c = runetochar(&tokname[i], &rune); + if(i < NAMESIZE) + i += c; + } + break; + + case '%': + case '\\': + switch(c = Bgetrune(finput)) { + case '0': return TERM; + case '<': return LEFT; + case '2': return BINARY; + case '>': return RIGHT; + case '%': + case '\\': return MARK; + case '=': return PREC; + case '{': return LCURLY; + default: reserve = 1; + } + + default: + /* number */ + if(!isvalidchar(c)) + return c; + if(isdigit(c)) { + numbval = c-'0'; + base = (c=='0')? 8: 10; + for(c = Bgetrune(finput); isdigit(c); c = Bgetrune(finput)) + numbval = numbval*base + (c-'0'); + Bungetrune(finput); + return NUMBER; + } + if(islower(c) || isupper(c) || c=='_' || c=='.' || c=='$') { + i = 0; + while(isvalidchar(c) && (islower(c) || isupper(c) || isdigit(c) || + c == '-' || c=='_' || c=='.' || c=='$')) { + if(reserve && isupper(c)) + c += 'a'-'A'; + rune = c; + c = runetochar(&tokname[i], &rune); + if(i < NAMESIZE) + i += c; + c = Bgetrune(finput); + } + } else + return c; + if(c == Beof) + return ENDFILE; + Bungetrune(finput); + } + tokname[i] = 0; + + /* find a reserved word */ + if(reserve) { + for(c=0; resrv[c].name; c++) + if(strcmp(tokname, resrv[c].name) == 0) + return resrv[c].value; + error("invalid escape, or illegal reserved word: %s", tokname); + } + + /* look ahead to distinguish IDENTIFIER from IDENTCOLON */ + c = Bgetrune(finput); + while(c == ' ' || c == '\t'|| c == '\n' || c == '\f' || c == '/') { + if(c == '\n') + peekline++; + /* look for comments */ + if(c == '/') + peekline += skipcom(); + c = Bgetrune(finput); + } + if(c == ':') + return IDENTCOLON; + Bungetrune(finput); + return IDENTIFIER; +} + +/* + * determine the type of a symbol + */ +int +fdtype(int t) +{ + int v; + + if(t >= NTBASE) + v = nontrst[t-NTBASE].value; + else + v = TYPE(toklev[t]); + if(v <= 0) + error("must specify type for %s", (t>=NTBASE)? + nontrst[t-NTBASE].name: tokset[t].name); + return v; +} + +int +chfind(int t, char *s) +{ + int i; + + if(s[0] == ' ') + t = 0; + TLOOP(i) + if(!strcmp(s, tokset[i].name)) + return i; + NTLOOP(i) + if(!strcmp(s, nontrst[i].name)) + return NTBASE+i; + + /* cannot find name */ + if(t > 1) + error("%s should have been defined earlier", s); + return defin(t, s); +} + +/* + * copy the union declaration to the output, and the define file if present + */ +void +cpyunion(void) +{ + long c; + int level; + + Bprint(ftable, "\n"); + if(yyline) + Bprint(ftable, "#line\t%d\t\"%s\"\n", lineno, infile); + Bprint(ftable, "typedef union "); + if(fdefine != 0) + Bprint(fdefine, "\ntypedef union "); + + level = 0; + for(;;) { + if((c=Bgetrune(finput)) == Beof) + error("EOF encountered while processing %%union"); + Bputrune(ftable, c); + if(fdefine != 0) + Bputrune(fdefine, c); + switch(c) { + case '\n': + lineno++; + break; + case '{': + level++; + break; + case '}': + level--; + + /* we are finished copying */ + if(level == 0) { + Bprint(ftable, " YYSTYPE;\n"); + if(fdefine != 0){ + Bprint(fdefine, "\tYYSTYPE;\n"); + if(!yyarg) + Bprint(fdefine, "extern\tYYSTYPE\tyylval;\n"); + } + return; + } + } + } +} + +/* + * copies code between \{ and \} + */ +void +cpycode(void) +{ + long c; + + c = Bgetrune(finput); + if(c == '\n') { + c = Bgetrune(finput); + lineno++; + } + Bprint(ftable, "\n"); + if(yyline) + Bprint(ftable, "#line\t%d\t\"%s\"\n", lineno, infile); + while(c != Beof) { + if(c == '\\') { + if((c=Bgetrune(finput)) == '}') + return; + Bputc(ftable, '\\'); + } + if(c == '%') { + if((c=Bgetrune(finput)) == '}') + return; + Bputc(ftable, '%'); + } + Bputrune(ftable, c); + if(c == '\n') + lineno++; + c = Bgetrune(finput); + } + error("eof before %%}"); +} + +/* + * skip over comments + * skipcom is called after reading a '/' + */ +int +skipcom(void) +{ + long c; + int i; + + /* i is the number of lines skipped */ + i = 0; + if(Bgetrune(finput) != '*') + error("illegal comment"); + c = Bgetrune(finput); + while(c != Beof) { + while(c == '*') + if((c=Bgetrune(finput)) == '/') + return i; + if(c == '\n') + i++; + c = Bgetrune(finput); + } + error("EOF inside comment"); + return 0; +} + +/* + * copy C action to the next ; or closing } + */ +void +cpyact(int offset) +{ + long c; + int brac, match, j, s, fnd, tok; + + Bprint(faction, "\n"); + if(yyline) + Bprint(faction, "#line\t%d\t\"%s\"\n", lineno, infile); + brac = 0; + +loop: + c = Bgetrune(finput); +swt: + switch(c) { + case ';': + if(brac == 0) { + Bputrune(faction, c); + return; + } + goto lcopy; + + case '{': + brac++; + goto lcopy; + + case '$': + s = 1; + tok = -1; + c = Bgetrune(finput); + + /* type description */ + if(c == '<') { + Bungetrune(finput); + if(gettok() != TYPENAME) + error("bad syntax on $ clause"); + tok = numbval; + c = Bgetrune(finput); + } + if(c == '$') { + Bprint(faction, "yyval"); + + /* put out the proper tag... */ + if(ntypes) { + if(tok < 0) + tok = fdtype(*prdptr[nprod]); + Bprint(faction, ".%s", typeset[tok]); + } + goto loop; + } + if(c == '-') { + s = -s; + c = Bgetrune(finput); + } + if(isvalidchar(c) && isdigit(c)) { + j = 0; + while(isdigit(c)) { + j = j*10 + (c-'0'); + c = Bgetrune(finput); + } + Bungetrune(finput); + j = j*s - offset; + if(j > 0) + error("Illegal use of $%d", j+offset); + + dollar: + Bprint(faction, "yypt[-%d].yyv", -j); + + /* put out the proper tag */ + if(ntypes) { + if(j+offset <= 0 && tok < 0) + error("must specify type of $%d", j+offset); + if(tok < 0) + tok = fdtype(prdptr[nprod][j+offset]); + Bprint(faction, ".%s", typeset[tok]); + } + goto loop; + } + if(isvalidchar(c) && (isupper(c) || islower(c) || c == '_' || c == '.')) { + int tok; /* tok used oustide for type info */ + + /* look for $name */ + Bungetrune(finput); + if(gettok() != IDENTIFIER) + error("$ must be followed by an identifier"); + tok = chfind(2, tokname); + if((c = Bgetrune(finput)) != '#') { + Bungetrune(finput); + fnd = -1; + } else + if(gettok() != NUMBER) { + error("# must be followed by number"); + fnd = -1; + } else + fnd = numbval; + for(j=1; j<=offset; ++j) + if(tok == prdptr[nprod][j]) { + if(--fnd <= 0) { + j -= offset; + goto dollar; + } + } + error("$name or $name#number not found"); + } + Bputc(faction, '$'); + if(s < 0 ) + Bputc(faction, '-'); + goto swt; + + case '}': + brac--; + if(brac) + goto lcopy; + Bputrune(faction, c); + return; + + case '/': + /* look for comments */ + Bputrune(faction, c); + c = Bgetrune(finput); + if(c != '*') + goto swt; + + /* it really is a comment */ + Bputrune(faction, c); + c = Bgetrune(finput); + while(c >= 0) { + while(c == '*') { + Bputrune(faction, c); + if((c=Bgetrune(finput)) == '/') + goto lcopy; + } + Bputrune(faction, c); + if(c == '\n') + lineno++; + c = Bgetrune(finput); + } + error("EOF inside comment"); + + case '\'': + /* character constant */ + match = '\''; + goto string; + + case '"': + /* character string */ + match = '"'; + + string: + Bputrune(faction, c); + while((c = Bgetrune(finput)) >= 0) { + if(c == '\\') { + Bputrune(faction, c); + c = Bgetrune(finput); + if(c == '\n') + lineno++; + } else { + if(c == match) + goto lcopy; + if(c == '\n') + error("newline in string or char. const."); + } + Bputrune(faction, c); + } + error("EOF in string or character constant"); + + case Beof: + error("action does not terminate"); + + case '\n': + lineno++; + goto lcopy; + } + +lcopy: + Bputrune(faction, c); + goto loop; +} + +void +openup(char *stem, int dflag, int vflag, int ytab, char *ytabc) +{ + char buf[256]; + + if(vflag) { + sprint(buf, "%s.%s", stem, FILEU); + foutput = Bopen(buf, OWRITE); + if(foutput == 0) + error("cannot open %s", buf); + } + if(yydebug) { + sprint(buf, "%s.%s", stem, FILEDEBUG); + if((fdebug = Bopen(buf, OWRITE)) == 0) + error("can't open %s", buf); + } + if(dflag) { + sprint(buf, "%s.%s", stem, FILED); + fdefine = Bopen(buf, OWRITE); + if(fdefine == 0) + error("can't create %s", buf); + } + if(ytab == 0) + sprint(buf, "%s.%s", stem, OFILE); + else + strcpy(buf, ytabc); + ftable = Bopen(buf, OWRITE); + if(ftable == 0) + error("cannot open table file %s", buf); +} + +/* + * print the output for the states + */ +void +output(void) +{ + int i, k, c; + Wset *u, *v; + + Bprint(ftable, "static\tconst\tshort yyexca[] =\n{"); + if(fdebug) + Bprint(fdebug, "static\tconst\tchar* yystates[] =\n{\n"); + + /* output the stuff for state i */ + SLOOP(i) { + nolook = tystate[i]!=MUSTLOOKAHEAD; + closure(i); + + /* output actions */ + nolook = 1; + aryfil(temp1, ntokens+nnonter+1, 0); + WSLOOP(wsets, u) { + c = *(u->pitem); + if(c > 1 && c < NTBASE && temp1[c] == 0) { + WSLOOP(u, v) + if(c == *(v->pitem)) + putitem(v->pitem+1, (Lkset*)0); + temp1[c] = state(c); + } else + if(c > NTBASE && temp1[(c -= NTBASE) + ntokens] == 0) + temp1[c+ntokens] = amem[indgo[i]+c]; + } + if(i == 1) + temp1[1] = ACCEPTCODE; + + /* now, we have the shifts; look at the reductions */ + lastred = 0; + WSLOOP(wsets, u) { + c = *u->pitem; + + /* reduction */ + if(c <= 0) { + lastred = -c; + TLOOP(k) + if(BIT(u->ws.lset, k)) { + if(temp1[k] == 0) + temp1[k] = c; + else + if(temp1[k] < 0) { /* reduce/reduce conflict */ + if(foutput) + Bprint(foutput, + "\n%d: reduce/reduce conflict" + " (red'ns %d and %d ) on %s", + i, -temp1[k], lastred, + symnam(k)); + if(-temp1[k] > lastred) + temp1[k] = -lastred; + zzrrconf++; + } else + /* potential shift/reduce conflict */ + precftn( lastred, k, i ); + } + } + } + wract(i); + } + + if(fdebug) + Bprint(fdebug, "};\n"); + Bprint(ftable, "};\n"); + Bprint(ftable, "#define YYNPROD %d\n", nprod); + Bprint(ftable, "#define YYPRIVATE %d\n", PRIVATE); + if(yydebug) + Bprint(ftable, "#define yydebug %s\n", yydebug); +} + +/* + * pack state i from temp1 into amem + */ +int +apack(int *p, int n) +{ + int *pp, *qq, *rr, off, *q, *r; + + /* we don't need to worry about checking because + * we will only look at entries known to be there... + * eliminate leading and trailing 0's + */ + + q = p+n; + for(pp = p, off = 0; *pp == 0 && pp <= q; ++pp, --off) + ; + /* no actions */ + if(pp > q) + return 0; + p = pp; + + /* now, find a place for the elements from p to q, inclusive */ + r = &amem[ACTSIZE-1]; + for(rr = amem; rr <= r; rr++, off++) { + for(qq = rr, pp = p; pp <= q; pp++, qq++) + if(*pp != 0) + if(*pp != *qq && *qq != 0) + goto nextk; + + /* we have found an acceptable k */ + if(pkdebug && foutput != 0) + Bprint(foutput, "off = %d, k = %d\n", off, (int)(rr-amem)); + for(qq = rr, pp = p; pp <= q; pp++, qq++) + if(*pp) { + if(qq > r) + error("action table overflow"); + if(qq > memp) + memp = qq; + *qq = *pp; + } + if(pkdebug && foutput != 0) + for(pp = amem; pp <= memp; pp += 10) { + Bprint(foutput, "\t"); + for(qq = pp; qq <= pp+9; qq++) + Bprint(foutput, "%d ", *qq); + Bprint(foutput, "\n"); + } + return(off); + nextk:; + } + error("no space in action table"); + return 0; +} + +/* + * output the gotos for the nontermninals + */ +void +go2out(void) +{ + int i, j, k, best, count, cbest, times; + + /* mark begining of gotos */ + Bprint(ftemp, "$\n"); + for(i = 1; i <= nnonter; i++) { + go2gen(i); + + /* find the best one to make default */ + best = -1; + times = 0; + + /* is j the most frequent */ + for(j = 0; j <= nstate; j++) { + if(tystate[j] == 0) + continue; + if(tystate[j] == best) + continue; + + /* is tystate[j] the most frequent */ + count = 0; + cbest = tystate[j]; + for(k = j; k <= nstate; k++) + if(tystate[k] == cbest) + count++; + if(count > times) { + best = cbest; + times = count; + } + } + + /* best is now the default entry */ + zzgobest += times-1; + for(j = 0; j <= nstate; j++) + if(tystate[j] != 0 && tystate[j] != best) { + Bprint(ftemp, "%d,%d,", j, tystate[j]); + zzgoent++; + } + + /* now, the default */ + if(best == -1) + best = 0; + zzgoent++; + Bprint(ftemp, "%d\n", best); + } +} + +/* + * output the gotos for nonterminal c + */ +void +go2gen(int c) +{ + int i, work, cc; + Item *p, *q; + + + /* first, find nonterminals with gotos on c */ + aryfil(temp1, nnonter+1, 0); + temp1[c] = 1; + work = 1; + while(work) { + work = 0; + PLOOP(0, i) + + /* cc is a nonterminal */ + if((cc=prdptr[i][1]-NTBASE) >= 0) + /* cc has a goto on c */ + if(temp1[cc] != 0) { + + /* thus, the left side of production i does too */ + cc = *prdptr[i]-NTBASE; + if(temp1[cc] == 0) { + work = 1; + temp1[cc] = 1; + } + } + } + + /* now, we have temp1[c] = 1 if a goto on c in closure of cc */ + if(g2debug && foutput != 0) { + Bprint(foutput, "%s: gotos on ", nontrst[c].name); + NTLOOP(i) + if(temp1[i]) + Bprint(foutput, "%s ", nontrst[i].name); + Bprint(foutput, "\n"); + } + + /* now, go through and put gotos into tystate */ + aryfil(tystate, nstate, 0); + SLOOP(i) + ITMLOOP(i, p, q) + if((cc = *p->pitem) >= NTBASE) + /* goto on c is possible */ + if(temp1[cc-NTBASE]) { + tystate[i] = amem[indgo[i]+c]; + break; + } +} + +/* + * decide a shift/reduce conflict by precedence. + * r is a rule number, t a token number + * the conflict is in state s + * temp1[t] is changed to reflect the action + */ +void +precftn(int r, int t, int s) +{ + int lp, lt, action; + + lp = levprd[r]; + lt = toklev[t]; + if(PLEVEL(lt) == 0 || PLEVEL(lp) == 0) { + + /* conflict */ + if(foutput != 0) + Bprint(foutput, + "\n%d: shift/reduce conflict (shift %d(%d), red'n %d(%d)) on %s", + s, temp1[t], PLEVEL(lt), r, PLEVEL(lp), symnam(t)); + zzsrconf++; + return; + } + if(PLEVEL(lt) == PLEVEL(lp)) + action = ASSOC(lt); + else + if(PLEVEL(lt) > PLEVEL(lp)) + action = RASC; /* shift */ + else + action = LASC; /* reduce */ + switch(action) { + case BASC: /* error action */ + temp1[t] = ERRCODE; + break; + case LASC: /* reduce */ + temp1[t] = -r; + break; + } +} + +/* + * output state i + * temp1 has the actions, lastred the default + */ +void +wract(int i) +{ + int p, p0, p1, ntimes, tred, count, j, flag; + + /* find the best choice for lastred */ + lastred = 0; + ntimes = 0; + TLOOP(j) { + if(temp1[j] >= 0) + continue; + if(temp1[j]+lastred == 0) + continue; + /* count the number of appearances of temp1[j] */ + count = 0; + tred = -temp1[j]; + levprd[tred] |= REDFLAG; + TLOOP(p) + if(temp1[p]+tred == 0) + count++; + if(count > ntimes) { + lastred = tred; + ntimes = count; + } + } + + /* + * for error recovery, arrange that, if there is a shift on the + * error recovery token, `error', that the default be the error action + */ + if(temp1[2] > 0) + lastred = 0; + + /* clear out entries in temp1 which equal lastred */ + TLOOP(p) + if(temp1[p]+lastred == 0) + temp1[p] = 0; + + wrstate(i); + defact[i] = lastred; + flag = 0; + TLOOP(p0) + if((p1=temp1[p0]) != 0) { + if(p1 < 0) { + p1 = -p1; + goto exc; + } + if(p1 == ACCEPTCODE) { + p1 = -1; + goto exc; + } + if(p1 == ERRCODE) { + p1 = 0; + exc: + if(flag++ == 0) + Bprint(ftable, "-1, %d,\n", i); + Bprint(ftable, "\t%d, %d,\n", p0, p1); + zzexcp++; + continue; + } + Bprint(ftemp, "%d,%d,", p0, p1); + zzacent++; + } + if(flag) { + defact[i] = -2; + Bprint(ftable, "\t-2, %d,\n", lastred); + } + Bprint(ftemp, "\n"); +} + +/* + * writes state i + */ +void +wrstate(int i) +{ + int j0, j1; + Item *pp, *qq; + Wset *u; + + if(fdebug) { + if(lastred) { + Bprint(fdebug, " 0, /*%d*/\n", i); + } else { + Bprint(fdebug, " \""); + ITMLOOP(i, pp, qq) + Bprint(fdebug, "%s\\n", writem(pp->pitem)); + if(tystate[i] == MUSTLOOKAHEAD) + WSLOOP(wsets + (pstate[i+1] - pstate[i]), u) + if(*u->pitem < 0) + Bprint(fdebug, "%s\\n", writem(u->pitem)); + Bprint(fdebug, "\", /*%d*/\n", i); + } + } + if(foutput == 0) + return; + Bprint(foutput, "\nstate %d\n", i); + ITMLOOP(i, pp, qq) + Bprint(foutput, "\t%s\n", writem(pp->pitem)); + if(tystate[i] == MUSTLOOKAHEAD) + /* print out empty productions in closure */ + WSLOOP(wsets+(pstate[i+1]-pstate[i]), u) + if(*u->pitem < 0) + Bprint(foutput, "\t%s\n", writem(u->pitem)); + + /* check for state equal to another */ + TLOOP(j0) + if((j1=temp1[j0]) != 0) { + Bprint(foutput, "\n\t%s ", symnam(j0)); + /* shift, error, or accept */ + if(j1 > 0) { + if(j1 == ACCEPTCODE) + Bprint(foutput, "accept"); + else + if(j1 == ERRCODE) + Bprint(foutput, "error"); + else + Bprint(foutput, "shift %d", j1); + } else + Bprint(foutput, "reduce %d (src line %d)", -j1, rlines[-j1]); + } + + /* output the final production */ + if(lastred) + Bprint(foutput, "\n\t. reduce %d (src line %d)\n\n", + lastred, rlines[lastred]); + else + Bprint(foutput, "\n\t. error\n\n"); + + /* now, output nonterminal actions */ + j1 = ntokens; + for(j0 = 1; j0 <= nnonter; j0++) { + j1++; + if(temp1[j1]) + Bprint(foutput, "\t%s goto %d\n", symnam(j0+NTBASE), temp1[j1]); + } +} + +void +warray(char *s, int *v, int n) +{ + int i; + + Bprint(ftable, "static\tconst\tshort %s[] =\n{", s); + for(i=0;;) { + if(i%10 == 0) + Bprint(ftable, "\n"); + Bprint(ftable, "%4d", v[i]); + i++; + if(i >= n) { + Bprint(ftable, "\n};\n"); + break; + } + Bprint(ftable, ","); + } +} + +/* + * in order to free up the mem and amem arrays for the optimizer, + * and still be able to output yyr1, etc., after the sizes of + * the action array is known, we hide the nonterminals + * derived by productions in levprd. + */ + +void +hideprod(void) +{ + int i, j; + + j = 0; + levprd[0] = 0; + PLOOP(1, i) { + if(!(levprd[i] & REDFLAG)) { + j++; + if(foutput != 0) + Bprint(foutput, "Rule not reduced: %s\n", writem(prdptr[i])); + } + levprd[i] = *prdptr[i] - NTBASE; + } + if(j) + print("%d rules never reduced\n", j); +} + +void +callopt(void) +{ + int i, *p, j, k, *q; + + /* read the arrays from tempfile and set parameters */ + finput = Bopen(tempname, OREAD); + if(finput == 0) + error("optimizer cannot open tempfile"); + + pgo[0] = 0; + temp1[0] = 0; + nstate = 0; + nnonter = 0; + for(;;) { + switch(gtnm()) { + case '\n': + nstate++; + pmem--; + temp1[nstate] = pmem - mem0; + case ',': + continue; + case '$': + break; + default: + error("bad tempfile %s", tempname); + } + break; + } + + pmem--; + temp1[nstate] = yypgo[0] = pmem - mem0; + for(;;) { + switch(gtnm()) { + case '\n': + nnonter++; + yypgo[nnonter] = pmem-mem0; + case ',': + continue; + case -1: + break; + default: + error("bad tempfile"); + } + break; + } + pmem--; + yypgo[nnonter--] = pmem - mem0; + for(i = 0; i < nstate; i++) { + k = 32000; + j = 0; + q = mem0 + temp1[i+1]; + for(p = mem0 + temp1[i]; p < q ; p += 2) { + if(*p > j) + j = *p; + if(*p < k) + k = *p; + } + /* nontrivial situation */ + if(k <= j) { + /* j is now the range */ +/* j -= k; *//* call scj */ + if(k > maxoff) + maxoff = k; + } + tystate[i] = (temp1[i+1]-temp1[i]) + 2*j; + if(j > maxspr) + maxspr = j; + } + + /* initialize ggreed table */ + for(i = 1; i <= nnonter; i++) { + ggreed[i] = 1; + j = 0; + + /* minimum entry index is always 0 */ + q = mem0 + yypgo[i+1] - 1; + for(p = mem0+yypgo[i]; p < q ; p += 2) { + ggreed[i] += 2; + if(*p > j) + j = *p; + } + ggreed[i] = ggreed[i] + 2*j; + if(j > maxoff) + maxoff = j; + } + + /* now, prepare to put the shift actions into the amem array */ + for(i = 0; i < ACTSIZE; i++) + amem[i] = 0; + maxa = amem; + for(i = 0; i < nstate; i++) { + if(tystate[i] == 0 && adb > 1) + Bprint(ftable, "State %d: null\n", i); + indgo[i] = YYFLAG1; + } + while((i = nxti()) != NOMORE) + if(i >= 0) + stin(i); + else + gin(-i); + + /* print amem array */ + if(adb > 2 ) + for(p = amem; p <= maxa; p += 10) { + Bprint(ftable, "%4d ", (int)(p-amem)); + for(i = 0; i < 10; ++i) + Bprint(ftable, "%4d ", p[i]); + Bprint(ftable, "\n"); + } + + /* write out the output appropriate to the language */ + aoutput(); + osummary(); + ZAPFILE(tempname); +} + +void +gin(int i) +{ + int *p, *r, *s, *q1, *q2; + + /* enter gotos on nonterminal i into array amem */ + ggreed[i] = 0; + + q2 = mem0+ yypgo[i+1] - 1; + q1 = mem0 + yypgo[i]; + + /* now, find amem place for it */ + for(p = amem; p < &amem[ACTSIZE]; p++) { + if(*p) + continue; + for(r = q1; r < q2; r += 2) { + s = p + *r + 1; + if(*s) + goto nextgp; + if(s > maxa) + if((maxa = s) > &amem[ACTSIZE]) + error("a array overflow"); + } + /* we have found amem spot */ + *p = *q2; + if(p > maxa) + if((maxa = p) > &amem[ACTSIZE]) + error("a array overflow"); + for(r = q1; r < q2; r += 2) { + s = p + *r + 1; + *s = r[1]; + } + pgo[i] = p-amem; + if(adb > 1) + Bprint(ftable, "Nonterminal %d, entry at %d\n", i, pgo[i]); + return; + + nextgp:; + } + error("cannot place goto %d\n", i); +} + +void +stin(int i) +{ + int *r, *s, n, flag, j, *q1, *q2; + + tystate[i] = 0; + + /* enter state i into the amem array */ + q2 = mem0+temp1[i+1]; + q1 = mem0+temp1[i]; + /* find an acceptable place */ + for(n = -maxoff; n < ACTSIZE; n++) { + flag = 0; + for(r = q1; r < q2; r += 2) { + if(*r + n < 0) + goto nextn; + s = *r + n + amem; + if(*s == 0) + flag++; + else + if(*s != r[1]) + goto nextn; + } + + /* check that the position equals another only if the states are identical */ + for(j=0; j 1) + Bprint(ftable, + "State %d: entry at %d equals state %d\n", + i, n, j); + return; + } + + /* we have some disagreement */ + goto nextn; + } + } + + for(r = q1; r < q2; r += 2) { + if((s = *r+n+amem) >= &amem[ACTSIZE]) + error("out of space in optimizer a array"); + if(s > maxa) + maxa = s; + if(*s != 0 && *s != r[1]) + error("clobber of a array, pos'n %d, by %d", s-amem, r[1]); + *s = r[1]; + } + indgo[i] = n; + if(adb > 1) + Bprint(ftable, "State %d: entry at %d\n", i, indgo[i]); + return; + nextn:; + } + error("Error; failure to place state %d\n", i); +} + +/* + * finds the next i + */ +int +nxti(void) +{ + int i, max, maxi; + + max = 0; + maxi = 0; + for(i = 1; i <= nnonter; i++) + if(ggreed[i] >= max) { + max = ggreed[i]; + maxi = -i; + } + for(i = 0; i < nstate; ++i) + if(tystate[i] >= max) { + max = tystate[i]; + maxi = i; + } + if(nxdb) + Bprint(ftable, "nxti = %d, max = %d\n", maxi, max); + if(max == 0) + return NOMORE; + return maxi; +} + +/* + * write summary + */ +void +osummary(void) +{ + + int i, *p; + + if(foutput == 0) + return; + i = 0; + for(p = maxa; p >= amem; p--) + if(*p == 0) + i++; + + Bprint(foutput, "Optimizer space used: input %d/%d, output %d/%d\n", + (int)(pmem-mem0+1), MEMSIZE, (int)(maxa-amem+1), ACTSIZE); + Bprint(foutput, "%d table entries, %d zero\n", (int)(maxa-amem+1), i); + Bprint(foutput, "maximum spread: %d, maximum offset: %d\n", maxspr, maxoff); +} + +/* + * this version is for C + * write out the optimized parser + */ +void +aoutput(void) +{ + Bprint(ftable, "#define\tYYLAST\t%d\n", (int)(maxa-amem+1)); + arout("yyact", amem, (maxa-amem)+1); + arout("yypact", indgo, nstate); + arout("yypgo", pgo, nnonter+1); +} + +void +arout(char *s, int *v, int n) +{ + int i; + + Bprint(ftable, "static\tconst\tshort %s[] =\n{", s); + for(i = 0; i < n;) { + if(i%10 == 0) + Bprint(ftable, "\n"); + Bprint(ftable, "%4d", v[i]); + i++; + if(i == n) + Bprint(ftable, "\n};\n"); + else + Bprint(ftable, ","); + } +} + +/* + * read and convert an integer from the standard input + * return the terminating character + * blanks, tabs, and newlines are ignored + */ +int +gtnm(void) +{ + int sign, val, c; + + sign = 0; + val = 0; + while((c=Bgetrune(finput)) != Beof) { + if(isvalidchar(c) && isdigit(c)) { + val = val*10 + c-'0'; + continue; + } + if(c == '-') { + sign = 1; + continue; + } + break; + } + if(sign) + val = -val; + *pmem++ = val; + if(pmem >= &mem0[MEMSIZE]) + error("out of space"); + return c; +} diff --git a/include/bio.h b/include/bio.h new file mode 100644 index 0000000..07d7c5f --- /dev/null +++ b/include/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 /* 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/include/fmt.h b/include/fmt.h new file mode 100644 index 0000000..795e83e --- /dev/null +++ b/include/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 +#include + +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/include/lib9.h b/include/lib9.h new file mode 100644 index 0000000..07a1f97 --- /dev/null +++ b/include/lib9.h @@ -0,0 +1,2 @@ +#include +#include diff --git a/include/libc.h b/include/libc.h new file mode 100644 index 0000000..1e24f0b --- /dev/null +++ b/include/libc.h @@ -0,0 +1,950 @@ +// This file originated as Plan 9's /sys/include/libc.h. +// The plan9port-specific changes may be distributed +// using the license in ../src/lib9/LICENSE. + +/* + * 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 +#include + +/* + * 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 ) + * +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 ) + * +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 + * +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 ) + * +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 ) + * +typedef struct Fmt Fmt; +struct Fmt{ + uchar runes; + void *start; + void *to; + void *stop; + int (*flush)(Fmt *); + void *farg; + int nfmt; + va_list args; + int r; + int width; + int prec; + ulong flags; +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + + FmtFlag = FmtByte << 1 +}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int sprint(char*, char*, ...); +extern int fprint(int, char*, ...); +extern int vfprint(int, char*, va_list); + +extern int runesprint(Rune*, char*, ...); +extern int runesnprint(Rune*, int, char*, ...); +extern int runevsnprint(Rune*, int, char*, va_list); +extern Rune* runeseprint(Rune*, Rune*, char*, ...); +extern Rune* runevseprint(Rune*, Rune*, char*, va_list); +extern Rune* runesmprint(char*, ...); +extern Rune* runevsmprint(char*, va_list); + +extern int fmtfdinit(Fmt*, int, char*, int); +extern int fmtfdflush(Fmt*); +extern int fmtstrinit(Fmt*); +extern char* fmtstrflush(Fmt*); +extern int runefmtstrinit(Fmt*); +extern Rune* runefmtstrflush(Fmt*); + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int dorfmt(Fmt*, Rune*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); +extern int fmtrunestrcpy(Fmt*, Rune*); + */ + +/* + * error string for %r + * supplied on per os basis, not part of fmt library + * + * (provided by lib9, but declared in fmt.h) + * +extern int errfmt(Fmt *f); + */ + +/* + * quoted strings + */ +extern char *unquotestrdup(char*); +extern Rune *unquoterunestrdup(Rune*); +extern char *quotestrdup(char*); +extern Rune *quoterunestrdup(Rune*); +/* + * in fmt.h + * +extern void quotefmtinstall(void); +extern int quotestrfmt(Fmt*); +extern int quoterunestrfmt(Fmt*); + */ +#ifndef NOPLAN9DEFINES +#define doquote fmtdoquote +#endif +extern int needsrcquote(int); + +/* + * random number + */ +extern void p9srand(long); +extern int p9rand(void); + +extern int p9nrand(int); +extern long p9lrand(void); +extern long p9lnrand(long); +extern double p9frand(void); +extern ulong truerand(void); /* uses /dev/random */ +extern ulong ntruerand(ulong); /* uses /dev/random */ + +#ifndef NOPLAN9DEFINES +#define srand p9srand +#define rand p9rand +#define nrand p9nrand +#define lrand p9lrand +#define lnrand p9lnrand +#define frand p9frand +#endif + +/* + * math + */ +extern ulong getfcr(void); +extern void setfsr(ulong); +extern ulong getfsr(void); +extern void setfcr(ulong); +extern double NaN(void); +extern double Inf(int); +extern int isNaN(double); +extern int isInf(double, int); +extern ulong umuldiv(ulong, ulong, ulong); +extern long muldiv(long, long, long); + +/* + * provided by math.h + * +extern double pow(double, double); +extern double atan2(double, double); +extern double fabs(double); +extern double atan(double); +extern double log(double); +extern double log10(double); +extern double exp(double); +extern double floor(double); +extern double ceil(double); +extern double hypot(double, double); +extern double sin(double); +extern double cos(double); +extern double tan(double); +extern double asin(double); +extern double acos(double); +extern double sinh(double); +extern double cosh(double); +extern double tanh(double); +extern double sqrt(double); +extern double fmod(double, double); +#define HUGE 3.4028234e38 +#define PIO2 1.570796326794896619231e0 +#define PI (PIO2+PIO2) + */ +#define PI M_PI +#define PIO2 M_PI_2 + +/* + * Time-of-day + */ + +typedef +struct Tm +{ + int sec; + int min; + int hour; + int mday; + int mon; + int year; + int wday; + int yday; + char zone[4]; + int tzoff; +} Tm; + +extern Tm* p9gmtime(long); +extern Tm* p9localtime(long); +extern char* p9asctime(Tm*); +extern char* p9ctime(long); +extern double p9cputime(void); +extern long p9times(long*); +extern long p9tm2sec(Tm*); +extern vlong p9nsec(void); + +#ifndef NOPLAN9DEFINES +#define gmtime p9gmtime +#define localtime p9localtime +#define asctime p9asctime +#define ctime p9ctime +#define cputime p9cputime +#define times p9times +#define tm2sec p9tm2sec +#define nsec p9nsec +#endif + +/* + * one-of-a-kind + */ +enum +{ + PNPROC = 1, + PNGROUP = 2 +}; + +/* extern int abs(int); */ +extern int p9atexit(void(*)(void)); +extern void p9atexitdont(void(*)(void)); +extern int atnotify(int(*)(void*, char*), int); +/* + * +extern double atof(char*); + */ +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 p9frexp(double, int*); +extern ulong getcallerpc(void*); +#if defined(__GNUC__) || defined(__clang__) || defined(__IBMC__) +#define getcallerpc(x) ((ulong)__builtin_return_address(0)) +#endif +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); */ +/* extern double ldexp(double, int); */ +extern void p9longjmp(p9jmp_buf, int); +extern char* mktemp(char*); +extern int opentemp(char*, int); +/* extern double modf(double, double*); */ +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*); */ +extern char* searchpath(char*); +/* extern int p9setjmp(p9jmp_buf); */ +#define p9setjmp(b) sigsetjmp((void*)(b), 1) +/* + * +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); */ +/* extern int toupper(int); */ +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 +#undef frexp +#define frexp p9frexp +#define getenv p9getenv +#define getwd p9getwd +#define longjmp p9longjmp +#undef setjmp +#define setjmp p9setjmp +#define putenv p9putenv +#define notejmp p9notejmp +#define jmp_buf p9jmp_buf +#define time p9time +#define pow10 p9pow10 +#define strtod fmtstrtod +#define charstod fmtcharstod +#define syslog p9syslog +#endif + +/* + * just enough information so that libc can be + * properly locked without dragging in all of libthread + */ +typedef struct _Thread _Thread; +typedef struct _Threadlist _Threadlist; +struct _Threadlist +{ + _Thread *head; + _Thread *tail; +}; + +extern _Thread *(*threadnow)(void); + +/* + * synchronization + */ +typedef struct Lock Lock; +struct Lock +{ + int init; + pthread_mutex_t mutex; + 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, void *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*); */ +extern int p9chdir(char*); +extern int p9close(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 +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); */ +extern int remove(const char*); +/* extern void* sbrk(ulong); */ +/* 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); +/* +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 +#undef close +#define close p9close +#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); */ +/* 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/include/u.h b/include/u.h new file mode 100644 index 0000000..856e10f --- /dev/null +++ b/include/u.h @@ -0,0 +1,195 @@ +// See ../src/lib9/LICENSE + +#ifndef _U_H_ +#define _U_H_ 1 +#if defined(__cplusplus) +extern "C" { +#endif + +#define HAS_SYS_TERMIOS 1 + +#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 +#define _DEFAULT_SOURCE 1 +#if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__AIX__) +# define _XOPEN_SOURCE 1000 +# define _XOPEN_SOURCE_EXTENDED 1 +#endif +#if defined(__FreeBSD__) +# include + /* for strtoll */ +# undef __ISO_C_VISIBLE +# define __ISO_C_VISIBLE 1999 +# undef __LONG_LONG_SUPPORTED +# define __LONG_LONG_SUPPORTED +#endif +#if defined(__AIX__) +# define _ALL_SOURCE +# undef HAS_SYS_TERMIOS +#endif +#define _LARGEFILE64_SOURCE 1 +#define _FILE_OFFSET_BITS 64 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* 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 +# include +# if defined(__USE_MISC) +# undef _NEEDUSHORT +# undef _NEEDUINT +# undef _NEEDULONG +# endif +#elif defined(__sun__) +# include +# include +# undef _NEEDUSHORT +# undef _NEEDUINT +# undef _NEEDULONG +# define nil 0 /* no cast to void* */ +#elif defined(__FreeBSD__) +# include +# include +# include +# if !defined(_POSIX_SOURCE) +# undef _NEEDUSHORT +# undef _NEEDUINT +# endif +#elif defined(__APPLE__) +# include +# include +# 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 +# include +# include +# undef _NEEDUSHORT +# undef _NEEDUINT +# undef _NEEDULONG +#elif defined(__OpenBSD__) +# include +# include +# undef _NEEDUSHORT +# undef _NEEDUINT +# undef _NEEDULONG +#else + /* No idea what system this is -- try some defaults */ +# include +#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 uvlong u64int; +typedef vlong 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 uint u32int; +typedef int s32int; + +typedef u32int uint32; +typedef s32int int32; +typedef u16int uint16; +typedef s16int int16; +typedef u64int uint64; +typedef s64int int64; +typedef u8int uint8; +typedef s8int int8; + +#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/include/utf.h b/include/utf.h new file mode 100644 index 0000000..44052f4 --- /dev/null +++ b/include/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/lib/bio/_lib9.h b/lib/bio/_lib9.h new file mode 100644 index 0000000..843f755 --- /dev/null +++ b/lib/bio/_lib9.h @@ -0,0 +1,12 @@ +#include +#include +#include +#include +#include + +#define OREAD O_RDONLY +#define OWRITE O_WRONLY + +#include + +#define nil ((void*)0) diff --git a/lib/bio/bbuffered.c b/lib/bio/bbuffered.c new file mode 100644 index 0000000..dfc0cf5 --- /dev/null +++ b/lib/bio/bbuffered.c @@ -0,0 +1,20 @@ +#include "lib9.h" +#include + +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/lib/bio/bcat.c b/lib/bio/bcat.c new file mode 100644 index 0000000..cc8e53d --- /dev/null +++ b/lib/bio/bcat.c @@ -0,0 +1,46 @@ +#include +#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, ""); + }else{ + for(i=1; i + +int +Bfildes(Biobuf *bp) +{ + + return bp->fid; +} diff --git a/lib/bio/bflush.c b/lib/bio/bflush.c new file mode 100644 index 0000000..0ab8126 --- /dev/null +++ b/lib/bio/bflush.c @@ -0,0 +1,33 @@ +#include "lib9.h" +#include + +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/lib/bio/bgetc.c b/lib/bio/bgetc.c new file mode 100644 index 0000000..741b692 --- /dev/null +++ b/lib/bio/bgetc.c @@ -0,0 +1,53 @@ +#include "lib9.h" +#include + +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/lib/bio/bgetd.c b/lib/bio/bgetd.c new file mode 100644 index 0000000..e7dd305 --- /dev/null +++ b/lib/bio/bgetd.c @@ -0,0 +1,36 @@ +#include "lib9.h" +#include + +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/lib/bio/bgetrune.c b/lib/bio/bgetrune.c new file mode 100644 index 0000000..46991e1 --- /dev/null +++ b/lib/bio/bgetrune.c @@ -0,0 +1,47 @@ +#include "lib9.h" +#include +#include + +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/lib/bio/binit.c b/lib/bio/binit.c new file mode 100644 index 0000000..95a70a2 --- /dev/null +++ b/lib/bio/binit.c @@ -0,0 +1,155 @@ +#include "lib9.h" +#include + +enum +{ + MAXBUFS = 20 +}; + +static Biobuf* wbufs[MAXBUFS]; +static int atexitflag; + +static +void +batexit(void) +{ + Biobuf *bp; + int i; + + for(i=0; istate = 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) +{ + int ret; + + deinstall(bp); + ret = Bflush(bp); + if(bp->flag == Bmagic) { + bp->flag = 0; + if(close(bp->fid) < 0) + ret = -1; + free(bp); + } + return ret; +} diff --git a/lib/bio/boffset.c b/lib/bio/boffset.c new file mode 100644 index 0000000..88c4c35 --- /dev/null +++ b/lib/bio/boffset.c @@ -0,0 +1,25 @@ +#include "lib9.h" +#include + +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/lib/bio/bprint.c b/lib/bio/bprint.c new file mode 100644 index 0000000..8951512 --- /dev/null +++ b/lib/bio/bprint.c @@ -0,0 +1,14 @@ +#include "lib9.h" +#include + +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/lib/bio/bputc.c b/lib/bio/bputc.c new file mode 100644 index 0000000..d05bada --- /dev/null +++ b/lib/bio/bputc.c @@ -0,0 +1,20 @@ +#include "lib9.h" +#include + +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/lib/bio/bputrune.c b/lib/bio/bputrune.c new file mode 100644 index 0000000..651ae7b --- /dev/null +++ b/lib/bio/bputrune.c @@ -0,0 +1,23 @@ +#include "lib9.h" +#include +#include + +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/lib/bio/brdline.c b/lib/bio/brdline.c new file mode 100644 index 0000000..4ac6316 --- /dev/null +++ b/lib/bio/brdline.c @@ -0,0 +1,94 @@ +#include "lib9.h" +#include + +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/lib/bio/brdstr.c b/lib/bio/brdstr.c new file mode 100644 index 0000000..30d40db --- /dev/null +++ b/lib/bio/brdstr.c @@ -0,0 +1,111 @@ +#include "lib9.h" +#include + +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/lib/bio/bread.c b/lib/bio/bread.c new file mode 100644 index 0000000..0254d01 --- /dev/null +++ b/lib/bio/bread.c @@ -0,0 +1,45 @@ +#include "lib9.h" +#include + +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/lib/bio/bseek.c b/lib/bio/bseek.c new file mode 100644 index 0000000..b3f8191 --- /dev/null +++ b/lib/bio/bseek.c @@ -0,0 +1,60 @@ +#include "lib9.h" +#include + +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/lib/bio/bvprint.c b/lib/bio/bvprint.c new file mode 100644 index 0000000..0b1aaf6 --- /dev/null +++ b/lib/bio/bvprint.c @@ -0,0 +1,38 @@ +#include "lib9.h" +#include + +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/lib/bio/bwrite.c b/lib/bio/bwrite.c new file mode 100644 index 0000000..2dfeaaa --- /dev/null +++ b/lib/bio/bwrite.c @@ -0,0 +1,38 @@ +#include "lib9.h" +#include + +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/lib/bio/lib9.std.h b/lib/bio/lib9.std.h new file mode 100644 index 0000000..e489702 --- /dev/null +++ b/lib/bio/lib9.std.h @@ -0,0 +1,25 @@ +#define _FILE_OFFSET_BITS 64 +#define _LARGEFILE64_SOURCE + +#include +#include + +#include +#include +#include +#include + +#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/lib/bio/libbio.sh.build b/lib/bio/libbio.sh.build new file mode 100755 index 0000000..0ad6b53 --- /dev/null +++ b/lib/bio/libbio.sh.build @@ -0,0 +1,47 @@ +#!/bin/sh + +git clean -xdf . + +cc \ + -I ../../include\ + -DPLAN9PORT \ + -O0 \ + -c \ + -g -ggdb \ + -Wall \ + -Wno-parentheses \ + -Wno-missing-braces \ + -Wno-switch \ + -Wno-comment \ + -Wno-sign-compare \ + -Wno-unknown-pragmas \ + -Wno-misleading-indentation \ + -Wno-stringop-truncation \ + -Wno-stringop-overflow \ + -Wno-format-truncation \ + -fno-omit-frame-pointer \ + -fsigned-char \ + -fcommon \ + bbuffered.c\ + bfildes.c\ + bflush.c\ + bgetc.c\ + bgetrune.c\ + bgetd.c\ + binit.c\ + boffset.c\ + bprint.c\ + bputc.c\ + bputrune.c\ + brdline.c\ + brdstr.c\ + bread.c\ + bseek.c\ + bvprint.c\ + bwrite.c + +mkdir -p $JEHANNE/hacking/lib/ +ar rcs $JEHANNE/hacking/lib/libbio.a *.o + +git clean -xdf . + diff --git a/lib/bio/mkfile b/lib/bio/mkfile new file mode 100644 index 0000000..cbe8dac --- /dev/null +++ b/lib/bio/mkfile @@ -0,0 +1,31 @@ +<$PLAN9/src/mkhdr + +LIB=libbio.a + +OFILES=\ + bbuffered.$O\ + bfildes.$O\ + bflush.$O\ + bgetc.$O\ + bgetrune.$O\ + bgetd.$O\ + binit.$O\ + boffset.$O\ + bprint.$O\ + bputc.$O\ + bputrune.$O\ + brdline.$O\ + brdstr.$O\ + bread.$O\ + bseek.$O\ + bvprint.$O\ + bwrite.$O\ + +HFILES=\ + $PLAN9/include/bio.h\ + +<$PLAN9/src/mksyslib + +bcat: bcat.$O $PLAN9/lib/$LIB + $LD -o bcat bcat.$O -lbio -l9 + diff --git a/lib/bio/portdate b/lib/bio/portdate new file mode 100644 index 0000000..cbcc88c --- /dev/null +++ b/lib/bio/portdate @@ -0,0 +1,18 @@ +bbuffered.c 2004/1225 +bcat.c 2004/1225 +bfildes.c 2004/1225 +bflush.c 2004/1225 +bfmt.c 2004/1225 +bgetc.c 2004/1225 +bgetd.c 2004/1225 +bgetrune.c 2004/1225 +binit.c 2004/1225 +boffset.c 2004/1225 +bprint.c 2004/1225 +bputc.c 2004/1225 +bputrune.c 2004/1225 +brdline.c 2004/1225 +brdstr.c 2004/1225 +bread.c 2004/1225 +bseek.c 2004/1225 +bwrite.c 2004/1225 diff --git a/lib/lib9/LICENSE b/lib/lib9/LICENSE new file mode 100644 index 0000000..c6665b2 --- /dev/null +++ b/lib/lib9/LICENSE @@ -0,0 +1,92 @@ +The files listed below were written from scrach for plan9port +and do not derive from the Plan 9 from Bell Labs distribution. +They are made available under an MIT-style license, using the +same terms as the main distribution. + + ../../include/u.h + non-Plan 9 code in ../../include/libc.h + _exits.c + _p9dialparse.c + _p9dir.c + _p9translate.c + announce.c + argv0.c + atoi.c + atol.c + atoll.c + await.c + create.c + debugmalloc.c + dial.c + dirfstat.c + dirfwstat.c + dirstat.c + dirwstat.c + dup.c + errstr.c + exec.c + execl.c + exitcode.c + fmtlock2.c + fork.c + get9root.c + getcallerpc-386.c + getcallerpc-arm.c + getcallerpc-power.c + getcallerpc-sun4u.s + getcallerpc-x86_64.c + getenv.c + getnetconn.c + getns.c + getuser.c + getwd.c + jmp.c + lock.c + main.c + malloc.c + malloctag.c + mallocz.c + nan.c + needstack.c + notify.c + open.c + opentemp.c + pin.c + pipe.c + post9p.c + postnote.c + priv.c + qlock.c + readcons.c + rfork.c + searchpath.c + seek.c + sendfd.c + sleep.c + strdup.c + sysfatal.c + sysname.c + time.c + truerand.c + udp.c + unsharp.c + +Copyright 2001-2007 Russ Cox. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/lib/lib9/_exits.c b/lib/lib9/_exits.c new file mode 100644 index 0000000..a17c25a --- /dev/null +++ b/lib/lib9/_exits.c @@ -0,0 +1,10 @@ +#include +#include + +void +_exits(char *s) +{ + if(s == 0 || *s == 0) + _exit(0); + _exit(exitcode(s)); +} diff --git a/lib/lib9/_p9dialparse.c b/lib/lib9/_p9dialparse.c new file mode 100644 index 0000000..4e56f5e --- /dev/null +++ b/lib/lib9/_p9dialparse.c @@ -0,0 +1,185 @@ +#include +#define NOPLAN9DEFINES +#include + +#include +#include +#include +#include +#include + +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 +setport(struct sockaddr_storage *ss, int port) +{ + switch(ss->ss_family){ + case AF_INET: + ((struct sockaddr_in*)ss)->sin_port = htons(port); + break; + case AF_INET6: + ((struct sockaddr_in6*)ss)->sin6_port = htons(port); + break; + default: + errstr("unknown protocol family %d", ss->ss_family); + return -1; + } + return 0; +} + +int +p9dialparse(char *addr, char **pnet, char **punix, void *phost, int *pport) +{ + char *net, *host, *port, *e; + int i; + struct servent *se; + struct hostent *he; + struct sockaddr_storage *ss; + struct addrinfo *result; + + ss = phost; + + memset(ss, 0, sizeof *ss); + + *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 ((struct sockaddr_un*)ss)->sun_path){ + werrstr("unix socket name too long"); + return -1; + } + *punix = host; + *pnet = "unix"; + ss->ss_family = AF_UNIX; + strcpy(((struct sockaddr_un*)ss)->sun_path, host); + *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){ + ss->ss_family = AF_INET6; + ((struct sockaddr_in6*)ss)->sin6_addr = in6addr_any; + }else if((he = gethostbyname(host)) != nil && he->h_addr_list[0] != nil){ + ss->ss_family = he->h_addrtype; + switch(ss->ss_family){ + case AF_INET: + ((struct sockaddr_in*)ss)->sin_addr = *(struct in_addr*) *(he->h_addr_list); + break; + case AF_INET6: + ((struct sockaddr_in6*)ss)->sin6_addr = *(struct in6_addr*) *(he->h_addr_list); + break; + default: + errstr("unknown protocol family %d", ss->ss_family); + return -1; + } + }else if(getaddrinfo(host, NULL, NULL, &result) == 0) { + switch (result->ai_family) { + case AF_INET: + memmove((struct sockaddr_in*)ss, result->ai_addr, result->ai_addrlen); + break; + case AF_INET6: + memmove((struct sockaddr_in6*)ss, result->ai_addr, result->ai_addrlen); + break; + default: + errstr("unknown protocol family %d", ss->ss_family); + return -1; + } + }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 setport(ss, *pport); + } + } + } + + for(i=0; is_port); + return setport(ss, *pport); + } + werrstr("unknown service %s!*!%s", net, port); + return -1; +} diff --git a/lib/lib9/_p9dir.c b/lib/lib9/_p9dir.c new file mode 100644 index 0000000..58c6357 --- /dev/null +++ b/lib/lib9/_p9dir.c @@ -0,0 +1,242 @@ +#include +#define NOPLAN9DEFINES +#include +#include +#include +#include +#include +#include + +#if defined(__APPLE__) +#define _HAVESTGEN +#include +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 +#include +#include +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 +#include +#include +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 +#include +#include +#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)){ + if((fd = open(name, O_RDONLY)) >= 0){ + d->length = disksize(fd, st); + close(fd); + } + } + } + + return sz; +} diff --git a/lib/lib9/announce.c b/lib/lib9/announce.c new file mode 100644 index 0000000..1587988 --- /dev/null +++ b/lib/lib9/announce.c @@ -0,0 +1,155 @@ +#include +#define NOPLAN9DEFINES +#include + +#include +#include +#include +#include +#include + +#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 + +static int +addrlen(struct sockaddr_storage *ss) +{ + switch(ss->ss_family){ + case AF_INET: + return sizeof(struct sockaddr_in); + case AF_INET6: + return sizeof(struct sockaddr_in6); + case AF_UNIX: + return sizeof(struct sockaddr_un); + } + return 0; +} + +int +p9announce(char *addr, char *dir) +{ + int proto; + char *buf, *unix; + char *net; + int port, s; + int n; + socklen_t sn; + struct sockaddr_storage ss; + + buf = strdup(addr); + if(buf == nil) + return -1; + + if(p9dialparse(buf, &net, &unix, &ss, &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); + + if((s = socket(ss.ss_family, 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*)&ss, addrlen(&ss)) < 0){ + close(s); + return -1; + } + if(proto == SOCK_STREAM){ + listen(s, 8); + putfd(dir, s); + } + return s; + +Unix: + if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0) + return -1; + if(bind(s, (struct sockaddr*)&ss, addrlen(&ss)) < 0){ + if(errno == EADDRINUSE + && connect(s, (struct sockaddr*)&ss, addrlen(&ss)) < 0 + && errno == ECONNREFUSED){ + /* dead socket, so remove it */ + remove(unix); + close(s); + if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0) + return -1; + if(bind(s, (struct sockaddr*)&ss, addrlen(&ss)) >= 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/lib/lib9/argv0.c b/lib/lib9/argv0.c new file mode 100644 index 0000000..4c61f44 --- /dev/null +++ b/lib/lib9/argv0.c @@ -0,0 +1,9 @@ +#include + +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/lib/lib9/atexit.c b/lib/lib9/atexit.c new file mode 100644 index 0000000..7fcf0f3 --- /dev/null +++ b/lib/lib9/atexit.c @@ -0,0 +1,56 @@ +#include +#include + +#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= 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/lib/lib9/atnotify.c b/lib/lib9/atnotify.c new file mode 100644 index 0000000..60e8ad0 --- /dev/null +++ b/lib/lib9/atnotify.c @@ -0,0 +1,58 @@ +#include +#include + +#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 +#include + +int +atoi(char *s) +{ + return strtol(s, 0, 0); +} diff --git a/lib/lib9/atol.c b/lib/lib9/atol.c new file mode 100644 index 0000000..6ead300 --- /dev/null +++ b/lib/lib9/atol.c @@ -0,0 +1,8 @@ +#include +#include + +long +atol(char *s) +{ + return strtol(s, 0, 0); +} diff --git a/lib/lib9/atoll.c b/lib/lib9/atoll.c new file mode 100644 index 0000000..d4ce0a9 --- /dev/null +++ b/lib/lib9/atoll.c @@ -0,0 +1,8 @@ +#include +#include + +vlong +atoll(char *s) +{ + return strtoll(s, 0, 0); +} diff --git a/lib/lib9/await.c b/lib/lib9/await.c new file mode 100644 index 0000000..7aba5b5 --- /dev/null +++ b/lib/lib9/await.c @@ -0,0 +1,136 @@ +#define NOPLAN9DEFINES +#include +#include + +#include +#include +#include +#include +#include + +#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 +#include + +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/lib/lib9/cistrncmp.c b/lib/lib9/cistrncmp.c new file mode 100644 index 0000000..8f24d41 --- /dev/null +++ b/lib/lib9/cistrncmp.c @@ -0,0 +1,28 @@ +#include +#include + +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/lib/lib9/cistrstr.c b/lib/lib9/cistrstr.c new file mode 100644 index 0000000..0a11322 --- /dev/null +++ b/lib/lib9/cistrstr.c @@ -0,0 +1,23 @@ +#include +#include + +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/lib/lib9/cleanname.c b/lib/lib9/cleanname.c new file mode 100644 index 0000000..cfcb482 --- /dev/null +++ b/lib/lib9/cleanname.c @@ -0,0 +1,52 @@ +#include +#include + +/* + * 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/lib/lib9/convD2M.c b/lib/lib9/convD2M.c new file mode 100644 index 0000000..e1ef00c --- /dev/null +++ b/lib/lib9/convD2M.c @@ -0,0 +1,101 @@ +#include +#include +#include + +uint +sizeD2M(Dir *d) +{ + 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; + + ns = 0; + for(i = 0; i < nstr; i++) + if(sv[i]) + ns += strlen(sv[i]); + + return fixlen + ns; +} + +uint +convD2M(Dir *d, uchar *buf, uint nbuf) +{ + 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; + + 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(ss != p - buf) + return 0; + + return p - buf; +} diff --git a/lib/lib9/convM2D.c b/lib/lib9/convM2D.c new file mode 100644 index 0000000..410fa60 --- /dev/null +++ b/lib/lib9/convM2D.c @@ -0,0 +1,98 @@ +#include +#include +#include + +int +statcheck(uchar *buf, uint nbuf) +{ + uchar *ebuf; + int i, nstr; + + ebuf = buf + nbuf; + + if(nbuf < STATFIXLEN || nbuf != BIT16SZ + GBIT16(buf)) + return -1; + + buf += STATFIXLEN - 4 * BIT16SZ; + + nstr = 4; + for(i = 0; i < nstr; i++){ + if(buf + BIT16SZ > ebuf) + return -1; + buf += BIT16SZ + GBIT16(buf); + } + + if(buf != ebuf) + return -1; + + return 0; +} + +static char nullstring[] = ""; + +uint +convM2D(uchar *buf, uint nbuf, Dir *d, char *strs) +{ + uchar *p, *ebuf; + char *sv[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; + 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(strs){ + d->name = sv[0]; + d->uid = sv[1]; + d->gid = sv[2]; + d->muid = sv[3]; + d->ext = nullstring; + }else{ + d->name = nullstring; + d->uid = nullstring; + d->gid = nullstring; + d->muid = nullstring; + d->ext = nullstring; + } + + return p - buf; +} diff --git a/lib/lib9/convM2S.c b/lib/lib9/convM2S.c new file mode 100644 index 0000000..a9c9205 --- /dev/null +++ b/lib/lib9/convM2S.c @@ -0,0 +1,326 @@ +#include +#include +#include + +static +uchar* +gstring(uchar *p, uchar *ep, char **s) +{ + uint n; + + if(p+BIT16SZ > ep) + return nil; + n = GBIT16(p); + p += BIT16SZ - 1; + if(p+n+1 > ep) + return nil; + /* move it down, on top of count, to make room for '\0' */ + memmove(p, p + 1, n); + p[n] = '\0'; + *s = (char*)p; + p += n+1; + return p; +} + +static +uchar* +gqid(uchar *p, uchar *ep, Qid *q) +{ + if(p+QIDSZ > ep) + return nil; + q->type = GBIT8(p); + p += BIT8SZ; + q->vers = GBIT32(p); + p += BIT32SZ; + q->path = GBIT64(p); + p += BIT64SZ; + return p; +} + +/* + * no syntactic checks. + * three causes for error: + * 1. message size field is incorrect + * 2. input buffer too short for its own data (counts too long, etc.) + * 3. too many names or qids + * gqid() and gstring() return nil if they would reach beyond buffer. + * main switch statement checks range and also can fall through + * to test at end of routine. + */ +uint +convM2S(uchar *ap, uint nap, Fcall *f) +{ + uchar *p, *ep; + uint i, size; + + p = ap; + ep = p + nap; + + if(p+BIT32SZ+BIT8SZ+BIT16SZ > ep) + return 0; + size = GBIT32(p); + p += BIT32SZ; + + if(size < BIT32SZ+BIT8SZ+BIT16SZ) + return 0; + + f->type = GBIT8(p); + p += BIT8SZ; + f->tag = GBIT16(p); + p += BIT16SZ; + + switch(f->type) + { + default: + return 0; + + case Tversion: + if(p+BIT32SZ > ep) + return 0; + f->msize = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->version); + break; + + case Tflush: + if(p+BIT16SZ > ep) + return 0; + f->oldtag = GBIT16(p); + p += BIT16SZ; + break; + + case Tauth: + if(p+BIT32SZ > ep) + return 0; + f->afid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->uname); + if(p == nil) + break; + p = gstring(p, ep, &f->aname); + if(p == nil) + break; + f->uidnum = NOUID; + 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; + 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; inwname; i++){ + p = gstring(p, ep, &f->wname[i]); + if(p == nil) + break; + } + break; + + case Topen: + case Topenfd: + if(p+BIT32SZ+BIT8SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + + case Tcreate: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->name); + if(p == nil) + break; + if(p+BIT32SZ+BIT8SZ > ep) + return 0; + f->perm = GBIT32(p); + p += BIT32SZ; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + + case Tread: + if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + break; + + case Twrite: + if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + if(p+f->count > ep) + return 0; + f->data = (char*)p; + p += f->count; + break; + + case Tclunk: + case Tremove: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + + case Tstat: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + + case Twstat: + if(p+BIT32SZ+BIT16SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->nstat = GBIT16(p); + p += BIT16SZ; + if(p+f->nstat > ep) + return 0; + f->stat = p; + p += f->nstat; + break; + +/* + */ + case Rversion: + if(p+BIT32SZ > ep) + return 0; + f->msize = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->version); + break; + + case Rerror: + p = gstring(p, ep, &f->ename); + f->errornum = 0; + 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; inwqid; i++){ + p = gqid(p, ep, &f->wqid[i]); + if(p == nil) + break; + } + break; + + case Ropen: + case Ropenfd: + case Rcreate: + p = gqid(p, ep, &f->qid); + if(p == nil) + break; + if(p+BIT32SZ > ep) + return 0; + f->iounit = GBIT32(p); + p += BIT32SZ; + if(f->type == Ropenfd){ + if(p+BIT32SZ > ep) + return 0; + f->unixfd = GBIT32(p); + p += BIT32SZ; + } + break; + + case Rread: + if(p+BIT32SZ > ep) + return 0; + f->count = GBIT32(p); + p += BIT32SZ; + if(p+f->count > ep) + return 0; + f->data = (char*)p; + p += f->count; + break; + + case Rwrite: + if(p+BIT32SZ > ep) + return 0; + f->count = GBIT32(p); + p += BIT32SZ; + break; + + case Rclunk: + case Rremove: + break; + + case Rstat: + if(p+BIT16SZ > ep) + return 0; + f->nstat = GBIT16(p); + p += BIT16SZ; + if(p+f->nstat > ep) + return 0; + f->stat = p; + p += f->nstat; + break; + + case Rwstat: + break; + } + + if(p==nil || p>ep) + return 0; + if(ap+size == p) + return size; + return 0; +} diff --git a/lib/lib9/convS2M.c b/lib/lib9/convS2M.c new file mode 100644 index 0000000..6e9d271 --- /dev/null +++ b/lib/lib9/convS2M.c @@ -0,0 +1,399 @@ +#include +#include +#include + +static +uchar* +pstring(uchar *p, char *s) +{ + uint n; + + if(s == nil){ + PBIT16(p, 0); + p += BIT16SZ; + return p; + } + + n = strlen(s); + PBIT16(p, n); + p += BIT16SZ; + memmove(p, s, n); + p += n; + return p; +} + +static +uchar* +pqid(uchar *p, Qid *q) +{ + PBIT8(p, q->type); + p += BIT8SZ; + PBIT32(p, q->vers); + p += BIT32SZ; + PBIT64(p, q->path); + p += BIT64SZ; + return p; +} + +static +uint +stringsz(char *s) +{ + if(s == nil) + return BIT16SZ; + + return BIT16SZ+strlen(s); +} + +uint +sizeS2M(Fcall *f) +{ + uint n; + int i; + + n = 0; + n += BIT32SZ; /* size */ + n += BIT8SZ; /* type */ + n += BIT16SZ; /* tag */ + + switch(f->type) + { + default: + return 0; + + case Tversion: + n += BIT32SZ; + n += stringsz(f->version); + break; + + case Tflush: + n += BIT16SZ; + break; + + case Tauth: + n += BIT32SZ; + n += stringsz(f->uname); + n += stringsz(f->aname); + break; + + case Tattach: + n += BIT32SZ; + n += BIT32SZ; + n += stringsz(f->uname); + n += stringsz(f->aname); + break; + + case Twalk: + n += BIT32SZ; + n += BIT32SZ; + n += BIT16SZ; + for(i=0; inwname; i++) + n += stringsz(f->wname[i]); + break; + + case Topen: + case Topenfd: + n += BIT32SZ; + n += BIT8SZ; + break; + + case Tcreate: + n += BIT32SZ; + n += stringsz(f->name); + n += BIT32SZ; + n += BIT8SZ; + break; + + case Tread: + n += BIT32SZ; + n += BIT64SZ; + n += BIT32SZ; + break; + + case Twrite: + n += BIT32SZ; + n += BIT64SZ; + n += BIT32SZ; + n += f->count; + break; + + case Tclunk: + case Tremove: + n += BIT32SZ; + break; + + case Tstat: + n += BIT32SZ; + break; + + case Twstat: + n += BIT32SZ; + n += BIT16SZ; + n += f->nstat; + break; +/* + */ + + case Rversion: + n += BIT32SZ; + n += stringsz(f->version); + break; + + case Rerror: + n += stringsz(f->ename); + break; + + case Rflush: + break; + + case Rauth: + n += QIDSZ; + break; + + case Rattach: + n += QIDSZ; + break; + + case Rwalk: + n += BIT16SZ; + n += f->nwqid*QIDSZ; + break; + + case Ropen: + case Rcreate: + n += QIDSZ; + n += BIT32SZ; + break; + + case Ropenfd: + n += QIDSZ; + n += BIT32SZ; + n += BIT32SZ; + break; + + case Rread: + n += BIT32SZ; + n += f->count; + break; + + case Rwrite: + n += BIT32SZ; + break; + + case Rclunk: + break; + + case Rremove: + break; + + case Rstat: + n += BIT16SZ; + n += f->nstat; + break; + + case Rwstat: + break; + } + return n; +} + +uint +convS2M(Fcall *f, uchar *ap, uint nap) +{ + uchar *p; + uint i, size; + + size = sizeS2M(f); + if(size == 0) + return 0; + if(size > nap) + return 0; + + p = (uchar*)ap; + + PBIT32(p, size); + p += BIT32SZ; + PBIT8(p, f->type); + p += BIT8SZ; + PBIT16(p, f->tag); + p += BIT16SZ; + + switch(f->type) + { + default: + return 0; + + case Tversion: + PBIT32(p, f->msize); + p += BIT32SZ; + p = pstring(p, f->version); + break; + + case Tflush: + PBIT16(p, f->oldtag); + p += BIT16SZ; + break; + + case Tauth: + PBIT32(p, f->afid); + p += BIT32SZ; + p = pstring(p, f->uname); + p = pstring(p, f->aname); + break; + + case Tattach: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT32(p, f->afid); + p += BIT32SZ; + p = pstring(p, f->uname); + p = pstring(p, f->aname); + break; + + case Twalk: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT32(p, f->newfid); + p += BIT32SZ; + PBIT16(p, f->nwname); + p += BIT16SZ; + if(f->nwname > MAXWELEM) + return 0; + for(i=0; inwname; i++) + p = pstring(p, f->wname[i]); + break; + + case Topen: + case Topenfd: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT8(p, f->mode); + p += BIT8SZ; + break; + + case Tcreate: + PBIT32(p, f->fid); + p += BIT32SZ; + p = pstring(p, f->name); + PBIT32(p, f->perm); + p += BIT32SZ; + PBIT8(p, f->mode); + p += BIT8SZ; + break; + + case Tread: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT32(p, f->count); + p += BIT32SZ; + break; + + case Twrite: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT32(p, f->count); + p += BIT32SZ; + memmove(p, f->data, f->count); + p += f->count; + break; + + case Tclunk: + case Tremove: + PBIT32(p, f->fid); + p += BIT32SZ; + break; + + case Tstat: + PBIT32(p, f->fid); + p += BIT32SZ; + break; + + case Twstat: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT16(p, f->nstat); + p += BIT16SZ; + memmove(p, f->stat, f->nstat); + p += f->nstat; + break; +/* + */ + + case Rversion: + PBIT32(p, f->msize); + p += BIT32SZ; + p = pstring(p, f->version); + break; + + case Rerror: + p = pstring(p, f->ename); + break; + + case Rflush: + break; + + case Rauth: + p = pqid(p, &f->aqid); + break; + + case Rattach: + p = pqid(p, &f->qid); + break; + + case Rwalk: + PBIT16(p, f->nwqid); + p += BIT16SZ; + if(f->nwqid > MAXWELEM) + return 0; + for(i=0; inwqid; i++) + p = pqid(p, &f->wqid[i]); + break; + + case Ropen: + case Rcreate: + case Ropenfd: + p = pqid(p, &f->qid); + PBIT32(p, f->iounit); + p += BIT32SZ; + if(f->type == Ropenfd){ + PBIT32(p, f->unixfd); + p += BIT32SZ; + } + break; + + case Rread: + PBIT32(p, f->count); + p += BIT32SZ; + memmove(p, f->data, f->count); + p += f->count; + break; + + case Rwrite: + PBIT32(p, f->count); + p += BIT32SZ; + break; + + case Rclunk: + break; + + case Rremove: + break; + + case Rstat: + PBIT16(p, f->nstat); + p += BIT16SZ; + memmove(p, f->stat, f->nstat); + p += f->nstat; + break; + + case Rwstat: + break; + } + if(size != p-ap) + return 0; + return size; +} diff --git a/lib/lib9/crypt.c b/lib/lib9/crypt.c new file mode 100644 index 0000000..0524c94 --- /dev/null +++ b/lib/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 +#include +#include +#include + +/* + * 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/lib/lib9/ctime.c b/lib/lib9/ctime.c new file mode 100644 index 0000000..97ebfd2 --- /dev/null +++ b/lib/lib9/ctime.c @@ -0,0 +1,180 @@ +/* + * 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 +#include + +#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 - 1); + 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) +{ + const 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/lib/lib9/debugmalloc.c b/lib/lib9/debugmalloc.c new file mode 100644 index 0000000..744af83 --- /dev/null +++ b/lib/lib9/debugmalloc.c @@ -0,0 +1,157 @@ +#include +#define NOPLAN9DEFINES +#include + +/* + * The Unix libc routines cannot be trusted to do their own locking. + * Sad but apparently true. + */ +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()); */ + mallocpid = getpid(); + v = malloc(n+Overhead); + v = mark(v, getcallerpc(&n), n, MallocMagic); +/*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()); */ + mallocpid = getpid(); + v = mark(v, getcallerpc(&v), 0, FreeMagic); + free(v); +/*fprint(2, "%s %d donefree\n", argv0, getpid()); */ +} + +void* +p9calloc(ulong a, ulong b) +{ + void *v; + +/*fprint(2, "%s %d calloc\n", argv0, getpid()); */ + mallocpid = getpid(); + v = calloc(a*b+Overhead, 1); + v = mark(v, getcallerpc(&a), a*b, CallocMagic); +/*fprint(2, "%s %d donecalloc\n", argv0, getpid()); */ + return v; +} + +void* +p9realloc(void *v, ulong n) +{ +/*fprint(2, "%s %d realloc\n", argv0, getpid()); */ + mallocpid = getpid(); + v = mark(v, getcallerpc(&v), 0, CheckMagic); + v = realloc(v, n+Overhead); + v = mark(v, getcallerpc(&v), n, ReallocMagic); +/*fprint(2, "%s %d donerealloc\n", argv0, getpid()); */ + return v; +} diff --git a/lib/lib9/dial.c b/lib/lib9/dial.c new file mode 100644 index 0000000..81e3a83 --- /dev/null +++ b/lib/lib9/dial.c @@ -0,0 +1,159 @@ +#include +#include + +#undef accept +#undef announce +#undef dial +#undef setnetmtpt +#undef hangup +#undef listen +#undef netmkaddr +#undef reject + +#include +#include +#include +#include +#include + +#undef unix +#define unix xunix + +static int +isany(struct sockaddr_storage *ss) +{ + switch(ss->ss_family){ + case AF_INET: + return (((struct sockaddr_in*)ss)->sin_addr.s_addr == INADDR_ANY); + case AF_INET6: + return (memcmp(((struct sockaddr_in6*)ss)->sin6_addr.s6_addr, + in6addr_any.s6_addr, sizeof (struct in6_addr)) == 0); + } + return 0; +} + +static int +addrlen(struct sockaddr_storage *ss) +{ + switch(ss->ss_family){ + case AF_INET: + return sizeof(struct sockaddr_in); + case AF_INET6: + return sizeof(struct sockaddr_in6); + case AF_UNIX: + return sizeof(struct sockaddr_un); + } + return 0; +} + +int +p9dial(char *addr, char *local, char *dummy2, int *dummy3) +{ + char *buf; + char *net, *unix; + int port; + int proto; + socklen_t sn; + int n; + struct sockaddr_storage ss, ssl; + 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, &ss, &port) < 0){ + free(buf); + return -1; + } + if(strcmp(net, "unix") != 0 && isany(&ss)){ + 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(ss.ss_family, proto, 0)) < 0) + return -1; + + if(local){ + buf = strdup(local); + if(buf == nil){ + close(s); + return -1; + } + if(p9dialparse(buf, &net, &unix, &ss, &port) < 0){ + badlocal: + free(buf); + close(s); + return -1; + } + if(unix){ + werrstr("bad local address %s for dial %s", local, addr); + goto badlocal; + } + 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*)&ssl, addrlen(&ssl)) < 0) + goto badlocal; + free(buf); + } + + n = 1; + setsockopt(s, SOL_SOCKET, SO_BROADCAST, &n, sizeof n); + if(!isany(&ss)){ + if(connect(s, (struct sockaddr*)&ss, addrlen(&ss)) < 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){ + free(buf); + return s; + } + free(buf); + if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0){ + werrstr("socket: %r"); + return -1; + } + if(connect(s, (struct sockaddr*)&ss, addrlen(&ss)) < 0){ + werrstr("connect %s: %r", ((struct sockaddr_un*)&ss)->sun_path); + close(s); + return -1; + } + return s; +} diff --git a/lib/lib9/dirfstat.c b/lib/lib9/dirfstat.c new file mode 100644 index 0000000..37b59a0 --- /dev/null +++ b/lib/lib9/dirfstat.c @@ -0,0 +1,28 @@ +#include +#define NOPLAN9DEFINES +#include + +#include + +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/lib/lib9/dirfwstat.c b/lib/lib9/dirfwstat.c new file mode 100644 index 0000000..27ed37a --- /dev/null +++ b/lib/lib9/dirfwstat.c @@ -0,0 +1,56 @@ +#define NOPLAN9DEFINES +#include +#include +#include +#include + +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__linux__) +/* do nothing -- futimes exists and is fine */ + +#elif defined(__SunOS5_9__) +/* use futimesat */ +static int +futimes(int fd, struct timeval *tv) +{ + return futimesat(fd, 0, tv); +} + +#else +/* provide dummy */ +/* rename just in case -- linux provides an unusable one */ +#undef futimes +#define futimes myfutimes +static int +futimes(int fd, struct timeval *tv) +{ + werrstr("futimes not available"); + return -1; +} + +#endif + +int +dirfwstat(int fd, Dir *dir) +{ + int ret; + struct timeval tv[2]; + + ret = 0; + if(~dir->mode != 0){ + if(fchmod(fd, dir->mode) < 0) + ret = -1; + } + if(~dir->mtime != 0){ + tv[0].tv_sec = dir->mtime; + tv[0].tv_usec = 0; + tv[1].tv_sec = dir->mtime; + tv[1].tv_usec = 0; + if(futimes(fd, tv) < 0) + ret = -1; + } + if(~dir->length != 0){ + if(ftruncate(fd, dir->length) < 0) + ret = -1; + } + return ret; +} diff --git a/lib/lib9/dirmodefmt.c b/lib/lib9/dirmodefmt.c new file mode 100644 index 0000000..05b1ce6 --- /dev/null +++ b/lib/lib9/dirmodefmt.c @@ -0,0 +1,62 @@ +#include +#include + +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/lib/lib9/dirstat.c b/lib/lib9/dirstat.c new file mode 100644 index 0000000..a644b6b --- /dev/null +++ b/lib/lib9/dirstat.c @@ -0,0 +1,31 @@ +#include +#define NOPLAN9DEFINES +#include + +#include + +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/lib/lib9/dirwstat.c b/lib/lib9/dirwstat.c new file mode 100644 index 0000000..37d9e0e --- /dev/null +++ b/lib/lib9/dirwstat.c @@ -0,0 +1,31 @@ +#include +#define NOPLAN9DEFINES +#include +#include +#include +#include + +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/lib/lib9/dup.c b/lib/lib9/dup.c new file mode 100644 index 0000000..feec1b7 --- /dev/null +++ b/lib/lib9/dup.c @@ -0,0 +1,12 @@ +#include +#include + +#undef dup + +int +p9dup(int old, int new) +{ + if(new == -1) + return dup(old); + return dup2(old, new); +} diff --git a/lib/lib9/encodefmt.c b/lib/lib9/encodefmt.c new file mode 100644 index 0000000..5033505 --- /dev/null +++ b/lib/lib9/encodefmt.c @@ -0,0 +1,76 @@ +#include + +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, ""); + + 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, ""); +} diff --git a/lib/lib9/errstr.c b/lib/lib9/errstr.c new file mode 100644 index 0000000..9493e84 --- /dev/null +++ b/lib/lib9/errstr.c @@ -0,0 +1,80 @@ +/* + * 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 +#include +#include +#include + +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/lib/lib9/exec.c b/lib/lib9/exec.c new file mode 100644 index 0000000..ffe8134 --- /dev/null +++ b/lib/lib9/exec.c @@ -0,0 +1,9 @@ +#include +#include + +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/lib/lib9/execl.c b/lib/lib9/execl.c new file mode 100644 index 0000000..63b86d6 --- /dev/null +++ b/lib/lib9/execl.c @@ -0,0 +1,28 @@ +#include +#include + +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/lib/lib9/exitcode.c b/lib/lib9/exitcode.c new file mode 100644 index 0000000..797e9d5 --- /dev/null +++ b/lib/lib9/exitcode.c @@ -0,0 +1,8 @@ +#include +#include + +int +exitcode(char *s) +{ + return 1; +} diff --git a/lib/lib9/fcallfmt.c b/lib/lib9/fcallfmt.c new file mode 100644 index 0000000..8198013 --- /dev/null +++ b/lib/lib9/fcallfmt.c @@ -0,0 +1,253 @@ +#include +#include +#include + +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; inwname; 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; inwqid; 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, ""); + return strlen(ans); + } + printable = 1; + if(count > DUMPL) + count = DUMPL; + for(i=0; i127) + printable = 0; + p = ans; + *p++ = '\''; + if(printable){ + if(2*count > e-p-2) + count = (e-p-2)/2; + for(i=0; i e-p-2) + count = (e-p-2)/2; + for(i=0; i0 && 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/lib/lib9/fmt/LICENSE b/lib/lib9/fmt/LICENSE new file mode 100644 index 0000000..560e4d7 --- /dev/null +++ b/lib/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 . + diff --git a/lib/lib9/fmt/README b/lib/lib9/fmt/README new file mode 100644 index 0000000..5dc21cb --- /dev/null +++ b/lib/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 . + diff --git a/lib/lib9/fmt/charstod.c b/lib/lib9/fmt/charstod.c new file mode 100644 index 0000000..6ff5767 --- /dev/null +++ b/lib/lib9/fmt/charstod.c @@ -0,0 +1,73 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/dofmt.c b/lib/lib9/fmt/dofmt.c new file mode 100644 index 0000000..f714f8f --- /dev/null +++ b/lib/lib9/fmt/dofmt.c @@ -0,0 +1,617 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +/* Copyright (c) 2004 Google Inc.; see LICENSE */ + +#include +#include +#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, "", 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; jprec && s[i]; j++) + i += chartorune(&r, s+i); +#else + /* ANSI requires precision in bytes, not Runes */ + for(i=0; iprec; 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, "", 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/lib/lib9/fmt/dorfmt.c b/lib/lib9/fmt/dorfmt.c new file mode 100644 index 0000000..21e42eb --- /dev/null +++ b/lib/lib9/fmt/dorfmt.c @@ -0,0 +1,50 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/errfmt.c b/lib/lib9/fmt/errfmt.c new file mode 100644 index 0000000..7d19470 --- /dev/null +++ b/lib/lib9/fmt/errfmt.c @@ -0,0 +1,16 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#include +#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/lib/lib9/fmt/fltfmt.c b/lib/lib9/fmt/fltfmt.c new file mode 100644 index 0000000..4045ffd --- /dev/null +++ b/lib/lib9/fmt/fltfmt.c @@ -0,0 +1,667 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 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/lib/lib9/fmt/fmt.c b/lib/lib9/fmt/fmt.c new file mode 100644 index 0000000..2b87d8b --- /dev/null +++ b/lib/lib9/fmt/fmt.c @@ -0,0 +1,229 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include + +/* + * As of 2020, older systems like RHEL 6 and AIX still do not have C11 atomics. + * On those systems, make the code use volatile int accesses and hope for the best. + * (Most uses of fmtinstall are not actually racing with calls to print that lookup + * formats. The code used volatile here for years without too many problems, + * even though that's technically racy. A mutex is not OK, because we want to + * be able to call print from signal handlers.) + * + * RHEL is using an old GCC (atomics were added in GCC 4.9). + * AIX is using its own IBM compiler (XL C). + */ +#if __IBMC__ || !__clang__ && __GNUC__ && (__GNUC__ < 4 || (__GNUC__==4 && __GNUC_MINOR__<9)) +#warning not using C11 stdatomic on legacy system +#define _Atomic volatile +#define atomic_load(x) (*(x)) +#define atomic_store(x, y) (*(x)=(y)) +#define ATOMIC_VAR_INIT(x) (x) +#else +#include +#endif + +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +enum +{ + Maxfmt = 128 +}; + +typedef struct Convfmt Convfmt; +struct Convfmt +{ + int c; + Fmts fmt; +}; + +static struct +{ + /* + * lock updates to fmt by calling __fmtlock, __fmtunlock. + * reads can start at nfmt and work backward without + * further locking. later fmtinstalls take priority over earlier + * ones because of the backwards loop. + * once installed, a format is never overwritten. + */ + _Atomic int nfmt; + Convfmt fmt[Maxfmt]; +} fmtalloc = { + 27, + { + {' ', __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}, + } +}; + +int (*fmtdoquote)(int); + +/* + * __fmtlock() must be set + */ +static int +__fmtinstall(int c, Fmts f) +{ + Convfmt *p; + int i; + + if(c<=0 || c>=65536) + return -1; + if(!f) + f = __badfmt; + + i = atomic_load(&fmtalloc.nfmt); + if(i == Maxfmt) + return -1; + p = &fmtalloc.fmt[i]; + p->c = c; + p->fmt = f; + atomic_store(&fmtalloc.nfmt, i+1); + + 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[atomic_load(&fmtalloc.nfmt)]; + for(p=ep; p-- > fmtalloc.fmt; ) + if(p->c == c) + return p->fmt; + + 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/lib/lib9/fmt/fmtdef.h b/lib/lib9/fmt/fmtdef.h new file mode 100644 index 0000000..2bafd36 --- /dev/null +++ b/lib/lib9/fmt/fmtdef.h @@ -0,0 +1,104 @@ +/* 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); +int __fmtpad(Fmt *f, int n); +double __fmtpow10(int n); +int __fmtrcpy(Fmt *f, const void *vm, int n); +void __fmtlock(void); +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/lib/lib9/fmt/fmtfd.c b/lib/lib9/fmt/fmtfd.c new file mode 100644 index 0000000..1e7998b --- /dev/null +++ b/lib/lib9/fmt/fmtfd.c @@ -0,0 +1,36 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/fmtfdflush.c b/lib/lib9/fmt/fmtfdflush.c new file mode 100644 index 0000000..7abb982 --- /dev/null +++ b/lib/lib9/fmt/fmtfdflush.c @@ -0,0 +1,22 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/fmtlocale.c b/lib/lib9/fmt/fmtlocale.c new file mode 100644 index 0000000..33ca401 --- /dev/null +++ b/lib/lib9/fmt/fmtlocale.c @@ -0,0 +1,54 @@ +/* Copyright (c) 2004 Google Inc.; see LICENSE */ + +#include +#include +#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/lib/lib9/fmt/fmtlock.c b/lib/lib9/fmt/fmtlock.c new file mode 100644 index 0000000..cabe05f --- /dev/null +++ b/lib/lib9/fmt/fmtlock.c @@ -0,0 +1,15 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include "plan9.h" +#include "fmt.h" +#include "fmtdef.h" + +void +__fmtlock(void) +{ +} + +void +__fmtunlock(void) +{ +} diff --git a/lib/lib9/fmt/fmtnull.c b/lib/lib9/fmt/fmtnull.c new file mode 100644 index 0000000..22ac078 --- /dev/null +++ b/lib/lib9/fmt/fmtnull.c @@ -0,0 +1,32 @@ +/* Copyright (c) 2004 Google Inc.; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/fmtprint.c b/lib/lib9/fmt/fmtprint.c new file mode 100644 index 0000000..41fc833 --- /dev/null +++ b/lib/lib9/fmt/fmtprint.c @@ -0,0 +1,35 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/fmtquote.c b/lib/lib9/fmt/fmtquote.c new file mode 100644 index 0000000..8c8daae --- /dev/null +++ b/lib/lib9/fmt/fmtquote.c @@ -0,0 +1,259 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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*)"", 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/lib/lib9/fmt/fmtrune.c b/lib/lib9/fmt/fmtrune.c new file mode 100644 index 0000000..fa42468 --- /dev/null +++ b/lib/lib9/fmt/fmtrune.c @@ -0,0 +1,28 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/fmtstr.c b/lib/lib9/fmt/fmtstr.c new file mode 100644 index 0000000..34d4a58 --- /dev/null +++ b/lib/lib9/fmt/fmtstr.c @@ -0,0 +1,16 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/fmtvprint.c b/lib/lib9/fmt/fmtvprint.c new file mode 100644 index 0000000..694a6cf --- /dev/null +++ b/lib/lib9/fmt/fmtvprint.c @@ -0,0 +1,36 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/fprint.c b/lib/lib9/fmt/fprint.c new file mode 100644 index 0000000..43b07af --- /dev/null +++ b/lib/lib9/fmt/fprint.c @@ -0,0 +1,17 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#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/lib/lib9/fmt/nan.h b/lib/lib9/fmt/nan.h new file mode 100644 index 0000000..be78e14 --- /dev/null +++ b/lib/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/lib/lib9/fmt/nan64.c b/lib/lib9/fmt/nan64.c new file mode 100644 index 0000000..78c3fde --- /dev/null +++ b/lib/lib9/fmt/nan64.c @@ -0,0 +1,78 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ + +/* + * 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 +#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/lib/lib9/fmt/plan9.h b/lib/lib9/fmt/plan9.h new file mode 100644 index 0000000..0de44f1 --- /dev/null +++ b/lib/lib9/fmt/plan9.h @@ -0,0 +1,37 @@ +#include + +/* + * 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/lib/lib9/fmt/portdate b/lib/lib9/fmt/portdate new file mode 100644 index 0000000..064bb4c --- /dev/null +++ b/lib/lib9/fmt/portdate @@ -0,0 +1,30 @@ +dofmt.c 2004/1225 +dorfmt.c 2004/1225 +errfmt.c 2004/1225 +fltfmt.c 2004/1225 +fmt.c 2004/1225 +fmtfd.c 2004/1225 +fmtlock.c 2004/1225 +fmtprint.c 2004/1225 +fmtquote.c 2004/1225 +fmtrune.c 2004/1225 +fmtstr.c 2004/1225 +fmtvprint.c 2004/1225 +fprint.c 2004/1225 +print.c 2004/1225 +runefmtstr.c 2004/1225 +runeseprint.c 2004/1225 +runesmprint.c 2004/1225 +runesnprint.c 2004/1225 +runesprint.c 2004/1225 +runevseprint.c 2004/1225 +runevsmprint.c 2004/1225 +runevsnprint.c 2004/1225 +seprint.c 2004/1225 +smprint.c 2004/1225 +snprint.c 2004/1225 +sprint.c 2004/1225 +vfprint.c 2004/1225 +vseprint.c 2004/1225 +vsmprint.c 2004/1225 +vsnprint.c 2004/1225 diff --git a/lib/lib9/fmt/pow10.c b/lib/lib9/fmt/pow10.c new file mode 100644 index 0000000..7e2cde2 --- /dev/null +++ b/lib/lib9/fmt/pow10.c @@ -0,0 +1,45 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/print.c b/lib/lib9/fmt/print.c new file mode 100644 index 0000000..c0b0834 --- /dev/null +++ b/lib/lib9/fmt/print.c @@ -0,0 +1,17 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#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/lib/lib9/fmt/runefmtstr.c b/lib/lib9/fmt/runefmtstr.c new file mode 100644 index 0000000..52ee96a --- /dev/null +++ b/lib/lib9/fmt/runefmtstr.c @@ -0,0 +1,16 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/runeseprint.c b/lib/lib9/fmt/runeseprint.c new file mode 100644 index 0000000..ffc72cb --- /dev/null +++ b/lib/lib9/fmt/runeseprint.c @@ -0,0 +1,18 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/runesmprint.c b/lib/lib9/fmt/runesmprint.c new file mode 100644 index 0000000..5ddb2ba --- /dev/null +++ b/lib/lib9/fmt/runesmprint.c @@ -0,0 +1,18 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/runesnprint.c b/lib/lib9/fmt/runesnprint.c new file mode 100644 index 0000000..afc3f50 --- /dev/null +++ b/lib/lib9/fmt/runesnprint.c @@ -0,0 +1,18 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/runesprint.c b/lib/lib9/fmt/runesprint.c new file mode 100644 index 0000000..f7f4d29 --- /dev/null +++ b/lib/lib9/fmt/runesprint.c @@ -0,0 +1,18 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/runevseprint.c b/lib/lib9/fmt/runevseprint.c new file mode 100644 index 0000000..213351a --- /dev/null +++ b/lib/lib9/fmt/runevseprint.c @@ -0,0 +1,28 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/runevsmprint.c b/lib/lib9/fmt/runevsmprint.c new file mode 100644 index 0000000..de29d2c --- /dev/null +++ b/lib/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 +#include +#include "fmtdef.h" +#else +#include +#include +#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/lib/lib9/fmt/runevsnprint.c b/lib/lib9/fmt/runevsnprint.c new file mode 100644 index 0000000..f1e28e7 --- /dev/null +++ b/lib/lib9/fmt/runevsnprint.c @@ -0,0 +1,28 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/seprint.c b/lib/lib9/fmt/seprint.c new file mode 100644 index 0000000..0729b4d --- /dev/null +++ b/lib/lib9/fmt/seprint.c @@ -0,0 +1,17 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#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/lib/lib9/fmt/smprint.c b/lib/lib9/fmt/smprint.c new file mode 100644 index 0000000..8640a5f --- /dev/null +++ b/lib/lib9/fmt/smprint.c @@ -0,0 +1,17 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#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/lib/lib9/fmt/snprint.c b/lib/lib9/fmt/snprint.c new file mode 100644 index 0000000..d019b45 --- /dev/null +++ b/lib/lib9/fmt/snprint.c @@ -0,0 +1,17 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#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/lib/lib9/fmt/sprint.c b/lib/lib9/fmt/sprint.c new file mode 100644 index 0000000..4155d88 --- /dev/null +++ b/lib/lib9/fmt/sprint.c @@ -0,0 +1,30 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmt/strtod.c b/lib/lib9/fmt/strtod.c new file mode 100644 index 0000000..1dfd1b0 --- /dev/null +++ b/lib/lib9/fmt/strtod.c @@ -0,0 +1,520 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#include +#include +#include +#include +#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<>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>= 1; + } + frnorm(mid); + + /* compare */ + c = fpcmp(a, mid); + if(c > 0) { + c = 1; + for(i=0; i= 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; i0; 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> 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 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; + n -= c<= (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<= (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/lib/lib9/fmt/strtod.h b/lib/lib9/fmt/strtod.h new file mode 100644 index 0000000..82c3d46 --- /dev/null +++ b/lib/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/lib/lib9/fmt/test.c b/lib/lib9/fmt/test.c new file mode 100644 index 0000000..4e1b788 --- /dev/null +++ b/lib/lib9/fmt/test.c @@ -0,0 +1,53 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +/* Copyright (c) 2004 Google Inc.; see LICENSE */ + +#include +#include +#include +#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/lib/lib9/fmt/test2.c b/lib/lib9/fmt/test2.c new file mode 100644 index 0000000..715fcd5 --- /dev/null +++ b/lib/lib9/fmt/test2.c @@ -0,0 +1,9 @@ +#include +#include +#include + +int +main(int argc, char **argv) +{ + print("%020.10d\n", 100); +} diff --git a/lib/lib9/fmt/test3.c b/lib/lib9/fmt/test3.c new file mode 100644 index 0000000..7cda8dc --- /dev/null +++ b/lib/lib9/fmt/test3.c @@ -0,0 +1,52 @@ +#include +#include +#include + +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/lib/lib9/fmt/vfprint.c b/lib/lib9/fmt/vfprint.c new file mode 100644 index 0000000..a75e224 --- /dev/null +++ b/lib/lib9/fmt/vfprint.c @@ -0,0 +1,21 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#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/lib/lib9/fmt/vseprint.c b/lib/lib9/fmt/vseprint.c new file mode 100644 index 0000000..8266c65 --- /dev/null +++ b/lib/lib9/fmt/vseprint.c @@ -0,0 +1,27 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#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/lib/lib9/fmt/vsmprint.c b/lib/lib9/fmt/vsmprint.c new file mode 100644 index 0000000..0a88e98 --- /dev/null +++ b/lib/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 +#include +#include "fmtdef.h" +#else +#include +#include +#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/lib/lib9/fmt/vsnprint.c b/lib/lib9/fmt/vsnprint.c new file mode 100644 index 0000000..1859265 --- /dev/null +++ b/lib/lib9/fmt/vsnprint.c @@ -0,0 +1,28 @@ +/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ +#include +#include +#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/lib/lib9/fmtlock2.c b/lib/lib9/fmtlock2.c new file mode 100644 index 0000000..d711e6d --- /dev/null +++ b/lib/lib9/fmtlock2.c @@ -0,0 +1,16 @@ +#include +#include + +static Lock fmtlock; + +void +__fmtlock(void) +{ + lock(&fmtlock); +} + +void +__fmtunlock(void) +{ + unlock(&fmtlock); +} diff --git a/lib/lib9/fork.c b/lib/lib9/fork.c new file mode 100644 index 0000000..841d2c2 --- /dev/null +++ b/lib/lib9/fork.c @@ -0,0 +1,22 @@ +#include +#include +#include +#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/lib/lib9/frand.c b/lib/lib9/frand.c new file mode 100644 index 0000000..841f968 --- /dev/null +++ b/lib/lib9/frand.c @@ -0,0 +1,17 @@ +#include +#include + +#define MASK 0x7fffffffL +#define NORM (1.0/(1.0+MASK)) + +double +p9frand(void) +{ + double x; + + do { + x = lrand() * NORM; + x = (x + lrand()) * NORM; + } while(x >= 1); + return x; +} diff --git a/lib/lib9/frexp.c b/lib/lib9/frexp.c new file mode 100644 index 0000000..fe3e2f7 --- /dev/null +++ b/lib/lib9/frexp.c @@ -0,0 +1,9 @@ +#include +#define NOPLAN9DEFINES +#include + +double +p9frexp(double d, int *i) +{ + return frexp(d, i); +} diff --git a/lib/lib9/get9root.c b/lib/lib9/get9root.c new file mode 100644 index 0000000..1d9a214 --- /dev/null +++ b/lib/lib9/get9root.c @@ -0,0 +1,17 @@ +#include +#include + +char* +get9root(void) +{ + static char *s; + + if(s) + return s; + + if((s = getenv("JEHANNE_HACKING")) != 0) + return s; + /* could do better - search $PATH */ + s = "/usr/local/plan9"; + return s; +} diff --git a/lib/lib9/getcallerpc.c b/lib/lib9/getcallerpc.c new file mode 100644 index 0000000..3067f05 --- /dev/null +++ b/lib/lib9/getcallerpc.c @@ -0,0 +1,13 @@ +#include +#include + +/* + * On gcc and clang, getcallerpc is a macro invoking a compiler builtin. + * If the macro in libc.h did not trigger, there's no implementation. + */ +#undef getcallerpc +ulong +getcallerpc(void *v) +{ + return 1; +} diff --git a/lib/lib9/getenv.c b/lib/lib9/getenv.c new file mode 100644 index 0000000..5dfc857 --- /dev/null +++ b/lib/lib9/getenv.c @@ -0,0 +1,20 @@ +#include +#define NOPLAN9DEFINES +#include + +char* +p9getenv(char *s) +{ + char *t; + + t = getenv(s); + if(t == 0) + return 0; + return strdup(t); +} + +int +p9putenv(char *s, char *v) +{ + return setenv(s, v, 1); +} diff --git a/lib/lib9/getfields.c b/lib/lib9/getfields.c new file mode 100644 index 0000000..79f7aba --- /dev/null +++ b/lib/lib9/getfields.c @@ -0,0 +1,36 @@ +#include + +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/lib/lib9/getnetconn.c b/lib/lib9/getnetconn.c new file mode 100644 index 0000000..e88516c --- /dev/null +++ b/lib/lib9/getnetconn.c @@ -0,0 +1,160 @@ +#include +#define NOPLAN9DEFINES +#include + +#include +#include +#include +#include +#include +#include + +#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; + struct sockaddr_in6 *sin6; + 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_INET6: + sin6 = (void*)sa; + if (memcmp(&sin6->sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0) + *lsys = strdup("*"); + else{ + *lsys = malloc(INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &sin6->sin6_addr, *lsys, INET6_ADDRSTRLEN); + } + *lserv = smprint("%d", ntohs(sin6->sin6_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_in6 sin6; + 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/lib/lib9/getns.c b/lib/lib9/getns.c new file mode 100644 index 0000000..35bbeeb --- /dev/null +++ b/lib/lib9/getns.c @@ -0,0 +1,97 @@ +#include +#include +#include + +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/lib/lib9/getuser.c b/lib/lib9/getuser.c new file mode 100644 index 0000000..09c0704 --- /dev/null +++ b/lib/lib9/getuser.c @@ -0,0 +1,16 @@ +#include +#include +#include + +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/lib/lib9/getwd.c b/lib/lib9/getwd.c new file mode 100644 index 0000000..d6f7a13 --- /dev/null +++ b/lib/lib9/getwd.c @@ -0,0 +1,10 @@ +#include +#include + +#undef getwd + +char* +p9getwd(char *s, int ns) +{ + return getcwd(s, ns); +} diff --git a/lib/lib9/jmp.c b/lib/lib9/jmp.c new file mode 100644 index 0000000..5ec21e7 --- /dev/null +++ b/lib/lib9/jmp.c @@ -0,0 +1,16 @@ +#include +#define NOPLAN9DEFINES +#include + +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/lib/lib9/lib9.sh.build b/lib/lib9/lib9.sh.build new file mode 100755 index 0000000..747d56c --- /dev/null +++ b/lib/lib9/lib9.sh.build @@ -0,0 +1,169 @@ +#!/bin/sh + +git clean -xdf . + +cc \ + -I ../../include\ + -DPLAN9PORT \ + -O0 \ + -c \ + -g -ggdb \ + -Wall \ + -Wno-parentheses \ + -Wno-missing-braces \ + -Wno-switch \ + -Wno-comment \ + -Wno-sign-compare \ + -Wno-unknown-pragmas \ + -Wno-misleading-indentation \ + -Wno-stringop-truncation \ + -Wno-stringop-overflow \ + -Wno-format-truncation \ + -fno-omit-frame-pointer \ + -fsigned-char \ + -fcommon \ + fmt/dofmt.c\ + fmt/fltfmt.c\ + fmt/fmt.c\ + fmt/fmtfd.c\ + fmt/fmtfdflush.c\ + fmt/fmtlocale.c\ + fmt/fmtnull.c\ + fmt/fmtprint.c\ + fmt/fmtquote.c\ + fmt/fmtrune.c\ + fmt/fmtstr.c\ + fmt/fmtvprint.c\ + fmt/fprint.c\ + fmt/nan64.c\ + fmt/print.c\ + fmt/runefmtstr.c\ + fmt/runeseprint.c\ + fmt/runesmprint.c\ + fmt/runesnprint.c\ + fmt/runesprint.c\ + fmt/runevseprint.c\ + fmt/runevsmprint.c\ + fmt/runevsnprint.c\ + fmt/seprint.c\ + fmt/smprint.c\ + fmt/snprint.c\ + fmt/sprint.c\ + fmt/strtod.c\ + fmt/vfprint.c\ + fmt/vseprint.c\ + fmt/vsmprint.c\ + fmt/vsnprint.c\ + fmt/charstod.c\ + fmt/pow10.c\ + fmtlock2.c\ + frexp.c\ + utf/rune.c\ + utf/runestrcat.c\ + utf/runestrchr.c\ + utf/runestrcmp.c\ + utf/runestrcpy.c\ + utf/runestrdup.c\ + utf/runestrlen.c\ + utf/runestrecpy.c\ + utf/runestrncat.c\ + utf/runestrncmp.c\ + utf/runestrncpy.c\ + utf/runestrrchr.c\ + utf/runestrstr.c\ + utf/runetype.c\ + utf/utfecpy.c\ + utf/utflen.c\ + utf/utfnlen.c\ + utf/utfrrune.c\ + utf/utfrune.c\ + utf/utfutf.c\ + _exits.c\ + _p9dialparse.c\ + _p9dir.c\ + announce.c\ + argv0.c\ + atexit.c\ + atoi.c\ + atol.c\ + atoll.c\ + atnotify.c\ + await.c\ + cistrcmp.c\ + cistrncmp.c\ + cistrstr.c\ + cleanname.c\ + ctime.c\ + dial.c\ + dirfstat.c\ + dirfwstat.c\ + dirmodefmt.c\ + dirstat.c\ + dirwstat.c\ + dup.c\ + encodefmt.c\ + errstr.c\ + exec.c\ + execl.c\ + exitcode.c\ + frand.c\ + get9root.c\ + getcallerpc.c\ + getenv.c\ + getfields.c\ + getnetconn.c\ + getns.c\ + getuser.c\ + getwd.c\ + jmp.c\ + lrand.c\ + lnrand.c\ + main.c\ + malloc.c\ + malloctag.c\ + mallocz.c\ + nan.c\ + needsrcquote.c\ + needstack.c\ + netmkaddr.c\ + notify.c\ + nrand.c\ + nulldir.c\ + open.c\ + opentemp.c\ + pin.c\ + pipe.c\ + post9p.c\ + postnote.c\ + qlock.c\ + quote.c\ + rand.c\ + readcons.c\ + readn.c\ + rfork.c\ + searchpath.c\ + sendfd.c\ + sleep.c\ + strdup.c\ + strecpy.c\ + sysfatal.c\ + syslog.c\ + sysname.c\ + time.c\ + tm2sec.c\ + tokenize.c\ + truerand.c\ + u16.c\ + u32.c\ + u64.c\ + unsharp.c\ + wait.c\ + waitpid.c\ + write.c\ + zoneinfo.c + +mkdir -p $JEHANNE/hacking/lib/ +ar rcs $JEHANNE/hacking/lib/lib9.a *.o + +git clean -xdf . + diff --git a/lib/lib9/lnrand.c b/lib/lib9/lnrand.c new file mode 100644 index 0000000..5b648d0 --- /dev/null +++ b/lib/lib9/lnrand.c @@ -0,0 +1,18 @@ +#include +#include + +#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/lib/lib9/lock.c b/lib/lib9/lock.c new file mode 100644 index 0000000..eb3ea21 --- /dev/null +++ b/lib/lib9/lock.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include + +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/lib/lib9/lrand.c b/lib/lib9/lrand.c new file mode 100644 index 0000000..3d1b05f --- /dev/null +++ b/lib/lib9/lrand.c @@ -0,0 +1,81 @@ +#include +#include + +/* + * 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); + + if(rng_tap <= rng_vec) { + if(rng_feed == 0) + isrand(1); + rng_tap += LEN; + } + rng_tap--; + if(rng_feed <= rng_vec) + rng_feed += LEN; + rng_feed--; + x = (*rng_feed + *rng_tap) & MASK; + *rng_feed = x; + + unlock(&lk); + + return x; +} diff --git a/lib/lib9/main.c b/lib/lib9/main.c new file mode 100644 index 0000000..4a42905 --- /dev/null +++ b/lib/lib9/main.c @@ -0,0 +1,13 @@ +#include +#define NOPLAN9DEFINES +#include + +extern void p9main(int, char**); + +int +main(int argc, char **argv) +{ + p9main(argc, argv); + exits("main"); + return 99; +} diff --git a/lib/lib9/malloc.c b/lib/lib9/malloc.c new file mode 100644 index 0000000..695ff8b --- /dev/null +++ b/lib/lib9/malloc.c @@ -0,0 +1,37 @@ +/* + * These are here mainly so that I can link against + * debugmalloc.c instead and not recompile the world. + */ + +#include +#define NOPLAN9DEFINES +#include + + +void* +p9malloc(ulong n) +{ + if(n == 0) + n++; + return malloc(n); +} + +void +p9free(void *v) +{ + free(v); +} + +void* +p9calloc(ulong a, ulong b) +{ + if(a*b == 0) + a = b = 1; + return calloc(a, b); +} + +void* +p9realloc(void *v, ulong n) +{ + return realloc(v, n); +} diff --git a/lib/lib9/malloctag.c b/lib/lib9/malloctag.c new file mode 100644 index 0000000..e5682bc --- /dev/null +++ b/lib/lib9/malloctag.c @@ -0,0 +1,15 @@ +#include + +void +setmalloctag(void *v, ulong t) +{ + USED(v); + USED(t); +} + +void +setrealloctag(void *v, ulong t) +{ + USED(v); + USED(t); +} diff --git a/lib/lib9/mallocz.c b/lib/lib9/mallocz.c new file mode 100644 index 0000000..a3e9151 --- /dev/null +++ b/lib/lib9/mallocz.c @@ -0,0 +1,15 @@ +#include +#include +#include +#include + +void* +mallocz(unsigned long n, int clr) +{ + void *v; + + v = malloc(n); + if(clr && v) + memset(v, 0, n); + return v; +} diff --git a/lib/lib9/mkfile b/lib/lib9/mkfile new file mode 100644 index 0000000..db267df --- /dev/null +++ b/lib/lib9/mkfile @@ -0,0 +1,196 @@ +<$PLAN9/src/mkhdr + +LIB=lib9.a + +NUM=\ + charstod.$O\ + pow10.$O\ + +# Could add errfmt, but we want to pick it up from lib9 instead. +FMTOFILES=\ + dofmt.$O\ + fltfmt.$O\ + fmt.$O\ + fmtfd.$O\ + fmtfdflush.$O\ + fmtlocale.$O\ + fmtlock2.$O\ + fmtnull.$O\ + fmtprint.$O\ + fmtquote.$O\ + fmtrune.$O\ + fmtstr.$O\ + fmtvprint.$O\ + fprint.$O\ + frexp.$O\ + nan64.$O\ + print.$O\ + runefmtstr.$O\ + runeseprint.$O\ + runesmprint.$O\ + runesnprint.$O\ + runesprint.$O\ + runevseprint.$O\ + runevsmprint.$O\ + runevsnprint.$O\ + seprint.$O\ + smprint.$O\ + snprint.$O\ + sprint.$O\ + strtod.$O\ + vfprint.$O\ + vseprint.$O\ + vsmprint.$O\ + vsnprint.$O\ + $NUM\ + +UTFOFILES=\ + rune.$O\ + runestrcat.$O\ + runestrchr.$O\ + runestrcmp.$O\ + runestrcpy.$O\ + runestrdup.$O\ + runestrlen.$O\ + runestrecpy.$O\ + runestrncat.$O\ + runestrncmp.$O\ + runestrncpy.$O\ + runestrrchr.$O\ + runestrstr.$O\ + runetype.$O\ + utfecpy.$O\ + utflen.$O\ + utfnlen.$O\ + utfrrune.$O\ + utfrune.$O\ + utfutf.$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\ + convD2M.$O\ + convM2D.$O\ + convM2S.$O\ + convS2M.$O\ + crypt.$O\ + ctime.$O\ + dial.$O\ + dirfstat.$O\ + dirfwstat.$O\ + dirmodefmt.$O\ + dirstat.$O\ + dirwstat.$O\ + dup.$O\ + encodefmt.$O\ + errstr.$O\ + exec.$O\ + execl.$O\ + exitcode.$O\ + fcallfmt.$O\ + frand.$O\ + get9root.$O\ + getcallerpc.$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\ + netcrypt.$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\ + 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=\ + $LIB9OFILES\ + $FMTOFILES\ + $UTFOFILES\ + +HFILES=\ + $PLAN9/include/lib9.h\ + +<$PLAN9/src/mksyslib + +%.$O: fmt/%.c + $CC $CFLAGS -Ifmt fmt/$stem.c + +%.$O: utf/%.c + $CC $CFLAGS utf/$stem.c + +XLIB=$PLAN9/lib/$LIB + +testfmt: testfmt.$O $XLIB + $LD -o $target testfmt.$O + +testfltfmt: testfltfmt.$O $XLIB + $LD -o $target testfltfmt.$O + +testprint: testprint.$O $XLIB + $LD -o $target testprint.$O + +# debugging only - should go away (5/22/2006) +testgoogfmt: testfltfmt.$O googfmt.$O $XLIB + $LD -o $target testfltfmt.$O googfmt.$O + +testgoogprint: testprint.$O googfmt.$O $XLIB + $LD -o $target testprint.$O googfmt.$O + +ctime.$O tm2sec.$O zoneinfo.$O: zoneinfo.h diff --git a/lib/lib9/nan.c b/lib/lib9/nan.c new file mode 100644 index 0000000..34feb15 --- /dev/null +++ b/lib/lib9/nan.c @@ -0,0 +1,27 @@ +#include +#include +#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/lib/lib9/needsrcquote.c b/lib/lib9/needsrcquote.c new file mode 100644 index 0000000..a9e62bc --- /dev/null +++ b/lib/lib9/needsrcquote.c @@ -0,0 +1,12 @@ +#include +#include + +int +needsrcquote(int c) +{ + if(c <= ' ' || c >= 0x80) + return 1; + if(strchr("`^#*[]=|\\?${}()'<>&;", c)) + return 1; + return 0; +} diff --git a/lib/lib9/needstack.c b/lib/lib9/needstack.c new file mode 100644 index 0000000..b392d66 --- /dev/null +++ b/lib/lib9/needstack.c @@ -0,0 +1,8 @@ +#include +#include + +void +needstack(int howmuch) +{ + USED(howmuch); +} diff --git a/lib/lib9/netcrypt.c b/lib/lib9/netcrypt.c new file mode 100644 index 0000000..08fa676 --- /dev/null +++ b/lib/lib9/netcrypt.c @@ -0,0 +1,18 @@ +#include +#include +#include + +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/lib/lib9/netmkaddr.c b/lib/lib9/netmkaddr.c new file mode 100644 index 0000000..420c2cf --- /dev/null +++ b/lib/lib9/netmkaddr.c @@ -0,0 +1,62 @@ +#include +#include +#include + +/* + * 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/lib/lib9/notify.c b/lib/lib9/notify.c new file mode 100644 index 0000000..adb3815 --- /dev/null +++ b/lib/lib9/notify.c @@ -0,0 +1,272 @@ +/* + * Signal handling for Plan 9 programs. + * We stubbornly use the strings from Plan 9 instead + * of the enumerated Unix constants. + * There are some weird translations. In particular, + * a "kill" note is the same as SIGTERM in Unix. + * There is no equivalent note to Unix's SIGKILL, since + * it's not a deliverable signal anyway. + * + * We do not handle SIGABRT or SIGSEGV, mainly because + * the thread library queues its notes for later, and we want + * to dump core with the state at time of delivery. + * + * We have to add some extra entry points to provide the + * ability to tweak which signals are deliverable and which + * are acted upon. Notifydisable and notifyenable play with + * the process signal mask. Notifyignore enables the signal + * but will not call notifyf when it comes in. This is occasionally + * useful. + */ + +#include +#include +#define NOPLAN9DEFINES +#include + +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; ib)){ + 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; isig) != SIG_DFL) + continue; + notifyseton(sig->sig, !(sig->flags&NoNotify)); + } +} diff --git a/lib/lib9/nrand.c b/lib/lib9/nrand.c new file mode 100644 index 0000000..cf9c17c --- /dev/null +++ b/lib/lib9/nrand.c @@ -0,0 +1,17 @@ +#include + +#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/lib/lib9/nulldir.c b/lib/lib9/nulldir.c new file mode 100644 index 0000000..302b996 --- /dev/null +++ b/lib/lib9/nulldir.c @@ -0,0 +1,9 @@ +#include +#include + +void +nulldir(Dir *d) +{ + memset(d, ~0, sizeof(Dir)); + d->name = d->uid = d->gid = d->muid = d->ext = ""; +} diff --git a/lib/lib9/open.c b/lib/lib9/open.c new file mode 100644 index 0000000..2354268 --- /dev/null +++ b/lib/lib9/open.c @@ -0,0 +1,347 @@ +#define _GNU_SOURCE /* for Linux O_DIRECT */ +#include +#include +#include +#include +#include +#define NOPLAN9DEFINES +#include + +static struct { + Lock lk; + DIR **d; + int nd; +} dirs; + +static int +dirput(int fd, DIR *d) +{ + int i, nd; + DIR **dp; + + if(fd < 0) { + werrstr("invalid fd"); + return -1; + } + lock(&dirs.lk); + if(fd >= dirs.nd) { + nd = dirs.nd*2; + if(nd <= fd) + nd = fd+1; + dp = realloc(dirs.d, nd*sizeof dirs.d[0]); + if(dp == nil) { + werrstr("out of memory"); + unlock(&dirs.lk); + return -1; + } + for(i=dirs.nd; i= 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; +} + +int +p9open(char *name, int mode) +{ + int cexec, rclose; + int fd, umode, lock, rdwr; + struct flock fl; + struct stat st; + DIR *d; + + 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(fstat(fd, &st) >= 0 && S_ISDIR(st.st_mode)) { + d = fdopendir(fd); + if(d == nil) { + close(fd); + return -1; + } + if(dirput(fd, d) < 0) { + closedir(d); + return -1; + } + } + if(rclose) + remove(name); + } + return fd; +} + +vlong +p9seek(int fd, vlong offset, int whence) +{ + DIR *d; + + if((d = dirget(fd)) != nil) { + if(whence == 1 && offset == 0) + return telldir(d); + if(whence == 0) { + seekdir(d, offset); + return 0; + } + werrstr("bad seek in directory"); + return -1; + } + + return lseek(fd, offset, whence); +} + +int +p9close(int fd) +{ + DIR *d; + + if((d = dirdel(fd)) != nil) + return closedir(d); + return close(fd); +} + +typedef struct DirBuild DirBuild; +struct DirBuild { + Dir *d; + int nd; + int md; + char *str; + char *estr; +}; + +extern int _p9dir(struct stat*, struct stat*, char*, Dir*, char**, char*); + +static int +dirbuild1(DirBuild *b, struct stat *lst, struct stat *st, char *name) +{ + int i, nstr; + Dir *d; + int md, mstr; + char *lo, *hi, *newlo; + + nstr = _p9dir(lst, st, name, nil, nil, nil); + if(b->md-b->nd < 1 || b->estr-b->str < nstr) { + // expand either d space or str space or both. + md = b->md; + if(b->md-b->nd < 1) { + md *= 2; + if(md < 16) + md = 16; + } + mstr = b->estr-(char*)&b->d[b->md]; + if(b->estr-b->str < nstr) { + mstr += nstr; + mstr += mstr/2; + } + if(mstr < 512) + mstr = 512; + d = realloc(b->d, md*sizeof d[0] + mstr); + if(d == nil) + return -1; + // move strings and update pointers in Dirs + lo = (char*)&b->d[b->md]; + newlo = (char*)&d[md]; + hi = b->str; + memmove(newlo, lo+((char*)d-(char*)b->d), hi-lo); + for(i=0; ind; i++) { + if(lo <= d[i].name && d[i].name < hi) + d[i].name += newlo - lo; + if(lo <= d[i].uid && d[i].uid < hi) + d[i].uid += newlo - lo; + if(lo <= d[i].gid && d[i].gid < hi) + d[i].gid += newlo - lo; + if(lo <= d[i].muid && d[i].muid < hi) + d[i].muid += newlo - lo; + } + b->d = d; + b->md = md; + b->str += newlo - lo; + b->estr = newlo + mstr; + } + _p9dir(lst, st, name, &b->d[b->nd], &b->str, b->estr); + b->nd++; + return 0; +} + +static long +dirreadmax(int fd, Dir **dp, int max) +{ + int i; + DIR *dir; + DirBuild b; + struct dirent *de; + struct stat st, lst; + + if((dir = dirget(fd)) == nil) { + werrstr("not a directory"); + return -1; + } + + memset(&b, 0, sizeof b); + for(i=0; max == -1 || id_name[0]=='.' && de->d_name[1]==0) + continue; + if(de->d_name[0]=='.' && de->d_name[1]=='.' && de->d_name[2]==0) + continue; + if(fstatat(fd, de->d_name, &lst, AT_SYMLINK_NOFOLLOW) < 0) + continue; + st = lst; + if(S_ISLNK(lst.st_mode)) + fstatat(fd, de->d_name, &st, 0); + dirbuild1(&b, &lst, &st, de->d_name); + } + *dp = b.d; + return b.nd; +} + +long +dirread(int fd, Dir **dp) +{ + return dirreadmax(fd, dp, 10); +} + +long +dirreadall(int fd, Dir **dp) +{ + return dirreadmax(fd, dp, -1); +} diff --git a/lib/lib9/opentemp.c b/lib/lib9/opentemp.c new file mode 100644 index 0000000..c999f49 --- /dev/null +++ b/lib/lib9/opentemp.c @@ -0,0 +1,19 @@ +#include +#include + +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/lib/lib9/pin.c b/lib/lib9/pin.c new file mode 100644 index 0000000..c50587f --- /dev/null +++ b/lib/lib9/pin.c @@ -0,0 +1,10 @@ +#include +#include + +static void +nop(void) +{ +} + +void (*_pin)(void) = nop; +void (*_unpin)(void) = nop; diff --git a/lib/lib9/pipe.c b/lib/lib9/pipe.c new file mode 100644 index 0000000..01b244e --- /dev/null +++ b/lib/lib9/pipe.c @@ -0,0 +1,15 @@ +#include +#define NOPLAN9DEFINES +#include +#include + +/* + * 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/lib/lib9/portdate b/lib/lib9/portdate new file mode 100644 index 0000000..a9fb519 --- /dev/null +++ b/lib/lib9/portdate @@ -0,0 +1,55 @@ +announce.c 2004/1225 +atexit.c 2004/1225 +atnotify.c 2004/1225 +atol.c 2004/1225 +atoll.c 2004/1225 +cistrcmp.c 2004/1225 +cistrncmp.c 2004/1225 +cistrstr.c 2004/1225 +cleanname.c 2004/1225 +convD2M.c 2004/1225 +convM2D.c 2004/1225 +convM2S.c 2004/1225 +convS2M.c 2004/1225 +ctime.c 2004/1225 +dial.c 2004/1225 +dirfstat.c 2004/1225 +dirfwstat.c 2004/1225 +dirmodefmt.c 2004/1225 +dirread.c 2004/1225 +dirstat.c 2004/1225 +dirwstat.c 2004/1225 +encodefmt.c 2004/1225 +fcallfmt.c 2004/1225 +fork.c 2004/1225 +getenv.c 2004/1225 +getfields.c 2004/1225 +getuser.c 2004/1225 +getwd.c 2004/1225 +lnrand.c 2004/1225 +lock.c 2004/1225 +lrand.c 2004/1225 +malloc.c 2004/1225 +nan.c 2004/1225 +needsrcquote.c 2004/1225 +netmkaddr.c 2004/1225 +nrand.c 2004/1225 +nulldir.c 2004/1225 +postnote.c 2004/1225 +qlock.c 2004/1225 +quote.c 2004/1225 +rand.c 2004/1225 +read9pmsg.c 2004/1225 +readn.c 2004/1225 +strdup.c 2004/1225 +strecpy.c 2004/1225 +sysfatal.c 2004/1225 +sysname.c 2004/1225 +time.c 2004/1225 +tokenize.c 2004/1225 +truerand.c 2004/1225 +u16.c 2004/1225 +u32.c 2004/1225 +u64.c 2004/1225 +wait.c 2004/1225 +waitpid.c 2004/1225 diff --git a/lib/lib9/post9p.c b/lib/lib9/post9p.c new file mode 100644 index 0000000..e894ef9 --- /dev/null +++ b/lib/lib9/post9p.c @@ -0,0 +1,83 @@ +#include +#include + +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/lib/lib9/postnote.c b/lib/lib9/postnote.c new file mode 100644 index 0000000..d750c69 --- /dev/null +++ b/lib/lib9/postnote.c @@ -0,0 +1,37 @@ +#include +#define NOPLAN9DEFINES +#include + +#include + + +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; + } + + if(pid <= 0){ + werrstr("bad pid in postnote"); + 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/lib/lib9/priv.c b/lib/lib9/priv.c new file mode 100644 index 0000000..fb493c7 --- /dev/null +++ b/lib/lib9/priv.c @@ -0,0 +1,31 @@ +#include +#include +#include "9proc.h" + +static Lock privlock; +static ulong privmap; + +int +privalloc(void) +{ + int i; + + lock(&privlock); + for(i=0; ipriv[i]; +} diff --git a/lib/lib9/qlock.c b/lib/lib9/qlock.c new file mode 100644 index 0000000..7d9d064 --- /dev/null +++ b/lib/lib9/qlock.c @@ -0,0 +1,166 @@ +#include +#include + +/* + * 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/lib/lib9/quote.c b/lib/lib9/quote.c new file mode 100644 index 0000000..8ab6592 --- /dev/null +++ b/lib/lib9/quote.c @@ -0,0 +1,136 @@ +#include +#include + + +/* 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, "elen) == 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, "elen) == 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/lib/lib9/rand.c b/lib/lib9/rand.c new file mode 100644 index 0000000..75d1d47 --- /dev/null +++ b/lib/lib9/rand.c @@ -0,0 +1,7 @@ +#include + +int +p9rand(void) +{ + return lrand() & 0x7fff; +} diff --git a/lib/lib9/read9pmsg.c b/lib/lib9/read9pmsg.c new file mode 100644 index 0000000..9e90ec5 --- /dev/null +++ b/lib/lib9/read9pmsg.c @@ -0,0 +1,31 @@ +#include +#include +#include + +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/lib/lib9/readcons.c b/lib/lib9/readcons.c new file mode 100644 index 0000000..c07e597 --- /dev/null +++ b/lib/lib9/readcons.c @@ -0,0 +1,104 @@ +#include +#define NOPLAN9DEFINES +#include +#include +#ifdef HAS_SYS_TERMIOS +#include +#endif + +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/lib/lib9/readn.c b/lib/lib9/readn.c new file mode 100644 index 0000000..e7b9d13 --- /dev/null +++ b/lib/lib9/readn.c @@ -0,0 +1,21 @@ +#include + +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/lib/lib9/rfork.c b/lib/lib9/rfork.c new file mode 100644 index 0000000..c737b49 --- /dev/null +++ b/lib/lib9/rfork.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#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; + struct sigaction oldchld; + + memset(&oldchld, 0, sizeof oldchld); + + 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){ + sigaction(SIGCHLD, nil, &oldchld); + 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]); + } + } + sigaction(SIGCHLD, &oldchld, nil); + } + 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/lib/lib9/searchpath.c b/lib/lib9/searchpath.c new file mode 100644 index 0000000..75e0695 --- /dev/null +++ b/lib/lib9/searchpath.c @@ -0,0 +1,61 @@ +#include +#include + +/* + * 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/lib/lib9/sendfd.c b/lib/lib9/sendfd.c new file mode 100644 index 0000000..e73225e --- /dev/null +++ b/lib/lib9/sendfd.c @@ -0,0 +1,88 @@ +#include +#define NOPLAN9DEFINES +#include +#include +#include +#include +#include + +#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/lib/lib9/sleep.c b/lib/lib9/sleep.c new file mode 100644 index 0000000..bc0d192 --- /dev/null +++ b/lib/lib9/sleep.c @@ -0,0 +1,47 @@ +#include +#define NOPLAN9DEFINES +#include +#include +#include +#include + +#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/lib/lib9/strdup.c b/lib/lib9/strdup.c new file mode 100644 index 0000000..5c67f0b --- /dev/null +++ b/lib/lib9/strdup.c @@ -0,0 +1,16 @@ +#include +#include + +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/lib/lib9/strecpy.c b/lib/lib9/strecpy.c new file mode 100644 index 0000000..7d2f227 --- /dev/null +++ b/lib/lib9/strecpy.c @@ -0,0 +1,16 @@ +#include + +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/lib/lib9/sysfatal.c b/lib/lib9/sysfatal.c new file mode 100644 index 0000000..11f4234 --- /dev/null +++ b/lib/lib9/sysfatal.c @@ -0,0 +1,20 @@ +#include + +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 : "", buf); + exits("fatal"); +} diff --git a/lib/lib9/syslog.c b/lib/lib9/syslog.c new file mode 100644 index 0000000..8bb602e --- /dev/null +++ b/lib/lib9/syslog.c @@ -0,0 +1,119 @@ +#include +#include + +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/lib/lib9/sysname.c b/lib/lib9/sysname.c new file mode 100644 index 0000000..2060b98 --- /dev/null +++ b/lib/lib9/sysname.c @@ -0,0 +1,30 @@ +#include +#include + +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/lib/lib9/test.c b/lib/lib9/test.c new file mode 100644 index 0000000..3a358c6 --- /dev/null +++ b/lib/lib9/test.c @@ -0,0 +1,8 @@ +#include + +int +main(int argc, char **argv) +{ + werrstr("hello world"); + print("%r\n"); +} diff --git a/lib/lib9/testfltfmt.c b/lib/lib9/testfltfmt.c new file mode 100644 index 0000000..c602ce4 --- /dev/null +++ b/lib/lib9/testfltfmt.c @@ -0,0 +1,183 @@ +#include +#include +#include + +/* + * 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/lib/lib9/testfmt.c b/lib/lib9/testfmt.c new file mode 100644 index 0000000..11708ba --- /dev/null +++ b/lib/lib9/testfmt.c @@ -0,0 +1,148 @@ +#include +#include +#include + +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/lib/lib9/testfork.c b/lib/lib9/testfork.c new file mode 100644 index 0000000..a5e6371 --- /dev/null +++ b/lib/lib9/testfork.c @@ -0,0 +1,21 @@ +#include + +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/lib/lib9/testprint.c b/lib/lib9/testprint.c new file mode 100644 index 0000000..b2c0420 --- /dev/null +++ b/lib/lib9/testprint.c @@ -0,0 +1,14 @@ +#include +#include + +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/lib/lib9/time.c b/lib/lib9/time.c new file mode 100644 index 0000000..92ad2ca --- /dev/null +++ b/lib/lib9/time.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#define NOPLAN9DEFINES +#include + +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/lib/lib9/tm2sec.c b/lib/lib9/tm2sec.c new file mode 100644 index 0000000..58bbe99 --- /dev/null +++ b/lib/lib9/tm2sec.c @@ -0,0 +1,110 @@ +#include +#include + +#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; imon; 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/lib/lib9/tokenize.c b/lib/lib9/tokenize.c new file mode 100644 index 0000000..6fa9fc7 --- /dev/null +++ b/lib/lib9/tokenize.c @@ -0,0 +1,106 @@ +#include + +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 +#include + +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 +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/lib/lib9/u32.c b/lib/lib9/u32.c new file mode 100644 index 0000000..1eb0c6e --- /dev/null +++ b/lib/lib9/u32.c @@ -0,0 +1,109 @@ +#include + +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>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/lib/lib9/u64.c b/lib/lib9/u64.c new file mode 100644 index 0000000..84e5fe0 --- /dev/null +++ b/lib/lib9/u64.c @@ -0,0 +1,126 @@ +#include + +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/lib/lib9/udp.c b/lib/lib9/udp.c new file mode 100644 index 0000000..a9c4cb6 --- /dev/null +++ b/lib/lib9/udp.c @@ -0,0 +1,51 @@ +#include +#define NOPLAN9DEFINES +#include +#include + +#include +#include + +/* + * 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/lib/lib9/unsharp.c b/lib/lib9/unsharp.c new file mode 100644 index 0000000..251cae5 --- /dev/null +++ b/lib/lib9/unsharp.c @@ -0,0 +1,44 @@ +#include +#include + +/* + * 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 +#include "utf.h" + +#define nil ((void*)0) + +#define uchar _fmtuchar +#define ushort _fmtushort +#define uint _fmtuint +#define ulong _fmtulong +#define vlong _fmtvlong +#define uvlong _fmtuvlong + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; diff --git a/lib/lib9/utf/plan9.h b/lib/lib9/utf/plan9.h new file mode 100644 index 0000000..1ca8ace --- /dev/null +++ b/lib/lib9/utf/plan9.h @@ -0,0 +1,28 @@ +/* + * 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/lib/lib9/utf/portdate b/lib/lib9/utf/portdate new file mode 100644 index 0000000..1385cf0 --- /dev/null +++ b/lib/lib9/utf/portdate @@ -0,0 +1,20 @@ +rune.c 2004/1225 +runestrcat.c 2004/1225 +runestrchr.c 2004/1225 +runestrcmp.c 2004/1225 +runestrcpy.c 2004/1225 +runestrdup.c 2004/1225 +runestrecpy.c 2004/1225 +runestrlen.c 2004/1225 +runestrncat.c 2004/1225 +runestrncmp.c 2004/1225 +runestrncpy.c 2004/1225 +runestrrchr.c 2004/1225 +runestrstr.c 2004/1225 +runetype.c 2004/1225 +utfecpy.c 2004/1225 +utflen.c 2004/1225 +utfnlen.c 2004/1225 +utfrrune.c 2004/1225 +utfrune.c 2004/1225 +utfutf.c 2004/1225 diff --git a/lib/lib9/utf/rune.c b/lib/lib9/utf/rune.c new file mode 100644 index 0000000..bb2d82c --- /dev/null +++ b/lib/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 +#include +#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< 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/lib/lib9/utf/runestrcat.c b/lib/lib9/utf/runestrcat.c new file mode 100644 index 0000000..65d4c0f --- /dev/null +++ b/lib/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 +#include +#include "plan9.h" +#include "utf.h" + +Rune* +runestrcat(Rune *s1, Rune *s2) +{ + + runestrcpy(runestrchr(s1, 0), s2); + return s1; +} diff --git a/lib/lib9/utf/runestrchr.c b/lib/lib9/utf/runestrchr.c new file mode 100644 index 0000000..21fbeeb --- /dev/null +++ b/lib/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 +#include +#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/lib/lib9/utf/runestrcmp.c b/lib/lib9/utf/runestrcmp.c new file mode 100644 index 0000000..a368613 --- /dev/null +++ b/lib/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 +#include +#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/lib/lib9/utf/runestrcpy.c b/lib/lib9/utf/runestrcpy.c new file mode 100644 index 0000000..0659fc3 --- /dev/null +++ b/lib/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 +#include +#include "plan9.h" +#include "utf.h" + +Rune* +runestrcpy(Rune *s1, Rune *s2) +{ + Rune *os1; + + os1 = s1; + while(*s1++ = *s2++) + ; + return os1; +} diff --git a/lib/lib9/utf/runestrdup.c b/lib/lib9/utf/runestrdup.c new file mode 100644 index 0000000..4f9d6f4 --- /dev/null +++ b/lib/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 +#include +#include +#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/lib/lib9/utf/runestrecpy.c b/lib/lib9/utf/runestrecpy.c new file mode 100644 index 0000000..c543e22 --- /dev/null +++ b/lib/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 +#include +#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/lib/lib9/utf/runestrlen.c b/lib/lib9/utf/runestrlen.c new file mode 100644 index 0000000..0a13ecd --- /dev/null +++ b/lib/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 +#include +#include "plan9.h" +#include "utf.h" + +long +runestrlen(Rune *s) +{ + + return runestrchr(s, 0) - s; +} diff --git a/lib/lib9/utf/runestrncat.c b/lib/lib9/utf/runestrncat.c new file mode 100644 index 0000000..9653637 --- /dev/null +++ b/lib/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 +#include +#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/lib/lib9/utf/runestrncmp.c b/lib/lib9/utf/runestrncmp.c new file mode 100644 index 0000000..5e9a3b6 --- /dev/null +++ b/lib/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 +#include +#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/lib/lib9/utf/runestrncpy.c b/lib/lib9/utf/runestrncpy.c new file mode 100644 index 0000000..ffcb3e1 --- /dev/null +++ b/lib/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 +#include +#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/lib/lib9/utf/runestrrchr.c b/lib/lib9/utf/runestrrchr.c new file mode 100644 index 0000000..1b0edbb --- /dev/null +++ b/lib/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 +#include +#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/lib/lib9/utf/runestrstr.c b/lib/lib9/utf/runestrstr.c new file mode 100644 index 0000000..f5fa997 --- /dev/null +++ b/lib/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 +#include +#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/lib/lib9/utf/runetype.c b/lib/lib9/utf/runetype.c new file mode 100644 index 0000000..ac6d7b5 --- /dev/null +++ b/lib/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 +#include +#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/lib/lib9/utf/utfdef.h b/lib/lib9/utf/utfdef.h new file mode 100644 index 0000000..1db7076 --- /dev/null +++ b/lib/lib9/utf/utfdef.h @@ -0,0 +1,32 @@ +/* + * compiler directive on Plan 9 + */ +#ifndef USED +#define USED(x) if(x);else +#endif + +/* + * easiest way to make sure these are defined + */ +#define uchar _fmtuchar +#define ushort _fmtushort +#define uint _fmtuint +#define ulong _fmtulong +#define vlong _fmtvlong +#define uvlong _fmtuvlong +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned long long uvlong; +typedef long long vlong; + +/* + * nil cannot be ((void*)0) on ANSI C, + * because it is used for function pointers + */ +#undef nil +#define nil 0 + +#undef nelem +#define nelem ((void*)0) diff --git a/lib/lib9/utf/utfecpy.c b/lib/lib9/utf/utfecpy.c new file mode 100644 index 0000000..bab8136 --- /dev/null +++ b/lib/lib9/utf/utfecpy.c @@ -0,0 +1,38 @@ +/* + * 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 */ +#define _DEFAULT_SOURCE 1 +#include +#include +#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/lib/lib9/utf/utflen.c b/lib/lib9/utf/utflen.c new file mode 100644 index 0000000..769805a --- /dev/null +++ b/lib/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 +#include +#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/lib/lib9/utf/utfnlen.c b/lib/lib9/utf/utfnlen.c new file mode 100644 index 0000000..6680329 --- /dev/null +++ b/lib/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 +#include +#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/lib/lib9/utf/utfrrune.c b/lib/lib9/utf/utfrrune.c new file mode 100644 index 0000000..cff12b5 --- /dev/null +++ b/lib/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 +#include +#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/lib/lib9/utf/utfrune.c b/lib/lib9/utf/utfrune.c new file mode 100644 index 0000000..52b8359 --- /dev/null +++ b/lib/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 +#include +#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/lib/lib9/utf/utfutf.c b/lib/lib9/utf/utfutf.c new file mode 100644 index 0000000..13c8502 --- /dev/null +++ b/lib/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 +#include +#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/lib/lib9/wait.c b/lib/lib9/wait.c new file mode 100644 index 0000000..17d7678 --- /dev/null +++ b/lib/lib9/wait.c @@ -0,0 +1,53 @@ +#include +#include + +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/lib/lib9/waitpid.c b/lib/lib9/waitpid.c new file mode 100644 index 0000000..4c90bb7 --- /dev/null +++ b/lib/lib9/waitpid.c @@ -0,0 +1,19 @@ +#include +#include + +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/lib/lib9/write.c b/lib/lib9/write.c new file mode 100644 index 0000000..aa32db7 --- /dev/null +++ b/lib/lib9/write.c @@ -0,0 +1,23 @@ +#include +#define NOPLAN9DEFINES +#include + +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/lib/lib9/zoneinfo.c b/lib/lib9/zoneinfo.c new file mode 100644 index 0000000..36b4b6e --- /dev/null +++ b/lib/lib9/zoneinfo.c @@ -0,0 +1,213 @@ +#include +#include + +/* + * 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 MIT license used in the rest of the distribution. + */ +#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(itzoff = get4(p); + ti->dlflag = p[4]; + assert(p[5]zone = (char*)z.pzone + p[5]; +} + +static +void +readtimezone(void) +{ + char *tmp; + + z.timecnt = 0; + if(zonefile==nil) { + if ((tmp=getenv("timezone"))!=nil) { + tzdata = readtzfile(tmp); + free(tmp); + goto havedata; + } + zonefile = "/etc/localtime"; + } + tzdata = readtzfile(zonefile); + if (tzdata==nil) + return; + +havedata: + 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; i0) { + 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; iyynerrs +#define yyerrflag yyarg->yyerrflag +#define yyval yyarg->yyval +#define yylval yyarg->yylval +#else +int yynerrs = 0; /* number of errors */ +int yyerrflag = 0; /* error recovery flag */ +#endif + +extern int fprint(int, char*, ...); +extern int sprint(char*, char*, ...); + +static const char* +yytokname(int yyc) +{ + static char x[10]; + + if(yyc > 0 && yyc <= sizeof(yytoknames)/sizeof(yytoknames[0])) + if(yytoknames[yyc-1]) + return yytoknames[yyc-1]; + sprint(x, "<%d>", yyc); + return x; +} + +static const char* +yystatname(int yys) +{ + static char x[10]; + + if(yys >= 0 && yys < sizeof(yystates)/sizeof(yystates[0])) + if(yystates[yys]) + return yystates[yys]; + sprint(x, "<%d>\n", yys); + return x; +} + +static long +#ifdef YYARG +yylex1(struct Yyarg *yyarg) +#else +yylex1(void) +#endif +{ + long yychar; + const long *t3p; + int c; + +#ifdef YYARG + yychar = yylex(yyarg); +#else + yychar = yylex(); +#endif + if(yychar <= 0) { + c = yytok1[0]; + goto out; + } + if(yychar < sizeof(yytok1)/sizeof(yytok1[0])) { + c = yytok1[yychar]; + goto out; + } + if(yychar >= YYPRIVATE) + if(yychar < YYPRIVATE+sizeof(yytok2)/sizeof(yytok2[0])) { + c = yytok2[yychar-YYPRIVATE]; + goto out; + } + for(t3p=yytok3;; t3p+=2) { + c = t3p[0]; + if(c == yychar) { + c = t3p[1]; + goto out; + } + if(c == 0) + break; + } + c = 0; + +out: + if(c == 0) + c = yytok2[1]; /* unknown char */ + if(yydebug >= 3) + fprint(2, "lex %.4lux %s\n", yychar, yytokname(c)); + return c; +} + +int +#ifdef YYARG +yyparse(struct Yyarg *yyarg) +#else +yyparse(void) +#endif +{ + struct + { + YYSTYPE yyv; + int yys; + } yys[YYMAXDEPTH], *yyp, *yypt; + const short *yyxi; + int yyj, yym, yystate, yyn, yyg; + long yychar; +#ifndef YYARG + YYSTYPE save1, save2; + int save3, save4; + + save1 = yylval; + save2 = yyval; + save3 = yynerrs; + save4 = yyerrflag; +#endif + + yystate = 0; + yychar = -1; + yynerrs = 0; + yyerrflag = 0; + yyp = &yys[0]; + yyp--; + goto yystack; + +ret0: + yyn = 0; + goto ret; + +ret1: + yyn = 1; + goto ret; + +ret: +#ifndef YYARG + yylval = save1; + yyval = save2; + yynerrs = save3; + yyerrflag = save4; +#endif + return yyn; + +yystack: + /* put a state and value onto the stack */ + if(yydebug >= 4) + fprint(2, "char %s in %s", yytokname(yychar), yystatname(yystate)); + + yyp++; + if(yyp >= &yys[YYMAXDEPTH]) { + yyerror("yacc stack overflow"); + goto ret1; + } + yyp->yys = yystate; + yyp->yyv = yyval; + +yynewstate: + yyn = yypact[yystate]; + if(yyn <= YYFLAG) + goto yydefault; /* simple state */ + if(yychar < 0) +#ifdef YYARG + yychar = yylex1(yyarg); +#else + yychar = yylex1(); +#endif + yyn += yychar; + if(yyn < 0 || yyn >= YYLAST) + goto yydefault; + yyn = yyact[yyn]; + if(yychk[yyn] == yychar) { /* valid shift */ + yychar = -1; + yyval = yylval; + yystate = yyn; + if(yyerrflag > 0) + yyerrflag--; + goto yystack; + } + +yydefault: + /* default state action */ + yyn = yydef[yystate]; + if(yyn == -2) { + if(yychar < 0) +#ifdef YYARG + yychar = yylex1(yyarg); +#else + yychar = yylex1(); +#endif + + /* look through exception table */ + for(yyxi=yyexca;; yyxi+=2) + if(yyxi[0] == -1 && yyxi[1] == yystate) + break; + for(yyxi += 2;; yyxi += 2) { + yyn = yyxi[0]; + if(yyn < 0 || yyn == yychar) + break; + } + yyn = yyxi[1]; + if(yyn < 0) + goto ret0; + } + if(yyn == 0) { + /* error ... attempt to resume parsing */ + switch(yyerrflag) { + case 0: /* brand new error */ + yyerror("syntax error"); + if(yydebug >= 1) { + fprint(2, "%s", yystatname(yystate)); + fprint(2, "saw %s\n", yytokname(yychar)); + } + goto yyerrlab; + yyerrlab: + yynerrs++; + + case 1: + case 2: /* incompletely recovered error ... try again */ + yyerrflag = 3; + + /* find a state where "error" is a legal shift action */ + while(yyp >= yys) { + yyn = yypact[yyp->yys] + YYERRCODE; + if(yyn >= 0 && yyn < YYLAST) { + yystate = yyact[yyn]; /* simulate a shift of "error" */ + if(yychk[yystate] == YYERRCODE) + goto yystack; + } + + /* the current yyp has no shift onn "error", pop stack */ + if(yydebug >= 2) + fprint(2, "error recovery pops state %d, uncovers %d\n", + yyp->yys, (yyp-1)->yys ); + yyp--; + } + /* there is no state on the stack with an error shift ... abort */ + goto ret1; + + case 3: /* no shift yet; clobber input char */ + if(yydebug >= 2) + fprint(2, "error recovery discards %s\n", yytokname(yychar)); + if(yychar == YYEOFCODE) + goto ret1; + yychar = -1; + goto yynewstate; /* try again in the same state */ + } + } + + /* reduction by production yyn */ + if(yydebug >= 2) + fprint(2, "reduce %d in:\n\t%s", yyn, yystatname(yystate)); + + yypt = yyp; + yyp -= yyr2[yyn]; + yyval = (yyp+1)->yyv; + yym = yyn; + + /* consult goto table to find next state */ + yyn = yyr1[yyn]; + yyg = yypgo[yyn]; + yyj = yyg + yyp->yys + 1; + + if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn) + yystate = yyact[yyg]; + switch(yym) { + $A + } + goto yystack; /* stack new state and value */ +} diff --git a/lib/yaccpars b/lib/yaccpars new file mode 100644 index 0000000..532f50b --- /dev/null +++ b/lib/yaccpars @@ -0,0 +1,274 @@ +#define YYFLAG -1000 +#define YYERROR goto yyerrlab +#define YYACCEPT return(0) +#define YYABORT return(1) +#define yyclearin yychar = -1 +#define yyerrok yyerrflag = 0 + +#ifdef yydebug +#include "y.debug" +#else +#define yydebug 0 +static const char* yytoknames[1]; /* for debugging */ +static const char* yystates[1]; /* for debugging */ +#endif + +/* parser for yacc output */ +#ifdef YYARG +#define yynerrs yyarg->yynerrs +#define yyerrflag yyarg->yyerrflag +#define yyval yyarg->yyval +#define yylval yyarg->yylval +#else +int yynerrs = 0; /* number of errors */ +int yyerrflag = 0; /* error recovery flag */ +#endif + +static const char* +yytokname(int yyc) +{ + static char x[10]; + + if(yyc > 0 && yyc <= sizeof(yytoknames)/sizeof(yytoknames[0])) + if(yytoknames[yyc-1]) + return yytoknames[yyc-1]; + sprintf(x, "<%d>", yyc); + return x; +} + +static const char* +yystatname(int yys) +{ + static char x[10]; + + if(yys >= 0 && yys < sizeof(yystates)/sizeof(yystates[0])) + if(yystates[yys]) + return yystates[yys]; + sprintf(x, "<%d>\n", yys); + return x; +} + +static long +#ifdef YYARG +yylex1(struct Yyarg *yyarg) +#else +yylex1(void) +#endif +{ + long yychar; + const long *t3p; + int c; + +#ifdef YYARG + yychar = yylex(yyarg); +#else + yychar = yylex(); +#endif + if(yychar <= 0) { + c = yytok1[0]; + goto out; + } + if(yychar < sizeof(yytok1)/sizeof(yytok1[0])) { + c = yytok1[yychar]; + goto out; + } + if(yychar >= YYPRIVATE) + if(yychar < YYPRIVATE+sizeof(yytok2)/sizeof(yytok2[0])) { + c = yytok2[yychar-YYPRIVATE]; + goto out; + } + for(t3p=yytok3;; t3p+=2) { + c = t3p[0]; + if(c == yychar) { + c = t3p[1]; + goto out; + } + if(c == 0) + break; + } + c = 0; + +out: + if(c == 0) + c = yytok2[1]; /* unknown char */ + if(yydebug >= 3) + printf("lex %.4lX %s\n", yychar, yytokname(c)); + return c; +} + +int +#ifdef YYARG +yyparse(struct Yyarg *yyarg) +#else +yyparse(void) +#endif +{ + struct + { + YYSTYPE yyv; + int yys; + } yys[YYMAXDEPTH], *yyp, *yypt; + const short *yyxi; + int yyj, yym, yystate, yyn, yyg; + long yychar; +#ifndef YYARG + YYSTYPE save1, save2; + int save3, save4; + + save1 = yylval; + save2 = yyval; + save3 = yynerrs; + save4 = yyerrflag; +#endif + + yystate = 0; + yychar = -1; + yynerrs = 0; + yyerrflag = 0; + yyp = &yys[0]; + yyp--; + goto yystack; + +ret0: + yyn = 0; + goto ret; + +ret1: + yyn = 1; + goto ret; + +ret: +#ifndef YYARG + yylval = save1; + yyval = save2; + yynerrs = save3; + yyerrflag = save4; +#endif + return yyn; + +yystack: + /* put a state and value onto the stack */ + if(yydebug >= 4) + printf("char %s in %s", yytokname(yychar), yystatname(yystate)); + + yyp++; + if(yyp >= &yys[YYMAXDEPTH]) { + yyerror("yacc stack overflow"); + goto ret1; + } + yyp->yys = yystate; + yyp->yyv = yyval; + +yynewstate: + yyn = yypact[yystate]; + if(yyn <= YYFLAG) + goto yydefault; /* simple state */ + if(yychar < 0) +#ifdef YYARG + yychar = yylex1(yyarg); +#else + yychar = yylex1(); +#endif + yyn += yychar; + if(yyn < 0 || yyn >= YYLAST) + goto yydefault; + yyn = yyact[yyn]; + if(yychk[yyn] == yychar) { /* valid shift */ + yychar = -1; + yyval = yylval; + yystate = yyn; + if(yyerrflag > 0) + yyerrflag--; + goto yystack; + } + +yydefault: + /* default state action */ + yyn = yydef[yystate]; + if(yyn == -2) { + if(yychar < 0) +#ifdef YYARG + yychar = yylex1(yyarg); +#else + yychar = yylex1(); +#endif + + /* look through exception table */ + for(yyxi=yyexca;; yyxi+=2) + if(yyxi[0] == -1 && yyxi[1] == yystate) + break; + for(yyxi += 2;; yyxi += 2) { + yyn = yyxi[0]; + if(yyn < 0 || yyn == yychar) + break; + } + yyn = yyxi[1]; + if(yyn < 0) + goto ret0; + } + if(yyn == 0) { + /* error ... attempt to resume parsing */ + switch(yyerrflag) { + case 0: /* brand new error */ + yyerror("syntax error"); + if(yydebug >= 1) { + printf("%s", yystatname(yystate)); + printf("saw %s\n", yytokname(yychar)); + } + goto yyerrlab; + yyerrlab: + yynerrs++; + + case 1: + case 2: /* incompletely recovered error ... try again */ + yyerrflag = 3; + + /* find a state where "error" is a legal shift action */ + while(yyp >= yys) { + yyn = yypact[yyp->yys] + YYERRCODE; + if(yyn >= 0 && yyn < YYLAST) { + yystate = yyact[yyn]; /* simulate a shift of "error" */ + if(yychk[yystate] == YYERRCODE) + goto yystack; + } + + /* the current yyp has no shift onn "error", pop stack */ + if(yydebug >= 2) + printf("error recovery pops state %d, uncovers %d\n", + yyp->yys, (yyp-1)->yys ); + yyp--; + } + /* there is no state on the stack with an error shift ... abort */ + goto ret1; + + case 3: /* no shift yet; clobber input char */ + if(yydebug >= YYEOFCODE) + printf("error recovery discards %s\n", yytokname(yychar)); + if(yychar == YYEOFCODE) + goto ret1; + yychar = -1; + goto yynewstate; /* try again in the same state */ + } + } + + /* reduction by production yyn */ + if(yydebug >= 2) + printf("reduce %d in:\n\t%s", yyn, yystatname(yystate)); + + yypt = yyp; + yyp -= yyr2[yyn]; + yyval = (yyp+1)->yyv; + yym = yyn; + + /* consult goto table to find next state */ + yyn = yyr1[yyn]; + yyg = yypgo[yyn]; + yyj = yyg + yyp->yys + 1; + + if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn) + yystate = yyact[yyg]; + switch(yym) { + $A + } + goto yystack; /* stack new state and value */ +}