From 934846f35caaf236b554c5253cabc1032af2a27a Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 8 Aug 2005 12:50:13 +0000 Subject: [PATCH] a --- Makefile | 82 ++ Notes | 16 + args.h | 20 + cpu.c | 606 +++++++++ drawterm.h | 10 + drawterm.ico | Bin 0 -> 1662 bytes drawterm.rc | 72 + drawterm.res | Bin 0 -> 1756 bytes exportfs/Makefile | 16 + exportfs/exportfs.c | 503 +++++++ exportfs/exportfs.h | 148 ++ exportfs/exportsrv.c | 679 ++++++++++ exportfs/mkfile | 11 + gui-win32/alloc.c | 23 + gui-win32/cload.c | 10 + gui-win32/draw.c | 22 + gui-win32/load.c | 10 + gui-win32/mkfile | 14 + gui-win32/screen.c | 642 +++++++++ gui-win32/wstrtoutf.c | 35 + gui-x11/Makefile | 20 + gui-x11/alloc.c | 209 +++ gui-x11/cload.c | 16 + gui-x11/draw.c | 189 +++ gui-x11/keysym2ucs-x11.c | 857 ++++++++++++ gui-x11/keysym2ucs.h | 9 + gui-x11/ksym2utf.h | 754 +++++++++++ gui-x11/load.c | 16 + gui-x11/screen.c | 1094 +++++++++++++++ gui-x11/xmem.h | 60 + include/a.out | Bin 0 -> 4522 bytes include/auth.h | 144 ++ include/authsrv.h | 166 +++ include/cursor.h | 6 + include/draw.h | 519 ++++++++ include/dtos.h | 10 + include/fcall.h | 110 ++ include/keyboard.h | 42 + include/lib.h | 246 ++++ include/libc.h | 3 + include/libsec.h | 340 +++++ include/memdraw.h | 200 +++ include/memlayer.h | 51 + include/mp.h | 134 ++ include/u.h | 26 + include/unix.h | 14 + include/user.h | 65 + include/x | 0 include/x.c | 6 + kern/Makefile | 50 + kern/allocb.c | 165 +++ kern/cache.c | 46 + kern/chan.c | 1496 +++++++++++++++++++++ kern/dat.h | 519 ++++++++ kern/data.c | 31 + kern/dev.c | 468 +++++++ kern/devcons.c | 1238 +++++++++++++++++ kern/devdraw.c | 2100 +++++++++++++++++++++++++++++ kern/devfs.c | 638 +++++++++ kern/devip-posix.c | 209 +++ kern/devip-win.c | 208 +++ kern/devip.c | 936 +++++++++++++ kern/devip.h | 17 + kern/devmnt.c | 1214 +++++++++++++++++ kern/devmouse.c | 237 ++++ kern/devntfs.c | 704 ++++++++++ kern/devpipe.c | 398 ++++++ kern/devroot.c | 268 ++++ kern/devssl.c | 1512 +++++++++++++++++++++ kern/devtab.c | 27 + kern/error.c | 50 + kern/error.h | 50 + kern/exportfs.c | 821 ++++++++++++ kern/fns.h | 377 ++++++ kern/mkfile | 47 + kern/netif.h | 133 ++ kern/os-windows.c | 184 +++ kern/parse.c | 113 ++ kern/pgrp.c | 272 ++++ kern/posix.c | 190 +++ kern/procinit.c | 67 + kern/qio.c | 1524 +++++++++++++++++++++ kern/qlock.c | 94 ++ kern/rendez.c | 90 ++ kern/rwlock.c | 39 + kern/screen.h | 59 + kern/sleep.c | 90 ++ kern/smalloc.c | 18 + kern/stub.c | 170 +++ kern/syscall.c | 837 ++++++++++++ kern/sysfile.c | 1242 +++++++++++++++++ kern/sysproc.c | 32 + kern/term.c | 204 +++ kern/todo.c | 0 kern/uart.c | 14 + kern/waserror.c | 27 + kern/winduhz.h | 18 + kern/x | 601 +++++++++ latin1.c | 177 +++ libauthsrv/Makefile | 28 + libauthsrv/authdial.c | 31 + libauthsrv/convA2M.c | 25 + libauthsrv/convM2A.c | 23 + libauthsrv/convM2PR.c | 28 + libauthsrv/convM2T.c | 28 + libauthsrv/convM2TR.c | 28 + libauthsrv/convPR2M.c | 28 + libauthsrv/convT2M.c | 27 + libauthsrv/convTR2M.c | 27 + libauthsrv/mkfile | 25 + libauthsrv/nvcsum.c | 16 + libauthsrv/opasstokey.c | 29 + libauthsrv/passtokey.c | 33 + libauthsrv/readnvram.c | 368 +++++ libc/Makefile | 89 ++ libc/charstod.c | 81 ++ libc/cleanname.c | 52 + libc/convD2M.c | 95 ++ libc/convM2D.c | 94 ++ libc/convM2S.c | 315 +++++ libc/convS2M.c | 386 ++++++ libc/crypt.c | 68 + libc/dial.c | 209 +++ libc/dirfstat.c | 37 + libc/dirfwstat.c | 19 + libc/dirmodefmt.c | 48 + libc/dirstat.c | 37 + libc/dirwstat.c | 19 + libc/dofmt.c | 520 ++++++++ libc/dorfmt.c | 46 + libc/errfmt.c | 12 + libc/fcallfmt.c | 234 ++++ libc/fltfmt.c | 312 +++++ libc/fmt.c | 193 +++ libc/fmt.h | 99 ++ libc/fmtdef.h | 85 ++ libc/fmtfd.c | 31 + libc/fmtlock.c | 16 + libc/fmtprint.c | 32 + libc/fmtquote.c | 247 ++++ libc/fmtrune.c | 25 + libc/fmtstr.c | 11 + libc/fmtvprint.c | 31 + libc/fprint.c | 14 + libc/frand.c | 17 + libc/getfields.c | 37 + libc/getpid.c | 17 + libc/lnrand.c | 18 + libc/lock.c | 64 + libc/lrand.c | 83 ++ libc/mallocz.c | 13 + libc/mkfile | 90 ++ libc/nan.h | 4 + libc/nan64.c | 55 + libc/netmkaddr.c | 52 + libc/nrand.c | 18 + libc/nsec.c | 66 + libc/pow10.c | 49 + libc/print.c | 14 + libc/pushssl.c | 44 + libc/rand.c | 8 + libc/read9pmsg.c | 31 + libc/readn.c | 22 + libc/rune.c | 162 +++ libc/runefmtstr.c | 11 + libc/runeseprint.c | 14 + libc/runesmprint.c | 14 + libc/runesnprint.c | 15 + libc/runesprint.c | 14 + libc/runestrcat.c | 10 + libc/runestrchr.c | 20 + libc/runestrcmp.c | 20 + libc/runestrcpy.c | 13 + libc/runestrdup.c | 14 + libc/runestrecpy.c | 17 + libc/runestrlen.c | 9 + libc/runestrncat.c | 17 + libc/runestrncmp.c | 22 + libc/runestrncpy.c | 18 + libc/runestrrchr.c | 15 + libc/runestrstr.c | 29 + libc/runetype.c | 1138 ++++++++++++++++ libc/runevseprint.c | 23 + libc/runevsmprint.c | 70 + libc/runevsnprint.c | 22 + libc/seprint.c | 14 + libc/smprint.c | 14 + libc/snprint.c | 15 + libc/sprint.c | 14 + libc/strecpy.c | 17 + libc/strtod.c | 532 ++++++++ libc/strtod.h | 4 + libc/strtoll.c | 93 ++ libc/sysfatal.c | 30 + libc/time.c | 51 + libc/tokenize.c | 107 ++ libc/truerand.c | 17 + libc/u16.c | 53 + libc/u32.c | 110 ++ libc/u64.c | 127 ++ libc/utf.h | 51 + libc/utfdef.h | 14 + libc/utfecpy.c | 21 + libc/utflen.c | 23 + libc/utfnlen.c | 26 + libc/utfrrune.c | 31 + libc/utfrune.c | 30 + libc/utfutf.c | 26 + libc/vfprint.c | 34 + libc/vseprint.c | 23 + libc/vsmprint.c | 70 + libc/vsnprint.c | 22 + libdraw/Makefile | 24 + libdraw/alloc.c | 237 ++++ libdraw/arith.c | 206 +++ libdraw/bytesperline.c | 34 + libdraw/chan.c | 77 ++ libdraw/defont.c | 402 ++++++ libdraw/drawrepl.c | 23 + libdraw/icossin.c | 140 ++ libdraw/icossin2.c | 261 ++++ libdraw/mkfile | 18 + libdraw/rectclip.c | 25 + libdraw/rgb.c | 99 ++ libmemdraw/Makefile | 33 + libmemdraw/alloc.c | 200 +++ libmemdraw/alpha.hoc | 9 + libmemdraw/arc.c | 117 ++ libmemdraw/arctest.c | 62 + libmemdraw/cload.c | 68 + libmemdraw/cmap.c | 320 +++++ libmemdraw/cread.c | 96 ++ libmemdraw/defont.c | 68 + libmemdraw/draw.c | 2499 ++++++++++++++++++++++++++++++++++ libmemdraw/drawtest.c | 1004 ++++++++++++++ libmemdraw/ellipse.c | 248 ++++ libmemdraw/fillpoly.c | 523 ++++++++ libmemdraw/hwdraw.c | 12 + libmemdraw/iprint.c | 12 + libmemdraw/line.c | 485 +++++++ libmemdraw/load.c | 72 + libmemdraw/mkcmap.c | 79 ++ libmemdraw/mkfile | 27 + libmemdraw/openmemsubfont.c | 53 + libmemdraw/poly.c | 24 + libmemdraw/read.c | 111 ++ libmemdraw/string.c | 68 + libmemdraw/subfont.c | 34 + libmemdraw/times | 19 + libmemdraw/unload.c | 25 + libmemdraw/write.c | 183 +++ libmemlayer/Makefile | 26 + libmemlayer/draw.c | 192 +++ libmemlayer/lalloc.c | 79 ++ libmemlayer/layerop.c | 112 ++ libmemlayer/ldelete.c | 67 + libmemlayer/lhide.c | 67 + libmemlayer/line.c | 122 ++ libmemlayer/load.c | 55 + libmemlayer/lorigin.c | 107 ++ libmemlayer/lsetrefresh.c | 35 + libmemlayer/ltofront.c | 80 ++ libmemlayer/ltorear.c | 69 + libmemlayer/mkfile | 23 + libmemlayer/unload.c | 52 + libmp/Makefile | 46 + libmp/betomp.c | 40 + libmp/crt.c | 122 ++ libmp/crttest.c | 54 + libmp/dat.h | 15 + libmp/letomp.c | 29 + libmp/mkfile | 40 + libmp/mpadd.c | 54 + libmp/mpaux.c | 185 +++ libmp/mpcmp.c | 28 + libmp/mpdigdiv.c | 43 + libmp/mpdiv.c | 112 ++ libmp/mpeuclid.c | 82 ++ libmp/mpexp.c | 86 ++ libmp/mpextendedgcd.c | 97 ++ libmp/mpfmt.c | 193 +++ libmp/mpinvert.c | 21 + libmp/mpleft.c | 46 + libmp/mpmod.c | 15 + libmp/mpmul.c | 156 +++ libmp/mprand.c | 42 + libmp/mpright.c | 40 + libmp/mpsub.c | 52 + libmp/mptobe.c | 57 + libmp/mptoi.c | 44 + libmp/mptole.c | 54 + libmp/mptoui.c | 35 + libmp/mptouv.c | 49 + libmp/mptov.c | 69 + libmp/mpvecadd.c | 35 + libmp/mpveccmp.c | 28 + libmp/mpvecdigmuladd.c | 103 ++ libmp/mpvecsub.c | 34 + libmp/os.h | 3 + libmp/reduce | 16 + libmp/strtomp.c | 205 +++ libsec/Makefile | 60 + libsec/aes.c | 1544 +++++++++++++++++++++ libsec/blowfish.c | 579 ++++++++ libsec/decodepem.c | 59 + libsec/des.c | 480 +++++++ libsec/des3CBC.c | 59 + libsec/des3ECB.c | 48 + libsec/desCBC.c | 59 + libsec/desECB.c | 48 + libsec/desmodes.c | 31 + libsec/dsaalloc.c | 69 + libsec/dsagen.c | 61 + libsec/dsaprimes.c | 97 ++ libsec/dsaprivtopub.c | 16 + libsec/dsasign.c | 52 + libsec/dsaverify.c | 46 + libsec/egalloc.c | 67 + libsec/egdecrypt.c | 28 + libsec/egencrypt.c | 38 + libsec/eggen.c | 21 + libsec/egprivtopub.c | 17 + libsec/egsign.c | 43 + libsec/egtest.c | 34 + libsec/egverify.c | 29 + libsec/fastrand.c | 16 + libsec/genprime.c | 27 + libsec/genrandom.c | 62 + libsec/gensafeprime.c | 36 + libsec/genstrongprime.c | 57 + libsec/hmac.c | 56 + libsec/hmactest.c | 19 + libsec/md4.c | 271 ++++ libsec/md4test.c | 31 + libsec/md5.c | 148 ++ libsec/md5block.c | 267 ++++ libsec/md5pickle.c | 37 + libsec/mkfile | 55 + libsec/nfastrand.c | 23 + libsec/os.h | 2 + libsec/primetest.c | 41 + libsec/prng.c | 15 + libsec/probably_prime.c | 84 ++ libsec/ranlib.core | Bin 0 -> 430080 bytes libsec/rc4.c | 104 ++ libsec/readcert.c | 50 + libsec/rsaalloc.c | 52 + libsec/rsadecrypt.c | 37 + libsec/rsaencrypt.c | 12 + libsec/rsafill.c | 61 + libsec/rsagen.c | 82 ++ libsec/rsaprivtopub.c | 16 + libsec/rsatest.c | 57 + libsec/sha1.c | 127 ++ libsec/sha1block.c | 187 +++ libsec/sha1pickle.c | 38 + libsec/smallprimes.c | 1004 ++++++++++++++ libsec/smallprimetest.c | 1039 +++++++++++++++ libsec/thumb.c | 97 ++ libsec/tlshand.c | 2291 +++++++++++++++++++++++++++++++ libsec/x509.c | 2520 +++++++++++++++++++++++++++++++++++ main.c | 126 ++ mkfile | 95 ++ mkfile-Windows | 54 + mkfile-posix | 9 + mkone | 50 + posix-386/Makefile | 16 + posix-386/getcallerpc.c | 8 + posix-386/md5block.s | 241 ++++ posix-386/sha1block.s | 214 +++ posix-386/tas.c | 23 + posix-power/Makefile | 16 + posix-power/getcallerpc.c | 8 + posix-power/tas.c | 42 + readcons.c | 110 ++ resource.h | 16 + secstore.c | 667 +++++++++ win32-386/getcallerpc.c | 8 + win32-386/md5block.s | 241 ++++ win32-386/mkfile | 10 + win32-386/sha1block.s | 214 +++ win32-386/tas.c | 22 + 382 files changed, 62614 insertions(+) create mode 100644 Makefile create mode 100644 Notes create mode 100644 args.h create mode 100644 cpu.c create mode 100644 drawterm.h create mode 100644 drawterm.ico create mode 100644 drawterm.rc create mode 100644 drawterm.res create mode 100644 exportfs/Makefile create mode 100644 exportfs/exportfs.c create mode 100644 exportfs/exportfs.h create mode 100644 exportfs/exportsrv.c create mode 100644 exportfs/mkfile create mode 100644 gui-win32/alloc.c create mode 100644 gui-win32/cload.c create mode 100644 gui-win32/draw.c create mode 100644 gui-win32/load.c create mode 100644 gui-win32/mkfile create mode 100644 gui-win32/screen.c create mode 100644 gui-win32/wstrtoutf.c create mode 100644 gui-x11/Makefile create mode 100644 gui-x11/alloc.c create mode 100644 gui-x11/cload.c create mode 100644 gui-x11/draw.c create mode 100644 gui-x11/keysym2ucs-x11.c create mode 100644 gui-x11/keysym2ucs.h create mode 100644 gui-x11/ksym2utf.h create mode 100644 gui-x11/load.c create mode 100644 gui-x11/screen.c create mode 100644 gui-x11/xmem.h create mode 100644 include/a.out create mode 100644 include/auth.h create mode 100644 include/authsrv.h create mode 100644 include/cursor.h create mode 100644 include/draw.h create mode 100644 include/dtos.h create mode 100644 include/fcall.h create mode 100644 include/keyboard.h create mode 100644 include/lib.h create mode 100644 include/libc.h create mode 100644 include/libsec.h create mode 100644 include/memdraw.h create mode 100644 include/memlayer.h create mode 100644 include/mp.h create mode 100644 include/u.h create mode 100644 include/unix.h create mode 100644 include/user.h create mode 100644 include/x create mode 100644 include/x.c create mode 100644 kern/Makefile create mode 100644 kern/allocb.c create mode 100644 kern/cache.c create mode 100644 kern/chan.c create mode 100644 kern/dat.h create mode 100644 kern/data.c create mode 100644 kern/dev.c create mode 100644 kern/devcons.c create mode 100644 kern/devdraw.c create mode 100644 kern/devfs.c create mode 100644 kern/devip-posix.c create mode 100644 kern/devip-win.c create mode 100644 kern/devip.c create mode 100644 kern/devip.h create mode 100644 kern/devmnt.c create mode 100644 kern/devmouse.c create mode 100644 kern/devntfs.c create mode 100644 kern/devpipe.c create mode 100644 kern/devroot.c create mode 100644 kern/devssl.c create mode 100644 kern/devtab.c create mode 100644 kern/error.c create mode 100644 kern/error.h create mode 100644 kern/exportfs.c create mode 100644 kern/fns.h create mode 100644 kern/mkfile create mode 100644 kern/netif.h create mode 100644 kern/os-windows.c create mode 100644 kern/parse.c create mode 100644 kern/pgrp.c create mode 100644 kern/posix.c create mode 100644 kern/procinit.c create mode 100644 kern/qio.c create mode 100644 kern/qlock.c create mode 100644 kern/rendez.c create mode 100644 kern/rwlock.c create mode 100644 kern/screen.h create mode 100644 kern/sleep.c create mode 100644 kern/smalloc.c create mode 100644 kern/stub.c create mode 100644 kern/syscall.c create mode 100644 kern/sysfile.c create mode 100644 kern/sysproc.c create mode 100644 kern/term.c create mode 100644 kern/todo.c create mode 100644 kern/uart.c create mode 100644 kern/waserror.c create mode 100644 kern/winduhz.h create mode 100644 kern/x create mode 100644 latin1.c create mode 100644 libauthsrv/Makefile create mode 100644 libauthsrv/authdial.c create mode 100644 libauthsrv/convA2M.c create mode 100644 libauthsrv/convM2A.c create mode 100644 libauthsrv/convM2PR.c create mode 100644 libauthsrv/convM2T.c create mode 100644 libauthsrv/convM2TR.c create mode 100644 libauthsrv/convPR2M.c create mode 100644 libauthsrv/convT2M.c create mode 100644 libauthsrv/convTR2M.c create mode 100644 libauthsrv/mkfile create mode 100644 libauthsrv/nvcsum.c create mode 100644 libauthsrv/opasstokey.c create mode 100644 libauthsrv/passtokey.c create mode 100644 libauthsrv/readnvram.c create mode 100644 libc/Makefile create mode 100644 libc/charstod.c create mode 100644 libc/cleanname.c create mode 100644 libc/convD2M.c create mode 100644 libc/convM2D.c create mode 100644 libc/convM2S.c create mode 100644 libc/convS2M.c create mode 100644 libc/crypt.c create mode 100644 libc/dial.c create mode 100644 libc/dirfstat.c create mode 100644 libc/dirfwstat.c create mode 100644 libc/dirmodefmt.c create mode 100644 libc/dirstat.c create mode 100644 libc/dirwstat.c create mode 100644 libc/dofmt.c create mode 100644 libc/dorfmt.c create mode 100644 libc/errfmt.c create mode 100644 libc/fcallfmt.c create mode 100644 libc/fltfmt.c create mode 100644 libc/fmt.c create mode 100644 libc/fmt.h create mode 100644 libc/fmtdef.h create mode 100644 libc/fmtfd.c create mode 100644 libc/fmtlock.c create mode 100644 libc/fmtprint.c create mode 100644 libc/fmtquote.c create mode 100644 libc/fmtrune.c create mode 100644 libc/fmtstr.c create mode 100644 libc/fmtvprint.c create mode 100644 libc/fprint.c create mode 100644 libc/frand.c create mode 100644 libc/getfields.c create mode 100644 libc/getpid.c create mode 100644 libc/lnrand.c create mode 100644 libc/lock.c create mode 100644 libc/lrand.c create mode 100644 libc/mallocz.c create mode 100644 libc/mkfile create mode 100644 libc/nan.h create mode 100644 libc/nan64.c create mode 100644 libc/netmkaddr.c create mode 100644 libc/nrand.c create mode 100644 libc/nsec.c create mode 100644 libc/pow10.c create mode 100644 libc/print.c create mode 100644 libc/pushssl.c create mode 100644 libc/rand.c create mode 100644 libc/read9pmsg.c create mode 100644 libc/readn.c create mode 100644 libc/rune.c create mode 100644 libc/runefmtstr.c create mode 100644 libc/runeseprint.c create mode 100644 libc/runesmprint.c create mode 100644 libc/runesnprint.c create mode 100644 libc/runesprint.c create mode 100644 libc/runestrcat.c create mode 100644 libc/runestrchr.c create mode 100644 libc/runestrcmp.c create mode 100644 libc/runestrcpy.c create mode 100644 libc/runestrdup.c create mode 100644 libc/runestrecpy.c create mode 100644 libc/runestrlen.c create mode 100644 libc/runestrncat.c create mode 100644 libc/runestrncmp.c create mode 100644 libc/runestrncpy.c create mode 100644 libc/runestrrchr.c create mode 100644 libc/runestrstr.c create mode 100644 libc/runetype.c create mode 100644 libc/runevseprint.c create mode 100644 libc/runevsmprint.c create mode 100644 libc/runevsnprint.c create mode 100644 libc/seprint.c create mode 100644 libc/smprint.c create mode 100644 libc/snprint.c create mode 100644 libc/sprint.c create mode 100644 libc/strecpy.c create mode 100644 libc/strtod.c create mode 100644 libc/strtod.h create mode 100644 libc/strtoll.c create mode 100644 libc/sysfatal.c create mode 100644 libc/time.c create mode 100644 libc/tokenize.c create mode 100644 libc/truerand.c create mode 100644 libc/u16.c create mode 100644 libc/u32.c create mode 100644 libc/u64.c create mode 100644 libc/utf.h create mode 100644 libc/utfdef.h create mode 100644 libc/utfecpy.c create mode 100644 libc/utflen.c create mode 100644 libc/utfnlen.c create mode 100644 libc/utfrrune.c create mode 100644 libc/utfrune.c create mode 100644 libc/utfutf.c create mode 100644 libc/vfprint.c create mode 100644 libc/vseprint.c create mode 100644 libc/vsmprint.c create mode 100644 libc/vsnprint.c create mode 100644 libdraw/Makefile create mode 100644 libdraw/alloc.c create mode 100644 libdraw/arith.c create mode 100644 libdraw/bytesperline.c create mode 100644 libdraw/chan.c create mode 100644 libdraw/defont.c create mode 100644 libdraw/drawrepl.c create mode 100644 libdraw/icossin.c create mode 100644 libdraw/icossin2.c create mode 100644 libdraw/mkfile create mode 100644 libdraw/rectclip.c create mode 100644 libdraw/rgb.c create mode 100644 libmemdraw/Makefile create mode 100644 libmemdraw/alloc.c create mode 100644 libmemdraw/alpha.hoc create mode 100644 libmemdraw/arc.c create mode 100644 libmemdraw/arctest.c create mode 100644 libmemdraw/cload.c create mode 100644 libmemdraw/cmap.c create mode 100644 libmemdraw/cread.c create mode 100644 libmemdraw/defont.c create mode 100644 libmemdraw/draw.c create mode 100644 libmemdraw/drawtest.c create mode 100644 libmemdraw/ellipse.c create mode 100644 libmemdraw/fillpoly.c create mode 100644 libmemdraw/hwdraw.c create mode 100644 libmemdraw/iprint.c create mode 100644 libmemdraw/line.c create mode 100644 libmemdraw/load.c create mode 100644 libmemdraw/mkcmap.c create mode 100644 libmemdraw/mkfile create mode 100644 libmemdraw/openmemsubfont.c create mode 100644 libmemdraw/poly.c create mode 100644 libmemdraw/read.c create mode 100644 libmemdraw/string.c create mode 100644 libmemdraw/subfont.c create mode 100644 libmemdraw/times create mode 100644 libmemdraw/unload.c create mode 100644 libmemdraw/write.c create mode 100644 libmemlayer/Makefile create mode 100644 libmemlayer/draw.c create mode 100644 libmemlayer/lalloc.c create mode 100644 libmemlayer/layerop.c create mode 100644 libmemlayer/ldelete.c create mode 100644 libmemlayer/lhide.c create mode 100644 libmemlayer/line.c create mode 100644 libmemlayer/load.c create mode 100644 libmemlayer/lorigin.c create mode 100644 libmemlayer/lsetrefresh.c create mode 100644 libmemlayer/ltofront.c create mode 100644 libmemlayer/ltorear.c create mode 100644 libmemlayer/mkfile create mode 100644 libmemlayer/unload.c create mode 100644 libmp/Makefile create mode 100644 libmp/betomp.c create mode 100644 libmp/crt.c create mode 100644 libmp/crttest.c create mode 100644 libmp/dat.h create mode 100644 libmp/letomp.c create mode 100644 libmp/mkfile create mode 100644 libmp/mpadd.c create mode 100644 libmp/mpaux.c create mode 100644 libmp/mpcmp.c create mode 100644 libmp/mpdigdiv.c create mode 100644 libmp/mpdiv.c create mode 100644 libmp/mpeuclid.c create mode 100644 libmp/mpexp.c create mode 100644 libmp/mpextendedgcd.c create mode 100644 libmp/mpfmt.c create mode 100644 libmp/mpinvert.c create mode 100644 libmp/mpleft.c create mode 100644 libmp/mpmod.c create mode 100644 libmp/mpmul.c create mode 100644 libmp/mprand.c create mode 100644 libmp/mpright.c create mode 100644 libmp/mpsub.c create mode 100644 libmp/mptobe.c create mode 100644 libmp/mptoi.c create mode 100644 libmp/mptole.c create mode 100644 libmp/mptoui.c create mode 100644 libmp/mptouv.c create mode 100644 libmp/mptov.c create mode 100644 libmp/mpvecadd.c create mode 100644 libmp/mpveccmp.c create mode 100644 libmp/mpvecdigmuladd.c create mode 100644 libmp/mpvecsub.c create mode 100644 libmp/os.h create mode 100644 libmp/reduce create mode 100644 libmp/strtomp.c create mode 100644 libsec/Makefile create mode 100644 libsec/aes.c create mode 100644 libsec/blowfish.c create mode 100644 libsec/decodepem.c create mode 100644 libsec/des.c create mode 100644 libsec/des3CBC.c create mode 100644 libsec/des3ECB.c create mode 100644 libsec/desCBC.c create mode 100644 libsec/desECB.c create mode 100644 libsec/desmodes.c create mode 100644 libsec/dsaalloc.c create mode 100644 libsec/dsagen.c create mode 100644 libsec/dsaprimes.c create mode 100644 libsec/dsaprivtopub.c create mode 100644 libsec/dsasign.c create mode 100644 libsec/dsaverify.c create mode 100644 libsec/egalloc.c create mode 100644 libsec/egdecrypt.c create mode 100644 libsec/egencrypt.c create mode 100644 libsec/eggen.c create mode 100644 libsec/egprivtopub.c create mode 100644 libsec/egsign.c create mode 100644 libsec/egtest.c create mode 100644 libsec/egverify.c create mode 100644 libsec/fastrand.c create mode 100644 libsec/genprime.c create mode 100644 libsec/genrandom.c create mode 100644 libsec/gensafeprime.c create mode 100644 libsec/genstrongprime.c create mode 100644 libsec/hmac.c create mode 100644 libsec/hmactest.c create mode 100644 libsec/md4.c create mode 100644 libsec/md4test.c create mode 100644 libsec/md5.c create mode 100644 libsec/md5block.c create mode 100644 libsec/md5pickle.c create mode 100644 libsec/mkfile create mode 100644 libsec/nfastrand.c create mode 100644 libsec/os.h create mode 100644 libsec/primetest.c create mode 100644 libsec/prng.c create mode 100644 libsec/probably_prime.c create mode 100644 libsec/ranlib.core create mode 100644 libsec/rc4.c create mode 100644 libsec/readcert.c create mode 100644 libsec/rsaalloc.c create mode 100644 libsec/rsadecrypt.c create mode 100644 libsec/rsaencrypt.c create mode 100644 libsec/rsafill.c create mode 100644 libsec/rsagen.c create mode 100644 libsec/rsaprivtopub.c create mode 100644 libsec/rsatest.c create mode 100644 libsec/sha1.c create mode 100644 libsec/sha1block.c create mode 100644 libsec/sha1pickle.c create mode 100644 libsec/smallprimes.c create mode 100644 libsec/smallprimetest.c create mode 100644 libsec/thumb.c create mode 100644 libsec/tlshand.c create mode 100644 libsec/x509.c create mode 100644 main.c create mode 100644 mkfile create mode 100644 mkfile-Windows create mode 100644 mkfile-posix create mode 100644 mkone create mode 100644 posix-386/Makefile create mode 100644 posix-386/getcallerpc.c create mode 100644 posix-386/md5block.s create mode 100644 posix-386/sha1block.s create mode 100644 posix-386/tas.c create mode 100644 posix-power/Makefile create mode 100644 posix-power/getcallerpc.c create mode 100644 posix-power/tas.c create mode 100644 readcons.c create mode 100644 resource.h create mode 100644 secstore.c create mode 100644 win32-386/getcallerpc.c create mode 100644 win32-386/md5block.s create mode 100644 win32-386/mkfile create mode 100644 win32-386/sha1block.s create mode 100644 win32-386/tas.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2fe037a --- /dev/null +++ b/Makefile @@ -0,0 +1,82 @@ +TARG=drawterm +CC=gcc +CFLAGS=-Iinclude -c -ggdb -D_THREAD_SAFE -pthread # not ready for this yet: -Wall +O=o +#CC=cl +#CFLAGS=-c -nologo -W3 -YX -Zi -MT -Zl -Iinclude -DWINDOWS +#O=obj + +OFILES=\ + main.$O\ + cpu.$O\ + readcons.$O\ + secstore.$O\ + latin1.$O\ + +LIBS=\ + kern/libkern.a\ + exportfs/libexportfs.a\ + libauthsrv/libauthsrv.a\ + libsec/libsec.a\ + libmp/libmp.a\ + libmemdraw/libmemdraw.a\ + libmemlayer/libmemlayer.a\ + libdraw/libdraw.a\ + libc/libc.a\ + kern/libkern.a\ + gui-x11/libx11.a\ + libmemdraw/libmemdraw.a\ + libdraw/libdraw.a\ + kern/libkern.a\ + libc/libc.a\ + libmemdraw/libmemdraw.a\ + libmemlayer/libmemlayer.a\ + libdraw/libdraw.a\ + libmachdep.a + +$(TARG): $(OFILES) $(LIBS) + $(CC) -pthread -o $(TARG) $(OFILES) $(LIBS) -L/usr/X11R6/lib -lX11 -ggdb + +%.$O: %.c + $(CC) $(CFLAGS) $*.c + +clean: + rm -f *.o */*.o */*.a drawterm *.a + +kern/libkern.a: + (cd kern; make) + +exportfs/libexportfs.a: + (cd exportfs; make) + +libauthsrv/libauthsrv.a: + (cd libauthsrv; make) + +libmp/libmp.a: + (cd libmp; make) + +libsec/libsec.a: + (cd libsec; make) + +libmemdraw/libmemdraw.a: + (cd libmemdraw; make) + +libmemlayer/libmemlayer.a: + (cd libmemlayer; make) + +libdraw/libdraw.a: + (cd libdraw; make) + +libc/libc.a: + (cd libc; make) + +gui-x11/libx11.a: + (cd gui-x11; make) + +#libmachdep.a: +# arch=`uname -m|sed 's/i.86/386/;s/Power Macintosh/power/'`; \ +# (cd gcc$$arch && make) + +libmachdep.a: + (cd posix-386; make) + diff --git a/Notes b/Notes new file mode 100644 index 0000000..8806fad --- /dev/null +++ b/Notes @@ -0,0 +1,16 @@ + +Win32 port Notes: + +* Issues: + + ** ownership questions on files are completely deferred by + marking them as unknown. It works for now, but probably + should be handled correctly. + + ** No performance measurements have been done. The interactive + response seems faster than old drawterm, but the i/o seems + slower. + + ** fs functions in devntfs.c need to handle conversions to/from + widechar and utf-8. + diff --git a/args.h b/args.h new file mode 100644 index 0000000..d88de0a --- /dev/null +++ b/args.h @@ -0,0 +1,20 @@ +extern char *argv0; +#define ARGBEGIN for((argv0? 0: (argv0=*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 ARGC() _argc + +#define EARGF(x) (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): (x, (char*)0))) diff --git a/cpu.c b/cpu.c new file mode 100644 index 0000000..47e68ba --- /dev/null +++ b/cpu.c @@ -0,0 +1,606 @@ +/* + * cpu.c - Make a connection to a cpu server + * + * Invoked by listen as 'cpu -R | -N service net netdir' + * by users as 'cpu [-h system] [-c cmd args ...]' + */ + +#include +#include +#include +#include +#include +#include +#include "args.h" +#include "drawterm.h" + +#define Maxfdata 8192 +#define MaxStr 128 + +static char* getuser(void); +static void fatal(int, char*, ...); +static void catcher(void*, char*); +static void usage(void); +static void writestr(int, char*, char*, int); +static int readstr(int, char*, int); +static char *rexcall(int*, char*, char*); +static int setamalg(char*); +static char *keyspec = ""; +static AuthInfo *p9any(int); + +static int notechan; +#define system csystem +static char *system; +static int cflag; +int dbg; + +static char *srvname = "ncpu"; +static char *ealgs = "rc4_256 sha1"; + +/* message size for exportfs; may be larger so we can do big graphics in CPU window */ +static int msgsize = Maxfdata+IOHDRSZ; + +/* authentication mechanisms */ +static int netkeyauth(int); +static int netkeysrvauth(int, char*); +static int p9auth(int); +static int srvp9auth(int, char*); +static int noauth(int); +static int srvnoauth(int, char*); + +char *authserver; + +typedef struct AuthMethod AuthMethod; +struct AuthMethod { + char *name; /* name of method */ + int (*cf)(int); /* client side authentication */ + int (*sf)(int, char*); /* server side authentication */ +} authmethod[] = +{ + { "p9", p9auth, srvp9auth,}, + { "netkey", netkeyauth, netkeysrvauth,}, +// { "none", noauth, srvnoauth,}, + { nil, nil} +}; +AuthMethod *am = authmethod; /* default is p9 */ + +char *p9authproto = "p9any"; + +int setam(char*); + +void +exits(char *s) +{ + print("\ngoodbye\n"); + for(;;) osyield(); +} + +void +usage(void) +{ + fprint(2, "usage: drawterm [-a authserver] [-c cpuserver] [-s secstore] [-u user]\n"); + exits("usage"); +} +int fdd; + +void +cpumain(int argc, char **argv) +{ + char dat[MaxStr], buf[MaxStr], cmd[MaxStr], *p, *err, *secstoreserver, *s; + int fd, ms, data; + + /* see if we should use a larger message size */ + fd = open("/dev/draw", OREAD); + if(fd > 0){ + ms = iounit(fd); + if(msgsize < ms+IOHDRSZ) + msgsize = ms+IOHDRSZ; + close(fd); + } + + user = readcons("user", getenv("USER"), 0); + secstoreserver = nil; + ARGBEGIN{ + case 'a': + authserver = EARGF(usage()); + break; + case 'e': + ealgs = EARGF(usage()); + if(*ealgs == 0 || strcmp(ealgs, "clear") == 0) + ealgs = nil; + break; + case 'd': + dbg++; + break; + case 'c': + system = EARGF(usage()); + break; +/* + case 'c': + cflag++; + cmd[0] = '!'; + cmd[1] = '\0'; + while(p = ARGF()) { + strcat(cmd, " "); + strcat(cmd, p); + } + break; +*/ + case 'u': + user = EARGF(usage()); + break; + case 's': + secstoreserver = EARGF(usage()); + break; + default: + usage(); + }ARGEND; + + if(secstoreserver == nil) + secstoreserver = authserver; + + if(secstoreserver && havesecstore(secstoreserver, user)){ + s = secstorefetch(secstoreserver, user, nil); + if(s){ + if(strlen(s) >= sizeof secstorebuf) + panic("secstore data too big"); + strcpy(secstorebuf, s); + } + } + + + if(argc != 0) + usage(); + + if(system == nil) { + p = getenv("cpu"); + if(p == 0) + fatal(0, "set $cpu"); + system = p; + } + + if(err = rexcall(&data, system, srvname)) + fatal(1, "%s: %s", err, system); + + /* Tell the remote side the command to execute and where our working directory is */ + if(cflag) + writestr(data, cmd, "command", 0); + if(getcwd(dat, sizeof(dat)) == 0) + writestr(data, "NO", "dir", 0); + else + writestr(data, dat, "dir", 0); + + /* + * Wait for the other end to execute and start our file service + * of /mnt/term + */ + if(readstr(data, buf, sizeof(buf)) < 0) + fatal(1, "waiting for FS: %r"); + if(strncmp("FS", buf, 2) != 0) { + print("remote cpu: %s", buf); + exits(buf); + } + + if(readstr(data, buf, sizeof buf) < 0) + fatal(1, "waiting for remote export: %r"); + if(strcmp(buf, "/") != 0){ + print("remote cpu: %s" , buf); + exits(buf); + } + write(data, "OK", 2); + + /* Begin serving the gnot namespace */ + exportfs(data, msgsize); + fatal(1, "starting exportfs"); +} + +void +fatal(int syserr, char *fmt, ...) +{ + Fmt f; + char *str; + va_list arg; + + fmtstrinit(&f); + fmtprint(&f, "cpu: "); + va_start(arg, fmt); + fmtvprint(&f, fmt, arg); + va_end(arg); + if(syserr) + fmtprint(&f, ": %r"); + fmtprint(&f, "\n"); + str = fmtstrflush(&f); + write(2, str, strlen(str)); + exits(str); +} + +char *negstr = "negotiating authentication method"; + +char bug[256]; + +char* +rexcall(int *fd, char *host, char *service) +{ + char *na; + char dir[MaxStr]; + char err[ERRMAX]; + char msg[MaxStr]; + int n; + + na = netmkaddr(host, "tcp", "17010"); + if((*fd = dial(na, 0, dir, 0)) < 0) + return "can't dial"; + + /* negotiate authentication mechanism */ + if(ealgs != nil) + snprint(msg, sizeof(msg), "%s %s", am->name, ealgs); + else + snprint(msg, sizeof(msg), "%s", am->name); + writestr(*fd, msg, negstr, 0); + n = readstr(*fd, err, sizeof err); + if(n < 0) + return negstr; + if(*err){ + werrstr(err); + return negstr; + } + + /* authenticate */ + *fd = (*am->cf)(*fd); + if(*fd < 0) + return "can't authenticate"; + return 0; +} + +void +writestr(int fd, char *str, char *thing, int ignore) +{ + int l, n; + + l = strlen(str); + n = write(fd, str, l+1); + if(!ignore && n < 0) + fatal(1, "writing network: %s", thing); +} + +int +readstr(int fd, char *str, int len) +{ + int n; + + while(len) { + n = read(fd, str, 1); + if(n < 0) + return -1; + if(*str == '\0') + return 0; + str++; + len--; + } + return -1; +} + +static int +readln(char *buf, int n) +{ + int i; + char *p; + + n--; /* room for \0 */ + p = buf; + for(i=0; isecret, ai->nsecret); + if(ealgs == nil) + return fd; + + /* exchange random numbers */ + srand(truerand()); + for(i = 0; i < 4; i++) + key[i] = rand(); + if(write(fd, key, 4) != 4) + return -1; + if(readn(fd, key+12, 4) != 4) + return -1; + + /* scramble into two secrets */ + sha1(key, sizeof(key), digest, nil); + mksecret(fromclientsecret, digest); + mksecret(fromserversecret, digest+10); + + /* set up encryption */ + i = pushssl(fd, ealgs, fromclientsecret, fromserversecret, nil); + if(i < 0) + werrstr("can't establish ssl connection: %r"); + return i; +} + +int +authdial(char *net, char *dom) +{ + int fd; + fd = dial(netmkaddr(authserver, "tcp", "567"), 0, 0, 0); + //print("authdial %d\n", fd); + return fd; +} + +static int +getastickets(Ticketreq *tr, char *trbuf, char *tbuf) +{ + int asfd, rv; + char *dom; + + dom = tr->authdom; + asfd = authdial(nil, dom); + if(asfd < 0) + return -1; + rv = _asgetticket(asfd, trbuf, tbuf); + close(asfd); + return rv; +} + +static int +mkserverticket(Ticketreq *tr, char *authkey, char *tbuf) +{ + int i; + Ticket t; + + if(strcmp(tr->authid, tr->hostid) != 0) + return -1; + memset(&t, 0, sizeof(t)); + memmove(t.chal, tr->chal, CHALLEN); + strcpy(t.cuid, tr->uid); + strcpy(t.suid, tr->uid); + for(i=0; i= 0) + return 0; + return mkserverticket(tr, key, tbuf); +} + +AuthInfo* +p9any(int fd) +{ + char buf[1024], buf2[1024], cchal[CHALLEN], *bbuf, *p, *dom, *u; + char *pass; + char tbuf[TICKETLEN+TICKETLEN+AUTHENTLEN], trbuf[TICKREQLEN]; + char authkey[DESKEYLEN]; + Authenticator auth; + int i, v2, n; + Ticketreq tr; + Ticket t; + AuthInfo *ai; + + if((n = readstr(fd, buf, sizeof buf)) < 0) + fatal(1, "cannot read p9any negotiation"); + bbuf = buf; + v2 = 0; + if(strncmp(buf, "v.2 ", 4) == 0){ + v2 = 1; + bbuf += 4; + } + if(p = strchr(bbuf, ' ')) + *p = 0; + p = bbuf; + if((dom = strchr(p, '@')) == nil) + fatal(1, "bad p9any domain"); + *dom++ = 0; + if(strcmp(p, "p9sk1") != 0) + fatal(1, "server did not offer p9sk1"); + + sprint(buf2, "%s %s", p, dom); + if(write(fd, buf2, strlen(buf2)+1) != strlen(buf2)+1) + fatal(1, "cannot write user/domain choice in p9any"); + if(v2){ + if((n = readstr(fd, buf, sizeof buf)) != 3) + fatal(1, "cannot read OK in p9any"); + if(memcmp(buf, "OK\0", 3) != 0) + fatal(1, "did not get OK in p9any"); + } + for(i=0; isecret = mallocz(8, 1); + des56to64((uchar*)t.key, ai->secret); + ai->nsecret = 8; + ai->suid = strdup(t.suid); + ai->cuid = strdup(t.cuid); + memset(authkey, 0, sizeof authkey); + return ai; +} + +static int +noauth(int fd) +{ + ealgs = nil; + return fd; +} + +static int +srvnoauth(int fd, char *user) +{ + strecpy(user, user+MaxStr, getuser()); + ealgs = nil; + return fd; +} + +void +loghex(uchar *p, int n) +{ + char buf[100]; + int i; + + for(i = 0; i < n; i++) + sprint(buf+2*i, "%2.2ux", p[i]); +// syslog(0, "cpu", buf); +} + +static int +srvp9auth(int fd, char *user) +{ + return -1; +} + +/* + * set authentication mechanism + */ +int +setam(char *name) +{ + for(am = authmethod; am->name != nil; am++) + if(strcmp(am->name, name) == 0) + return 0; + am = authmethod; + return -1; +} + +/* + * set authentication mechanism and encryption/hash algs + */ +int +setamalg(char *s) +{ + ealgs = strchr(s, ' '); + if(ealgs != nil) + *ealgs++ = 0; + return setam(s); +} + +char* +getuser(void) +{ + return getenv("USER"); +} + diff --git a/drawterm.h b/drawterm.h new file mode 100644 index 0000000..d49e797 --- /dev/null +++ b/drawterm.h @@ -0,0 +1,10 @@ +extern int havesecstore(char *addr, char *owner); +extern char *secstore; +extern char secstorebuf[65536]; +extern char *secstorefetch(char *addr, char *owner, char *passwd); +extern char *authaddr; +extern char *readcons(char *prompt, char *def, int secret); +extern int exportfs(int, int); +extern char *user; +extern char *getkey(char*, char*); +extern char *findkey(char**, char*); diff --git a/drawterm.ico b/drawterm.ico new file mode 100644 index 0000000000000000000000000000000000000000..376dc442c62fa0b42542796d80b476e68a84abbc GIT binary patch literal 1662 zcmeH{zjG2n6vsC+YHbyJ#&KfD$Y0>d7{{@3%pt_a!kj?q$Q%<%3&&v_xPzo}Jjmrz z8c3F_llxUCu)F^P{{k-eV|Ow3HZF6!?|t5P-+Q|p0I)$n+3Z8G8s!H7Y|?d$K3V#F zq|XMpM`t={?9Y+OWI!&L1NnR&NRk9(Sq6%t0993iVHm)&EC3+{K@b2!2v{r@V7Xkf zG?cM{FT0W!GB+{&UQNo&I~8X=a8cVSs0Hh5p&{_VAH93ohi)+#4PSHawf0_Y4+1i& zqRwoW^QJ799UO!>bSuLTj3dRePGiqkYdp`a2w~bDHo5+$6a&mIh-6EvXq42R(yc@r_mcz;7ARly0eb*svDcJpM2eD3VTg~UmrzbLN4-ZPvGN} zqj5vzJv-481lPC|ar}+8rVDxWnBU$f#7^u;nAlh&^DR+t#QK$H--wj)ByiFexXSt= zG#4BA^MKiNfzRq(&yG%&R~#N!H5~)kd0IU&~Ho*j3JaI22k>HlW@SzAL9eSA+UpISuG4|H&jtq6MF> O7iE7$|BZig1HS<*UZ_a` literal 0 HcmV?d00001 diff --git a/drawterm.rc b/drawterm.rc new file mode 100644 index 0000000..451cfeb --- /dev/null +++ b/drawterm.rc @@ -0,0 +1,72 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON DISCARDABLE "drawterm.ico" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/drawterm.res b/drawterm.res new file mode 100644 index 0000000000000000000000000000000000000000..b92ed731dfd2330d1945e46e227122e763f59f27 GIT binary patch literal 1756 zcmeH|O>YuG7{`bBl6Wya7`-;cH1$U06Bvm##*49wpcfCa0!JfRQt1^I>b0;7?a8>% z?e=J)9roPr+qs3A`3igm9QI{))_V8iB(w9(@A=R3JTtp6006K~`*Im{Y9?CwZsfm3 zH7VbhS-W>T5^Yi=L;FYCQy|22&7UouPJ?VV3v#&}5Cj2;q6j2O0i|Ls zT-ODJ5U^M*Xg*q0*eGKSpLd0h$EKm-^lCy*+^(2O$60MVFX#2I`3A>bXY}?(A6f-> zG<+3vyH$F=HE_wKiuPvjW8M_SvWZ;}dsb!mfpH}1`f)IG~!rwW1XGV+h zn9~y}PIelbghWN4x~cKJ8&38oyZbC8A-EN6VE$?JTH`p9JwNYkPkPx6%wQhAZZw6x zCdV%id^aIyIl0I2am3NM!SkLG>Is4gZu%U5qopZa93|k_cL*^;+ZTp5?vVK=uU7;8 zO0%Q-%HY9q(&o6#>LD~1Yxwhk*>jH1>am_}ohmQ(oan2;$uhNv;cjd$EgINwo&*UT zH99Fex6GgET|^gPM~Mrd+H=V*h&l9Sts?_D5|V6L?znCdQ9PHutrx z(%GgrZ%@B8CeB{5`a~eNrHkS@Un9)vc8R FzX4Xcwjcli literal 0 HcmV?d00001 diff --git a/exportfs/Makefile b/exportfs/Makefile new file mode 100644 index 0000000..cbb5018 --- /dev/null +++ b/exportfs/Makefile @@ -0,0 +1,16 @@ +LIB=libexportfs.a +CC=gcc +CFLAGS=-I../include -I. -I.. -c -ggdb -D_THREAD_SAFE -pthread +O=o + +OFILES=\ + exportfs.$O\ + exportsrv.$O + +$(LIB): $(OFILES) + ar r $(LIB) $(OFILES) + ranlib $(LIB) + +%.$O: %.c + $(CC) $(CFLAGS) $*.c + diff --git a/exportfs/exportfs.c b/exportfs/exportfs.c new file mode 100644 index 0000000..8dfefeb --- /dev/null +++ b/exportfs/exportfs.c @@ -0,0 +1,503 @@ +/* + * exportfs - Export a plan 9 name space across a network + */ +#include +#include +#include +#include +#include "drawterm.h" +#define Extern +#include "exportfs.h" + +/* #define QIDPATH ((1LL<<48)-1) */ +#define QIDPATH ((((vlong)1)<<48)-1) +vlong newqid = 0; + +void (*fcalls[256])(Fsrpc*); + +/* accounting and debugging counters */ +int filecnt; +int freecnt; +int qidcnt; +int qfreecnt; +int ncollision; +int netfd; + +int +exportfs(int fd, int msgsz) +{ + char buf[ERRMAX], ebuf[ERRMAX]; + Fsrpc *r; + int n; + char *dbfile, *srv, *file; + ulong initial; + + fcalls[Tversion] = Xversion; + fcalls[Tauth] = Xauth; + fcalls[Tflush] = Xflush; + fcalls[Tattach] = Xattach; + fcalls[Twalk] = Xwalk; + fcalls[Topen] = slave; + fcalls[Tcreate] = Xcreate; + fcalls[Tclunk] = Xclunk; + fcalls[Tread] = slave; + fcalls[Twrite] = slave; + fcalls[Tremove] = Xremove; + fcalls[Tstat] = Xstat; + fcalls[Twstat] = Xwstat; + + srvfd = -1; + netfd = fd; + //dbg = 1; + + strcpy(buf, "this is buf"); + strcpy(ebuf, "this is ebuf"); + DEBUG(DFD, "exportfs: started\n"); + +// rfork(RFNOTEG); + + messagesize = msgsz; + if(messagesize == 0){ + messagesize = iounit(netfd); + if(messagesize == 0) + messagesize = 8192+IOHDRSZ; + } + + Workq = emallocz(sizeof(Fsrpc)*Nr_workbufs); +// for(i=0; ibuf, messagesize); + if(n <= 0) + fatal(nil); + + if(convM2S(r->buf, n, &r->work) == 0) + fatal("convM2S format error"); + + DEBUG(DFD, "%F\n", &r->work); + (fcalls[r->work.type])(r); + } +} + +void +reply(Fcall *r, Fcall *t, char *err) +{ + uchar *data; + int m, n; + + t->tag = r->tag; + t->fid = r->fid; + if(err) { + t->type = Rerror; + t->ename = err; + } + else + t->type = r->type + 1; + + DEBUG(DFD, "\t%F\n", t); + + data = malloc(messagesize); /* not mallocz; no need to clear */ + if(data == nil) + fatal(Enomem); + n = convS2M(t, data, messagesize); + if((m=write(netfd, data, n))!=n){ + fprint(2, "wrote %d got %d (%r)\n", n, m); + fatal("write"); + } + free(data); +} + +Fid * +getfid(int nr) +{ + Fid *f; + + for(f = fidhash(nr); f; f = f->next) + if(f->nr == nr) + return f; + + return 0; +} + +int +freefid(int nr) +{ + Fid *f, **l; + char buf[128]; + + l = &fidhash(nr); + for(f = *l; f; f = f->next) { + if(f->nr == nr) { + if(f->mid) { + sprint(buf, "/mnt/exportfs/%d", f->mid); + unmount(0, buf); + psmap[f->mid] = 0; + } + if(f->f) { + freefile(f->f); + f->f = nil; + } + *l = f->next; + f->next = fidfree; + fidfree = f; + return 1; + } + l = &f->next; + } + + return 0; +} + +Fid * +newfid(int nr) +{ + Fid *new, **l; + int i; + + l = &fidhash(nr); + for(new = *l; new; new = new->next) + if(new->nr == nr) + return 0; + + if(fidfree == 0) { + fidfree = emallocz(sizeof(Fid) * Fidchunk); + + for(i = 0; i < Fidchunk-1; i++) + fidfree[i].next = &fidfree[i+1]; + + fidfree[Fidchunk-1].next = 0; + } + + new = fidfree; + fidfree = new->next; + + memset(new, 0, sizeof(Fid)); + new->next = *l; + *l = new; + new->nr = nr; + new->fid = -1; + new->mid = 0; + + return new; +} + +Fsrpc * +getsbuf(void) +{ + static int ap; + int look, rounds; + Fsrpc *wb; + int small_instead_of_fast = 1; + + if(small_instead_of_fast) + ap = 0; /* so we always start looking at the beginning and reuse buffers */ + + for(rounds = 0; rounds < 10; rounds++) { + for(look = 0; look < Nr_workbufs; look++) { + if(++ap == Nr_workbufs) + ap = 0; + if(Workq[ap].busy == 0) + break; + } + + if(look == Nr_workbufs){ + sleep(10 * rounds); + continue; + } + + wb = &Workq[ap]; + wb->pid = 0; + wb->canint = 0; + wb->flushtag = NOTAG; + wb->busy = 1; + if(wb->buf == nil) /* allocate buffers dynamically to keep size down */ + wb->buf = emallocz(messagesize); + return wb; + } + fatal("No more work buffers"); + return nil; +} + +void +freefile(File *f) +{ + File *parent, *child; + +Loop: + f->ref--; + if(f->ref > 0) + return; + freecnt++; + if(f->ref < 0) abort(); + DEBUG(DFD, "free %s\n", f->name); + /* delete from parent */ + parent = f->parent; + if(parent->child == f) + parent->child = f->childlist; + else{ + for(child=parent->child; child->childlist!=f; child=child->childlist) + if(child->childlist == nil) + fatal("bad child list"); + child->childlist = f->childlist; + } + freeqid(f->qidt); + free(f->name); + f->name = nil; + free(f); + f = parent; + if(f != nil) + goto Loop; +} + +File * +file(File *parent, char *name) +{ + Dir *dir; + char *path; + File *f; + + DEBUG(DFD, "\tfile: 0x%p %s name %s\n", parent, parent->name, name); + + path = makepath(parent, name); + dir = dirstat(path); + free(path); + if(dir == nil) + return nil; + + for(f = parent->child; f; f = f->childlist) + if(strcmp(name, f->name) == 0) + break; + + if(f == nil){ + f = emallocz(sizeof(File)); + f->name = estrdup(name); + + f->parent = parent; + f->childlist = parent->child; + parent->child = f; + parent->ref++; + f->ref = 0; + filecnt++; + } + f->ref++; + f->qid.type = dir->qid.type; + f->qid.vers = dir->qid.vers; + f->qidt = uniqueqid(dir); + f->qid.path = f->qidt->uniqpath; + + f->inval = 0; + + free(dir); + + return f; +} + +void +initroot(void) +{ + Dir *dir; + + root = emallocz(sizeof(File)); + root->name = estrdup("."); + + dir = dirstat(root->name); + if(dir == nil) + fatal("root stat"); + + root->ref = 1; + root->qid.vers = dir->qid.vers; + root->qidt = uniqueqid(dir); + root->qid.path = root->qidt->uniqpath; + root->qid.type = QTDIR; + free(dir); + + psmpt = emallocz(sizeof(File)); + psmpt->name = estrdup("/"); + + dir = dirstat(psmpt->name); + if(dir == nil) + return; + + psmpt->ref = 1; + psmpt->qid.vers = dir->qid.vers; + psmpt->qidt = uniqueqid(dir); + psmpt->qid.path = psmpt->qidt->uniqpath; + free(dir); + + psmpt = file(psmpt, "mnt"); + if(psmpt == 0) + return; + psmpt = file(psmpt, "exportfs"); +} + +char* +makepath(File *p, char *name) +{ + int i, n; + char *c, *s, *path, *seg[256]; + + seg[0] = name; + n = strlen(name)+2; + for(i = 1; i < 256 && p; i++, p = p->parent){ + seg[i] = p->name; + n += strlen(p->name)+1; + } + path = malloc(n); + if(path == nil) + fatal("out of memory"); + s = path; + + while(i--) { + for(c = seg[i]; *c; c++) + *s++ = *c; + *s++ = '/'; + } + while(s[-1] == '/') + s--; + *s = '\0'; + + return path; +} + +int +qidhash(vlong path) +{ + int h, n; + + h = 0; + for(n=0; n<64; n+=Nqidbits){ + h ^= path; + path >>= Nqidbits; + } + return h & (Nqidtab-1); +} + +void +freeqid(Qidtab *q) +{ + ulong h; + Qidtab *l; + + q->ref--; + if(q->ref > 0) + return; + qfreecnt++; + h = qidhash(q->path); + if(qidtab[h] == q) + qidtab[h] = q->next; + else{ + for(l=qidtab[h]; l->next!=q; l=l->next) + if(l->next == nil) + fatal("bad qid list"); + l->next = q->next; + } + free(q); +} + +Qidtab* +qidlookup(Dir *d) +{ + ulong h; + Qidtab *q; + + h = qidhash(d->qid.path); + for(q=qidtab[h]; q!=nil; q=q->next) + if(q->type==d->type && q->dev==d->dev && q->path==d->qid.path) + return q; + return nil; +} + +int +qidexists(vlong path) +{ + int h; + Qidtab *q; + + for(h=0; hnext) + if(q->uniqpath == path) + return 1; + return 0; +} + +Qidtab* +uniqueqid(Dir *d) +{ + ulong h; + vlong path; + Qidtab *q; + + q = qidlookup(d); + if(q != nil){ + q->ref++; + return q; + } + path = d->qid.path; + while(qidexists(path)){ + DEBUG(DFD, "collision on %s\n", d->name); + /* collision: find a new one */ + ncollision++; + path &= QIDPATH; + ++newqid; + if(newqid >= (1<<16)){ + DEBUG(DFD, "collision wraparound\n"); + newqid = 1; + } + path |= newqid<<48; + DEBUG(DFD, "assign qid %.16llux\n", path); + } + q = mallocz(sizeof(Qidtab), 1); + if(q == nil) + fatal("no memory for qid table"); + qidcnt++; + q->ref = 1; + q->type = d->type; + q->dev = d->dev; + q->path = d->qid.path; + q->uniqpath = path; + h = qidhash(d->qid.path); + q->next = qidtab[h]; + qidtab[h] = q; + return q; +} + +void +fatal(char *s, ...) +{ + char buf[ERRMAX]; + va_list arg; + Proc *m; + + if (s) { + va_start(arg, s); + vsnprint(buf, ERRMAX, s, arg); + va_end(arg); + } + + /* Clear away the slave children */ +// for(m = Proclist; m; m = m->next) +// postnote(PNPROC, m->pid, "kill"); + + DEBUG(DFD, "%s\n", buf); + if (s) + sysfatal(buf); + else + exits(nil); +} + diff --git a/exportfs/exportfs.h b/exportfs/exportfs.h new file mode 100644 index 0000000..aa0676f --- /dev/null +++ b/exportfs/exportfs.h @@ -0,0 +1,148 @@ +/* + * exportfs.h - definitions for exporting file server + */ + +#define DEBUG if(!dbg){}else fprint +#define DFD 2 +#define fidhash(s) fhash[s%FHASHSIZE] + +#define Proc Exproc + + +typedef struct Fsrpc Fsrpc; +typedef struct Fid Fid; +typedef struct File File; +typedef struct Proc Proc; +typedef struct Qidtab Qidtab; + +struct Fsrpc +{ + int busy; /* Work buffer has pending rpc to service */ + int pid; /* Pid of slave process executing the rpc */ + int canint; /* Interrupt gate */ + int flushtag; /* Tag on which to reply to flush */ + Fcall work; /* Plan 9 incoming Fcall */ + uchar *buf; /* Data buffer */ +}; + +struct Fid +{ + int fid; /* system fd for i/o */ + File *f; /* File attached to this fid */ + int mode; + int nr; /* fid number */ + int mid; /* Mount id */ + Fid *next; /* hash link */ +}; + +struct File +{ + char *name; + int ref; + Qid qid; + Qidtab *qidt; + int inval; + File *parent; + File *child; + File *childlist; +}; + +struct Proc +{ + int pid; + int busy; + Proc *next; +}; + +struct Qidtab +{ + int ref; + int type; + int dev; + vlong path; + vlong uniqpath; + Qidtab *next; +}; + +enum +{ + MAXPROC = 50, + FHASHSIZE = 64, + Nr_workbufs = 50, + Fidchunk = 1000, + Npsmpt = 32, + Nqidbits = 5, + Nqidtab = (1< +#include +#include +#define Extern extern +#include "exportfs.h" + +char Ebadfid[] = "Bad fid"; +char Enotdir[] = "Not a directory"; +char Edupfid[] = "Fid already in use"; +char Eopen[] = "Fid already opened"; +char Exmnt[] = "Cannot .. past mount point"; +char Emip[] = "Mount in progress"; +char Enopsmt[] = "Out of pseudo mount points"; +char Enomem[] = "No memory"; +char Eversion[] = "Bad 9P2000 version"; + +int iounit(int x) +{ + return 8192+IOHDRSZ; +} + +void* +emallocz(ulong n) +{ + void *v; + + v = mallocz(n, 1); + if(v == nil) + panic("out of memory"); + return v; +} + +ulong messagesize; + +void +Xversion(Fsrpc *t) +{ + Fcall rhdr; + + if(t->work.msize > messagesize) + t->work.msize = messagesize; + messagesize = t->work.msize; + if(strncmp(t->work.version, "9P2000", 6) != 0){ + reply(&t->work, &rhdr, Eversion); + return; + } + rhdr.version = "9P2000"; + rhdr.msize = t->work.msize; + reply(&t->work, &rhdr, 0); + t->busy = 0; +} + +void +Xauth(Fsrpc *t) +{ + Fcall rhdr; + + reply(&t->work, &rhdr, "exportfs: authentication not required"); + t->busy = 0; +} + +void +Xflush(Fsrpc *t) +{ + Fsrpc *w, *e; + Fcall rhdr; + + e = &Workq[Nr_workbufs]; + + for(w = Workq; w < e; w++) { + if(w->work.tag == t->work.oldtag) { + DEBUG(DFD, "\tQ busy %d pid %d can %d\n", w->busy, w->pid, w->canint); + if(w->busy && w->pid) { + w->flushtag = t->work.tag; + DEBUG(DFD, "\tset flushtag %d\n", t->work.tag); + // if(w->canint) + // postnote(PNPROC, w->pid, "flush"); + t->busy = 0; + return; + } + } + } + + reply(&t->work, &rhdr, 0); + DEBUG(DFD, "\tflush reply\n"); + t->busy = 0; +} + +void +Xattach(Fsrpc *t) +{ + int i, nfd; + Fcall rhdr; + Fid *f; + char buf[128]; + + f = newfid(t->work.fid); + if(f == 0) { + reply(&t->work, &rhdr, Ebadfid); + t->busy = 0; + return; + } + + if(srvfd >= 0){ +/* + if(psmpt == 0){ + Nomount: + reply(&t->work, &rhdr, Enopsmt); + t->busy = 0; + freefid(t->work.fid); + return; + } + for(i=0; i= Npsmpt) + goto Nomount; + sprint(buf, "%d", i); + f->f = file(psmpt, buf); + if(f->f == nil) + goto Nomount; + sprint(buf, "/mnt/exportfs/%d", i); + nfd = dup(srvfd, -1); + if(amount(nfd, buf, MREPL|MCREATE, t->work.aname) < 0){ + errstr(buf, sizeof buf); + reply(&t->work, &rhdr, buf); + t->busy = 0; + freefid(t->work.fid); + close(nfd); + return; + } + psmap[i] = 1; + f->mid = i; +*/ + }else{ + f->f = root; + f->f->ref++; + } + + rhdr.qid = f->f->qid; + reply(&t->work, &rhdr, 0); + t->busy = 0; +} + +Fid* +clonefid(Fid *f, int new) +{ + Fid *n; + + n = newfid(new); + if(n == 0) { + n = getfid(new); + if(n == 0) + fatal("inconsistent fids"); + if(n->fid >= 0) + close(n->fid); + freefid(new); + n = newfid(new); + if(n == 0) + fatal("inconsistent fids2"); + } + n->f = f->f; + n->f->ref++; + return n; +} + +void +Xwalk(Fsrpc *t) +{ + char err[ERRMAX], *e; + Fcall rhdr; + Fid *f, *nf; + File *wf; + int i; + + f = getfid(t->work.fid); + if(f == 0) { + reply(&t->work, &rhdr, Ebadfid); + t->busy = 0; + return; + } + + nf = nil; + if(t->work.newfid != t->work.fid){ + nf = clonefid(f, t->work.newfid); + f = nf; + } + + rhdr.nwqid = 0; + e = nil; + for(i=0; iwork.nwname; i++){ + if(i == MAXWELEM){ + e = "Too many path elements"; + break; + } + + if(strcmp(t->work.wname[i], "..") == 0) { + if(f->f->parent == nil) { + e = Exmnt; + break; + } + wf = f->f->parent; + wf->ref++; + goto Accept; + } + + wf = file(f->f, t->work.wname[i]); + if(wf == 0){ + errstr(err, sizeof err); + e = err; + break; + } + Accept: + freefile(f->f); + rhdr.wqid[rhdr.nwqid++] = wf->qid; + f->f = wf; + continue; + } + + if(nf!=nil && (e!=nil || rhdr.nwqid!=t->work.nwname)) + freefid(t->work.newfid); + if(rhdr.nwqid > 0) + e = nil; + reply(&t->work, &rhdr, e); + t->busy = 0; +} + +void +Xclunk(Fsrpc *t) +{ + Fcall rhdr; + Fid *f; + + f = getfid(t->work.fid); + if(f == 0) { + reply(&t->work, &rhdr, Ebadfid); + t->busy = 0; + return; + } + + if(f->fid >= 0) + close(f->fid); + + freefid(t->work.fid); + reply(&t->work, &rhdr, 0); + t->busy = 0; +} + +void +Xstat(Fsrpc *t) +{ + char err[ERRMAX], *path; + Fcall rhdr; + Fid *f; + Dir *d; + int s; + uchar *statbuf; + + f = getfid(t->work.fid); + if(f == 0) { + reply(&t->work, &rhdr, Ebadfid); + t->busy = 0; + return; + } + if(f->fid >= 0) + d = dirfstat(f->fid); + else { + path = makepath(f->f, ""); + d = dirstat(path); + free(path); + } + + if(d == nil) { + errstr(err, sizeof err); + reply(&t->work, &rhdr, err); + t->busy = 0; + return; + } + + d->qid.path = f->f->qidt->uniqpath; + s = sizeD2M(d); + statbuf = emallocz(s); + s = convD2M(d, statbuf, s); + free(d); + rhdr.nstat = s; + rhdr.stat = statbuf; + reply(&t->work, &rhdr, 0); + free(statbuf); + t->busy = 0; +} + +static int +getiounit(int fd) +{ + int n; + + n = iounit(fd); + if(n > messagesize-IOHDRSZ) + n = messagesize-IOHDRSZ; + return n; +} + +void +Xcreate(Fsrpc *t) +{ + char err[ERRMAX], *path; + Fcall rhdr; + Fid *f; + File *nf; + + f = getfid(t->work.fid); + if(f == 0) { + reply(&t->work, &rhdr, Ebadfid); + t->busy = 0; + return; + } + + + path = makepath(f->f, t->work.name); + f->fid = create(path, t->work.mode, t->work.perm); + free(path); + if(f->fid < 0) { + errstr(err, sizeof err); + reply(&t->work, &rhdr, err); + t->busy = 0; + return; + } + + nf = file(f->f, t->work.name); + if(nf == 0) { + errstr(err, sizeof err); + reply(&t->work, &rhdr, err); + t->busy = 0; + return; + } + + f->mode = t->work.mode; + freefile(f->f); + f->f = nf; + rhdr.qid = f->f->qid; + rhdr.iounit = getiounit(f->fid); + reply(&t->work, &rhdr, 0); + t->busy = 0; +} + +void +Xremove(Fsrpc *t) +{ + char err[ERRMAX], *path; + Fcall rhdr; + Fid *f; + + f = getfid(t->work.fid); + if(f == 0) { + reply(&t->work, &rhdr, Ebadfid); + t->busy = 0; + return; + } + + path = makepath(f->f, ""); + DEBUG(DFD, "\tremove: %s\n", path); + if(remove(path) < 0) { + free(path); + errstr(err, sizeof err); + reply(&t->work, &rhdr, err); + t->busy = 0; + return; + } + free(path); + + f->f->inval = 1; + if(f->fid >= 0) + close(f->fid); + freefid(t->work.fid); + + reply(&t->work, &rhdr, 0); + t->busy = 0; +} + +void +Xwstat(Fsrpc *t) +{ + char err[ERRMAX], *path; + Fcall rhdr; + Fid *f; + int s; + char *strings; + Dir d; + + f = getfid(t->work.fid); + if(f == 0) { + reply(&t->work, &rhdr, Ebadfid); + t->busy = 0; + return; + } + strings = emallocz(t->work.nstat); /* ample */ + if(convM2D(t->work.stat, t->work.nstat, &d, strings) < 0){ + rerrstr(err, sizeof err); + reply(&t->work, &rhdr, err); + t->busy = 0; + free(strings); + return; + } + + if(f->fid >= 0) + s = dirfwstat(f->fid, &d); + else { + path = makepath(f->f, ""); + s = dirwstat(path, &d); + free(path); + } + if(s < 0) { + rerrstr(err, sizeof err); + reply(&t->work, &rhdr, err); + } + else { + /* wstat may really be rename */ + if(strcmp(d.name, f->f->name)!=0 && strcmp(d.name, "")!=0){ + free(f->f->name); + f->f->name = estrdup(d.name); + } + reply(&t->work, &rhdr, 0); + } + free(strings); + t->busy = 0; +} + +void +slave(Fsrpc *f) +{ + Proc *p; + int pid; + static int nproc; + + for(;;) { + for(p = Proclist; p; p = p->next) { + if(p->busy == 0) { + f->pid = p->pid; + p->busy = 1; + pid = rendezvous(p->pid, (ulong)f); + if(pid != p->pid) + fatal("rendezvous sync fail"); + return; + } + } + + if(++nproc > MAXPROC) + fatal("too many procs"); + + pid = kproc("slave", blockingslave, nil); + DEBUG(DFD, "slave pid %d\n", pid); + if(pid == -1) + fatal("kproc"); + + p = malloc(sizeof(Proc)); + if(p == 0) + fatal("out of memory"); + + p->busy = 0; + p->pid = pid; + p->next = Proclist; + Proclist = p; + +DEBUG(DFD, "parent %d rendez\n", pid); + rendezvous(pid, (ulong)p); +DEBUG(DFD, "parent %d went\n", pid); + } +} + +void +blockingslave(void *x) +{ + Fsrpc *p; + Fcall rhdr; + Proc *m; + int pid; + + USED(x); + + notify(flushaction); + + pid = getpid(); + +DEBUG(DFD, "blockingslave %d rendez\n", pid); + m = (Proc*)rendezvous(pid, 0); +DEBUG(DFD, "blockingslave %d rendez got %p\n", pid, m); + + for(;;) { + p = (Fsrpc*)rendezvous(pid, pid); + if((int)p == ~0) /* Interrupted */ + continue; + + DEBUG(DFD, "\tslave: %d %F b %d p %d\n", pid, &p->work, p->busy, p->pid); + if(p->flushtag != NOTAG) + goto flushme; + + switch(p->work.type) { + case Tread: + slaveread(p); + break; + + case Twrite: + slavewrite(p); + break; + + case Topen: + slaveopen(p); + break; + + default: + reply(&p->work, &rhdr, "exportfs: slave type error"); + } + if(p->flushtag != NOTAG) { +flushme: + p->work.type = Tflush; + p->work.tag = p->flushtag; + reply(&p->work, &rhdr, 0); + } + p->busy = 0; + m->busy = 0; + } +} + +int +openmount(int sfd) +{ + werrstr("openmount not implemented"); + return -1; +} + +void +slaveopen(Fsrpc *p) +{ + char err[ERRMAX], *path; + Fcall *work, rhdr; + Fid *f; + Dir *d; + + work = &p->work; + + f = getfid(work->fid); + if(f == 0) { + reply(work, &rhdr, Ebadfid); + return; + } + if(f->fid >= 0) { + close(f->fid); + f->fid = -1; + } + + path = makepath(f->f, ""); + DEBUG(DFD, "\topen: %s %d\n", path, work->mode); + + p->canint = 1; + if(p->flushtag != NOTAG){ + free(path); + return; + } + /* There is a race here I ignore because there are no locks */ + f->fid = open(path, work->mode); + free(path); + p->canint = 0; + if(f->fid < 0 || (d = dirfstat(f->fid)) == nil) { + Error: + errstr(err, sizeof err); + reply(work, &rhdr, err); + return; + } + f->f->qid = d->qid; + free(d); + if(f->f->qid.type & QTMOUNT){ /* fork new exportfs for this */ + f->fid = openmount(f->fid); + if(f->fid < 0) + goto Error; + } + + DEBUG(DFD, "\topen: fd %d\n", f->fid); + f->mode = work->mode; + rhdr.iounit = getiounit(f->fid); + rhdr.qid = f->f->qid; + reply(work, &rhdr, 0); +} + +void +slaveread(Fsrpc *p) +{ + Fid *f; + int n, r; + Fcall *work, rhdr; + char *data, err[ERRMAX]; + + work = &p->work; + + f = getfid(work->fid); + if(f == 0) { + reply(work, &rhdr, Ebadfid); + return; + } + + n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count; + p->canint = 1; + if(p->flushtag != NOTAG) + return; + data = malloc(n); + if(data == nil) + fatal(Enomem); + + /* can't just call pread, since directories must update the offset */ + r = pread(f->fid, data, n, work->offset); + p->canint = 0; + if(r < 0) { + free(data); + errstr(err, sizeof err); + reply(work, &rhdr, err); + return; + } + + DEBUG(DFD, "\tread: fd=%d %d bytes\n", f->fid, r); + + rhdr.data = data; + rhdr.count = r; + reply(work, &rhdr, 0); + free(data); +} + +void +slavewrite(Fsrpc *p) +{ + char err[ERRMAX]; + Fcall *work, rhdr; + Fid *f; + int n; + + work = &p->work; + + f = getfid(work->fid); + if(f == 0) { + reply(work, &rhdr, Ebadfid); + return; + } + + n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count; + p->canint = 1; + if(p->flushtag != NOTAG) + return; + n = pwrite(f->fid, work->data, n, work->offset); + p->canint = 0; + if(n < 0) { + errstr(err, sizeof err); + reply(work, &rhdr, err); + return; + } + + DEBUG(DFD, "\twrite: %d bytes fd=%d\n", n, f->fid); + + rhdr.count = n; + reply(work, &rhdr, 0); +} + +void +reopen(Fid *f) +{ + USED(f); + fatal("reopen"); +} + +void +flushaction(void *a, char *cause) +{ + USED(a); + if(strncmp(cause, "sys:", 4) == 0 && !strstr(cause, "pipe")) { + fprint(2, "exportsrv: note: %s\n", cause); + exits("noted"); + } + if(strncmp(cause, "kill", 4) == 0) + noted(NDFLT); + + noted(NCONT); +} diff --git a/exportfs/mkfile b/exportfs/mkfile new file mode 100644 index 0000000..9f36c36 --- /dev/null +++ b/exportfs/mkfile @@ -0,0 +1,11 @@ +<$DSRC/mkfile-$CONF +TARG=libexportfs.$L + +OFILES=\ + exportfs.$O\ + exportsrv.$O + +HFILES=\ + exportfs.h + +<$DSRC/mklib-$CONF diff --git a/gui-win32/alloc.c b/gui-win32/alloc.c new file mode 100644 index 0000000..cc7e977 --- /dev/null +++ b/gui-win32/alloc.c @@ -0,0 +1,23 @@ +#include +#include +#include +#include + +Memimage* +allocmemimage(Rectangle r, ulong chan) +{ + return _allocmemimage(r, chan); +} + +void +freememimage(Memimage *i) +{ + _freememimage(i); +} + +void +memfillcolor(Memimage *i, ulong val) +{ + _memfillcolor(i, val); +} + diff --git a/gui-win32/cload.c b/gui-win32/cload.c new file mode 100644 index 0000000..9d658ef --- /dev/null +++ b/gui-win32/cload.c @@ -0,0 +1,10 @@ +#include +#include +#include +#include + +int +cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) +{ + return _cloadmemimage(i, r, data, ndata); +} diff --git a/gui-win32/draw.c b/gui-win32/draw.c new file mode 100644 index 0000000..eaac6d5 --- /dev/null +++ b/gui-win32/draw.c @@ -0,0 +1,22 @@ +#include +#include +#include +#include + +void +memimagedraw(Memimage *dst, Rectangle r, Memimage *src, Point sp, Memimage *mask, Point mp, int op) +{ + _memimagedraw(_memimagedrawsetup(dst, r, src, sp, mask, mp, op)); +} + +ulong +pixelbits(Memimage *m, Point p) +{ + return _pixelbits(m, p); +} + +void +memimageinit(void) +{ + _memimageinit(); +} diff --git a/gui-win32/load.c b/gui-win32/load.c new file mode 100644 index 0000000..7cca3a6 --- /dev/null +++ b/gui-win32/load.c @@ -0,0 +1,10 @@ +#include +#include +#include +#include + +int +loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) +{ + return _loadmemimage(i, r, data, ndata); +} diff --git a/gui-win32/mkfile b/gui-win32/mkfile new file mode 100644 index 0000000..9a8daf2 --- /dev/null +++ b/gui-win32/mkfile @@ -0,0 +1,14 @@ +<$DSRC/mkfile-$CONF +TARG=libgui.$L + +OFILES=\ + alloc.$O\ + cload.$O\ + draw.$O\ + load.$O\ + screen.$O\ + wstrtoutf.$O + +HFILES=\ + +<$DSRC/mklib-$CONF diff --git a/gui-win32/screen.c b/gui-win32/screen.c new file mode 100644 index 0000000..4fa70cd --- /dev/null +++ b/gui-win32/screen.c @@ -0,0 +1,642 @@ +#include +// #include "winduhz.h" + +#undef Rectangle +#define Rectangle _Rectangle + +#include +#include +#include +#include +#include +#include "error.h" +#include "screen.h" +#include "keyboard.h" +#include "fns.h" + +Memimage *gscreen; +Screeninfo screen; + +extern int mousequeue; +static int depth; + +static HINSTANCE inst; +static HWND window; +static HPALETTE palette; +static LOGPALETTE *logpal; +static Lock gdilock; +static BITMAPINFO *bmi; +static HCURSOR hcursor; + +static void winproc(void *); +static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); +static void paletteinit(void); +static void bmiinit(void); +static void screenload2(Rectangle r, int ldepth, uchar *p, Point pt, int step); + +static int readybit; +static Rendez rend; + +Point ZP; + +static +isready(void*a) +{ + return readybit; +} + +void +screeninit(void) +{ + int fmt; + int dx, dy; + + memimageinit(); + if(depth == 0) + depth = GetDeviceCaps(GetDC(NULL), BITSPIXEL); + switch(depth){ + case 32: + screen.dibtype = DIB_RGB_COLORS; + screen.depth = 32; + fmt = XRGB32; + break; + case 24: + screen.dibtype = DIB_RGB_COLORS; + screen.depth = 24; + fmt = RGB24; + break; + case 16: + screen.dibtype = DIB_RGB_COLORS; + screen.depth = 16; + fmt = RGB15; /* [sic] */ + break; + case 8: + default: + screen.dibtype = DIB_PAL_COLORS; + screen.depth = 8; + depth = 8; + fmt = CMAP8; + break; + } + dx = GetDeviceCaps(GetDC(NULL), HORZRES); + dy = GetDeviceCaps(GetDC(NULL), VERTRES); + + gscreen = allocmemimage(Rect(0,0,dx,dy), fmt); + kproc("winscreen", winproc, 0); + sleep(&rend, isready, 0); +} + +uchar* +attachscreen(Rectangle *r, ulong *chan, int *depth, int *width, int *softscreen, void **X) +{ + *r = gscreen->r; + *chan = gscreen->chan; + *depth = gscreen->depth; + *width = gscreen->width; + *softscreen = 1; + + return gscreen->data->bdata; +} + +void +flushmemscreen(Rectangle r) +{ + screenload(r, gscreen->depth, byteaddr(gscreen, ZP), ZP, + gscreen->width*sizeof(ulong)); +// Sleep(100); +} + +void +screenload(Rectangle r, int depth, uchar *p, Point pt, int step) +{ + int dx, dy, delx; + HDC hdc; + RECT winr; + + if(depth != gscreen->depth) + panic("screenload: bad ldepth"); + + /* + * Sometimes we do get rectangles that are off the + * screen to the negative axes, for example, when + * dragging around a window border in a Move operation. + */ + if(rectclip(&r, gscreen->r) == 0) + return; + + if(step&3 != 0 || ((pt.x*depth)%32) != 0 || (ulong)p&3 != 0) + panic("screenload: bad params %d %d %ux", step, pt.x, p); + dx = r.max.x - r.min.x; + dy = r.max.y - r.min.y; + + if(dx <= 0 || dy <= 0) + return; + + if(depth == 24) + delx = r.min.x % 4; + else + delx = r.min.x & (31/depth); + + p += (r.min.y-pt.y)*step; + p += ((r.min.x-delx-pt.x)*depth)>>3; + + if(GetWindowRect(window, &winr)==0) + return; + if(rectclip(&r, Rect(0, 0, winr.right-winr.left, winr.bottom-winr.top))==0) + return; + + lock(&gdilock); + + hdc = GetDC(window); + SelectPalette(hdc, palette, 0); + RealizePalette(hdc); + +//FillRect(hdc,(void*)&r, GetStockObject(BLACK_BRUSH)); +//GdiFlush(); +//Sleep(100); + + bmi->bmiHeader.biWidth = (step*8)/depth; + bmi->bmiHeader.biHeight = -dy; /* - => origin upper left */ + + StretchDIBits(hdc, r.min.x, r.min.y, dx, dy, + delx, 0, dx, dy, p, bmi, screen.dibtype, SRCCOPY); + + ReleaseDC(window, hdc); + + GdiFlush(); + + unlock(&gdilock); +} + +static void +winproc(void *a) +{ + WNDCLASS wc; + MSG msg; + + inst = GetModuleHandle(NULL); + + paletteinit(); + bmiinit(); + terminit(); + + wc.style = 0; + wc.lpfnWndProc = WindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = inst; + wc.hIcon = LoadIcon(inst, NULL); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = GetStockObject(WHITE_BRUSH); + wc.lpszMenuName = 0; + wc.lpszClassName = "9pmgraphics"; + RegisterClass(&wc); + + window = CreateWindowEx( + 0, /* extended style */ + "9pmgraphics", /* class */ + "drawterm screen", /* caption */ + WS_OVERLAPPEDWINDOW, /* style */ + CW_USEDEFAULT, /* init. x pos */ + CW_USEDEFAULT, /* init. y pos */ + CW_USEDEFAULT, /* init. x size */ + CW_USEDEFAULT, /* init. y size */ + NULL, /* parent window (actually owner window for overlapped)*/ + NULL, /* menu handle */ + inst, /* program handle */ + NULL /* create parms */ + ); + + if(window == nil) + panic("can't make window\n"); + + ShowWindow(window, SW_SHOWDEFAULT); + UpdateWindow(window); + + readybit = 1; + wakeup(&rend); + + screen.reshaped = 0; + + while(GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +// MessageBox(0, "winproc", "exits", MB_OK); + ExitProcess(0); +} + +int +col(int v, int n) +{ + int i, c; + + c = 0; + for(i = 0; i < 8; i += n) + c |= v << (16-(n+i)); + return c >> 8; +} + + +void +paletteinit(void) +{ + PALETTEENTRY *pal; + int r, g, b, cr, cg, cb, v; + int num, den; + int i, j; + + logpal = mallocz(sizeof(LOGPALETTE) + 256*sizeof(PALETTEENTRY), 1); + if(logpal == nil) + panic("out of memory"); + logpal->palVersion = 0x300; + logpal->palNumEntries = 256; + pal = logpal->palPalEntry; + + for(r=0,i=0; r<4; r++) { + for(v=0; v<4; v++,i+=16){ + for(g=0,j=v-r; g<4; g++) { + for(b=0; b<4; b++,j++){ + den=r; + if(g>den) + den=g; + if(b>den) + den=b; + /* divide check -- pick grey shades */ + if(den==0) + cr=cg=cb=v*17; + else{ + num=17*(4*den+v); + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + pal[i+(j&15)].peRed = cr; + pal[i+(j&15)].peGreen = cg; + pal[i+(j&15)].peBlue = cb; + pal[i+(j&15)].peFlags = 0; + } + } + } + } + palette = CreatePalette(logpal); +} + + +void +getcolor(ulong i, ulong *r, ulong *g, ulong *b) +{ + PALETTEENTRY *pal; + + pal = logpal->palPalEntry; + *r = pal[i].peRed; + *g = pal[i].peGreen; + *b = pal[i].peBlue; +} + +void +bmiinit(void) +{ + ushort *p; + int i; + + bmi = mallocz(sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD), 1); + if(bmi == 0) + panic("out of memory"); + bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi->bmiHeader.biWidth = 0; + bmi->bmiHeader.biHeight = 0; /* - => origin upper left */ + bmi->bmiHeader.biPlanes = 1; + bmi->bmiHeader.biBitCount = depth; + bmi->bmiHeader.biCompression = BI_RGB; + bmi->bmiHeader.biSizeImage = 0; + bmi->bmiHeader.biXPelsPerMeter = 0; + bmi->bmiHeader.biYPelsPerMeter = 0; + bmi->bmiHeader.biClrUsed = 0; + bmi->bmiHeader.biClrImportant = 0; /* number of important colors: 0 means all */ + + p = (ushort*)bmi->bmiColors; + for(i = 0; i < 256; i++) + p[i] = i; +} + +LRESULT CALLBACK +WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + PAINTSTRUCT paint; + HDC hdc; + LONG x, y, b; + int i; + Rectangle r; + + switch(msg) { + case WM_CREATE: + break; + case WM_SETCURSOR: + /* User set */ + if(hcursor != NULL) { + SetCursor(hcursor); + return 1; + } + return DefWindowProc(hwnd, msg, wparam, lparam); + case WM_MOUSEMOVE: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + x = LOWORD(lparam); + y = HIWORD(lparam); + b = 0; + if(wparam & MK_LBUTTON) + b = 1; + if(wparam & MK_MBUTTON) + b |= 2; + if(wparam & MK_RBUTTON) { + if(wparam & MK_SHIFT) + b |= 2; + else + b |= 4; + } + lock(&mouse.lk); + i = mouse.wi; + if(mousequeue) { + if(i == mouse.ri || mouse.lastb != b || mouse.trans) { + mouse.wi = (i+1)%Mousequeue; + if(mouse.wi == mouse.ri) + mouse.ri = (mouse.ri+1)%Mousequeue; + mouse.trans = mouse.lastb != b; + } else { + i = (i-1+Mousequeue)%Mousequeue; + } + } else { + mouse.wi = (i+1)%Mousequeue; + mouse.ri = i; + } + mouse.queue[i].xy.x = x; + mouse.queue[i].xy.y = y; + mouse.queue[i].buttons = b; + mouse.queue[i].msec = ticks(); + mouse.lastb = b; + unlock(&mouse.lk); + wakeup(&mouse.r); + break; + + case WM_CHAR: + /* repeat count is lparam & 0xf */ + switch(wparam){ + case '\n': + wparam = '\r'; + break; + case '\r': + wparam = '\n'; + break; + } + kbdputc(kbdq, wparam); + break; + + case WM_SYSKEYUP: + break; + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + switch(wparam) { + case VK_MENU: + kbdputc(kbdq, Kalt); + break; + case VK_INSERT: + kbdputc(kbdq, Kins); + break; + case VK_DELETE: +// kbdputc(kbdq, Kdel); + kbdputc(kbdq, 0x7f); // should have Kdel in keyboard.h + break; + case VK_UP: + kbdputc(kbdq, Kup); + break; + case VK_DOWN: + kbdputc(kbdq, Kdown); + break; + case VK_LEFT: + kbdputc(kbdq, Kleft); + break; + case VK_RIGHT: + kbdputc(kbdq, Kright); + break; + } + break; + + case WM_CLOSE: + DestroyWindow(hwnd); + break; + + case WM_DESTROY: + PostQuitMessage(0); + break; + + case WM_PALETTECHANGED: + if((HWND)wparam == hwnd) + break; + /* fall through */ + case WM_QUERYNEWPALETTE: + hdc = GetDC(hwnd); + SelectPalette(hdc, palette, 0); + if(RealizePalette(hdc) != 0) + InvalidateRect(hwnd, nil, 0); + ReleaseDC(hwnd, hdc); + break; + + case WM_PAINT: + hdc = BeginPaint(hwnd, &paint); + r.min.x = paint.rcPaint.left; + r.min.y = paint.rcPaint.top; + r.max.x = paint.rcPaint.right; + r.max.y = paint.rcPaint.bottom; + flushmemscreen(r); + EndPaint(hwnd, &paint); + break; + case WM_COMMAND: + case WM_SETFOCUS: + case WM_DEVMODECHANGE: + case WM_WININICHANGE: + case WM_INITMENU: + default: + return DefWindowProc(hwnd, msg, wparam, lparam); + } + return 0; +} + +void +mouseset(Point xy) +{ + POINT pt; + + pt.x = xy.x; + pt.y = xy.y; + MapWindowPoints(window, 0, &pt, 1); + SetCursorPos(pt.x, pt.y); +} + +void +setcursor(void) +{ + HCURSOR nh; + int x, y, h, w; + uchar *sp, *cp; + uchar *and, *xor; + + h = GetSystemMetrics(SM_CYCURSOR); + w = (GetSystemMetrics(SM_CXCURSOR)+7)/8; + + and = mallocz(h*w, 1); + memset(and, 0xff, h*w); + xor = mallocz(h*w, 1); + + lock(&cursor.lk); + for(y=0,sp=cursor.set,cp=cursor.clr; y<16; y++) { + for(x=0; x<2; x++) { + and[y*w+x] = ~(*sp|*cp); + xor[y*w+x] = ~*sp & *cp; + cp++; + sp++; + } + } + nh = CreateCursor(inst, -cursor.offset.x, -cursor.offset.y, + GetSystemMetrics(SM_CXCURSOR), h, + and, xor); + if(nh != NULL) { + SetCursor(nh); + if(hcursor != NULL) + DestroyCursor(hcursor); + hcursor = nh; + } + unlock(&cursor.lk); + + free(and); + free(xor); + + PostMessage(window, WM_SETCURSOR, (int)window, 0); +} + +void +cursorarrow(void) +{ + if(hcursor != 0) { + DestroyCursor(hcursor); + hcursor = 0; + } + SetCursor(LoadCursor(0, IDC_ARROW)); + PostMessage(window, WM_SETCURSOR, (int)window, 0); +} + + +void +setcolor(ulong index, ulong red, ulong green, ulong blue) +{ +} + + +uchar* +clipreadunicode(HANDLE h) +{ + Rune *p; + int n; + uchar *q; + + p = GlobalLock(h); + n = wstrutflen(p)+1; + q = malloc(n); + wstrtoutf(q, p, n); + GlobalUnlock(h); + + return q; +} + +uchar * +clipreadutf(HANDLE h) +{ + uchar *p; + + p = GlobalLock(h); + p = strdup(p); + GlobalUnlock(h); + + return p; +} + + +uchar* +clipread() +{ + HANDLE h; + uchar *p; + + if(!OpenClipboard(window)) { + oserror(); + return strdup(""); + } + + if(h = GetClipboardData(CF_UNICODETEXT)) + p = clipreadunicode(h); + else if(h = GetClipboardData(CF_TEXT)) + p = clipreadutf(h); + else { + oserror(); + p = strdup(""); + } + + CloseClipboard(); + return p; +} + +int +clipwrite(char *buf) +{ + HANDLE h; + char *p, *e; + Rune *rp; + int n = strlen(buf); + + if(!OpenClipboard(window)) { + oserror(); + return -1; + } + + if(!EmptyClipboard()) { + oserror(); + CloseClipboard(); + return -1; + } + + h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, (n+1)*sizeof(Rune)); + if(h == NULL) + panic("out of memory"); + rp = GlobalLock(h); + p = buf; + e = p+n; + while(p +#include + +int +wstrutflen(Rune *s) +{ + int n; + + for(n=0; *s; n+=runelen(*s),s++) + ; + return n; +} + +int +wstrtoutf(char *s, Rune *t, int n) +{ + int i; + char *s0; + + s0 = s; + if(n <= 0) + return wstrutflen(t)+1; + while(*t) { + if(n < UTFmax+1 && n < runelen(*t)+1) { + *s = 0; + return i+wstrutflen(t)+1; + } + i = runetochar(s, t); + s += i; + n -= i; + t++; + } + *s = 0; + return s-s0; +} diff --git a/gui-x11/Makefile b/gui-x11/Makefile new file mode 100644 index 0000000..12da0fd --- /dev/null +++ b/gui-x11/Makefile @@ -0,0 +1,20 @@ +LIB=libx11.a +CC=gcc +CFLAGS=-I../include -I. -I/usr/X11R6/include -I../kern -c -ggdb -D_THREAD_SAFE -pthread +O=o + +OFILES=\ + alloc.$O\ + cload.$O\ + draw.$O\ + load.$O\ + screen.$O\ + keysym2ucs-x11.$O + +$(LIB): $(OFILES) + ar r $(LIB) $(OFILES) + ranlib $(LIB) + +%.$O: %.c + $(CC) $(CFLAGS) $*.c + diff --git a/gui-x11/alloc.c b/gui-x11/alloc.c new file mode 100644 index 0000000..447ba4f --- /dev/null +++ b/gui-x11/alloc.c @@ -0,0 +1,209 @@ +#include +#include +#include +#include +#include "xmem.h" + +/* perfect approximation to NTSC = .299r+.587g+.114b when 0 ≤ r,g,b < 256 */ +#define RGB2K(r,g,b) ((156763*(r)+307758*(g)+59769*(b))>>19) + +Memimage* +xallocmemimage(Rectangle r, ulong chan, int pmid) +{ + Memimage *m; + Xmem *xm; + XImage *xi; + int offset; + int d; + + m = _allocmemimage(r, chan); + if(chan != GREY1 && chan != xscreenchan) + return m; + + d = m->depth; + xm = mallocz(sizeof(Xmem), 1); + if(pmid != PMundef) + xm->pmid = pmid; + else + xm->pmid = XCreatePixmap(xdisplay, xscreenid, Dx(r), Dy(r), (d==32) ? 24 : d); + + if(m->depth == 24) + offset = r.min.x&(4-1); + else + offset = r.min.x&(31/m->depth); + r.min.x -= offset; + + assert(wordsperline(r, m->depth) <= m->width); + + xi = XCreateImage(xdisplay, xvis, m->depth==32?24:m->depth, ZPixmap, 0, + (char*)m->data->bdata, Dx(r), Dy(r), 32, m->width*sizeof(ulong)); + + if(xi == nil){ + _freememimage(m); + return nil; + } + + xm->xi = xi; + xm->pc = getcallerpc(&r); + xm->r = r; + + /* + * Set the parameters of the XImage so its memory looks exactly like a + * Memimage, so we can call _memimagedraw on the same data. All frame + * buffers we've seen, and Plan 9's graphics code, require big-endian + * bits within bytes, but little endian byte order within pixels. + */ + xi->bitmap_unit = m->depth < 8 || m->depth == 24 ? 8 : m->depth; + xi->byte_order = LSBFirst; + xi->bitmap_bit_order = MSBFirst; + xi->bitmap_pad = 32; + xm->r = Rect(0,0,0,0); + XInitImage(xi); + XFlush(xdisplay); + + m->X = xm; + return m; +} + +Memimage* +allocmemimage(Rectangle r, ulong chan) +{ + return xallocmemimage(r, chan, PMundef); +} + +void +freememimage(Memimage *m) +{ + Xmem *xm; + + if(m == nil) + return; + + if(m->data->ref == 1){ + if((xm = m->X) != nil){ + if(xm->xi){ + xm->xi->data = nil; + XFree(xm->xi); + } + XFreePixmap(xdisplay, xm->pmid); + free(xm); + m->X = nil; + } + } + _freememimage(m); +} + +void +memfillcolor(Memimage *m, ulong val) +{ + _memfillcolor(m, val); + if(m->X){ + if((val & 0xFF) == 0xFF) + xfillcolor(m, m->r, _rgbatoimg(m, val)); + else + putXdata(m, m->r); + } +} + +static void +addrect(Rectangle *rp, Rectangle r) +{ + if(rp->min.x >= rp->max.x) + *rp = r; + else + combinerect(rp, r); +} + +XImage* +getXdata(Memimage *m, Rectangle r) +{ + uchar *p; + int x, y; + Xmem *xm; + Point xdelta, delta; + Point tp; + + xm = m->X; + if(xm == nil) + return; + + assert(xm != nil && xm->xi != nil); + + if(xm->dirty == 0) + return xm->xi; + + r = xm->dirtyr; + if(Dx(r)==0 || Dy(r)==0) + return xm->xi; + + delta = subpt(r.min, m->r.min); + tp = xm->r.min; /* avoid unaligned access on digital unix */ + xdelta = subpt(r.min, tp); + + XGetSubImage(xdisplay, xm->pmid, delta.x, delta.y, Dx(r), Dy(r), + AllPlanes, ZPixmap, xm->xi, xdelta.x, xdelta.y); + + if(xtblbit && m->chan == CMAP8) + for(y=r.min.y; ydirty = 0; + xm->dirtyr = Rect(0,0,0,0); + return xm->xi; +} + +void +putXdata(Memimage *m, Rectangle r) +{ + Xmem *xm; + XImage *xi; + GC g; + int offset; + Point xdelta, delta; + Point tp; + int x, y; + uchar *p; + + xm = m->X; + if(xm == nil) + return; + + assert(xm != nil); + assert(xm->xi != nil); + + xi = xm->xi; + + g = (m->chan == GREY1) ? xgccopy0 : xgccopy; + if(m->depth == 24) + offset = r.min.x % 4; + else + offset = m->r.min.x & (31/m->depth); + + delta = subpt(r.min, m->r.min); + tp = xm->r.min; /* avoid unaligned access on digital unix */ + xdelta = subpt(r.min, tp); + + if(xtblbit && m->chan == CMAP8) + for(y=r.min.y; ypmid, g, xi, xdelta.x, xdelta.y, delta.x, delta.y, Dx(r), Dy(r)); + + if(xtblbit && m->chan == CMAP8) + for(y=r.min.y; yX) != nil){ + xm->dirty = 1; + addrect(&xm->dirtyr, r); + } +} diff --git a/gui-x11/cload.c b/gui-x11/cload.c new file mode 100644 index 0000000..92a5b80 --- /dev/null +++ b/gui-x11/cload.c @@ -0,0 +1,16 @@ +#include +#include +#include +#include +#include "xmem.h" + +int +cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) +{ + int n; + + n = _cloadmemimage(i, r, data, ndata); + if(n > 0 && i->X) + putXdata(i, r); + return n; +} diff --git a/gui-x11/draw.c b/gui-x11/draw.c new file mode 100644 index 0000000..1e776b4 --- /dev/null +++ b/gui-x11/draw.c @@ -0,0 +1,189 @@ +#include +#include +#include +#include +#include "xmem.h" + +void xfillcolor(Memimage*, Rectangle, ulong); +static int xdraw(Memdrawparam*); + +int xgcfillcolor = 0; +int xgcfillcolor0 = 0; +int xgczeropm = 0; +int xgczeropm0 = 0; +int xgcsimplecolor = 0; +int xgcsimplecolor0 = 0; +int xgcsimplepm = 0; +int xgcsimplepm0 = 0; +int xgcreplsrctile = 0; +int xgcreplsrctile0 = 0; + +void +memimageinit(void) +{ + static int didinit = 0; + + if(didinit) + return; + + didinit = 1; + _memimageinit(); + + xfillcolor(memblack, memblack->r, 0); + xfillcolor(memwhite, memwhite->r, 1); +} + +void +memimagedraw(Memimage *dst, Rectangle r, Memimage *src, Point sp, Memimage *mask, Point mp, int op) +{ + int didx; + Rectangle dr, mr, sr; + Memdrawparam *par; + + if((par = _memimagedrawsetup(dst, r, src, sp, mask, mp, op)) == nil) + return; + _memimagedraw(par); + if(!xdraw(par)) + putXdata(dst, par->r); +} + +void +xfillcolor(Memimage *m, Rectangle r, ulong v) +{ + GC gc; + Xmem *dxm; + + dxm = m->X; + assert(dxm != nil); + r = rectsubpt(r, m->r.min); + + if(m->chan == GREY1){ + gc = xgcfill0; + if(xgcfillcolor0 != v){ + XSetForeground(xdisplay, gc, v); + xgcfillcolor0 = v; + } + }else{ + if(m->chan == CMAP8 && xtblbit) + v = plan9tox11[v]; + + gc = xgcfill; + if(xgcfillcolor != v){ + XSetForeground(xdisplay, gc, v); + xgcfillcolor = v; + } + } + XFillRectangle(xdisplay, dxm->pmid, gc, r.min.x, r.min.y, Dx(r), Dy(r)); +} + +static int +xdraw(Memdrawparam *par) +{ + int dy, dx; + unsigned m; + Memimage *src, *dst, *mask; + Xmem *dxm, *sxm, *mxm; + GC gc; + Rectangle r, sr, mr; + ulong sdval; + + dx = Dx(par->r); + dy = Dy(par->r); + src = par->src; + dst = par->dst; + mask = par->mask; + r = par->r; + sr = par->sr; + mr = par->mr; + sdval = par->sdval; + +return 0; + if((dxm = dst->X) == nil) + return 0; + + /* + * If we have an opaque mask and source is one opaque pixel we can convert to the + * destination format and just XFillRectangle. + */ + m = Simplesrc|Simplemask|Fullmask; + if((par->state&m)==m){ + xfillcolor(dst, r, sdval); + dirtyXdata(dst, par->r); + return 1; + } + + /* + * If no source alpha, an opaque mask, we can just copy the + * source onto the destination. If the channels are the same and + * the source is not replicated, XCopyArea suffices. + */ + m = Simplemask|Fullmask; + if((par->state&(m|Replsrc))==m && src->chan == dst->chan && src->X){ + sxm = src->X; + r = rectsubpt(r, dst->r.min); + sr = rectsubpt(sr, src->r.min); + if(dst->chan == GREY1) + gc = xgccopy0; + else + gc = xgccopy; + XCopyArea(xdisplay, sxm->pmid, dxm->pmid, gc, + sr.min.x, sr.min.y, dx, dy, r.min.x, r.min.y); + dirtyXdata(dst, par->r); + return 1; + } + + /* + * If no source alpha, a 1-bit mask, and a simple source + * we can just copy through the mask onto the destination. + */ + if(dst->X && mask->X && !(mask->flags&Frepl) + && mask->chan == GREY1 && (par->state&Simplesrc)){ + Point p; + + mxm = mask->X; + r = rectsubpt(r, dst->r.min); + mr = rectsubpt(mr, mask->r.min); + p = subpt(r.min, mr.min); + if(dst->chan == GREY1){ + gc = xgcsimplesrc0; + if(xgcsimplecolor0 != sdval){ + XSetForeground(xdisplay, gc, sdval); + xgcsimplecolor0 = sdval; + } + if(xgcsimplepm0 != mxm->pmid){ + XSetStipple(xdisplay, gc, mxm->pmid); + xgcsimplepm0 = mxm->pmid; + } + }else{ + /* somehow this doesn't work on rob's mac + gc = xgcsimplesrc; + if(dst->chan == CMAP8 && xtblbit) + sdval = plan9tox11[sdval]; + + if(xgcsimplecolor != sdval){ + XSetForeground(xdisplay, gc, sdval); + xgcsimplecolor = sdval; + } + if(xgcsimplepm != mxm->pmid){ + XSetStipple(xdisplay, gc, mxm->pmid); + xgcsimplepm = mxm->pmid; + } + */ + return 0; + } + XSetTSOrigin(xdisplay, gc, p.x, p.y); + XFillRectangle(xdisplay, dxm->pmid, gc, r.min.x, r.min.y, dx, dy); + dirtyXdata(dst, par->r); + return 1; + } + return 0; +} + +ulong +pixelbits(Memimage *m, Point p) +{ + Xmem *xm; + if(m->X) + getXdata(m, Rect(p.x, p.y, p.x+1, p.y+1)); + return _pixelbits(m, p); +} diff --git a/gui-x11/keysym2ucs-x11.c b/gui-x11/keysym2ucs-x11.c new file mode 100644 index 0000000..b96d962 --- /dev/null +++ b/gui-x11/keysym2ucs-x11.c @@ -0,0 +1,857 @@ +/* $XFree86: xc/programs/xterm/keysym2ucs.c,v 1.5 2001/06/18 19:09:26 dickey Exp $ + * This module converts keysym values into the corresponding ISO 10646 + * (UCS, Unicode) values. + * + * The array keysymtab[] contains pairs of X11 keysym values for graphical + * characters and the corresponding Unicode value. The function + * keysym2ucs() maps a keysym onto a Unicode value using a binary search, + * therefore keysymtab[] must remain SORTED by keysym value. + * + * The keysym -> UTF-8 conversion will hopefully one day be provided + * by Xlib via XmbLookupString() and should ideally not have to be + * done in X applications. But we are not there yet. + * + * We allow to represent any UCS character in the range U-00000000 to + * U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff. + * This admittedly does not cover the entire 31-bit space of UCS, but + * it does cover all of the characters up to U-10FFFF, which can be + * represented by UTF-16, and more, and it is very unlikely that higher + * UCS codes will ever be assigned by ISO. So to get Unicode character + * U+ABCD you can directly use keysym 0x0100abcd. + * + * NOTE: The comments in the table below contain the actual character + * encoded in UTF-8, so for viewing and editing best use an editor in + * UTF-8 mode. + * + * Author: Markus G. Kuhn , University of Cambridge, April 2001 + * + * Special thanks to Richard Verhoeven for preparing + * an initial draft of the mapping table. + * + * This software is in the public domain. Share and enjoy! + * + * AUTOMATICALLY GENERATED FILE, DO NOT EDIT !!! (unicode/convmap.pl) + */ + +#ifndef KEYSYM2UCS_INCLUDED + +#include "keysym2ucs.h" +#define VISIBLE /* */ + +#else + +#define VISIBLE static + +#endif + +static struct codepair { + unsigned short keysym; + unsigned short ucs; +} keysymtab[] = { + { 0x01a1, 0x0104 }, /* Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */ + { 0x01a2, 0x02d8 }, /* breve ˘ BREVE */ + { 0x01a3, 0x0141 }, /* Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */ + { 0x01a5, 0x013d }, /* Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */ + { 0x01a6, 0x015a }, /* Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */ + { 0x01a9, 0x0160 }, /* Scaron Š LATIN CAPITAL LETTER S WITH CARON */ + { 0x01aa, 0x015e }, /* Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */ + { 0x01ab, 0x0164 }, /* Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */ + { 0x01ac, 0x0179 }, /* Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */ + { 0x01ae, 0x017d }, /* Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */ + { 0x01af, 0x017b }, /* Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */ + { 0x01b1, 0x0105 }, /* aogonek ą LATIN SMALL LETTER A WITH OGONEK */ + { 0x01b2, 0x02db }, /* ogonek ˛ OGONEK */ + { 0x01b3, 0x0142 }, /* lstroke ł LATIN SMALL LETTER L WITH STROKE */ + { 0x01b5, 0x013e }, /* lcaron ľ LATIN SMALL LETTER L WITH CARON */ + { 0x01b6, 0x015b }, /* sacute ś LATIN SMALL LETTER S WITH ACUTE */ + { 0x01b7, 0x02c7 }, /* caron ˇ CARON */ + { 0x01b9, 0x0161 }, /* scaron š LATIN SMALL LETTER S WITH CARON */ + { 0x01ba, 0x015f }, /* scedilla ş LATIN SMALL LETTER S WITH CEDILLA */ + { 0x01bb, 0x0165 }, /* tcaron ť LATIN SMALL LETTER T WITH CARON */ + { 0x01bc, 0x017a }, /* zacute ź LATIN SMALL LETTER Z WITH ACUTE */ + { 0x01bd, 0x02dd }, /* doubleacute ˝ DOUBLE ACUTE ACCENT */ + { 0x01be, 0x017e }, /* zcaron ž LATIN SMALL LETTER Z WITH CARON */ + { 0x01bf, 0x017c }, /* zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */ + { 0x01c0, 0x0154 }, /* Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */ + { 0x01c3, 0x0102 }, /* Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */ + { 0x01c5, 0x0139 }, /* Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */ + { 0x01c6, 0x0106 }, /* Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */ + { 0x01c8, 0x010c }, /* Ccaron Č LATIN CAPITAL LETTER C WITH CARON */ + { 0x01ca, 0x0118 }, /* Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */ + { 0x01cc, 0x011a }, /* Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */ + { 0x01cf, 0x010e }, /* Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */ + { 0x01d0, 0x0110 }, /* Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */ + { 0x01d1, 0x0143 }, /* Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */ + { 0x01d2, 0x0147 }, /* Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */ + { 0x01d5, 0x0150 }, /* Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ + { 0x01d8, 0x0158 }, /* Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */ + { 0x01d9, 0x016e }, /* Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */ + { 0x01db, 0x0170 }, /* Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ + { 0x01de, 0x0162 }, /* Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */ + { 0x01e0, 0x0155 }, /* racute ŕ LATIN SMALL LETTER R WITH ACUTE */ + { 0x01e3, 0x0103 }, /* abreve ă LATIN SMALL LETTER A WITH BREVE */ + { 0x01e5, 0x013a }, /* lacute ĺ LATIN SMALL LETTER L WITH ACUTE */ + { 0x01e6, 0x0107 }, /* cacute ć LATIN SMALL LETTER C WITH ACUTE */ + { 0x01e8, 0x010d }, /* ccaron č LATIN SMALL LETTER C WITH CARON */ + { 0x01ea, 0x0119 }, /* eogonek ę LATIN SMALL LETTER E WITH OGONEK */ + { 0x01ec, 0x011b }, /* ecaron ě LATIN SMALL LETTER E WITH CARON */ + { 0x01ef, 0x010f }, /* dcaron ď LATIN SMALL LETTER D WITH CARON */ + { 0x01f0, 0x0111 }, /* dstroke đ LATIN SMALL LETTER D WITH STROKE */ + { 0x01f1, 0x0144 }, /* nacute ń LATIN SMALL LETTER N WITH ACUTE */ + { 0x01f2, 0x0148 }, /* ncaron ň LATIN SMALL LETTER N WITH CARON */ + { 0x01f5, 0x0151 }, /* odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */ + { 0x01f8, 0x0159 }, /* rcaron ř LATIN SMALL LETTER R WITH CARON */ + { 0x01f9, 0x016f }, /* uring ů LATIN SMALL LETTER U WITH RING ABOVE */ + { 0x01fb, 0x0171 }, /* udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */ + { 0x01fe, 0x0163 }, /* tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */ + { 0x01ff, 0x02d9 }, /* abovedot ˙ DOT ABOVE */ + { 0x02a1, 0x0126 }, /* Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */ + { 0x02a6, 0x0124 }, /* Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ + { 0x02a9, 0x0130 }, /* Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */ + { 0x02ab, 0x011e }, /* Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */ + { 0x02ac, 0x0134 }, /* Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ + { 0x02b1, 0x0127 }, /* hstroke ħ LATIN SMALL LETTER H WITH STROKE */ + { 0x02b6, 0x0125 }, /* hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */ + { 0x02b9, 0x0131 }, /* idotless ı LATIN SMALL LETTER DOTLESS I */ + { 0x02bb, 0x011f }, /* gbreve ğ LATIN SMALL LETTER G WITH BREVE */ + { 0x02bc, 0x0135 }, /* jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */ + { 0x02c5, 0x010a }, /* Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */ + { 0x02c6, 0x0108 }, /* Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ + { 0x02d5, 0x0120 }, /* Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */ + { 0x02d8, 0x011c }, /* Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ + { 0x02dd, 0x016c }, /* Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */ + { 0x02de, 0x015c }, /* Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ + { 0x02e5, 0x010b }, /* cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */ + { 0x02e6, 0x0109 }, /* ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */ + { 0x02f5, 0x0121 }, /* gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */ + { 0x02f8, 0x011d }, /* gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */ + { 0x02fd, 0x016d }, /* ubreve ŭ LATIN SMALL LETTER U WITH BREVE */ + { 0x02fe, 0x015d }, /* scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */ + { 0x03a2, 0x0138 }, /* kra ĸ LATIN SMALL LETTER KRA */ + { 0x03a3, 0x0156 }, /* Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */ + { 0x03a5, 0x0128 }, /* Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */ + { 0x03a6, 0x013b }, /* Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */ + { 0x03aa, 0x0112 }, /* Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */ + { 0x03ab, 0x0122 }, /* Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */ + { 0x03ac, 0x0166 }, /* Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */ + { 0x03b3, 0x0157 }, /* rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */ + { 0x03b5, 0x0129 }, /* itilde ĩ LATIN SMALL LETTER I WITH TILDE */ + { 0x03b6, 0x013c }, /* lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */ + { 0x03ba, 0x0113 }, /* emacron ē LATIN SMALL LETTER E WITH MACRON */ + { 0x03bb, 0x0123 }, /* gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */ + { 0x03bc, 0x0167 }, /* tslash ŧ LATIN SMALL LETTER T WITH STROKE */ + { 0x03bd, 0x014a }, /* ENG Ŋ LATIN CAPITAL LETTER ENG */ + { 0x03bf, 0x014b }, /* eng ŋ LATIN SMALL LETTER ENG */ + { 0x03c0, 0x0100 }, /* Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */ + { 0x03c7, 0x012e }, /* Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */ + { 0x03cc, 0x0116 }, /* Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */ + { 0x03cf, 0x012a }, /* Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */ + { 0x03d1, 0x0145 }, /* Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */ + { 0x03d2, 0x014c }, /* Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */ + { 0x03d3, 0x0136 }, /* Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */ + { 0x03d9, 0x0172 }, /* Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */ + { 0x03dd, 0x0168 }, /* Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */ + { 0x03de, 0x016a }, /* Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */ + { 0x03e0, 0x0101 }, /* amacron ā LATIN SMALL LETTER A WITH MACRON */ + { 0x03e7, 0x012f }, /* iogonek į LATIN SMALL LETTER I WITH OGONEK */ + { 0x03ec, 0x0117 }, /* eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */ + { 0x03ef, 0x012b }, /* imacron ī LATIN SMALL LETTER I WITH MACRON */ + { 0x03f1, 0x0146 }, /* ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */ + { 0x03f2, 0x014d }, /* omacron ō LATIN SMALL LETTER O WITH MACRON */ + { 0x03f3, 0x0137 }, /* kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */ + { 0x03f9, 0x0173 }, /* uogonek ų LATIN SMALL LETTER U WITH OGONEK */ + { 0x03fd, 0x0169 }, /* utilde ũ LATIN SMALL LETTER U WITH TILDE */ + { 0x03fe, 0x016b }, /* umacron ū LATIN SMALL LETTER U WITH MACRON */ + { 0x047e, 0x203e }, /* overline ‾ OVERLINE */ + { 0x04a1, 0x3002 }, /* kana_fullstop 。 IDEOGRAPHIC FULL STOP */ + { 0x04a2, 0x300c }, /* kana_openingbracket 「 LEFT CORNER BRACKET */ + { 0x04a3, 0x300d }, /* kana_closingbracket 」 RIGHT CORNER BRACKET */ + { 0x04a4, 0x3001 }, /* kana_comma 、 IDEOGRAPHIC COMMA */ + { 0x04a5, 0x30fb }, /* kana_conjunctive ・ KATAKANA MIDDLE DOT */ + { 0x04a6, 0x30f2 }, /* kana_WO ヲ KATAKANA LETTER WO */ + { 0x04a7, 0x30a1 }, /* kana_a ァ KATAKANA LETTER SMALL A */ + { 0x04a8, 0x30a3 }, /* kana_i ィ KATAKANA LETTER SMALL I */ + { 0x04a9, 0x30a5 }, /* kana_u ゥ KATAKANA LETTER SMALL U */ + { 0x04aa, 0x30a7 }, /* kana_e ェ KATAKANA LETTER SMALL E */ + { 0x04ab, 0x30a9 }, /* kana_o ォ KATAKANA LETTER SMALL O */ + { 0x04ac, 0x30e3 }, /* kana_ya ャ KATAKANA LETTER SMALL YA */ + { 0x04ad, 0x30e5 }, /* kana_yu ュ KATAKANA LETTER SMALL YU */ + { 0x04ae, 0x30e7 }, /* kana_yo ョ KATAKANA LETTER SMALL YO */ + { 0x04af, 0x30c3 }, /* kana_tsu ッ KATAKANA LETTER SMALL TU */ + { 0x04b0, 0x30fc }, /* prolongedsound ー KATAKANA-HIRAGANA PROLONGED SOUND MARK */ + { 0x04b1, 0x30a2 }, /* kana_A ア KATAKANA LETTER A */ + { 0x04b2, 0x30a4 }, /* kana_I イ KATAKANA LETTER I */ + { 0x04b3, 0x30a6 }, /* kana_U ウ KATAKANA LETTER U */ + { 0x04b4, 0x30a8 }, /* kana_E エ KATAKANA LETTER E */ + { 0x04b5, 0x30aa }, /* kana_O オ KATAKANA LETTER O */ + { 0x04b6, 0x30ab }, /* kana_KA カ KATAKANA LETTER KA */ + { 0x04b7, 0x30ad }, /* kana_KI キ KATAKANA LETTER KI */ + { 0x04b8, 0x30af }, /* kana_KU ク KATAKANA LETTER KU */ + { 0x04b9, 0x30b1 }, /* kana_KE ケ KATAKANA LETTER KE */ + { 0x04ba, 0x30b3 }, /* kana_KO コ KATAKANA LETTER KO */ + { 0x04bb, 0x30b5 }, /* kana_SA サ KATAKANA LETTER SA */ + { 0x04bc, 0x30b7 }, /* kana_SHI シ KATAKANA LETTER SI */ + { 0x04bd, 0x30b9 }, /* kana_SU ス KATAKANA LETTER SU */ + { 0x04be, 0x30bb }, /* kana_SE セ KATAKANA LETTER SE */ + { 0x04bf, 0x30bd }, /* kana_SO ソ KATAKANA LETTER SO */ + { 0x04c0, 0x30bf }, /* kana_TA タ KATAKANA LETTER TA */ + { 0x04c1, 0x30c1 }, /* kana_CHI チ KATAKANA LETTER TI */ + { 0x04c2, 0x30c4 }, /* kana_TSU ツ KATAKANA LETTER TU */ + { 0x04c3, 0x30c6 }, /* kana_TE テ KATAKANA LETTER TE */ + { 0x04c4, 0x30c8 }, /* kana_TO ト KATAKANA LETTER TO */ + { 0x04c5, 0x30ca }, /* kana_NA ナ KATAKANA LETTER NA */ + { 0x04c6, 0x30cb }, /* kana_NI ニ KATAKANA LETTER NI */ + { 0x04c7, 0x30cc }, /* kana_NU ヌ KATAKANA LETTER NU */ + { 0x04c8, 0x30cd }, /* kana_NE ネ KATAKANA LETTER NE */ + { 0x04c9, 0x30ce }, /* kana_NO ノ KATAKANA LETTER NO */ + { 0x04ca, 0x30cf }, /* kana_HA ハ KATAKANA LETTER HA */ + { 0x04cb, 0x30d2 }, /* kana_HI ヒ KATAKANA LETTER HI */ + { 0x04cc, 0x30d5 }, /* kana_FU フ KATAKANA LETTER HU */ + { 0x04cd, 0x30d8 }, /* kana_HE ヘ KATAKANA LETTER HE */ + { 0x04ce, 0x30db }, /* kana_HO ホ KATAKANA LETTER HO */ + { 0x04cf, 0x30de }, /* kana_MA マ KATAKANA LETTER MA */ + { 0x04d0, 0x30df }, /* kana_MI ミ KATAKANA LETTER MI */ + { 0x04d1, 0x30e0 }, /* kana_MU ム KATAKANA LETTER MU */ + { 0x04d2, 0x30e1 }, /* kana_ME メ KATAKANA LETTER ME */ + { 0x04d3, 0x30e2 }, /* kana_MO モ KATAKANA LETTER MO */ + { 0x04d4, 0x30e4 }, /* kana_YA ヤ KATAKANA LETTER YA */ + { 0x04d5, 0x30e6 }, /* kana_YU ユ KATAKANA LETTER YU */ + { 0x04d6, 0x30e8 }, /* kana_YO ヨ KATAKANA LETTER YO */ + { 0x04d7, 0x30e9 }, /* kana_RA ラ KATAKANA LETTER RA */ + { 0x04d8, 0x30ea }, /* kana_RI リ KATAKANA LETTER RI */ + { 0x04d9, 0x30eb }, /* kana_RU ル KATAKANA LETTER RU */ + { 0x04da, 0x30ec }, /* kana_RE レ KATAKANA LETTER RE */ + { 0x04db, 0x30ed }, /* kana_RO ロ KATAKANA LETTER RO */ + { 0x04dc, 0x30ef }, /* kana_WA ワ KATAKANA LETTER WA */ + { 0x04dd, 0x30f3 }, /* kana_N ン KATAKANA LETTER N */ + { 0x04de, 0x309b }, /* voicedsound ゛ KATAKANA-HIRAGANA VOICED SOUND MARK */ + { 0x04df, 0x309c }, /* semivoicedsound ゜ KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ + { 0x05ac, 0x060c }, /* Arabic_comma ، ARABIC COMMA */ + { 0x05bb, 0x061b }, /* Arabic_semicolon ؛ ARABIC SEMICOLON */ + { 0x05bf, 0x061f }, /* Arabic_question_mark ؟ ARABIC QUESTION MARK */ + { 0x05c1, 0x0621 }, /* Arabic_hamza ء ARABIC LETTER HAMZA */ + { 0x05c2, 0x0622 }, /* Arabic_maddaonalef آ ARABIC LETTER ALEF WITH MADDA ABOVE */ + { 0x05c3, 0x0623 }, /* Arabic_hamzaonalef أ ARABIC LETTER ALEF WITH HAMZA ABOVE */ + { 0x05c4, 0x0624 }, /* Arabic_hamzaonwaw ؤ ARABIC LETTER WAW WITH HAMZA ABOVE */ + { 0x05c5, 0x0625 }, /* Arabic_hamzaunderalef إ ARABIC LETTER ALEF WITH HAMZA BELOW */ + { 0x05c6, 0x0626 }, /* Arabic_hamzaonyeh ئ ARABIC LETTER YEH WITH HAMZA ABOVE */ + { 0x05c7, 0x0627 }, /* Arabic_alef ا ARABIC LETTER ALEF */ + { 0x05c8, 0x0628 }, /* Arabic_beh ب ARABIC LETTER BEH */ + { 0x05c9, 0x0629 }, /* Arabic_tehmarbuta ة ARABIC LETTER TEH MARBUTA */ + { 0x05ca, 0x062a }, /* Arabic_teh ت ARABIC LETTER TEH */ + { 0x05cb, 0x062b }, /* Arabic_theh ث ARABIC LETTER THEH */ + { 0x05cc, 0x062c }, /* Arabic_jeem ج ARABIC LETTER JEEM */ + { 0x05cd, 0x062d }, /* Arabic_hah ح ARABIC LETTER HAH */ + { 0x05ce, 0x062e }, /* Arabic_khah خ ARABIC LETTER KHAH */ + { 0x05cf, 0x062f }, /* Arabic_dal د ARABIC LETTER DAL */ + { 0x05d0, 0x0630 }, /* Arabic_thal ذ ARABIC LETTER THAL */ + { 0x05d1, 0x0631 }, /* Arabic_ra ر ARABIC LETTER REH */ + { 0x05d2, 0x0632 }, /* Arabic_zain ز ARABIC LETTER ZAIN */ + { 0x05d3, 0x0633 }, /* Arabic_seen س ARABIC LETTER SEEN */ + { 0x05d4, 0x0634 }, /* Arabic_sheen ش ARABIC LETTER SHEEN */ + { 0x05d5, 0x0635 }, /* Arabic_sad ص ARABIC LETTER SAD */ + { 0x05d6, 0x0636 }, /* Arabic_dad ض ARABIC LETTER DAD */ + { 0x05d7, 0x0637 }, /* Arabic_tah ط ARABIC LETTER TAH */ + { 0x05d8, 0x0638 }, /* Arabic_zah ظ ARABIC LETTER ZAH */ + { 0x05d9, 0x0639 }, /* Arabic_ain ع ARABIC LETTER AIN */ + { 0x05da, 0x063a }, /* Arabic_ghain غ ARABIC LETTER GHAIN */ + { 0x05e0, 0x0640 }, /* Arabic_tatweel ـ ARABIC TATWEEL */ + { 0x05e1, 0x0641 }, /* Arabic_feh ف ARABIC LETTER FEH */ + { 0x05e2, 0x0642 }, /* Arabic_qaf ق ARABIC LETTER QAF */ + { 0x05e3, 0x0643 }, /* Arabic_kaf ك ARABIC LETTER KAF */ + { 0x05e4, 0x0644 }, /* Arabic_lam ل ARABIC LETTER LAM */ + { 0x05e5, 0x0645 }, /* Arabic_meem م ARABIC LETTER MEEM */ + { 0x05e6, 0x0646 }, /* Arabic_noon ن ARABIC LETTER NOON */ + { 0x05e7, 0x0647 }, /* Arabic_ha ه ARABIC LETTER HEH */ + { 0x05e8, 0x0648 }, /* Arabic_waw و ARABIC LETTER WAW */ + { 0x05e9, 0x0649 }, /* Arabic_alefmaksura ى ARABIC LETTER ALEF MAKSURA */ + { 0x05ea, 0x064a }, /* Arabic_yeh ي ARABIC LETTER YEH */ + { 0x05eb, 0x064b }, /* Arabic_fathatan ً ARABIC FATHATAN */ + { 0x05ec, 0x064c }, /* Arabic_dammatan ٌ ARABIC DAMMATAN */ + { 0x05ed, 0x064d }, /* Arabic_kasratan ٍ ARABIC KASRATAN */ + { 0x05ee, 0x064e }, /* Arabic_fatha َ ARABIC FATHA */ + { 0x05ef, 0x064f }, /* Arabic_damma ُ ARABIC DAMMA */ + { 0x05f0, 0x0650 }, /* Arabic_kasra ِ ARABIC KASRA */ + { 0x05f1, 0x0651 }, /* Arabic_shadda ّ ARABIC SHADDA */ + { 0x05f2, 0x0652 }, /* Arabic_sukun ْ ARABIC SUKUN */ + { 0x06a1, 0x0452 }, /* Serbian_dje ђ CYRILLIC SMALL LETTER DJE */ + { 0x06a2, 0x0453 }, /* Macedonia_gje ѓ CYRILLIC SMALL LETTER GJE */ + { 0x06a3, 0x0451 }, /* Cyrillic_io ё CYRILLIC SMALL LETTER IO */ + { 0x06a4, 0x0454 }, /* Ukrainian_ie є CYRILLIC SMALL LETTER UKRAINIAN IE */ + { 0x06a5, 0x0455 }, /* Macedonia_dse ѕ CYRILLIC SMALL LETTER DZE */ + { 0x06a6, 0x0456 }, /* Ukrainian_i і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ + { 0x06a7, 0x0457 }, /* Ukrainian_yi ї CYRILLIC SMALL LETTER YI */ + { 0x06a8, 0x0458 }, /* Cyrillic_je ј CYRILLIC SMALL LETTER JE */ + { 0x06a9, 0x0459 }, /* Cyrillic_lje љ CYRILLIC SMALL LETTER LJE */ + { 0x06aa, 0x045a }, /* Cyrillic_nje њ CYRILLIC SMALL LETTER NJE */ + { 0x06ab, 0x045b }, /* Serbian_tshe ћ CYRILLIC SMALL LETTER TSHE */ + { 0x06ac, 0x045c }, /* Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */ + { 0x06ae, 0x045e }, /* Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */ + { 0x06af, 0x045f }, /* Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */ + { 0x06b0, 0x2116 }, /* numerosign № NUMERO SIGN */ + { 0x06b1, 0x0402 }, /* Serbian_DJE Ђ CYRILLIC CAPITAL LETTER DJE */ + { 0x06b2, 0x0403 }, /* Macedonia_GJE Ѓ CYRILLIC CAPITAL LETTER GJE */ + { 0x06b3, 0x0401 }, /* Cyrillic_IO Ё CYRILLIC CAPITAL LETTER IO */ + { 0x06b4, 0x0404 }, /* Ukrainian_IE Є CYRILLIC CAPITAL LETTER UKRAINIAN IE */ + { 0x06b5, 0x0405 }, /* Macedonia_DSE Ѕ CYRILLIC CAPITAL LETTER DZE */ + { 0x06b6, 0x0406 }, /* Ukrainian_I І CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ + { 0x06b7, 0x0407 }, /* Ukrainian_YI Ї CYRILLIC CAPITAL LETTER YI */ + { 0x06b8, 0x0408 }, /* Cyrillic_JE Ј CYRILLIC CAPITAL LETTER JE */ + { 0x06b9, 0x0409 }, /* Cyrillic_LJE Љ CYRILLIC CAPITAL LETTER LJE */ + { 0x06ba, 0x040a }, /* Cyrillic_NJE Њ CYRILLIC CAPITAL LETTER NJE */ + { 0x06bb, 0x040b }, /* Serbian_TSHE Ћ CYRILLIC CAPITAL LETTER TSHE */ + { 0x06bc, 0x040c }, /* Macedonia_KJE Ќ CYRILLIC CAPITAL LETTER KJE */ + { 0x06be, 0x040e }, /* Byelorussian_SHORTU Ў CYRILLIC CAPITAL LETTER SHORT U */ + { 0x06bf, 0x040f }, /* Cyrillic_DZHE Џ CYRILLIC CAPITAL LETTER DZHE */ + { 0x06c0, 0x044e }, /* Cyrillic_yu ю CYRILLIC SMALL LETTER YU */ + { 0x06c1, 0x0430 }, /* Cyrillic_a а CYRILLIC SMALL LETTER A */ + { 0x06c2, 0x0431 }, /* Cyrillic_be б CYRILLIC SMALL LETTER BE */ + { 0x06c3, 0x0446 }, /* Cyrillic_tse ц CYRILLIC SMALL LETTER TSE */ + { 0x06c4, 0x0434 }, /* Cyrillic_de д CYRILLIC SMALL LETTER DE */ + { 0x06c5, 0x0435 }, /* Cyrillic_ie е CYRILLIC SMALL LETTER IE */ + { 0x06c6, 0x0444 }, /* Cyrillic_ef ф CYRILLIC SMALL LETTER EF */ + { 0x06c7, 0x0433 }, /* Cyrillic_ghe г CYRILLIC SMALL LETTER GHE */ + { 0x06c8, 0x0445 }, /* Cyrillic_ha х CYRILLIC SMALL LETTER HA */ + { 0x06c9, 0x0438 }, /* Cyrillic_i и CYRILLIC SMALL LETTER I */ + { 0x06ca, 0x0439 }, /* Cyrillic_shorti й CYRILLIC SMALL LETTER SHORT I */ + { 0x06cb, 0x043a }, /* Cyrillic_ka к CYRILLIC SMALL LETTER KA */ + { 0x06cc, 0x043b }, /* Cyrillic_el л CYRILLIC SMALL LETTER EL */ + { 0x06cd, 0x043c }, /* Cyrillic_em м CYRILLIC SMALL LETTER EM */ + { 0x06ce, 0x043d }, /* Cyrillic_en н CYRILLIC SMALL LETTER EN */ + { 0x06cf, 0x043e }, /* Cyrillic_o о CYRILLIC SMALL LETTER O */ + { 0x06d0, 0x043f }, /* Cyrillic_pe п CYRILLIC SMALL LETTER PE */ + { 0x06d1, 0x044f }, /* Cyrillic_ya я CYRILLIC SMALL LETTER YA */ + { 0x06d2, 0x0440 }, /* Cyrillic_er р CYRILLIC SMALL LETTER ER */ + { 0x06d3, 0x0441 }, /* Cyrillic_es с CYRILLIC SMALL LETTER ES */ + { 0x06d4, 0x0442 }, /* Cyrillic_te т CYRILLIC SMALL LETTER TE */ + { 0x06d5, 0x0443 }, /* Cyrillic_u у CYRILLIC SMALL LETTER U */ + { 0x06d6, 0x0436 }, /* Cyrillic_zhe ж CYRILLIC SMALL LETTER ZHE */ + { 0x06d7, 0x0432 }, /* Cyrillic_ve в CYRILLIC SMALL LETTER VE */ + { 0x06d8, 0x044c }, /* Cyrillic_softsign ь CYRILLIC SMALL LETTER SOFT SIGN */ + { 0x06d9, 0x044b }, /* Cyrillic_yeru ы CYRILLIC SMALL LETTER YERU */ + { 0x06da, 0x0437 }, /* Cyrillic_ze з CYRILLIC SMALL LETTER ZE */ + { 0x06db, 0x0448 }, /* Cyrillic_sha ш CYRILLIC SMALL LETTER SHA */ + { 0x06dc, 0x044d }, /* Cyrillic_e э CYRILLIC SMALL LETTER E */ + { 0x06dd, 0x0449 }, /* Cyrillic_shcha щ CYRILLIC SMALL LETTER SHCHA */ + { 0x06de, 0x0447 }, /* Cyrillic_che ч CYRILLIC SMALL LETTER CHE */ + { 0x06df, 0x044a }, /* Cyrillic_hardsign ъ CYRILLIC SMALL LETTER HARD SIGN */ + { 0x06e0, 0x042e }, /* Cyrillic_YU Ю CYRILLIC CAPITAL LETTER YU */ + { 0x06e1, 0x0410 }, /* Cyrillic_A А CYRILLIC CAPITAL LETTER A */ + { 0x06e2, 0x0411 }, /* Cyrillic_BE Б CYRILLIC CAPITAL LETTER BE */ + { 0x06e3, 0x0426 }, /* Cyrillic_TSE Ц CYRILLIC CAPITAL LETTER TSE */ + { 0x06e4, 0x0414 }, /* Cyrillic_DE Д CYRILLIC CAPITAL LETTER DE */ + { 0x06e5, 0x0415 }, /* Cyrillic_IE Е CYRILLIC CAPITAL LETTER IE */ + { 0x06e6, 0x0424 }, /* Cyrillic_EF Ф CYRILLIC CAPITAL LETTER EF */ + { 0x06e7, 0x0413 }, /* Cyrillic_GHE Г CYRILLIC CAPITAL LETTER GHE */ + { 0x06e8, 0x0425 }, /* Cyrillic_HA Х CYRILLIC CAPITAL LETTER HA */ + { 0x06e9, 0x0418 }, /* Cyrillic_I И CYRILLIC CAPITAL LETTER I */ + { 0x06ea, 0x0419 }, /* Cyrillic_SHORTI Й CYRILLIC CAPITAL LETTER SHORT I */ + { 0x06eb, 0x041a }, /* Cyrillic_KA К CYRILLIC CAPITAL LETTER KA */ + { 0x06ec, 0x041b }, /* Cyrillic_EL Л CYRILLIC CAPITAL LETTER EL */ + { 0x06ed, 0x041c }, /* Cyrillic_EM М CYRILLIC CAPITAL LETTER EM */ + { 0x06ee, 0x041d }, /* Cyrillic_EN Н CYRILLIC CAPITAL LETTER EN */ + { 0x06ef, 0x041e }, /* Cyrillic_O О CYRILLIC CAPITAL LETTER O */ + { 0x06f0, 0x041f }, /* Cyrillic_PE П CYRILLIC CAPITAL LETTER PE */ + { 0x06f1, 0x042f }, /* Cyrillic_YA Я CYRILLIC CAPITAL LETTER YA */ + { 0x06f2, 0x0420 }, /* Cyrillic_ER Р CYRILLIC CAPITAL LETTER ER */ + { 0x06f3, 0x0421 }, /* Cyrillic_ES С CYRILLIC CAPITAL LETTER ES */ + { 0x06f4, 0x0422 }, /* Cyrillic_TE Т CYRILLIC CAPITAL LETTER TE */ + { 0x06f5, 0x0423 }, /* Cyrillic_U У CYRILLIC CAPITAL LETTER U */ + { 0x06f6, 0x0416 }, /* Cyrillic_ZHE Ж CYRILLIC CAPITAL LETTER ZHE */ + { 0x06f7, 0x0412 }, /* Cyrillic_VE В CYRILLIC CAPITAL LETTER VE */ + { 0x06f8, 0x042c }, /* Cyrillic_SOFTSIGN Ь CYRILLIC CAPITAL LETTER SOFT SIGN */ + { 0x06f9, 0x042b }, /* Cyrillic_YERU Ы CYRILLIC CAPITAL LETTER YERU */ + { 0x06fa, 0x0417 }, /* Cyrillic_ZE З CYRILLIC CAPITAL LETTER ZE */ + { 0x06fb, 0x0428 }, /* Cyrillic_SHA Ш CYRILLIC CAPITAL LETTER SHA */ + { 0x06fc, 0x042d }, /* Cyrillic_E Э CYRILLIC CAPITAL LETTER E */ + { 0x06fd, 0x0429 }, /* Cyrillic_SHCHA Щ CYRILLIC CAPITAL LETTER SHCHA */ + { 0x06fe, 0x0427 }, /* Cyrillic_CHE Ч CYRILLIC CAPITAL LETTER CHE */ + { 0x06ff, 0x042a }, /* Cyrillic_HARDSIGN Ъ CYRILLIC CAPITAL LETTER HARD SIGN */ + { 0x07a1, 0x0386 }, /* Greek_ALPHAaccent Ά GREEK CAPITAL LETTER ALPHA WITH TONOS */ + { 0x07a2, 0x0388 }, /* Greek_EPSILONaccent Έ GREEK CAPITAL LETTER EPSILON WITH TONOS */ + { 0x07a3, 0x0389 }, /* Greek_ETAaccent Ή GREEK CAPITAL LETTER ETA WITH TONOS */ + { 0x07a4, 0x038a }, /* Greek_IOTAaccent Ί GREEK CAPITAL LETTER IOTA WITH TONOS */ + { 0x07a5, 0x03aa }, /* Greek_IOTAdiaeresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ + { 0x07a7, 0x038c }, /* Greek_OMICRONaccent Ό GREEK CAPITAL LETTER OMICRON WITH TONOS */ + { 0x07a8, 0x038e }, /* Greek_UPSILONaccent Ύ GREEK CAPITAL LETTER UPSILON WITH TONOS */ + { 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ + { 0x07ab, 0x038f }, /* Greek_OMEGAaccent Ώ GREEK CAPITAL LETTER OMEGA WITH TONOS */ + { 0x07ae, 0x0385 }, /* Greek_accentdieresis ΅ GREEK DIALYTIKA TONOS */ + { 0x07af, 0x2015 }, /* Greek_horizbar ― HORIZONTAL BAR */ + { 0x07b1, 0x03ac }, /* Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */ + { 0x07b2, 0x03ad }, /* Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */ + { 0x07b3, 0x03ae }, /* Greek_etaaccent ή GREEK SMALL LETTER ETA WITH TONOS */ + { 0x07b4, 0x03af }, /* Greek_iotaaccent ί GREEK SMALL LETTER IOTA WITH TONOS */ + { 0x07b5, 0x03ca }, /* Greek_iotadieresis ϊ GREEK SMALL LETTER IOTA WITH DIALYTIKA */ + { 0x07b6, 0x0390 }, /* Greek_iotaaccentdieresis ΐ GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ + { 0x07b7, 0x03cc }, /* Greek_omicronaccent ό GREEK SMALL LETTER OMICRON WITH TONOS */ + { 0x07b8, 0x03cd }, /* Greek_upsilonaccent ύ GREEK SMALL LETTER UPSILON WITH TONOS */ + { 0x07b9, 0x03cb }, /* Greek_upsilondieresis ϋ GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ + { 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis ΰ GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ + { 0x07bb, 0x03ce }, /* Greek_omegaaccent ώ GREEK SMALL LETTER OMEGA WITH TONOS */ + { 0x07c1, 0x0391 }, /* Greek_ALPHA Α GREEK CAPITAL LETTER ALPHA */ + { 0x07c2, 0x0392 }, /* Greek_BETA Β GREEK CAPITAL LETTER BETA */ + { 0x07c3, 0x0393 }, /* Greek_GAMMA Γ GREEK CAPITAL LETTER GAMMA */ + { 0x07c4, 0x0394 }, /* Greek_DELTA Δ GREEK CAPITAL LETTER DELTA */ + { 0x07c5, 0x0395 }, /* Greek_EPSILON Ε GREEK CAPITAL LETTER EPSILON */ + { 0x07c6, 0x0396 }, /* Greek_ZETA Ζ GREEK CAPITAL LETTER ZETA */ + { 0x07c7, 0x0397 }, /* Greek_ETA Η GREEK CAPITAL LETTER ETA */ + { 0x07c8, 0x0398 }, /* Greek_THETA Θ GREEK CAPITAL LETTER THETA */ + { 0x07c9, 0x0399 }, /* Greek_IOTA Ι GREEK CAPITAL LETTER IOTA */ + { 0x07ca, 0x039a }, /* Greek_KAPPA Κ GREEK CAPITAL LETTER KAPPA */ + { 0x07cb, 0x039b }, /* Greek_LAMBDA Λ GREEK CAPITAL LETTER LAMDA */ + { 0x07cc, 0x039c }, /* Greek_MU Μ GREEK CAPITAL LETTER MU */ + { 0x07cd, 0x039d }, /* Greek_NU Ν GREEK CAPITAL LETTER NU */ + { 0x07ce, 0x039e }, /* Greek_XI Ξ GREEK CAPITAL LETTER XI */ + { 0x07cf, 0x039f }, /* Greek_OMICRON Ο GREEK CAPITAL LETTER OMICRON */ + { 0x07d0, 0x03a0 }, /* Greek_PI Π GREEK CAPITAL LETTER PI */ + { 0x07d1, 0x03a1 }, /* Greek_RHO Ρ GREEK CAPITAL LETTER RHO */ + { 0x07d2, 0x03a3 }, /* Greek_SIGMA Σ GREEK CAPITAL LETTER SIGMA */ + { 0x07d4, 0x03a4 }, /* Greek_TAU Τ GREEK CAPITAL LETTER TAU */ + { 0x07d5, 0x03a5 }, /* Greek_UPSILON Υ GREEK CAPITAL LETTER UPSILON */ + { 0x07d6, 0x03a6 }, /* Greek_PHI Φ GREEK CAPITAL LETTER PHI */ + { 0x07d7, 0x03a7 }, /* Greek_CHI Χ GREEK CAPITAL LETTER CHI */ + { 0x07d8, 0x03a8 }, /* Greek_PSI Ψ GREEK CAPITAL LETTER PSI */ + { 0x07d9, 0x03a9 }, /* Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */ + { 0x07e1, 0x03b1 }, /* Greek_alpha α GREEK SMALL LETTER ALPHA */ + { 0x07e2, 0x03b2 }, /* Greek_beta β GREEK SMALL LETTER BETA */ + { 0x07e3, 0x03b3 }, /* Greek_gamma γ GREEK SMALL LETTER GAMMA */ + { 0x07e4, 0x03b4 }, /* Greek_delta δ GREEK SMALL LETTER DELTA */ + { 0x07e5, 0x03b5 }, /* Greek_epsilon ε GREEK SMALL LETTER EPSILON */ + { 0x07e6, 0x03b6 }, /* Greek_zeta ζ GREEK SMALL LETTER ZETA */ + { 0x07e7, 0x03b7 }, /* Greek_eta η GREEK SMALL LETTER ETA */ + { 0x07e8, 0x03b8 }, /* Greek_theta θ GREEK SMALL LETTER THETA */ + { 0x07e9, 0x03b9 }, /* Greek_iota ι GREEK SMALL LETTER IOTA */ + { 0x07ea, 0x03ba }, /* Greek_kappa κ GREEK SMALL LETTER KAPPA */ + { 0x07eb, 0x03bb }, /* Greek_lambda λ GREEK SMALL LETTER LAMDA */ + { 0x07ec, 0x03bc }, /* Greek_mu μ GREEK SMALL LETTER MU */ + { 0x07ed, 0x03bd }, /* Greek_nu ν GREEK SMALL LETTER NU */ + { 0x07ee, 0x03be }, /* Greek_xi ξ GREEK SMALL LETTER XI */ + { 0x07ef, 0x03bf }, /* Greek_omicron ο GREEK SMALL LETTER OMICRON */ + { 0x07f0, 0x03c0 }, /* Greek_pi π GREEK SMALL LETTER PI */ + { 0x07f1, 0x03c1 }, /* Greek_rho ρ GREEK SMALL LETTER RHO */ + { 0x07f2, 0x03c3 }, /* Greek_sigma σ GREEK SMALL LETTER SIGMA */ + { 0x07f3, 0x03c2 }, /* Greek_finalsmallsigma ς GREEK SMALL LETTER FINAL SIGMA */ + { 0x07f4, 0x03c4 }, /* Greek_tau τ GREEK SMALL LETTER TAU */ + { 0x07f5, 0x03c5 }, /* Greek_upsilon υ GREEK SMALL LETTER UPSILON */ + { 0x07f6, 0x03c6 }, /* Greek_phi φ GREEK SMALL LETTER PHI */ + { 0x07f7, 0x03c7 }, /* Greek_chi χ GREEK SMALL LETTER CHI */ + { 0x07f8, 0x03c8 }, /* Greek_psi ψ GREEK SMALL LETTER PSI */ + { 0x07f9, 0x03c9 }, /* Greek_omega ω GREEK SMALL LETTER OMEGA */ + { 0x08a1, 0x23b7 }, /* leftradical ⎷ ??? */ + { 0x08a2, 0x250c }, /* topleftradical ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ + { 0x08a3, 0x2500 }, /* horizconnector ─ BOX DRAWINGS LIGHT HORIZONTAL */ + { 0x08a4, 0x2320 }, /* topintegral ⌠ TOP HALF INTEGRAL */ + { 0x08a5, 0x2321 }, /* botintegral ⌡ BOTTOM HALF INTEGRAL */ + { 0x08a6, 0x2502 }, /* vertconnector │ BOX DRAWINGS LIGHT VERTICAL */ + { 0x08a7, 0x23a1 }, /* topleftsqbracket ⎡ ??? */ + { 0x08a8, 0x23a3 }, /* botleftsqbracket ⎣ ??? */ + { 0x08a9, 0x23a4 }, /* toprightsqbracket ⎤ ??? */ + { 0x08aa, 0x23a6 }, /* botrightsqbracket ⎦ ??? */ + { 0x08ab, 0x239b }, /* topleftparens ⎛ ??? */ + { 0x08ac, 0x239d }, /* botleftparens ⎝ ??? */ + { 0x08ad, 0x239e }, /* toprightparens ⎞ ??? */ + { 0x08ae, 0x23a0 }, /* botrightparens ⎠ ??? */ + { 0x08af, 0x23a8 }, /* leftmiddlecurlybrace ⎨ ??? */ + { 0x08b0, 0x23ac }, /* rightmiddlecurlybrace ⎬ ??? */ +/* 0x08b1 topleftsummation ? ??? */ +/* 0x08b2 botleftsummation ? ??? */ +/* 0x08b3 topvertsummationconnector ? ??? */ +/* 0x08b4 botvertsummationconnector ? ??? */ +/* 0x08b5 toprightsummation ? ??? */ +/* 0x08b6 botrightsummation ? ??? */ +/* 0x08b7 rightmiddlesummation ? ??? */ + { 0x08bc, 0x2264 }, /* lessthanequal ≤ LESS-THAN OR EQUAL TO */ + { 0x08bd, 0x2260 }, /* notequal ≠ NOT EQUAL TO */ + { 0x08be, 0x2265 }, /* greaterthanequal ≥ GREATER-THAN OR EQUAL TO */ + { 0x08bf, 0x222b }, /* integral ∫ INTEGRAL */ + { 0x08c0, 0x2234 }, /* therefore ∴ THEREFORE */ + { 0x08c1, 0x221d }, /* variation ∝ PROPORTIONAL TO */ + { 0x08c2, 0x221e }, /* infinity ∞ INFINITY */ + { 0x08c5, 0x2207 }, /* nabla ∇ NABLA */ + { 0x08c8, 0x223c }, /* approximate ∼ TILDE OPERATOR */ + { 0x08c9, 0x2243 }, /* similarequal ≃ ASYMPTOTICALLY EQUAL TO */ + { 0x08cd, 0x21d4 }, /* ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */ + { 0x08ce, 0x21d2 }, /* implies ⇒ RIGHTWARDS DOUBLE ARROW */ + { 0x08cf, 0x2261 }, /* identical ≡ IDENTICAL TO */ + { 0x08d6, 0x221a }, /* radical √ SQUARE ROOT */ + { 0x08da, 0x2282 }, /* includedin ⊂ SUBSET OF */ + { 0x08db, 0x2283 }, /* includes ⊃ SUPERSET OF */ + { 0x08dc, 0x2229 }, /* intersection ∩ INTERSECTION */ + { 0x08dd, 0x222a }, /* union ∪ UNION */ + { 0x08de, 0x2227 }, /* logicaland ∧ LOGICAL AND */ + { 0x08df, 0x2228 }, /* logicalor ∨ LOGICAL OR */ + { 0x08ef, 0x2202 }, /* partialderivative ∂ PARTIAL DIFFERENTIAL */ + { 0x08f6, 0x0192 }, /* function ƒ LATIN SMALL LETTER F WITH HOOK */ + { 0x08fb, 0x2190 }, /* leftarrow ← LEFTWARDS ARROW */ + { 0x08fc, 0x2191 }, /* uparrow ↑ UPWARDS ARROW */ + { 0x08fd, 0x2192 }, /* rightarrow → RIGHTWARDS ARROW */ + { 0x08fe, 0x2193 }, /* downarrow ↓ DOWNWARDS ARROW */ +/* 0x09df blank ? ??? */ + { 0x09e0, 0x25c6 }, /* soliddiamond ◆ BLACK DIAMOND */ + { 0x09e1, 0x2592 }, /* checkerboard ▒ MEDIUM SHADE */ + { 0x09e2, 0x2409 }, /* ht ␉ SYMBOL FOR HORIZONTAL TABULATION */ + { 0x09e3, 0x240c }, /* ff ␌ SYMBOL FOR FORM FEED */ + { 0x09e4, 0x240d }, /* cr ␍ SYMBOL FOR CARRIAGE RETURN */ + { 0x09e5, 0x240a }, /* lf ␊ SYMBOL FOR LINE FEED */ + { 0x09e8, 0x2424 }, /* nl ␤ SYMBOL FOR NEWLINE */ + { 0x09e9, 0x240b }, /* vt ␋ SYMBOL FOR VERTICAL TABULATION */ + { 0x09ea, 0x2518 }, /* lowrightcorner ┘ BOX DRAWINGS LIGHT UP AND LEFT */ + { 0x09eb, 0x2510 }, /* uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */ + { 0x09ec, 0x250c }, /* upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ + { 0x09ed, 0x2514 }, /* lowleftcorner └ BOX DRAWINGS LIGHT UP AND RIGHT */ + { 0x09ee, 0x253c }, /* crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ + { 0x09ef, 0x23ba }, /* horizlinescan1 ⎺ HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */ + { 0x09f0, 0x23bb }, /* horizlinescan3 ⎻ HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */ + { 0x09f1, 0x2500 }, /* horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */ + { 0x09f2, 0x23bc }, /* horizlinescan7 ⎼ HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */ + { 0x09f3, 0x23bd }, /* horizlinescan9 ⎽ HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */ + { 0x09f4, 0x251c }, /* leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ + { 0x09f5, 0x2524 }, /* rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */ + { 0x09f6, 0x2534 }, /* bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */ + { 0x09f7, 0x252c }, /* topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ + { 0x09f8, 0x2502 }, /* vertbar │ BOX DRAWINGS LIGHT VERTICAL */ + { 0x0aa1, 0x2003 }, /* emspace   EM SPACE */ + { 0x0aa2, 0x2002 }, /* enspace   EN SPACE */ + { 0x0aa3, 0x2004 }, /* em3space   THREE-PER-EM SPACE */ + { 0x0aa4, 0x2005 }, /* em4space   FOUR-PER-EM SPACE */ + { 0x0aa5, 0x2007 }, /* digitspace   FIGURE SPACE */ + { 0x0aa6, 0x2008 }, /* punctspace   PUNCTUATION SPACE */ + { 0x0aa7, 0x2009 }, /* thinspace   THIN SPACE */ + { 0x0aa8, 0x200a }, /* hairspace   HAIR SPACE */ + { 0x0aa9, 0x2014 }, /* emdash — EM DASH */ + { 0x0aaa, 0x2013 }, /* endash – EN DASH */ +/* 0x0aac signifblank ? ??? */ + { 0x0aae, 0x2026 }, /* ellipsis … HORIZONTAL ELLIPSIS */ + { 0x0aaf, 0x2025 }, /* doubbaselinedot ‥ TWO DOT LEADER */ + { 0x0ab0, 0x2153 }, /* onethird ⅓ VULGAR FRACTION ONE THIRD */ + { 0x0ab1, 0x2154 }, /* twothirds ⅔ VULGAR FRACTION TWO THIRDS */ + { 0x0ab2, 0x2155 }, /* onefifth ⅕ VULGAR FRACTION ONE FIFTH */ + { 0x0ab3, 0x2156 }, /* twofifths ⅖ VULGAR FRACTION TWO FIFTHS */ + { 0x0ab4, 0x2157 }, /* threefifths ⅗ VULGAR FRACTION THREE FIFTHS */ + { 0x0ab5, 0x2158 }, /* fourfifths ⅘ VULGAR FRACTION FOUR FIFTHS */ + { 0x0ab6, 0x2159 }, /* onesixth ⅙ VULGAR FRACTION ONE SIXTH */ + { 0x0ab7, 0x215a }, /* fivesixths ⅚ VULGAR FRACTION FIVE SIXTHS */ + { 0x0ab8, 0x2105 }, /* careof ℅ CARE OF */ + { 0x0abb, 0x2012 }, /* figdash ‒ FIGURE DASH */ + { 0x0abc, 0x2329 }, /* leftanglebracket 〈 LEFT-POINTING ANGLE BRACKET */ +/* 0x0abd decimalpoint ? ??? */ + { 0x0abe, 0x232a }, /* rightanglebracket 〉 RIGHT-POINTING ANGLE BRACKET */ +/* 0x0abf marker ? ??? */ + { 0x0ac3, 0x215b }, /* oneeighth ⅛ VULGAR FRACTION ONE EIGHTH */ + { 0x0ac4, 0x215c }, /* threeeighths ⅜ VULGAR FRACTION THREE EIGHTHS */ + { 0x0ac5, 0x215d }, /* fiveeighths ⅝ VULGAR FRACTION FIVE EIGHTHS */ + { 0x0ac6, 0x215e }, /* seveneighths ⅞ VULGAR FRACTION SEVEN EIGHTHS */ + { 0x0ac9, 0x2122 }, /* trademark ™ TRADE MARK SIGN */ + { 0x0aca, 0x2613 }, /* signaturemark ☓ SALTIRE */ +/* 0x0acb trademarkincircle ? ??? */ + { 0x0acc, 0x25c1 }, /* leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */ + { 0x0acd, 0x25b7 }, /* rightopentriangle ▷ WHITE RIGHT-POINTING TRIANGLE */ + { 0x0ace, 0x25cb }, /* emopencircle ○ WHITE CIRCLE */ + { 0x0acf, 0x25af }, /* emopenrectangle ▯ WHITE VERTICAL RECTANGLE */ + { 0x0ad0, 0x2018 }, /* leftsinglequotemark ‘ LEFT SINGLE QUOTATION MARK */ + { 0x0ad1, 0x2019 }, /* rightsinglequotemark ’ RIGHT SINGLE QUOTATION MARK */ + { 0x0ad2, 0x201c }, /* leftdoublequotemark “ LEFT DOUBLE QUOTATION MARK */ + { 0x0ad3, 0x201d }, /* rightdoublequotemark ” RIGHT DOUBLE QUOTATION MARK */ + { 0x0ad4, 0x211e }, /* prescription ℞ PRESCRIPTION TAKE */ + { 0x0ad6, 0x2032 }, /* minutes ′ PRIME */ + { 0x0ad7, 0x2033 }, /* seconds ″ DOUBLE PRIME */ + { 0x0ad9, 0x271d }, /* latincross ✝ LATIN CROSS */ +/* 0x0ada hexagram ? ??? */ + { 0x0adb, 0x25ac }, /* filledrectbullet ▬ BLACK RECTANGLE */ + { 0x0adc, 0x25c0 }, /* filledlefttribullet ◀ BLACK LEFT-POINTING TRIANGLE */ + { 0x0add, 0x25b6 }, /* filledrighttribullet ▶ BLACK RIGHT-POINTING TRIANGLE */ + { 0x0ade, 0x25cf }, /* emfilledcircle ● BLACK CIRCLE */ + { 0x0adf, 0x25ae }, /* emfilledrect ▮ BLACK VERTICAL RECTANGLE */ + { 0x0ae0, 0x25e6 }, /* enopencircbullet ◦ WHITE BULLET */ + { 0x0ae1, 0x25ab }, /* enopensquarebullet ▫ WHITE SMALL SQUARE */ + { 0x0ae2, 0x25ad }, /* openrectbullet ▭ WHITE RECTANGLE */ + { 0x0ae3, 0x25b3 }, /* opentribulletup △ WHITE UP-POINTING TRIANGLE */ + { 0x0ae4, 0x25bd }, /* opentribulletdown ▽ WHITE DOWN-POINTING TRIANGLE */ + { 0x0ae5, 0x2606 }, /* openstar ☆ WHITE STAR */ + { 0x0ae6, 0x2022 }, /* enfilledcircbullet • BULLET */ + { 0x0ae7, 0x25aa }, /* enfilledsqbullet ▪ BLACK SMALL SQUARE */ + { 0x0ae8, 0x25b2 }, /* filledtribulletup ▲ BLACK UP-POINTING TRIANGLE */ + { 0x0ae9, 0x25bc }, /* filledtribulletdown ▼ BLACK DOWN-POINTING TRIANGLE */ + { 0x0aea, 0x261c }, /* leftpointer ☜ WHITE LEFT POINTING INDEX */ + { 0x0aeb, 0x261e }, /* rightpointer ☞ WHITE RIGHT POINTING INDEX */ + { 0x0aec, 0x2663 }, /* club ♣ BLACK CLUB SUIT */ + { 0x0aed, 0x2666 }, /* diamond ♦ BLACK DIAMOND SUIT */ + { 0x0aee, 0x2665 }, /* heart ♥ BLACK HEART SUIT */ + { 0x0af0, 0x2720 }, /* maltesecross ✠ MALTESE CROSS */ + { 0x0af1, 0x2020 }, /* dagger † DAGGER */ + { 0x0af2, 0x2021 }, /* doubledagger ‡ DOUBLE DAGGER */ + { 0x0af3, 0x2713 }, /* checkmark ✓ CHECK MARK */ + { 0x0af4, 0x2717 }, /* ballotcross ✗ BALLOT X */ + { 0x0af5, 0x266f }, /* musicalsharp ♯ MUSIC SHARP SIGN */ + { 0x0af6, 0x266d }, /* musicalflat ♭ MUSIC FLAT SIGN */ + { 0x0af7, 0x2642 }, /* malesymbol ♂ MALE SIGN */ + { 0x0af8, 0x2640 }, /* femalesymbol ♀ FEMALE SIGN */ + { 0x0af9, 0x260e }, /* telephone ☎ BLACK TELEPHONE */ + { 0x0afa, 0x2315 }, /* telephonerecorder ⌕ TELEPHONE RECORDER */ + { 0x0afb, 0x2117 }, /* phonographcopyright ℗ SOUND RECORDING COPYRIGHT */ + { 0x0afc, 0x2038 }, /* caret ‸ CARET */ + { 0x0afd, 0x201a }, /* singlelowquotemark ‚ SINGLE LOW-9 QUOTATION MARK */ + { 0x0afe, 0x201e }, /* doublelowquotemark „ DOUBLE LOW-9 QUOTATION MARK */ +/* 0x0aff cursor ? ??? */ + { 0x0ba3, 0x003c }, /* leftcaret < LESS-THAN SIGN */ + { 0x0ba6, 0x003e }, /* rightcaret > GREATER-THAN SIGN */ + { 0x0ba8, 0x2228 }, /* downcaret ∨ LOGICAL OR */ + { 0x0ba9, 0x2227 }, /* upcaret ∧ LOGICAL AND */ + { 0x0bc0, 0x00af }, /* overbar ¯ MACRON */ + { 0x0bc2, 0x22a5 }, /* downtack ⊥ UP TACK */ + { 0x0bc3, 0x2229 }, /* upshoe ∩ INTERSECTION */ + { 0x0bc4, 0x230a }, /* downstile ⌊ LEFT FLOOR */ + { 0x0bc6, 0x005f }, /* underbar _ LOW LINE */ + { 0x0bca, 0x2218 }, /* jot ∘ RING OPERATOR */ + { 0x0bcc, 0x2395 }, /* quad ⎕ APL FUNCTIONAL SYMBOL QUAD */ + { 0x0bce, 0x22a4 }, /* uptack ⊤ DOWN TACK */ + { 0x0bcf, 0x25cb }, /* circle ○ WHITE CIRCLE */ + { 0x0bd3, 0x2308 }, /* upstile ⌈ LEFT CEILING */ + { 0x0bd6, 0x222a }, /* downshoe ∪ UNION */ + { 0x0bd8, 0x2283 }, /* rightshoe ⊃ SUPERSET OF */ + { 0x0bda, 0x2282 }, /* leftshoe ⊂ SUBSET OF */ + { 0x0bdc, 0x22a2 }, /* lefttack ⊢ RIGHT TACK */ + { 0x0bfc, 0x22a3 }, /* righttack ⊣ LEFT TACK */ + { 0x0cdf, 0x2017 }, /* hebrew_doublelowline ‗ DOUBLE LOW LINE */ + { 0x0ce0, 0x05d0 }, /* hebrew_aleph א HEBREW LETTER ALEF */ + { 0x0ce1, 0x05d1 }, /* hebrew_bet ב HEBREW LETTER BET */ + { 0x0ce2, 0x05d2 }, /* hebrew_gimel ג HEBREW LETTER GIMEL */ + { 0x0ce3, 0x05d3 }, /* hebrew_dalet ד HEBREW LETTER DALET */ + { 0x0ce4, 0x05d4 }, /* hebrew_he ה HEBREW LETTER HE */ + { 0x0ce5, 0x05d5 }, /* hebrew_waw ו HEBREW LETTER VAV */ + { 0x0ce6, 0x05d6 }, /* hebrew_zain ז HEBREW LETTER ZAYIN */ + { 0x0ce7, 0x05d7 }, /* hebrew_chet ח HEBREW LETTER HET */ + { 0x0ce8, 0x05d8 }, /* hebrew_tet ט HEBREW LETTER TET */ + { 0x0ce9, 0x05d9 }, /* hebrew_yod י HEBREW LETTER YOD */ + { 0x0cea, 0x05da }, /* hebrew_finalkaph ך HEBREW LETTER FINAL KAF */ + { 0x0ceb, 0x05db }, /* hebrew_kaph כ HEBREW LETTER KAF */ + { 0x0cec, 0x05dc }, /* hebrew_lamed ל HEBREW LETTER LAMED */ + { 0x0ced, 0x05dd }, /* hebrew_finalmem ם HEBREW LETTER FINAL MEM */ + { 0x0cee, 0x05de }, /* hebrew_mem מ HEBREW LETTER MEM */ + { 0x0cef, 0x05df }, /* hebrew_finalnun ן HEBREW LETTER FINAL NUN */ + { 0x0cf0, 0x05e0 }, /* hebrew_nun נ HEBREW LETTER NUN */ + { 0x0cf1, 0x05e1 }, /* hebrew_samech ס HEBREW LETTER SAMEKH */ + { 0x0cf2, 0x05e2 }, /* hebrew_ayin ע HEBREW LETTER AYIN */ + { 0x0cf3, 0x05e3 }, /* hebrew_finalpe ף HEBREW LETTER FINAL PE */ + { 0x0cf4, 0x05e4 }, /* hebrew_pe פ HEBREW LETTER PE */ + { 0x0cf5, 0x05e5 }, /* hebrew_finalzade ץ HEBREW LETTER FINAL TSADI */ + { 0x0cf6, 0x05e6 }, /* hebrew_zade צ HEBREW LETTER TSADI */ + { 0x0cf7, 0x05e7 }, /* hebrew_qoph ק HEBREW LETTER QOF */ + { 0x0cf8, 0x05e8 }, /* hebrew_resh ר HEBREW LETTER RESH */ + { 0x0cf9, 0x05e9 }, /* hebrew_shin ש HEBREW LETTER SHIN */ + { 0x0cfa, 0x05ea }, /* hebrew_taw ת HEBREW LETTER TAV */ + { 0x0da1, 0x0e01 }, /* Thai_kokai ก THAI CHARACTER KO KAI */ + { 0x0da2, 0x0e02 }, /* Thai_khokhai ข THAI CHARACTER KHO KHAI */ + { 0x0da3, 0x0e03 }, /* Thai_khokhuat ฃ THAI CHARACTER KHO KHUAT */ + { 0x0da4, 0x0e04 }, /* Thai_khokhwai ค THAI CHARACTER KHO KHWAI */ + { 0x0da5, 0x0e05 }, /* Thai_khokhon ฅ THAI CHARACTER KHO KHON */ + { 0x0da6, 0x0e06 }, /* Thai_khorakhang ฆ THAI CHARACTER KHO RAKHANG */ + { 0x0da7, 0x0e07 }, /* Thai_ngongu ง THAI CHARACTER NGO NGU */ + { 0x0da8, 0x0e08 }, /* Thai_chochan จ THAI CHARACTER CHO CHAN */ + { 0x0da9, 0x0e09 }, /* Thai_choching ฉ THAI CHARACTER CHO CHING */ + { 0x0daa, 0x0e0a }, /* Thai_chochang ช THAI CHARACTER CHO CHANG */ + { 0x0dab, 0x0e0b }, /* Thai_soso ซ THAI CHARACTER SO SO */ + { 0x0dac, 0x0e0c }, /* Thai_chochoe ฌ THAI CHARACTER CHO CHOE */ + { 0x0dad, 0x0e0d }, /* Thai_yoying ญ THAI CHARACTER YO YING */ + { 0x0dae, 0x0e0e }, /* Thai_dochada ฎ THAI CHARACTER DO CHADA */ + { 0x0daf, 0x0e0f }, /* Thai_topatak ฏ THAI CHARACTER TO PATAK */ + { 0x0db0, 0x0e10 }, /* Thai_thothan ฐ THAI CHARACTER THO THAN */ + { 0x0db1, 0x0e11 }, /* Thai_thonangmontho ฑ THAI CHARACTER THO NANGMONTHO */ + { 0x0db2, 0x0e12 }, /* Thai_thophuthao ฒ THAI CHARACTER THO PHUTHAO */ + { 0x0db3, 0x0e13 }, /* Thai_nonen ณ THAI CHARACTER NO NEN */ + { 0x0db4, 0x0e14 }, /* Thai_dodek ด THAI CHARACTER DO DEK */ + { 0x0db5, 0x0e15 }, /* Thai_totao ต THAI CHARACTER TO TAO */ + { 0x0db6, 0x0e16 }, /* Thai_thothung ถ THAI CHARACTER THO THUNG */ + { 0x0db7, 0x0e17 }, /* Thai_thothahan ท THAI CHARACTER THO THAHAN */ + { 0x0db8, 0x0e18 }, /* Thai_thothong ธ THAI CHARACTER THO THONG */ + { 0x0db9, 0x0e19 }, /* Thai_nonu น THAI CHARACTER NO NU */ + { 0x0dba, 0x0e1a }, /* Thai_bobaimai บ THAI CHARACTER BO BAIMAI */ + { 0x0dbb, 0x0e1b }, /* Thai_popla ป THAI CHARACTER PO PLA */ + { 0x0dbc, 0x0e1c }, /* Thai_phophung ผ THAI CHARACTER PHO PHUNG */ + { 0x0dbd, 0x0e1d }, /* Thai_fofa ฝ THAI CHARACTER FO FA */ + { 0x0dbe, 0x0e1e }, /* Thai_phophan พ THAI CHARACTER PHO PHAN */ + { 0x0dbf, 0x0e1f }, /* Thai_fofan ฟ THAI CHARACTER FO FAN */ + { 0x0dc0, 0x0e20 }, /* Thai_phosamphao ภ THAI CHARACTER PHO SAMPHAO */ + { 0x0dc1, 0x0e21 }, /* Thai_moma ม THAI CHARACTER MO MA */ + { 0x0dc2, 0x0e22 }, /* Thai_yoyak ย THAI CHARACTER YO YAK */ + { 0x0dc3, 0x0e23 }, /* Thai_rorua ร THAI CHARACTER RO RUA */ + { 0x0dc4, 0x0e24 }, /* Thai_ru ฤ THAI CHARACTER RU */ + { 0x0dc5, 0x0e25 }, /* Thai_loling ล THAI CHARACTER LO LING */ + { 0x0dc6, 0x0e26 }, /* Thai_lu ฦ THAI CHARACTER LU */ + { 0x0dc7, 0x0e27 }, /* Thai_wowaen ว THAI CHARACTER WO WAEN */ + { 0x0dc8, 0x0e28 }, /* Thai_sosala ศ THAI CHARACTER SO SALA */ + { 0x0dc9, 0x0e29 }, /* Thai_sorusi ษ THAI CHARACTER SO RUSI */ + { 0x0dca, 0x0e2a }, /* Thai_sosua ส THAI CHARACTER SO SUA */ + { 0x0dcb, 0x0e2b }, /* Thai_hohip ห THAI CHARACTER HO HIP */ + { 0x0dcc, 0x0e2c }, /* Thai_lochula ฬ THAI CHARACTER LO CHULA */ + { 0x0dcd, 0x0e2d }, /* Thai_oang อ THAI CHARACTER O ANG */ + { 0x0dce, 0x0e2e }, /* Thai_honokhuk ฮ THAI CHARACTER HO NOKHUK */ + { 0x0dcf, 0x0e2f }, /* Thai_paiyannoi ฯ THAI CHARACTER PAIYANNOI */ + { 0x0dd0, 0x0e30 }, /* Thai_saraa ะ THAI CHARACTER SARA A */ + { 0x0dd1, 0x0e31 }, /* Thai_maihanakat ั THAI CHARACTER MAI HAN-AKAT */ + { 0x0dd2, 0x0e32 }, /* Thai_saraaa า THAI CHARACTER SARA AA */ + { 0x0dd3, 0x0e33 }, /* Thai_saraam ำ THAI CHARACTER SARA AM */ + { 0x0dd4, 0x0e34 }, /* Thai_sarai ิ THAI CHARACTER SARA I */ + { 0x0dd5, 0x0e35 }, /* Thai_saraii ี THAI CHARACTER SARA II */ + { 0x0dd6, 0x0e36 }, /* Thai_saraue ึ THAI CHARACTER SARA UE */ + { 0x0dd7, 0x0e37 }, /* Thai_sarauee ื THAI CHARACTER SARA UEE */ + { 0x0dd8, 0x0e38 }, /* Thai_sarau ุ THAI CHARACTER SARA U */ + { 0x0dd9, 0x0e39 }, /* Thai_sarauu ู THAI CHARACTER SARA UU */ + { 0x0dda, 0x0e3a }, /* Thai_phinthu ฺ THAI CHARACTER PHINTHU */ +/* 0x0dde Thai_maihanakat_maitho ? ??? */ + { 0x0ddf, 0x0e3f }, /* Thai_baht ฿ THAI CURRENCY SYMBOL BAHT */ + { 0x0de0, 0x0e40 }, /* Thai_sarae เ THAI CHARACTER SARA E */ + { 0x0de1, 0x0e41 }, /* Thai_saraae แ THAI CHARACTER SARA AE */ + { 0x0de2, 0x0e42 }, /* Thai_sarao โ THAI CHARACTER SARA O */ + { 0x0de3, 0x0e43 }, /* Thai_saraaimaimuan ใ THAI CHARACTER SARA AI MAIMUAN */ + { 0x0de4, 0x0e44 }, /* Thai_saraaimaimalai ไ THAI CHARACTER SARA AI MAIMALAI */ + { 0x0de5, 0x0e45 }, /* Thai_lakkhangyao ๅ THAI CHARACTER LAKKHANGYAO */ + { 0x0de6, 0x0e46 }, /* Thai_maiyamok ๆ THAI CHARACTER MAIYAMOK */ + { 0x0de7, 0x0e47 }, /* Thai_maitaikhu ็ THAI CHARACTER MAITAIKHU */ + { 0x0de8, 0x0e48 }, /* Thai_maiek ่ THAI CHARACTER MAI EK */ + { 0x0de9, 0x0e49 }, /* Thai_maitho ้ THAI CHARACTER MAI THO */ + { 0x0dea, 0x0e4a }, /* Thai_maitri ๊ THAI CHARACTER MAI TRI */ + { 0x0deb, 0x0e4b }, /* Thai_maichattawa ๋ THAI CHARACTER MAI CHATTAWA */ + { 0x0dec, 0x0e4c }, /* Thai_thanthakhat ์ THAI CHARACTER THANTHAKHAT */ + { 0x0ded, 0x0e4d }, /* Thai_nikhahit ํ THAI CHARACTER NIKHAHIT */ + { 0x0df0, 0x0e50 }, /* Thai_leksun ๐ THAI DIGIT ZERO */ + { 0x0df1, 0x0e51 }, /* Thai_leknung ๑ THAI DIGIT ONE */ + { 0x0df2, 0x0e52 }, /* Thai_leksong ๒ THAI DIGIT TWO */ + { 0x0df3, 0x0e53 }, /* Thai_leksam ๓ THAI DIGIT THREE */ + { 0x0df4, 0x0e54 }, /* Thai_leksi ๔ THAI DIGIT FOUR */ + { 0x0df5, 0x0e55 }, /* Thai_lekha ๕ THAI DIGIT FIVE */ + { 0x0df6, 0x0e56 }, /* Thai_lekhok ๖ THAI DIGIT SIX */ + { 0x0df7, 0x0e57 }, /* Thai_lekchet ๗ THAI DIGIT SEVEN */ + { 0x0df8, 0x0e58 }, /* Thai_lekpaet ๘ THAI DIGIT EIGHT */ + { 0x0df9, 0x0e59 }, /* Thai_lekkao ๙ THAI DIGIT NINE */ + { 0x0ea1, 0x3131 }, /* Hangul_Kiyeog ㄱ HANGUL LETTER KIYEOK */ + { 0x0ea2, 0x3132 }, /* Hangul_SsangKiyeog ㄲ HANGUL LETTER SSANGKIYEOK */ + { 0x0ea3, 0x3133 }, /* Hangul_KiyeogSios ㄳ HANGUL LETTER KIYEOK-SIOS */ + { 0x0ea4, 0x3134 }, /* Hangul_Nieun ㄴ HANGUL LETTER NIEUN */ + { 0x0ea5, 0x3135 }, /* Hangul_NieunJieuj ㄵ HANGUL LETTER NIEUN-CIEUC */ + { 0x0ea6, 0x3136 }, /* Hangul_NieunHieuh ㄶ HANGUL LETTER NIEUN-HIEUH */ + { 0x0ea7, 0x3137 }, /* Hangul_Dikeud ㄷ HANGUL LETTER TIKEUT */ + { 0x0ea8, 0x3138 }, /* Hangul_SsangDikeud ㄸ HANGUL LETTER SSANGTIKEUT */ + { 0x0ea9, 0x3139 }, /* Hangul_Rieul ㄹ HANGUL LETTER RIEUL */ + { 0x0eaa, 0x313a }, /* Hangul_RieulKiyeog ㄺ HANGUL LETTER RIEUL-KIYEOK */ + { 0x0eab, 0x313b }, /* Hangul_RieulMieum ㄻ HANGUL LETTER RIEUL-MIEUM */ + { 0x0eac, 0x313c }, /* Hangul_RieulPieub ㄼ HANGUL LETTER RIEUL-PIEUP */ + { 0x0ead, 0x313d }, /* Hangul_RieulSios ㄽ HANGUL LETTER RIEUL-SIOS */ + { 0x0eae, 0x313e }, /* Hangul_RieulTieut ㄾ HANGUL LETTER RIEUL-THIEUTH */ + { 0x0eaf, 0x313f }, /* Hangul_RieulPhieuf ㄿ HANGUL LETTER RIEUL-PHIEUPH */ + { 0x0eb0, 0x3140 }, /* Hangul_RieulHieuh ㅀ HANGUL LETTER RIEUL-HIEUH */ + { 0x0eb1, 0x3141 }, /* Hangul_Mieum ㅁ HANGUL LETTER MIEUM */ + { 0x0eb2, 0x3142 }, /* Hangul_Pieub ㅂ HANGUL LETTER PIEUP */ + { 0x0eb3, 0x3143 }, /* Hangul_SsangPieub ㅃ HANGUL LETTER SSANGPIEUP */ + { 0x0eb4, 0x3144 }, /* Hangul_PieubSios ㅄ HANGUL LETTER PIEUP-SIOS */ + { 0x0eb5, 0x3145 }, /* Hangul_Sios ㅅ HANGUL LETTER SIOS */ + { 0x0eb6, 0x3146 }, /* Hangul_SsangSios ㅆ HANGUL LETTER SSANGSIOS */ + { 0x0eb7, 0x3147 }, /* Hangul_Ieung ㅇ HANGUL LETTER IEUNG */ + { 0x0eb8, 0x3148 }, /* Hangul_Jieuj ㅈ HANGUL LETTER CIEUC */ + { 0x0eb9, 0x3149 }, /* Hangul_SsangJieuj ㅉ HANGUL LETTER SSANGCIEUC */ + { 0x0eba, 0x314a }, /* Hangul_Cieuc ㅊ HANGUL LETTER CHIEUCH */ + { 0x0ebb, 0x314b }, /* Hangul_Khieuq ㅋ HANGUL LETTER KHIEUKH */ + { 0x0ebc, 0x314c }, /* Hangul_Tieut ㅌ HANGUL LETTER THIEUTH */ + { 0x0ebd, 0x314d }, /* Hangul_Phieuf ㅍ HANGUL LETTER PHIEUPH */ + { 0x0ebe, 0x314e }, /* Hangul_Hieuh ㅎ HANGUL LETTER HIEUH */ + { 0x0ebf, 0x314f }, /* Hangul_A ㅏ HANGUL LETTER A */ + { 0x0ec0, 0x3150 }, /* Hangul_AE ㅐ HANGUL LETTER AE */ + { 0x0ec1, 0x3151 }, /* Hangul_YA ㅑ HANGUL LETTER YA */ + { 0x0ec2, 0x3152 }, /* Hangul_YAE ㅒ HANGUL LETTER YAE */ + { 0x0ec3, 0x3153 }, /* Hangul_EO ㅓ HANGUL LETTER EO */ + { 0x0ec4, 0x3154 }, /* Hangul_E ㅔ HANGUL LETTER E */ + { 0x0ec5, 0x3155 }, /* Hangul_YEO ㅕ HANGUL LETTER YEO */ + { 0x0ec6, 0x3156 }, /* Hangul_YE ㅖ HANGUL LETTER YE */ + { 0x0ec7, 0x3157 }, /* Hangul_O ㅗ HANGUL LETTER O */ + { 0x0ec8, 0x3158 }, /* Hangul_WA ㅘ HANGUL LETTER WA */ + { 0x0ec9, 0x3159 }, /* Hangul_WAE ㅙ HANGUL LETTER WAE */ + { 0x0eca, 0x315a }, /* Hangul_OE ㅚ HANGUL LETTER OE */ + { 0x0ecb, 0x315b }, /* Hangul_YO ㅛ HANGUL LETTER YO */ + { 0x0ecc, 0x315c }, /* Hangul_U ㅜ HANGUL LETTER U */ + { 0x0ecd, 0x315d }, /* Hangul_WEO ㅝ HANGUL LETTER WEO */ + { 0x0ece, 0x315e }, /* Hangul_WE ㅞ HANGUL LETTER WE */ + { 0x0ecf, 0x315f }, /* Hangul_WI ㅟ HANGUL LETTER WI */ + { 0x0ed0, 0x3160 }, /* Hangul_YU ㅠ HANGUL LETTER YU */ + { 0x0ed1, 0x3161 }, /* Hangul_EU ㅡ HANGUL LETTER EU */ + { 0x0ed2, 0x3162 }, /* Hangul_YI ㅢ HANGUL LETTER YI */ + { 0x0ed3, 0x3163 }, /* Hangul_I ㅣ HANGUL LETTER I */ + { 0x0ed4, 0x11a8 }, /* Hangul_J_Kiyeog ᆨ HANGUL JONGSEONG KIYEOK */ + { 0x0ed5, 0x11a9 }, /* Hangul_J_SsangKiyeog ᆩ HANGUL JONGSEONG SSANGKIYEOK */ + { 0x0ed6, 0x11aa }, /* Hangul_J_KiyeogSios ᆪ HANGUL JONGSEONG KIYEOK-SIOS */ + { 0x0ed7, 0x11ab }, /* Hangul_J_Nieun ᆫ HANGUL JONGSEONG NIEUN */ + { 0x0ed8, 0x11ac }, /* Hangul_J_NieunJieuj ᆬ HANGUL JONGSEONG NIEUN-CIEUC */ + { 0x0ed9, 0x11ad }, /* Hangul_J_NieunHieuh ᆭ HANGUL JONGSEONG NIEUN-HIEUH */ + { 0x0eda, 0x11ae }, /* Hangul_J_Dikeud ᆮ HANGUL JONGSEONG TIKEUT */ + { 0x0edb, 0x11af }, /* Hangul_J_Rieul ᆯ HANGUL JONGSEONG RIEUL */ + { 0x0edc, 0x11b0 }, /* Hangul_J_RieulKiyeog ᆰ HANGUL JONGSEONG RIEUL-KIYEOK */ + { 0x0edd, 0x11b1 }, /* Hangul_J_RieulMieum ᆱ HANGUL JONGSEONG RIEUL-MIEUM */ + { 0x0ede, 0x11b2 }, /* Hangul_J_RieulPieub ᆲ HANGUL JONGSEONG RIEUL-PIEUP */ + { 0x0edf, 0x11b3 }, /* Hangul_J_RieulSios ᆳ HANGUL JONGSEONG RIEUL-SIOS */ + { 0x0ee0, 0x11b4 }, /* Hangul_J_RieulTieut ᆴ HANGUL JONGSEONG RIEUL-THIEUTH */ + { 0x0ee1, 0x11b5 }, /* Hangul_J_RieulPhieuf ᆵ HANGUL JONGSEONG RIEUL-PHIEUPH */ + { 0x0ee2, 0x11b6 }, /* Hangul_J_RieulHieuh ᆶ HANGUL JONGSEONG RIEUL-HIEUH */ + { 0x0ee3, 0x11b7 }, /* Hangul_J_Mieum ᆷ HANGUL JONGSEONG MIEUM */ + { 0x0ee4, 0x11b8 }, /* Hangul_J_Pieub ᆸ HANGUL JONGSEONG PIEUP */ + { 0x0ee5, 0x11b9 }, /* Hangul_J_PieubSios ᆹ HANGUL JONGSEONG PIEUP-SIOS */ + { 0x0ee6, 0x11ba }, /* Hangul_J_Sios ᆺ HANGUL JONGSEONG SIOS */ + { 0x0ee7, 0x11bb }, /* Hangul_J_SsangSios ᆻ HANGUL JONGSEONG SSANGSIOS */ + { 0x0ee8, 0x11bc }, /* Hangul_J_Ieung ᆼ HANGUL JONGSEONG IEUNG */ + { 0x0ee9, 0x11bd }, /* Hangul_J_Jieuj ᆽ HANGUL JONGSEONG CIEUC */ + { 0x0eea, 0x11be }, /* Hangul_J_Cieuc ᆾ HANGUL JONGSEONG CHIEUCH */ + { 0x0eeb, 0x11bf }, /* Hangul_J_Khieuq ᆿ HANGUL JONGSEONG KHIEUKH */ + { 0x0eec, 0x11c0 }, /* Hangul_J_Tieut ᇀ HANGUL JONGSEONG THIEUTH */ + { 0x0eed, 0x11c1 }, /* Hangul_J_Phieuf ᇁ HANGUL JONGSEONG PHIEUPH */ + { 0x0eee, 0x11c2 }, /* Hangul_J_Hieuh ᇂ HANGUL JONGSEONG HIEUH */ + { 0x0eef, 0x316d }, /* Hangul_RieulYeorinHieuh ㅭ HANGUL LETTER RIEUL-YEORINHIEUH */ + { 0x0ef0, 0x3171 }, /* Hangul_SunkyeongeumMieum ㅱ HANGUL LETTER KAPYEOUNMIEUM */ + { 0x0ef1, 0x3178 }, /* Hangul_SunkyeongeumPieub ㅸ HANGUL LETTER KAPYEOUNPIEUP */ + { 0x0ef2, 0x317f }, /* Hangul_PanSios ㅿ HANGUL LETTER PANSIOS */ + { 0x0ef3, 0x3181 }, /* Hangul_KkogjiDalrinIeung ㆁ HANGUL LETTER YESIEUNG */ + { 0x0ef4, 0x3184 }, /* Hangul_SunkyeongeumPhieuf ㆄ HANGUL LETTER KAPYEOUNPHIEUPH */ + { 0x0ef5, 0x3186 }, /* Hangul_YeorinHieuh ㆆ HANGUL LETTER YEORINHIEUH */ + { 0x0ef6, 0x318d }, /* Hangul_AraeA ㆍ HANGUL LETTER ARAEA */ + { 0x0ef7, 0x318e }, /* Hangul_AraeAE ㆎ HANGUL LETTER ARAEAE */ + { 0x0ef8, 0x11eb }, /* Hangul_J_PanSios ᇫ HANGUL JONGSEONG PANSIOS */ + { 0x0ef9, 0x11f0 }, /* Hangul_J_KkogjiDalrinIeung ᇰ HANGUL JONGSEONG YESIEUNG */ + { 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */ + { 0x0eff, 0x20a9 }, /* Korean_Won ₩ WON SIGN */ + { 0x13a4, 0x20ac }, /* Euro € EURO SIGN */ + { 0x13bc, 0x0152 }, /* OE Œ LATIN CAPITAL LIGATURE OE */ + { 0x13bd, 0x0153 }, /* oe œ LATIN SMALL LIGATURE OE */ + { 0x13be, 0x0178 }, /* Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */ + { 0x20ac, 0x20ac }, /* EuroSign € EURO SIGN */ +}; + +VISIBLE +long keysym2ucs(KeySym keysym) +{ + int min = 0; + int max = sizeof(keysymtab) / sizeof(struct codepair) - 1; + int mid; + + /* first check for Latin-1 characters (1:1 mapping) */ + if ((keysym >= 0x0020 && keysym <= 0x007e) || + (keysym >= 0x00a0 && keysym <= 0x00ff)) + return keysym; + + /* also check for directly encoded 24-bit UCS characters */ + if ((keysym & 0xff000000) == 0x01000000) + return keysym & 0x00ffffff; + + /* binary search in table */ + while (max >= min) { + mid = (min + max) / 2; + if (keysymtab[mid].keysym < keysym) + min = mid + 1; + else if (keysymtab[mid].keysym > keysym) + max = mid - 1; + else { + /* found it */ + return keysymtab[mid].ucs; + } + } + + /* no matching Unicode value found */ + return -1; +} diff --git a/gui-x11/keysym2ucs.h b/gui-x11/keysym2ucs.h new file mode 100644 index 0000000..1f23ac6 --- /dev/null +++ b/gui-x11/keysym2ucs.h @@ -0,0 +1,9 @@ +/* $XFree86: xc/programs/xterm/keysym2ucs.h,v 1.1 1999/06/12 15:37:18 dawes Exp $ */ +/* + * This module converts keysym values into the corresponding ISO 10646-1 + * (UCS, Unicode) values. + */ + +#include + +long keysym2ucs(KeySym keysym); diff --git a/gui-x11/ksym2utf.h b/gui-x11/ksym2utf.h new file mode 100644 index 0000000..cca43bd --- /dev/null +++ b/gui-x11/ksym2utf.h @@ -0,0 +1,754 @@ +static ulong +ksym2utf[] = { + [0x01a1] 0x0104, + [0x01a2] 0x02d8, + [0x01a3] 0x0141, + [0x01a5] 0x013d, + [0x01a6] 0x015a, + [0x01a9] 0x0160, + [0x01aa] 0x015e, + [0x01ab] 0x0164, + [0x01ac] 0x0179, + [0x01ae] 0x017d, + [0x01af] 0x017b, + [0x01b1] 0x0105, + [0x01b2] 0x02db, + [0x01b3] 0x0142, + [0x01b5] 0x013e, + [0x01b6] 0x015b, + [0x01b7] 0x02c7, + [0x01b9] 0x0161, + [0x01ba] 0x015f, + [0x01bb] 0x0165, + [0x01bc] 0x017a, + [0x01bd] 0x02dd, + [0x01be] 0x017e, + [0x01bf] 0x017c, + [0x01c0] 0x0154, + [0x01c3] 0x0102, + [0x01c5] 0x0139, + [0x01c6] 0x0106, + [0x01c8] 0x010c, + [0x01ca] 0x0118, + [0x01cc] 0x011a, + [0x01cf] 0x010e, + [0x01d0] 0x0110, + [0x01d1] 0x0143, + [0x01d2] 0x0147, + [0x01d5] 0x0150, + [0x01d8] 0x0158, + [0x01d9] 0x016e, + [0x01db] 0x0170, + [0x01de] 0x0162, + [0x01e0] 0x0155, + [0x01e3] 0x0103, + [0x01e5] 0x013a, + [0x01e6] 0x0107, + [0x01e8] 0x010d, + [0x01ea] 0x0119, + [0x01ec] 0x011b, + [0x01ef] 0x010f, + [0x01f0] 0x0111, + [0x01f1] 0x0144, + [0x01f2] 0x0148, + [0x01f5] 0x0151, + [0x01f8] 0x0159, + [0x01f9] 0x016f, + [0x01fb] 0x0171, + [0x01fe] 0x0163, + [0x01ff] 0x02d9, + [0x02a1] 0x0126, + [0x02a6] 0x0124, + [0x02a9] 0x0130, + [0x02ab] 0x011e, + [0x02ac] 0x0134, + [0x02b1] 0x0127, + [0x02b6] 0x0125, + [0x02b9] 0x0131, + [0x02bb] 0x011f, + [0x02bc] 0x0135, + [0x02c5] 0x010a, + [0x02c6] 0x0108, + [0x02d5] 0x0120, + [0x02d8] 0x011c, + [0x02dd] 0x016c, + [0x02de] 0x015c, + [0x02e5] 0x010b, + [0x02e6] 0x0109, + [0x02f5] 0x0121, + [0x02f8] 0x011d, + [0x02fd] 0x016d, + [0x02fe] 0x015d, + [0x03a2] 0x0138, + [0x03a3] 0x0156, + [0x03a5] 0x0128, + [0x03a6] 0x013b, + [0x03aa] 0x0112, + [0x03ab] 0x0122, + [0x03ac] 0x0166, + [0x03b3] 0x0157, + [0x03b5] 0x0129, + [0x03b6] 0x013c, + [0x03ba] 0x0113, + [0x03bb] 0x0123, + [0x03bc] 0x0167, + [0x03bd] 0x014a, + [0x03bf] 0x014b, + [0x03c0] 0x0100, + [0x03c7] 0x012e, + [0x03cc] 0x0116, + [0x03cf] 0x012a, + [0x03d1] 0x0145, + [0x03d2] 0x014c, + [0x03d3] 0x0136, + [0x03d9] 0x0172, + [0x03dd] 0x0168, + [0x03de] 0x016a, + [0x03e0] 0x0101, + [0x03e7] 0x012f, + [0x03ec] 0x0117, + [0x03ef] 0x012b, + [0x03f1] 0x0146, + [0x03f2] 0x014d, + [0x03f3] 0x0137, + [0x03f9] 0x0173, + [0x03fd] 0x0169, + [0x03fe] 0x016b, + [0x047e] 0x203e, + [0x04a1] 0x3002, + [0x04a2] 0x300c, + [0x04a3] 0x300d, + [0x04a4] 0x3001, + [0x04a5] 0x30fb, + [0x04a6] 0x30f2, + [0x04a7] 0x30a1, + [0x04a8] 0x30a3, + [0x04a9] 0x30a5, + [0x04aa] 0x30a7, + [0x04ab] 0x30a9, + [0x04ac] 0x30e3, + [0x04ad] 0x30e5, + [0x04ae] 0x30e7, + [0x04af] 0x30c3, + [0x04b0] 0x30fc, + [0x04b1] 0x30a2, + [0x04b2] 0x30a4, + [0x04b3] 0x30a6, + [0x04b4] 0x30a8, + [0x04b5] 0x30aa, + [0x04b6] 0x30ab, + [0x04b7] 0x30ad, + [0x04b8] 0x30af, + [0x04b9] 0x30b1, + [0x04ba] 0x30b3, + [0x04bb] 0x30b5, + [0x04bc] 0x30b7, + [0x04bd] 0x30b9, + [0x04be] 0x30bb, + [0x04bf] 0x30bd, + [0x04c0] 0x30bf, + [0x04c1] 0x30c1, + [0x04c2] 0x30c4, + [0x04c3] 0x30c6, + [0x04c4] 0x30c8, + [0x04c5] 0x30ca, + [0x04c6] 0x30cb, + [0x04c7] 0x30cc, + [0x04c8] 0x30cd, + [0x04c9] 0x30ce, + [0x04ca] 0x30cf, + [0x04cb] 0x30d2, + [0x04cc] 0x30d5, + [0x04cd] 0x30d8, + [0x04ce] 0x30db, + [0x04cf] 0x30de, + [0x04d0] 0x30df, + [0x04d1] 0x30e0, + [0x04d2] 0x30e1, + [0x04d3] 0x30e2, + [0x04d4] 0x30e4, + [0x04d5] 0x30e6, + [0x04d6] 0x30e8, + [0x04d7] 0x30e9, + [0x04d8] 0x30ea, + [0x04d9] 0x30eb, + [0x04da] 0x30ec, + [0x04db] 0x30ed, + [0x04dc] 0x30ef, + [0x04dd] 0x30f3, + [0x04de] 0x309b, + [0x04df] 0x309c, + [0x05ac] 0x060c, + [0x05bb] 0x061b, + [0x05bf] 0x061f, + [0x05c1] 0x0621, + [0x05c2] 0x0622, + [0x05c3] 0x0623, + [0x05c4] 0x0624, + [0x05c5] 0x0625, + [0x05c6] 0x0626, + [0x05c7] 0x0627, + [0x05c8] 0x0628, + [0x05c9] 0x0629, + [0x05ca] 0x062a, + [0x05cb] 0x062b, + [0x05cc] 0x062c, + [0x05cd] 0x062d, + [0x05ce] 0x062e, + [0x05cf] 0x062f, + [0x05d0] 0x0630, + [0x05d1] 0x0631, + [0x05d2] 0x0632, + [0x05d3] 0x0633, + [0x05d4] 0x0634, + [0x05d5] 0x0635, + [0x05d6] 0x0636, + [0x05d7] 0x0637, + [0x05d8] 0x0638, + [0x05d9] 0x0639, + [0x05da] 0x063a, + [0x05e0] 0x0640, + [0x05e1] 0x0641, + [0x05e2] 0x0642, + [0x05e3] 0x0643, + [0x05e4] 0x0644, + [0x05e5] 0x0645, + [0x05e6] 0x0646, + [0x05e7] 0x0647, + [0x05e8] 0x0648, + [0x05e9] 0x0649, + [0x05ea] 0x064a, + [0x05eb] 0x064b, + [0x05ec] 0x064c, + [0x05ed] 0x064d, + [0x05ee] 0x064e, + [0x05ef] 0x064f, + [0x05f0] 0x0650, + [0x05f1] 0x0651, + [0x05f2] 0x0652, + [0x06a1] 0x0452, + [0x06a2] 0x0453, + [0x06a3] 0x0451, + [0x06a4] 0x0454, + [0x06a5] 0x0455, + [0x06a6] 0x0456, + [0x06a7] 0x0457, + [0x06a8] 0x0458, + [0x06a9] 0x0459, + [0x06aa] 0x045a, + [0x06ab] 0x045b, + [0x06ac] 0x045c, + [0x06ae] 0x045e, + [0x06af] 0x045f, + [0x06b0] 0x2116, + [0x06b1] 0x0402, + [0x06b2] 0x0403, + [0x06b3] 0x0401, + [0x06b4] 0x0404, + [0x06b5] 0x0405, + [0x06b6] 0x0406, + [0x06b7] 0x0407, + [0x06b8] 0x0408, + [0x06b9] 0x0409, + [0x06ba] 0x040a, + [0x06bb] 0x040b, + [0x06bc] 0x040c, + [0x06be] 0x040e, + [0x06bf] 0x040f, + [0x06c0] 0x044e, + [0x06c1] 0x0430, + [0x06c2] 0x0431, + [0x06c3] 0x0446, + [0x06c4] 0x0434, + [0x06c5] 0x0435, + [0x06c6] 0x0444, + [0x06c7] 0x0433, + [0x06c8] 0x0445, + [0x06c9] 0x0438, + [0x06ca] 0x0439, + [0x06cb] 0x043a, + [0x06cc] 0x043b, + [0x06cd] 0x043c, + [0x06ce] 0x043d, + [0x06cf] 0x043e, + [0x06d0] 0x043f, + [0x06d1] 0x044f, + [0x06d2] 0x0440, + [0x06d3] 0x0441, + [0x06d4] 0x0442, + [0x06d5] 0x0443, + [0x06d6] 0x0436, + [0x06d7] 0x0432, + [0x06d8] 0x044c, + [0x06d9] 0x044b, + [0x06da] 0x0437, + [0x06db] 0x0448, + [0x06dc] 0x044d, + [0x06dd] 0x0449, + [0x06de] 0x0447, + [0x06df] 0x044a, + [0x06e0] 0x042e, + [0x06e1] 0x0410, + [0x06e2] 0x0411, + [0x06e3] 0x0426, + [0x06e4] 0x0414, + [0x06e5] 0x0415, + [0x06e6] 0x0424, + [0x06e7] 0x0413, + [0x06e8] 0x0425, + [0x06e9] 0x0418, + [0x06ea] 0x0419, + [0x06eb] 0x041a, + [0x06ec] 0x041b, + [0x06ed] 0x041c, + [0x06ee] 0x041d, + [0x06ef] 0x041e, + [0x06f0] 0x041f, + [0x06f1] 0x042f, + [0x06f2] 0x0420, + [0x06f3] 0x0421, + [0x06f4] 0x0422, + [0x06f5] 0x0423, + [0x06f6] 0x0416, + [0x06f7] 0x0412, + [0x06f8] 0x042c, + [0x06f9] 0x042b, + [0x06fa] 0x0417, + [0x06fb] 0x0428, + [0x06fc] 0x042d, + [0x06fd] 0x0429, + [0x06fe] 0x0427, + [0x06ff] 0x042a, + [0x07a1] 0x0386, + [0x07a2] 0x0388, + [0x07a3] 0x0389, + [0x07a4] 0x038a, + [0x07a5] 0x03aa, + [0x07a7] 0x038c, + [0x07a8] 0x038e, + [0x07a9] 0x03ab, + [0x07ab] 0x038f, + [0x07ae] 0x0385, + [0x07af] 0x2015, + [0x07b1] 0x03ac, + [0x07b2] 0x03ad, + [0x07b3] 0x03ae, + [0x07b4] 0x03af, + [0x07b5] 0x03ca, + [0x07b6] 0x0390, + [0x07b7] 0x03cc, + [0x07b8] 0x03cd, + [0x07b9] 0x03cb, + [0x07ba] 0x03b0, + [0x07bb] 0x03ce, + [0x07c1] 0x0391, + [0x07c2] 0x0392, + [0x07c3] 0x0393, + [0x07c4] 0x0394, + [0x07c5] 0x0395, + [0x07c6] 0x0396, + [0x07c7] 0x0397, + [0x07c8] 0x0398, + [0x07c9] 0x0399, + [0x07ca] 0x039a, + [0x07cb] 0x039b, + [0x07cc] 0x039c, + [0x07cd] 0x039d, + [0x07ce] 0x039e, + [0x07cf] 0x039f, + [0x07d0] 0x03a0, + [0x07d1] 0x03a1, + [0x07d2] 0x03a3, + [0x07d4] 0x03a4, + [0x07d5] 0x03a5, + [0x07d6] 0x03a6, + [0x07d7] 0x03a7, + [0x07d8] 0x03a8, + [0x07d9] 0x03a9, + [0x07e1] 0x03b1, + [0x07e2] 0x03b2, + [0x07e3] 0x03b3, + [0x07e4] 0x03b4, + [0x07e5] 0x03b5, + [0x07e6] 0x03b6, + [0x07e7] 0x03b7, + [0x07e8] 0x03b8, + [0x07e9] 0x03b9, + [0x07ea] 0x03ba, + [0x07eb] 0x03bb, + [0x07ec] 0x03bc, + [0x07ed] 0x03bd, + [0x07ee] 0x03be, + [0x07ef] 0x03bf, + [0x07f0] 0x03c0, + [0x07f1] 0x03c1, + [0x07f2] 0x03c3, + [0x07f3] 0x03c2, + [0x07f4] 0x03c4, + [0x07f5] 0x03c5, + [0x07f6] 0x03c6, + [0x07f7] 0x03c7, + [0x07f8] 0x03c8, + [0x07f9] 0x03c9, + [0x08a4] 0x2320, + [0x08a5] 0x2321, + [0x08a6] 0x2502, + [0x08bc] 0x2264, + [0x08bd] 0x2260, + [0x08be] 0x2265, + [0x08bf] 0x222b, + [0x08c0] 0x2234, + [0x08c1] 0x221d, + [0x08c2] 0x221e, + [0x08c5] 0x2207, + [0x08c8] 0x2245, + [0x08cd] 0x21d4, + [0x08ce] 0x21d2, + [0x08cf] 0x2261, + [0x08d6] 0x221a, + [0x08da] 0x2282, + [0x08db] 0x2283, + [0x08dc] 0x2229, + [0x08dd] 0x222a, + [0x08de] 0x2227, + [0x08df] 0x2228, + [0x08ef] 0x2202, + [0x08f6] 0x0192, + [0x08fb] 0x2190, + [0x08fc] 0x2191, + [0x08fd] 0x2192, + [0x08fe] 0x2193, + [0x09df] 0x2422, + [0x09e0] 0x25c6, + [0x09e1] 0x2592, + [0x09e2] 0x2409, + [0x09e3] 0x240c, + [0x09e4] 0x240d, + [0x09e5] 0x240a, + [0x09e8] 0x2424, + [0x09e9] 0x240b, + [0x09ea] 0x2518, + [0x09eb] 0x2510, + [0x09ec] 0x250c, + [0x09ed] 0x2514, + [0x09ee] 0x253c, + [0x09f1] 0x2500, + [0x09f4] 0x251c, + [0x09f5] 0x2524, + [0x09f6] 0x2534, + [0x09f7] 0x252c, + [0x09f8] 0x2502, + [0x0aa1] 0x2003, + [0x0aa2] 0x2002, + [0x0aa3] 0x2004, + [0x0aa4] 0x2005, + [0x0aa5] 0x2007, + [0x0aa6] 0x2008, + [0x0aa7] 0x2009, + [0x0aa8] 0x200a, + [0x0aa9] 0x2014, + [0x0aaa] 0x2013, + [0x0aae] 0x2026, + [0x0ab0] 0x2153, + [0x0ab1] 0x2154, + [0x0ab2] 0x2155, + [0x0ab3] 0x2156, + [0x0ab4] 0x2157, + [0x0ab5] 0x2158, + [0x0ab6] 0x2159, + [0x0ab7] 0x215a, + [0x0ab8] 0x2105, + [0x0abb] 0x2012, + [0x0abc] 0x2329, + [0x0abd] 0x002e, + [0x0abe] 0x232a, + [0x0ac3] 0x215b, + [0x0ac4] 0x215c, + [0x0ac5] 0x215d, + [0x0ac6] 0x215e, + [0x0ac9] 0x2122, + [0x0aca] 0x2613, + [0x0acc] 0x25c1, + [0x0acd] 0x25b7, + [0x0ace] 0x25cb, + [0x0acf] 0x25a1, + [0x0ad0] 0x2018, + [0x0ad1] 0x2019, + [0x0ad2] 0x201c, + [0x0ad3] 0x201d, + [0x0ad4] 0x211e, + [0x0ad6] 0x2032, + [0x0ad7] 0x2033, + [0x0ad9] 0x271d, + [0x0adb] 0x25ac, + [0x0adc] 0x25c0, + [0x0add] 0x25b6, + [0x0ade] 0x25cf, + [0x0adf] 0x25a0, + [0x0ae0] 0x25e6, + [0x0ae1] 0x25ab, + [0x0ae2] 0x25ad, + [0x0ae3] 0x25b3, + [0x0ae4] 0x25bd, + [0x0ae5] 0x2606, + [0x0ae6] 0x2022, + [0x0ae7] 0x25aa, + [0x0ae8] 0x25b2, + [0x0ae9] 0x25bc, + [0x0aea] 0x261c, + [0x0aeb] 0x261e, + [0x0aec] 0x2663, + [0x0aed] 0x2666, + [0x0aee] 0x2665, + [0x0af0] 0x2720, + [0x0af1] 0x2020, + [0x0af2] 0x2021, + [0x0af3] 0x2713, + [0x0af4] 0x2717, + [0x0af5] 0x266f, + [0x0af6] 0x266d, + [0x0af7] 0x2642, + [0x0af8] 0x2640, + [0x0af9] 0x260e, + [0x0afa] 0x2315, + [0x0afb] 0x2117, + [0x0afc] 0x2038, + [0x0afd] 0x201a, + [0x0afe] 0x201e, + [0x0ba3] 0x003c, + [0x0ba6] 0x003e, + [0x0ba8] 0x2228, + [0x0ba9] 0x2227, + [0x0bc0] 0x00af, + [0x0bc2] 0x22a4, + [0x0bc3] 0x2229, + [0x0bc4] 0x230a, + [0x0bc6] 0x005f, + [0x0bca] 0x2218, + [0x0bcc] 0x2395, + [0x0bce] 0x22a5, + [0x0bcf] 0x25cb, + [0x0bd3] 0x2308, + [0x0bd6] 0x222a, + [0x0bd8] 0x2283, + [0x0bda] 0x2282, + [0x0bdc] 0x22a3, + [0x0bfc] 0x22a2, + [0x0cdf] 0x2017, + [0x0ce0] 0x05d0, + [0x0ce1] 0x05d1, + [0x0ce2] 0x05d2, + [0x0ce3] 0x05d3, + [0x0ce4] 0x05d4, + [0x0ce5] 0x05d5, + [0x0ce6] 0x05d6, + [0x0ce7] 0x05d7, + [0x0ce8] 0x05d8, + [0x0ce9] 0x05d9, + [0x0cea] 0x05da, + [0x0ceb] 0x05db, + [0x0cec] 0x05dc, + [0x0ced] 0x05dd, + [0x0cee] 0x05de, + [0x0cef] 0x05df, + [0x0cf0] 0x05e0, + [0x0cf1] 0x05e1, + [0x0cf2] 0x05e2, + [0x0cf3] 0x05e3, + [0x0cf4] 0x05e4, + [0x0cf5] 0x05e5, + [0x0cf6] 0x05e6, + [0x0cf7] 0x05e7, + [0x0cf8] 0x05e8, + [0x0cf9] 0x05e9, + [0x0cfa] 0x05ea, + [0x0da1] 0x0e01, + [0x0da2] 0x0e02, + [0x0da3] 0x0e03, + [0x0da4] 0x0e04, + [0x0da5] 0x0e05, + [0x0da6] 0x0e06, + [0x0da7] 0x0e07, + [0x0da8] 0x0e08, + [0x0da9] 0x0e09, + [0x0daa] 0x0e0a, + [0x0dab] 0x0e0b, + [0x0dac] 0x0e0c, + [0x0dad] 0x0e0d, + [0x0dae] 0x0e0e, + [0x0daf] 0x0e0f, + [0x0db0] 0x0e10, + [0x0db1] 0x0e11, + [0x0db2] 0x0e12, + [0x0db3] 0x0e13, + [0x0db4] 0x0e14, + [0x0db5] 0x0e15, + [0x0db6] 0x0e16, + [0x0db7] 0x0e17, + [0x0db8] 0x0e18, + [0x0db9] 0x0e19, + [0x0dba] 0x0e1a, + [0x0dbb] 0x0e1b, + [0x0dbc] 0x0e1c, + [0x0dbd] 0x0e1d, + [0x0dbe] 0x0e1e, + [0x0dbf] 0x0e1f, + [0x0dc0] 0x0e20, + [0x0dc1] 0x0e21, + [0x0dc2] 0x0e22, + [0x0dc3] 0x0e23, + [0x0dc4] 0x0e24, + [0x0dc5] 0x0e25, + [0x0dc6] 0x0e26, + [0x0dc7] 0x0e27, + [0x0dc8] 0x0e28, + [0x0dc9] 0x0e29, + [0x0dca] 0x0e2a, + [0x0dcb] 0x0e2b, + [0x0dcc] 0x0e2c, + [0x0dcd] 0x0e2d, + [0x0dce] 0x0e2e, + [0x0dcf] 0x0e2f, + [0x0dd0] 0x0e30, + [0x0dd1] 0x0e31, + [0x0dd2] 0x0e32, + [0x0dd3] 0x0e33, + [0x0dd4] 0x0e34, + [0x0dd5] 0x0e35, + [0x0dd6] 0x0e36, + [0x0dd7] 0x0e37, + [0x0dd8] 0x0e38, + [0x0dd9] 0x0e39, + [0x0dda] 0x0e3a, + [0x0dde] 0x0e3e, + [0x0ddf] 0x0e3f, + [0x0de0] 0x0e40, + [0x0de1] 0x0e41, + [0x0de2] 0x0e42, + [0x0de3] 0x0e43, + [0x0de4] 0x0e44, + [0x0de5] 0x0e45, + [0x0de6] 0x0e46, + [0x0de7] 0x0e47, + [0x0de8] 0x0e48, + [0x0de9] 0x0e49, + [0x0dea] 0x0e4a, + [0x0deb] 0x0e4b, + [0x0dec] 0x0e4c, + [0x0ded] 0x0e4d, + [0x0df0] 0x0e50, + [0x0df1] 0x0e51, + [0x0df2] 0x0e52, + [0x0df3] 0x0e53, + [0x0df4] 0x0e54, + [0x0df5] 0x0e55, + [0x0df6] 0x0e56, + [0x0df7] 0x0e57, + [0x0df8] 0x0e58, + [0x0df9] 0x0e59, + [0x0ea1] 0x3131, + [0x0ea2] 0x3132, + [0x0ea3] 0x3133, + [0x0ea4] 0x3134, + [0x0ea5] 0x3135, + [0x0ea6] 0x3136, + [0x0ea7] 0x3137, + [0x0ea8] 0x3138, + [0x0ea9] 0x3139, + [0x0eaa] 0x313a, + [0x0eab] 0x313b, + [0x0eac] 0x313c, + [0x0ead] 0x313d, + [0x0eae] 0x313e, + [0x0eaf] 0x313f, + [0x0eb0] 0x3140, + [0x0eb1] 0x3141, + [0x0eb2] 0x3142, + [0x0eb3] 0x3143, + [0x0eb4] 0x3144, + [0x0eb5] 0x3145, + [0x0eb6] 0x3146, + [0x0eb7] 0x3147, + [0x0eb8] 0x3148, + [0x0eb9] 0x3149, + [0x0eba] 0x314a, + [0x0ebb] 0x314b, + [0x0ebc] 0x314c, + [0x0ebd] 0x314d, + [0x0ebe] 0x314e, + [0x0ebf] 0x314f, + [0x0ec0] 0x3150, + [0x0ec1] 0x3151, + [0x0ec2] 0x3152, + [0x0ec3] 0x3153, + [0x0ec4] 0x3154, + [0x0ec5] 0x3155, + [0x0ec6] 0x3156, + [0x0ec7] 0x3157, + [0x0ec8] 0x3158, + [0x0ec9] 0x3159, + [0x0eca] 0x315a, + [0x0ecb] 0x315b, + [0x0ecc] 0x315c, + [0x0ecd] 0x315d, + [0x0ece] 0x315e, + [0x0ecf] 0x315f, + [0x0ed0] 0x3160, + [0x0ed1] 0x3161, + [0x0ed2] 0x3162, + [0x0ed3] 0x3163, + [0x0ed4] 0x11a8, + [0x0ed5] 0x11a9, + [0x0ed6] 0x11aa, + [0x0ed7] 0x11ab, + [0x0ed8] 0x11ac, + [0x0ed9] 0x11ad, + [0x0eda] 0x11ae, + [0x0edb] 0x11af, + [0x0edc] 0x11b0, + [0x0edd] 0x11b1, + [0x0ede] 0x11b2, + [0x0edf] 0x11b3, + [0x0ee0] 0x11b4, + [0x0ee1] 0x11b5, + [0x0ee2] 0x11b6, + [0x0ee3] 0x11b7, + [0x0ee4] 0x11b8, + [0x0ee5] 0x11b9, + [0x0ee6] 0x11ba, + [0x0ee7] 0x11bb, + [0x0ee8] 0x11bc, + [0x0ee9] 0x11bd, + [0x0eea] 0x11be, + [0x0eeb] 0x11bf, + [0x0eec] 0x11c0, + [0x0eed] 0x11c1, + [0x0eee] 0x11c2, + [0x0eef] 0x316d, + [0x0ef0] 0x3171, + [0x0ef1] 0x3178, + [0x0ef2] 0x317f, + [0x0ef4] 0x3184, + [0x0ef5] 0x3186, + [0x0ef6] 0x318d, + [0x0ef7] 0x318e, + [0x0ef8] 0x11eb, + [0x0efa] 0x11f9, + [0x0eff] 0x20a9, + [0x13bc] 0x0152, + [0x13bd] 0x0153, + [0x13be] 0x0178, + [0x20a0] 0x20a0, + [0x20a1] 0x20a1, + [0x20a2] 0x20a2, + [0x20a3] 0x20a3, + [0x20a4] 0x20a4, + [0x20a5] 0x20a5, + [0x20a6] 0x20a6, + [0x20a7] 0x20a7, + [0x20a8] 0x20a8, + [0x20a9] 0x20a9, + [0x20aa] 0x20aa, + [0x20ab] 0x20ab, + [0x20ac] 0x20ac, +}; diff --git a/gui-x11/load.c b/gui-x11/load.c new file mode 100644 index 0000000..6a19c8a --- /dev/null +++ b/gui-x11/load.c @@ -0,0 +1,16 @@ +#include +#include +#include +#include +#include "xmem.h" + +int +loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) +{ + int n; + + n = _loadmemimage(i, r, data, ndata); + if(n > 0 && i->X) + putXdata(i, r); + return n; +} diff --git a/gui-x11/screen.c b/gui-x11/screen.c new file mode 100644 index 0000000..ddadb67 --- /dev/null +++ b/gui-x11/screen.c @@ -0,0 +1,1094 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "keysym2ucs.h" + +/* + * alias defs for image types to overcome name conflicts + */ +#define Point IPoint +#define Rectangle IRectangle +#define Display IDisplay +#define Font IFont +#define Screen IScreen + +#include +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "screen.h" + +#undef time +#undef Point +#undef Rectangle +#undef Display +#undef Font +#undef Screen + +typedef struct ICursor ICursor; +struct ICursor +{ + int w; + int h; + int hotx; + int hoty; + char *src; + char *mask; +}; + + +#define ABS(x) ((x) < 0 ? -(x) : (x)) + +enum +{ + DblTime = 300 /* double click time in msec */ +}; + +XColor map[256]; /* Plan 9 colormap array */ +XColor map7[128]; /* Plan 9 colormap array */ +uchar map7to8[128][2]; +Colormap xcmap; /* Default shared colormap */ +int plan9tox11[256]; /* Values for mapping between */ +int x11toplan9[256]; /* X11 and Plan 9 */ +int x24bitswap = 0; /* swap endian for 24bit RGB */ +int xtblbit; +extern int mousequeue; + +/* for copy/paste, lifted from plan9ports */ +Atom clipboard; +Atom utf8string; +Atom targets; +Atom text; +Atom compoundtext; + +static XModifierKeymap *modmap; +static int keypermod; +static Drawable xdrawable; +/* static Atom wm_take_focus; */ +static void xexpose(XEvent*); +static void xmouse(XEvent*); +static void xkeyboard(XEvent*); +static void xmapping(XEvent*); +static void xdestroy(XEvent*); +static void xselect(XEvent*); +static void xproc(void*); +static Memimage* xinitscreen(void); +static void initmap(Window); +static GC creategc(Drawable); +static void graphicscmap(XColor*); + int xscreendepth; + Drawable xscreenid; + Display* xdisplay; + Display* xkmcon; + Display* xsnarfcon; + Visual *xvis; + GC xgcfill, xgccopy, xgcsimplesrc, xgczero, xgcreplsrc; + GC xgcfill0, xgccopy0, xgcsimplesrc0, xgczero0, xgcreplsrc0; + ulong xblack; + ulong xwhite; + ulong xscreenchan; + +extern Memimage* xallocmemimage(IRectangle, ulong, int); +Memimage *gscreen; +Screeninfo screen; +XImage *ximage; + +void +screeninit(void) +{ + _memmkcmap(); + + gscreen = xinitscreen(); + kproc("xscreen", xproc, nil); + + memimageinit(); + terminit(); + flushmemscreen(gscreen->r); +} + +uchar* +attachscreen(IRectangle *r, ulong *chan, int *depth, + int *width, int *softscreen, void **X) +{ + *r = gscreen->r; + *chan = gscreen->chan; + *depth = gscreen->depth; + *width = gscreen->width; + *X = gscreen->X; + *softscreen = 1; + + return gscreen->data->bdata; +} + +void +flushmemscreen(IRectangle r) +{ + if(r.min.x >= r.max.x || r.min.y >= r.max.y) + return; + XCopyArea(xdisplay, xscreenid, xdrawable, xgccopy, r.min.x, r.min.y, Dx(r), Dy(r), r.min.x, r.min.y); + XFlush(xdisplay); +} + +static int +revbyte(int b) +{ + int r; + + r = 0; + r |= (b&0x01) << 7; + r |= (b&0x02) << 5; + r |= (b&0x04) << 3; + r |= (b&0x08) << 1; + r |= (b&0x10) >> 1; + r |= (b&0x20) >> 3; + r |= (b&0x40) >> 5; + r |= (b&0x80) >> 7; + return r; +} + +void +mouseset(IPoint xy) +{ + XWarpPointer(xdisplay, None, xdrawable, 0, 0, 0, 0, xy.x, xy.y); + XFlush(xdisplay); +} + +static Cursor xcursor; + +void +setcursor(void) +{ + Cursor xc; + XColor fg, bg; + Pixmap xsrc, xmask; + int i; + uchar src[2*16], mask[2*16]; + + for(i=0; i<2*16; i++){ + src[i] = revbyte(cursor.set[i]); + mask[i] = revbyte(cursor.set[i] | cursor.clr[i]); + } + + fg = map[0]; + bg = map[255]; + xsrc = XCreateBitmapFromData(xdisplay, xdrawable, src, 16, 16); + xmask = XCreateBitmapFromData(xdisplay, xdrawable, mask, 16, 16); + xc = XCreatePixmapCursor(xdisplay, xsrc, xmask, &fg, &bg, -cursor.offset.x, -cursor.offset.y); + if(xc != 0) { + XDefineCursor(xdisplay, xdrawable, xc); + if(xcursor != 0) + XFreeCursor(xdisplay, xcursor); + xcursor = xc; + } + XFreePixmap(xdisplay, xsrc); + XFreePixmap(xdisplay, xmask); + XFlush(xdisplay); +} + +void +cursorarrow(void) +{ + if(xcursor != 0){ + XFreeCursor(xdisplay, xcursor); + xcursor = 0; + } + XUndefineCursor(xdisplay, xdrawable); + XFlush(xdisplay); +} + +static void +xproc(void *arg) +{ + ulong mask; + XEvent event; + + mask = KeyPressMask| + ButtonPressMask| + ButtonReleaseMask| + PointerMotionMask| + Button1MotionMask| + Button2MotionMask| + Button3MotionMask| + Button4MotionMask| + Button5MotionMask| + ExposureMask| + StructureNotifyMask; + + XSelectInput(xkmcon, xdrawable, mask); + for(;;) { + //XWindowEvent(xkmcon, xdrawable, mask, &event); + XNextEvent(xkmcon, &event); + xselect(&event); + xkeyboard(&event); + xmouse(&event); + xexpose(&event); + xmapping(&event); + xdestroy(&event); + } +} + +static int +shutup(Display *d, XErrorEvent *e) +{ + char buf[200]; + iprint("X error: error code=%d, request_code=%d, minor=%d\n", e->error_code, e->request_code, e->minor_code); + XGetErrorText(d, e->error_code, buf, sizeof(buf)); + iprint("%s\n", buf); + USED(d); + USED(e); + return 0; +} + +static int +panicshutup(Display *d) +{ + panic("x error"); + return -1; +} + +static Memimage* +xinitscreen(void) +{ + Memimage *gscreen; + int i, xsize, ysize, pmid; + char *argv[2]; + char *disp_val; + Window rootwin; + IRectangle r; + XWMHints hints; + Screen *screen; + XVisualInfo xvi; + int rootscreennum; + XTextProperty name; + XClassHint classhints; + XSizeHints normalhints; + XSetWindowAttributes attrs; + XPixmapFormatValues *pfmt; + int n; + Memdata *md; + + xscreenid = 0; + xdrawable = 0; + + xdisplay = XOpenDisplay(NULL); + if(xdisplay == 0){ + disp_val = getenv("DISPLAY"); + if(disp_val == 0) + disp_val = "not set"; + iprint("drawterm: open %r, DISPLAY is %s\n", disp_val); + exit(0); + } + + XSetErrorHandler(shutup); + XSetIOErrorHandler(panicshutup); + rootscreennum = DefaultScreen(xdisplay); + rootwin = DefaultRootWindow(xdisplay); + + xscreendepth = DefaultDepth(xdisplay, rootscreennum); + if(XMatchVisualInfo(xdisplay, rootscreennum, 16, TrueColor, &xvi) + || XMatchVisualInfo(xdisplay, rootscreennum, 16, DirectColor, &xvi)){ + xvis = xvi.visual; + xscreendepth = 16; + xtblbit = 1; + } + else if(XMatchVisualInfo(xdisplay, rootscreennum, 24, TrueColor, &xvi) + || XMatchVisualInfo(xdisplay, rootscreennum, 24, DirectColor, &xvi)){ + xvis = xvi.visual; + xscreendepth = 24; + xtblbit = 1; + } + else if(XMatchVisualInfo(xdisplay, rootscreennum, 8, PseudoColor, &xvi) + || XMatchVisualInfo(xdisplay, rootscreennum, 8, StaticColor, &xvi)){ + if(xscreendepth > 8) + panic("drawterm: can't deal with colormapped depth %d screens\n", xscreendepth); + xvis = xvi.visual; + xscreendepth = 8; + } + else{ + if(xscreendepth != 8) + panic("drawterm: can't deal with depth %d screens\n", xscreendepth); + xvis = DefaultVisual(xdisplay, rootscreennum); + } + + /* + * xscreendepth is only the number of significant pixel bits, + * not the total. We need to walk the display list to find + * how many actual bits are being used per pixel. + */ + xscreenchan = 0; /* not a valid channel */ + pfmt = XListPixmapFormats(xdisplay, &n); + for(i=0; iclass != StaticColor){ + graphicscmap(map); + initmap(rootwin); + } + + if((modmap = XGetModifierMapping(xdisplay))) + keypermod = modmap->max_keypermod; + + r.min = ZP; + r.max.x = WidthOfScreen(screen); + r.max.y = HeightOfScreen(screen); + + md = mallocz(sizeof(Memdata), 1); + + xsize = Dx(r)*3/4; + ysize = Dy(r)*3/4; + + attrs.colormap = xcmap; + attrs.background_pixel = 0; + attrs.border_pixel = 0; + /* attrs.override_redirect = 1;*/ /* WM leave me alone! |CWOverrideRedirect */ + xdrawable = XCreateWindow(xdisplay, rootwin, 0, 0, xsize, ysize, 0, + xscreendepth, InputOutput, xvis, CWBackPixel|CWBorderPixel|CWColormap, &attrs); + + /* + * set up property as required by ICCCM + */ + name.value = (uchar*)"drawterm"; + name.encoding = XA_STRING; + name.format = 8; + name.nitems = strlen(name.value); + normalhints.flags = USSize|PMaxSize; + normalhints.max_width = Dx(r); + normalhints.max_height = Dy(r); + normalhints.width = xsize; + normalhints.height = ysize; + hints.flags = InputHint|StateHint; + hints.input = 1; + hints.initial_state = NormalState; + classhints.res_name = "drawterm"; + classhints.res_class = "Drawterm"; + argv[0] = "drawterm"; + argv[1] = nil; + XSetWMProperties(xdisplay, xdrawable, + &name, /* XA_WM_NAME property for ICCCM */ + &name, /* XA_WM_ICON_NAME */ + argv, /* XA_WM_COMMAND */ + 1, /* argc */ + &normalhints, /* XA_WM_NORMAL_HINTS */ + &hints, /* XA_WM_HINTS */ + &classhints); /* XA_WM_CLASS */ + XFlush(xdisplay); + + /* + * put the window on the screen + */ + XMapWindow(xdisplay, xdrawable); + XFlush(xdisplay); + + xscreenid = XCreatePixmap(xdisplay, xdrawable, Dx(r), Dy(r), xscreendepth); + gscreen = xallocmemimage(r, xscreenchan, xscreenid); + + xgcfill = creategc(xscreenid); + XSetFillStyle(xdisplay, xgcfill, FillSolid); + xgccopy = creategc(xscreenid); + xgcsimplesrc = creategc(xscreenid); + XSetFillStyle(xdisplay, xgcsimplesrc, FillStippled); + xgczero = creategc(xscreenid); + xgcreplsrc = creategc(xscreenid); + XSetFillStyle(xdisplay, xgcreplsrc, FillTiled); + + pmid = XCreatePixmap(xdisplay, xdrawable, 1, 1, 1); + xgcfill0 = creategc(pmid); + XSetForeground(xdisplay, xgcfill0, 0); + XSetFillStyle(xdisplay, xgcfill0, FillSolid); + xgccopy0 = creategc(pmid); + xgcsimplesrc0 = creategc(pmid); + XSetFillStyle(xdisplay, xgcsimplesrc0, FillStippled); + xgczero0 = creategc(pmid); + xgcreplsrc0 = creategc(pmid); + XSetFillStyle(xdisplay, xgcreplsrc0, FillTiled); + XFreePixmap(xdisplay, pmid); + + XSetForeground(xdisplay, xgccopy, plan9tox11[0]); + XFillRectangle(xdisplay, xscreenid, xgccopy, 0, 0, xsize, ysize); + + xkmcon = XOpenDisplay(NULL); + if(xkmcon == 0){ + disp_val = getenv("DISPLAY"); + if(disp_val == 0) + disp_val = "not set"; + iprint("drawterm: open %r, DISPLAY is %s\n", disp_val); + exit(0); + } + xsnarfcon = XOpenDisplay(NULL); + if(xsnarfcon == 0){ + disp_val = getenv("DISPLAY"); + if(disp_val == 0) + disp_val = "not set"; + iprint("drawterm: open %r, DISPLAY is %s\n", disp_val); + exit(0); + } + + clipboard = XInternAtom(xkmcon, "CLIPBOARD", False); + utf8string = XInternAtom(xkmcon, "UTF8_STRING", False); + targets = XInternAtom(xkmcon, "TARGETS", False); + text = XInternAtom(xkmcon, "TEXT", False); + compoundtext = XInternAtom(xkmcon, "COMPOUND_TEXT", False); + + xblack = screen->black_pixel; + xwhite = screen->white_pixel; + return gscreen; +} + +static void +graphicscmap(XColor *map) +{ + int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7; + + for(r=0; r!=4; r++) { + for(g = 0; g != 4; g++) { + for(b = 0; b!=4; b++) { + for(v = 0; v!=4; v++) { + den=r; + if(g > den) + den=g; + if(b > den) + den=b; + /* divide check -- pick grey shades */ + if(den==0) + cr=cg=cb=v*17; + else { + num=17*(4*den+v); + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + idx = r*64 + v*16 + ((g*4 + b + v - r) & 15); + map[idx].red = cr*0x0101; + map[idx].green = cg*0x0101; + map[idx].blue = cb*0x0101; + map[idx].pixel = idx; + map[idx].flags = DoRed|DoGreen|DoBlue; + + v7 = v >> 1; + idx7 = r*32 + v7*16 + g*4 + b; + if((v & 1) == v7){ + map7to8[idx7][0] = idx; + if(den == 0) { /* divide check -- pick grey shades */ + cr = ((255.0/7.0)*v7)+0.5; + cg = cr; + cb = cr; + } + else { + num=17*15*(4*den+v7*2)/14; + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + map7[idx7].red = cr*0x0101; + map7[idx7].green = cg*0x0101; + map7[idx7].blue = cb*0x0101; + map7[idx7].pixel = idx7; + map7[idx7].flags = DoRed|DoGreen|DoBlue; + } + else + map7to8[idx7][1] = idx; + } + } + } + } +} + +/* + * Initialize and install the drawterm colormap as a private colormap for this + * application. Drawterm gets the best colors here when it has the cursor focus. + */ +static void +initmap(Window w) +{ + XColor c; + int i; + ulong p, pp; + char buf[30]; + + if(xscreendepth <= 1) + return; + + if(xscreendepth >= 24) { + /* The pixel value returned from XGetPixel needs to + * be converted to RGB so we can call rgb2cmap() + * to translate between 24 bit X and our color. Unfortunately, + * the return value appears to be display server endian + * dependant. Therefore, we run some heuristics to later + * determine how to mask the int value correctly. + * Yeah, I know we can look at xvis->byte_order but + * some displays say MSB even though they run on LSB. + * Besides, this is more anal. + */ + + c = map[19]; + /* find out index into colormap for our RGB */ + if(!XAllocColor(xdisplay, xcmap, &c)) + panic("drawterm: screen-x11 can't alloc color"); + + p = c.pixel; + pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff); + if(pp!=map[19].pixel) { + /* check if endian is other way */ + pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff); + if(pp!=map[19].pixel) + panic("cannot detect x server byte order"); + switch(xscreenchan){ + case RGB24: + xscreenchan = BGR24; + break; + case XRGB32: + xscreenchan = XBGR32; + break; + default: + panic("don't know how to byteswap channel %s", + chantostr(buf, xscreenchan)); + break; + } + } + } else if(xvis->class == TrueColor || xvis->class == DirectColor) { + } else if(xvis->class == PseudoColor) { + if(xtblbit == 0){ + xcmap = XCreateColormap(xdisplay, w, xvis, AllocAll); + XStoreColors(xdisplay, xcmap, map, 256); + for(i = 0; i < 256; i++) { + plan9tox11[i] = i; + x11toplan9[i] = i; + } + } + else { + for(i = 0; i < 128; i++) { + c = map7[i]; + if(!XAllocColor(xdisplay, xcmap, &c)) { + iprint("drawterm: can't alloc colors in default map, don't use -7\n"); + exit(0); + } + plan9tox11[map7to8[i][0]] = c.pixel; + plan9tox11[map7to8[i][1]] = c.pixel; + x11toplan9[c.pixel] = map7to8[i][0]; + } + } + } + else + panic("drawterm: unsupported visual class %d\n", xvis->class); +} + +static void +xdestroy(XEvent *e) +{ + XDestroyWindowEvent *xe; + if(e->type != DestroyNotify) + return; + xe = (XDestroyWindowEvent*)e; + if(xe->window == xdrawable) + exit(0); +} + +static void +xselection(XEvent *e) +{ + XSelectionRequestEvent *xre; + XSelectionEvent *xe; +} + +static void +xmapping(XEvent *e) +{ + XMappingEvent *xe; + + if(e->type != MappingNotify) + return; + xe = (XMappingEvent*)e; + if(modmap) + XFreeModifiermap(modmap); + modmap = XGetModifierMapping(xe->display); + if(modmap) + keypermod = modmap->max_keypermod; +} + + +/* + * Disable generation of GraphicsExpose/NoExpose events in the GC. + */ +static GC +creategc(Drawable d) +{ + XGCValues gcv; + + gcv.function = GXcopy; + gcv.graphics_exposures = False; + return XCreateGC(xdisplay, d, GCFunction|GCGraphicsExposures, &gcv); +} + +static void +xexpose(XEvent *e) +{ + IRectangle r; + XExposeEvent *xe; + + if(e->type != Expose) + return; + xe = (XExposeEvent*)e; + r.min.x = xe->x; + r.min.y = xe->y; + r.max.x = xe->x + xe->width; + r.max.y = xe->y + xe->height; + drawflushr(r); +} + +static void +xkeyboard(XEvent *e) +{ + KeySym k; + unsigned int md; + + /* + * I tried using XtGetActionKeysym, but it didn't seem to + * do case conversion properly + * (at least, with Xterminal servers and R4 intrinsics) + */ + if(e->xany.type != KeyPress) + return; + + + XLookupString(e,NULL,0,&k,NULL); + + if(k == XK_Multi_key || k == NoSymbol) + return; + if(k&0xFF00){ + switch(k){ + case XK_BackSpace: + case XK_Tab: + case XK_Escape: + case XK_Delete: + case XK_KP_0: + case XK_KP_1: + case XK_KP_2: + case XK_KP_3: + case XK_KP_4: + case XK_KP_5: + case XK_KP_6: + case XK_KP_7: + case XK_KP_8: + case XK_KP_9: + case XK_KP_Divide: + case XK_KP_Multiply: + case XK_KP_Subtract: + case XK_KP_Add: + case XK_KP_Decimal: + k &= 0x7F; + break; + case XK_Linefeed: + k = '\r'; + break; + case XK_KP_Space: + k = ' '; + break; + case XK_Home: + case XK_KP_Home: + k = Khome; + break; + case XK_Left: + case XK_KP_Left: + k = Kleft; + break; + case XK_Up: + case XK_KP_Up: + k = Kup; + break; + case XK_Down: + case XK_KP_Down: + k = Kdown; + break; + case XK_Right: + case XK_KP_Right: + k = Kright; + break; + case XK_Page_Down: + case XK_KP_Page_Down: + k = Kpgdown; + break; + case XK_End: + case XK_KP_End: + k = Kend; + break; + case XK_Page_Up: + case XK_KP_Page_Up: + k = Kpgup; + break; + case XK_Insert: + case XK_KP_Insert: + k = Kins; + break; + case XK_KP_Enter: + case XK_Return: + k = '\n'; + break; + case XK_Alt_L: + case XK_Alt_R: + k = Kalt; + break; + case XK_Shift_L: + case XK_Shift_R: + case XK_Control_L: + case XK_Control_R: + case XK_Caps_Lock: + case XK_Shift_Lock: + + case XK_Meta_L: + case XK_Meta_R: + case XK_Super_L: + case XK_Super_R: + case XK_Hyper_L: + case XK_Hyper_R: + return; + default: /* not ISO-1 or tty control */ + if(k>0xff) { + k = keysym2ucs(k); /* supplied by X */ + if(k == -1) return; + } + } + } + + /* Compensate for servers that call a minus a hyphen */ + if(k == XK_hyphen) + k = XK_minus; + /* Do control mapping ourselves if translator doesn't */ + if(e->xkey.state&ControlMask) + k &= 0x9f; + if(k == NoSymbol) { + return; + } + + kbdputc(kbdq, k); +} + +static void +xmouse(XEvent *e) +{ + Mousestate ms; + int i, s; + XButtonEvent *be; + XMotionEvent *me; + + switch(e->type){ + case ButtonPress: + be = (XButtonEvent *)e; + ms.xy.x = be->x; + ms.xy.y = be->y; + s = be->state; + ms.msec = be->time; + switch(be->button){ + case 1: + s |= Button1Mask; + break; + case 2: + s |= Button2Mask; + break; + case 3: + s |= Button3Mask; + break; + case 4: + s |= Button4Mask; + break; + case 5: + s |= Button5Mask; + break; + } + break; + case ButtonRelease: + be = (XButtonEvent *)e; + ms.xy.x = be->x; + ms.xy.y = be->y; + ms.msec = be->time; + s = be->state; + switch(be->button){ + case 1: + s &= ~Button1Mask; + break; + case 2: + s &= ~Button2Mask; + break; + case 3: + s &= ~Button3Mask; + break; + case 4: + s &= ~Button4Mask; + break; + case 5: + s &= ~Button5Mask; + break; + } + break; + case MotionNotify: + me = (XMotionEvent *)e; + s = me->state; + ms.xy.x = me->x; + ms.xy.y = me->y; + ms.msec = me->time; + break; + default: + return; + } + + ms.buttons = 0; + if(s & Button1Mask) + ms.buttons |= 1; + if(s & Button2Mask) + ms.buttons |= 2; + if(s & Button3Mask) + ms.buttons |= 4; + if(s & Button4Mask) + ms.buttons |= 8; + if(s & Button5Mask) + ms.buttons |= 16; + + lock(&mouse.lk); + i = mouse.wi; + if(mousequeue) { + if(i == mouse.ri || mouse.lastb != ms.buttons || mouse.trans) { + mouse.wi = (i+1)%Mousequeue; + if(mouse.wi == mouse.ri) + mouse.ri = (mouse.ri+1)%Mousequeue; + mouse.trans = mouse.lastb != ms.buttons; + } else { + i = (i-1+Mousequeue)%Mousequeue; + } + } else { + mouse.wi = (i+1)%Mousequeue; + mouse.ri = i; + } + mouse.queue[i] = ms; + mouse.lastb = ms.buttons; + unlock(&mouse.lk); + wakeup(&mouse.r); +} + +void +getcolor(ulong i, ulong *r, ulong *g, ulong *b) +{ + ulong v; + + v = cmap2rgb(i); + *r = (v>>16)&0xFF; + *g = (v>>8)&0xFF; + *b = v&0xFF; +} + +void +setcolor(ulong i, ulong r, ulong g, ulong b) +{ + /* no-op */ +} + +typedef struct Clip Clip; +struct Clip +{ + char buf[SnarfSize]; + ulong n; + int want, have; + QLock lk; + Rendez vous; +}; + +Clip clip; + +enum { + Chunk = 2048 +}; + +static void +xselect(XEvent *e) +{ + XSelectionRequestEvent *q; + XEvent r; + Atom a[4]; + char *name; + + + if(e->type != SelectionRequest) + return; + + /* + * The lock is around the whole routine because we use the + * lock to make sure two people aren't sending on xkmcon + * at once. + */ + q = (XSelectionRequestEvent*)e; + + r.xselection.property = q->property; + if(q->target == targets) { + a[0] = XA_STRING; + a[1] = utf8string; + a[2] = text; + a[3] = compoundtext; + + XChangeProperty(xkmcon, q->requestor, q->property, q->target, + 8, PropModeReplace, (uchar*)a, sizeof a); + }else if(q->target == XA_STRING || q->target == utf8string || q->target == text || q->target == compoundtext){ + qlock(&clip.lk); + XChangeProperty(xkmcon, q->requestor, q->property, q->target, 8, + PropModeReplace, (uchar*)clip.buf, strlen(clip.buf)); + qunlock(&clip.lk); + }else { + name = XGetAtomName(xkmcon, q->target); + if(strcmp(name, "TIMESTAMP") != 0) + fprint(2, "%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)q->target); + r.xselection.property = None; + } + + r.xselection.type = SelectionNotify; + r.xselection.display = q->display; + r.xselection.requestor = q->requestor; + r.xselection.selection = q->selection; + r.xselection.target = q->target; + r.xselection.time = q->time; + XSendEvent(xkmcon, q->requestor, False, 0, &r); + XFlush(xkmcon); +} + +int +haveclip(void *a) +{ + return clip.want == clip.have; +} + +#undef long /* sic */ +char* +clipread(void) +{ + Window w; + XEvent e; + Atom type; + unsigned long len, lleft, left, dummy; + int i, fmt, res; + uchar *data; + + qlock(&clip.lk); + w = XGetSelectionOwner(xsnarfcon, XA_PRIMARY); + if(w == xdrawable) + data = (uchar*)strdup(clip.buf); + else if(w == None) + data = nil; + else { + /* + * we're supposed to get a notification, but we seem not to, + * so let's just watch and see when the buffer stabilizes. + * if you know how to fix this, mail rsc@plan9.bell-labs.com. + */ + XChangeProperty(xsnarfcon, xdrawable, XA_PRIMARY, XA_STRING, 8, PropModeReplace, + (uchar*)"", 0); + XConvertSelection(xsnarfcon, XA_PRIMARY, XA_STRING, None, xdrawable, CurrentTime); + XFlush(xsnarfcon); + for(i=0; i<30; i++){ + osmsleep(100); + XGetWindowProperty(xsnarfcon, xdrawable, XA_STRING, 0, 0, 0, AnyPropertyType, + &type, &fmt, &len, &left, &data); + if(lleft == left && left > 0) + break; + lleft = left; + } + if(left > 0){ + res = XGetWindowProperty(xsnarfcon, xdrawable, XA_STRING, 0, left, 0, + AnyPropertyType, &type, &fmt, &len, &dummy, &data); + data = (uchar*)strdup(data); + }else + data = nil; + } + qunlock(&clip.lk); + return (char*)data; +} + +int +clipwrite(char *buf) +{ + int n; + + n = strlen(buf); + qlock(&clip.lk); + if(n >= SnarfSize) + n = SnarfSize - 1; + memmove(clip.buf, buf, n); + clip.buf[n] = 0; + clip.n = n; + /* + * xkmcon so that we get the event in the select loop. + * It seems to be okay to send a message and read an event + * from a Display* at the same time. Let's hope so. + */ + XSetSelectionOwner(xkmcon, XA_PRIMARY, xdrawable, CurrentTime); + XFlush(xkmcon); + qunlock(&clip.lk); + return n; +} +#define long int /* sic */ + +int +atlocalconsole(void) +{ + char *p, *q; + char buf[128]; + + p = getenv("DISPLAY"); + if(p == nil) + return 0; + + /* extract host part */ + q = strchr(p, ':'); + if(q == nil) + return 0; + *q = 0; + + if(strcmp(p, "") == 0) + return 1; + + /* try to match against system name (i.e. for ssh) */ + if(gethostname(buf, sizeof buf) == 0){ + if(strcmp(p, buf) == 0) + return 1; + if(strncmp(p, buf, strlen(p)) == 0 && buf[strlen(p)]=='.') + return 1; + } + + return 0; +} diff --git a/gui-x11/xmem.h b/gui-x11/xmem.h new file mode 100644 index 0000000..a92a47e --- /dev/null +++ b/gui-x11/xmem.h @@ -0,0 +1,60 @@ +#define Font XXFont +#define Screen XXScreen +#define Display XXDisplay + +#include +/* #include */ +#include +#include +#include +#include + +#undef Font +#undef Screen +#undef Display + +/* + * Structure pointed to by X field of Memimage + */ +typedef struct Xmem Xmem; + +enum +{ + PMundef = ~0 /* undefined pixmap id */ +}; + + +struct Xmem +{ + int pmid; /* pixmap id for screen ldepth instance */ + XImage *xi; /* local image if we currenty have the data */ + int dirty; + Rectangle dirtyr; + Rectangle r; + ulong pc; /* who wrote into xi */ +}; + +extern int xtblbit; +extern int x24bitswap; +extern int plan9tox11[]; +extern int x11toplan9[]; +extern int xscreendepth; +extern XXDisplay *xdisplay; +extern Drawable xscreenid; +extern Visual *xvis; +extern GC xgcfill, xgcfill0; +extern int xgcfillcolor, xgcfillcolor0; +extern GC xgccopy, xgccopy0; +extern GC xgczero, xgczero0; +extern int xgczeropm, xgczeropm0; +extern GC xgcsimplesrc, xgcsimplesrc0; +extern int xgcsimplecolor, xgcsimplecolor0, xgcsimplepm, xgcsimplepm0; +extern GC xgcreplsrc, xgcreplsrc0; +extern int xgcreplsrcpm, xgcreplsrcpm0, xgcreplsrctile, xgcreplsrctile0; +extern XImage* allocXdata(Memimage*, Rectangle); +extern void putXdata(Memimage*, Rectangle); +extern XImage* getXdata(Memimage*, Rectangle); +extern void freeXdata(Memimage*); +extern void dirtyXdata(Memimage*, Rectangle); +extern ulong xscreenchan; +extern void xfillcolor(Memimage*, Rectangle, ulong); diff --git a/include/a.out b/include/a.out new file mode 100644 index 0000000000000000000000000000000000000000..0922117fef9e17b336ea36fa09cc099ff71623b6 GIT binary patch literal 4522 zcmb_fTWDNW6g`<4ZLG1Kq*1HpW5B41w%0srtcuvYiNrRhsSip$+|1l$M(4%cJDMtD z<5*;jK}7{W1TFpue)yrNpkV4$u|HaiuMZIgm62Ac8fwtcajkRaBscL{bPs#>*=Mh_ z&*ScW9uM>n57gAu2txr86x2o@4ApLd+_FYnwup#0Q?!VS#70QellK7wo!` zg6GMwkvE`Brj5RrdV@`Xn_wWby$EZ&s(T7HFdKxx?3eZc#3tZ3!A5=`I+=F%L9PY9 zb~;o`jzA|H+m*@0_?5}n=42wBpR(MH)uC;0t;ROEeHTi)ANED!oYcu*-3DQu61&k}o@pK6}v_raHERmLZ*NF)!X+n*i^JL0S zq){`O%A~6`QVb3c_4LXPtBq~~YJ?moe`?8CEy9V!IkaAqlxQ08X3TvZ1|<&rS|*|c&ATf|xX{;Nu*!h)gpAMPlAUHIn2$k=Fn zUje8H{%*b{E+EP!faf279R2Jpv%&7^(hL@6WTwES-#_OCDo1AaEFLZ?492qW`X=_7 zZ#~eA?>rOUf7Cm_c(O1TdVzZ7=u&n5*n4LXT64HmJgMh#AOsygFW^+@JX|NXWWn|<|Cb~yXiWfYaCUZH*9np3&?!lAm|cgQ`Z+1C`{ZT{=A7Mq&OJ1)%o*Cb zcTi%0nJ-X}^~`V_Cm@ePNAa`>?N!Vg$;RAAjFW3I22~#H?Y%bAg6*I+(rI;C9g&W< zw$8S#o$Zm9K4&~(rz6>%lXH@e?K-VO{5N>J_TJUe^{>32Cj2zV{y%%S+`TE!9v7DD z<+O~OQX=g+xva3#8PBo0dxkc9_N1`lwi_2#Y;PJ3we)hrnoQ@dyPcey$fQ>&5;lAW z;&maL^n{g2C-Cn%Q}~~N0#hbtdp7*3e50-h)Pfdyy4Y;@Xj>!3M{DZ~=?SZ*Iu+`!Fhq zgB#I^^S==Wy}@Xt!97;R@tg48!#fc7T0;i+G%Ae07T{W7Fg9XM`8kxi7t^1*E5YzS z%KRQA;y8{M!SqKQPm6gN{%m|N`*4fEE&c_L$D8rT^EQMI*_RBCfk_fss42bu-E5IBZ zV}UX1_xJ32A=ddC8h?QOfw(WQQs25UPvnEvwFBn4)^!ieXRWh$owr)YWI^UU45}}$ z^HAHl!{~g|x^g3!S6b&R^trBep5EABmh8+p{Vt#kRwI*fuXWxAbbM=lozm6)9i^ba zqJS8$ltHy}%ja%T-2im_X**AUjCTpIcTUEWw(Ce?+-P0jF4TPy6+v+xtCuLaVn?8T zL+8B} z0{Hb}2vS{Im42Ss1^JAxzq_D+?9*5Ge-G>nOa2ErXaTav$uUfAYVd#E;7enXsf#(X_chX+{-t6xN=zLc&QfTjk?tgFD#8<~d ze9oswu>SW$H}Tsb&JhLLef}STex$lS4S_mw0J@2{dfaAThaU0a-(q`Te;-2kzxTde zVP7wfK{pgZp|VOBQ9GIRz4Jsg=eb^fV#12z{ntCTV^j_g?HrR5THgxo#@;1U7H>k_ zT(U{Wb7EFIps|dcOlHRIq*V7DY3HYex(LZwK9$;QT=#G9TlQ>}fzj@p`lZp>jVUjC zRhKB`b&3P+yM252O+&pxPGn{L9(dgBv`+wxmkD)+vz@M35>Q&&dmsJkM7U0b`XNyn4f zsPy9b^j+5Yl#qkNJ9@f@<&J@Yo&95Sth;BpA0I={isPxeu8wu}o8I5sjd#mYXENd9 RGTfWAUDt6jw-rzP-vPV8D4qZS literal 0 HcmV?d00001 diff --git a/include/auth.h b/include/auth.h new file mode 100644 index 0000000..cb2d481 --- /dev/null +++ b/include/auth.h @@ -0,0 +1,144 @@ +#pragma src "/sys/src/libauth" +#pragma lib "libauth.a" + +/* + * Interface for typical callers. + */ + +typedef struct AuthInfo AuthInfo; +typedef struct Chalstate Chalstate; +typedef struct Chapreply Chapreply; +typedef struct MSchapreply MSchapreply; +typedef struct UserPasswd UserPasswd; +typedef struct AuthRpc AuthRpc; + +enum +{ + MAXCHLEN= 256, /* max challenge length */ + MAXNAMELEN= 256, /* maximum name length */ + MD5LEN= 16, + + ARok = 0, /* rpc return values */ + ARdone, + ARerror, + ARneedkey, + ARbadkey, + ARwritenext, + ARtoosmall, + ARtoobig, + ARrpcfailure, + ARphase, + + AuthRpcMax = 4096, +}; + +struct AuthRpc +{ + int afd; + char ibuf[AuthRpcMax]; + char obuf[AuthRpcMax]; + char *arg; + uint narg; +}; + +struct AuthInfo +{ + char *cuid; /* caller id */ + char *suid; /* server id */ + char *cap; /* capability (only valid on server side) */ + int nsecret; /* length of secret */ + uchar *secret; /* secret */ +}; + +struct Chalstate +{ + char *user; + char chal[MAXCHLEN]; + int nchal; + void *resp; + int nresp; + +/* for implementation only */ + int afd; /* to factotum */ + AuthRpc *rpc; /* to factotum */ + char userbuf[MAXNAMELEN]; /* temp space if needed */ + int userinchal; /* user was sent to obtain challenge */ +}; + +struct Chapreply /* for protocol "chap" */ +{ + uchar id; + char resp[MD5LEN]; +}; + +struct MSchapreply /* for protocol "mschap" */ +{ + char LMresp[24]; /* Lan Manager response */ + char NTresp[24]; /* NT response */ +}; + +struct UserPasswd +{ + char *user; + char *passwd; +}; + +extern int newns(char*, char*); +extern int addns(char*, char*); + +extern int noworld(char*); +extern int amount(int, char*, int, char*); + +/* these two may get generalized away -rsc */ +extern int login(char*, char*, char*); +extern int httpauth(char*, char*); + +typedef struct Attr Attr; +typedef struct String String; +enum { + AttrNameval, /* name=val -- when matching, must have name=val */ + AttrQuery, /* name? -- when matching, must be present */ + AttrDefault, /* name:=val -- when matching, if present must match INTERNAL */ +}; +struct Attr +{ + int type; + Attr *next; + char *name; + char *val; +}; + +typedef int AuthGetkey(char*); + +int _attrfmt(Fmt*); +Attr *_copyattr(Attr*); +Attr *_delattr(Attr*, char*); +Attr *_findattr(Attr*, char*); +void _freeattr(Attr*); +Attr *_mkattr(int, char*, char*, Attr*); +Attr *_parseattr(char*); +char *_strfindattr(Attr*, char*); +#pragma varargck type "A" Attr* + +extern AuthInfo* fauth_proxy(int, AuthRpc *rpc, AuthGetkey *getkey, char *params); +extern AuthInfo* auth_proxy(int fd, AuthGetkey *getkey, char *fmt, ...); +extern int auth_getkey(char*); +extern int (*amount_getkey)(char*); +extern void auth_freeAI(AuthInfo *ai); +extern int auth_chuid(AuthInfo *ai, char *ns); +extern Chalstate *auth_challenge(char*, ...); +extern AuthInfo* auth_response(Chalstate*); +extern int auth_respond(void*, uint, char*, uint, void*, uint, AuthGetkey *getkey, char*, ...); +extern void auth_freechal(Chalstate*); +extern AuthInfo* auth_userpasswd(char *user, char *passwd); +extern UserPasswd* auth_getuserpasswd(AuthGetkey *getkey, char*, ...); +extern AuthInfo* auth_getinfo(AuthRpc *rpc); +extern AuthRpc* auth_allocrpc(int afd); +extern Attr* auth_attr(AuthRpc *rpc); +extern void auth_freerpc(AuthRpc *rpc); +extern uint auth_rpc(AuthRpc *rpc, char *verb, void *a, int n); +extern int auth_wep(char*, char*, ...); +#pragma varargck argpos auth_proxy 3 +#pragma varargck argpos auth_challenge 1 +#pragma varargck argpos auth_respond 3 +#pragma varargck argpos auth_getuserpasswd 2 diff --git a/include/authsrv.h b/include/authsrv.h new file mode 100644 index 0000000..0e408ee --- /dev/null +++ b/include/authsrv.h @@ -0,0 +1,166 @@ +#pragma src "/sys/src/libauthsrv" +#pragma lib "libauthsrv.a" + +/* + * Interface for talking to authentication server. + */ +typedef struct Ticket Ticket; +typedef struct Ticketreq Ticketreq; +typedef struct Authenticator Authenticator; +typedef struct Nvrsafe Nvrsafe; +typedef struct Passwordreq Passwordreq; +typedef struct OChapreply OChapreply; +typedef struct OMSchapreply OMSchapreply; + +enum +{ + ANAMELEN= 28, /* maximum size of name in previous proto */ + AERRLEN= 64, /* maximum size of errstr in previous proto */ + DOMLEN= 48, /* length of an authentication domain name */ + DESKEYLEN= 7, /* length of a des key for encrypt/decrypt */ + CHALLEN= 8, /* length of a plan9 sk1 challenge */ + NETCHLEN= 16, /* max network challenge length (used in AS protocol) */ + CONFIGLEN= 14, + SECRETLEN= 32, /* max length of a secret */ + + KEYDBOFF= 8, /* length of random data at the start of key file */ + OKEYDBLEN= ANAMELEN+DESKEYLEN+4+2, /* length of an entry in old key file */ + KEYDBLEN= OKEYDBLEN+SECRETLEN, /* length of an entry in key file */ + OMD5LEN= 16, +}; + +/* encryption numberings (anti-replay) */ +enum +{ + AuthTreq=1, /* ticket request */ + AuthChal=2, /* challenge box request */ + AuthPass=3, /* change password */ + AuthOK=4, /* fixed length reply follows */ + AuthErr=5, /* error follows */ + AuthMod=6, /* modify user */ + AuthApop=7, /* apop authentication for pop3 */ + AuthOKvar=9, /* variable length reply follows */ + AuthChap=10, /* chap authentication for ppp */ + AuthMSchap=11, /* MS chap authentication for ppp */ + AuthCram=12, /* CRAM verification for IMAP (RFC2195 & rfc2104) */ + AuthHttp=13, /* http domain login */ + AuthVNC=14, /* VNC server login (deprecated) */ + + + AuthTs=64, /* ticket encrypted with server's key */ + AuthTc, /* ticket encrypted with client's key */ + AuthAs, /* server generated authenticator */ + AuthAc, /* client generated authenticator */ + AuthTp, /* ticket encrypted with client's key for password change */ + AuthHr, /* http reply */ +}; + +struct Ticketreq +{ + char type; + char authid[ANAMELEN]; /* server's encryption id */ + char authdom[DOMLEN]; /* server's authentication domain */ + char chal[CHALLEN]; /* challenge from server */ + char hostid[ANAMELEN]; /* host's encryption id */ + char uid[ANAMELEN]; /* uid of requesting user on host */ +}; +#define TICKREQLEN (3*ANAMELEN+CHALLEN+DOMLEN+1) + +struct Ticket +{ + char num; /* replay protection */ + char chal[CHALLEN]; /* server challenge */ + char cuid[ANAMELEN]; /* uid on client */ + char suid[ANAMELEN]; /* uid on server */ + char key[DESKEYLEN]; /* nonce DES key */ +}; +#define TICKETLEN (CHALLEN+2*ANAMELEN+DESKEYLEN+1) + +struct Authenticator +{ + char num; /* replay protection */ + char chal[CHALLEN]; + ulong id; /* authenticator id, ++'d with each auth */ +}; +#define AUTHENTLEN (CHALLEN+4+1) + +struct Passwordreq +{ + char num; + char old[ANAMELEN]; + char new[ANAMELEN]; + char changesecret; + char secret[SECRETLEN]; /* new secret */ +}; +#define PASSREQLEN (2*ANAMELEN+1+1+SECRETLEN) + +struct OChapreply +{ + uchar id; + char uid[ANAMELEN]; + char resp[OMD5LEN]; +}; + +struct OMSchapreply +{ + char uid[ANAMELEN]; + char LMresp[24]; /* Lan Manager response */ + char NTresp[24]; /* NT response */ +}; + +/* + * convert to/from wire format + */ +extern int convT2M(Ticket*, char*, char*); +extern void convM2T(char*, Ticket*, char*); +extern void convM2Tnoenc(char*, Ticket*); +extern int convA2M(Authenticator*, char*, char*); +extern void convM2A(char*, Authenticator*, char*); +extern int convTR2M(Ticketreq*, char*); +extern void convM2TR(char*, Ticketreq*); +extern int convPR2M(Passwordreq*, char*, char*); +extern void convM2PR(char*, Passwordreq*, char*); + +/* + * convert ascii password to DES key + */ +extern int opasstokey(char*, char*); +extern int passtokey(char*, char*); + +/* + * Nvram interface + */ +enum { + NVwrite = 1<<0, /* always prompt and rewrite nvram */ + NVwriteonerr = 1<<1, /* prompt and rewrite nvram when corrupt */ +}; + +struct Nvrsafe +{ + char machkey[DESKEYLEN]; + uchar machsum; + char authkey[DESKEYLEN]; + uchar authsum; + char config[CONFIGLEN]; + uchar configsum; + char authid[ANAMELEN]; + uchar authidsum; + char authdom[DOMLEN]; + uchar authdomsum; +}; + +extern uchar nvcsum(void*, int); +extern int readnvram(Nvrsafe*, int); + +/* + * call up auth server + */ +extern int authdial(char *netroot, char *authdom); + +/* + * exchange messages with auth server + */ +extern int _asgetticket(int, char*, char*); +extern int _asrdresp(int, char*, int); +extern int sslnegotiate(int, Ticket*, char**, char**); +extern int srvsslnegotiate(int, Ticket*, char**, char**); diff --git a/include/cursor.h b/include/cursor.h new file mode 100644 index 0000000..f305d8a --- /dev/null +++ b/include/cursor.h @@ -0,0 +1,6 @@ +struct Cursor +{ + Point offset; + uchar clr[2*16]; + uchar set[2*16]; +}; diff --git a/include/draw.h b/include/draw.h new file mode 100644 index 0000000..00c31e0 --- /dev/null +++ b/include/draw.h @@ -0,0 +1,519 @@ +#pragma src "/sys/src/libdraw" +#pragma lib "libdraw.a" + +typedef struct Cachefont Cachefont; +typedef struct Cacheinfo Cacheinfo; +typedef struct Cachesubf Cachesubf; +typedef struct Display Display; +typedef struct Font Font; +typedef struct Fontchar Fontchar; +typedef struct Image Image; +typedef struct Mouse Mouse; +typedef struct Point Point; +typedef struct Rectangle Rectangle; +typedef struct RGB RGB; +typedef struct Screen Screen; +typedef struct Subfont Subfont; + +#pragma varargck type "R" Rectangle +#pragma varargck type "P" Point +extern int Rfmt(Fmt*); +extern int Pfmt(Fmt*); + +enum +{ + DOpaque = 0xFFFFFFFF, + DTransparent = 0x00000000, /* only useful for allocimage, memfillcolor */ + DBlack = 0x000000FF, + DWhite = 0xFFFFFFFF, + DRed = 0xFF0000FF, + DGreen = 0x00FF00FF, + DBlue = 0x0000FFFF, + DCyan = 0x00FFFFFF, + DMagenta = 0xFF00FFFF, + DYellow = 0xFFFF00FF, + DPaleyellow = 0xFFFFAAFF, + DDarkyellow = 0xEEEE9EFF, + DDarkgreen = 0x448844FF, + DPalegreen = 0xAAFFAAFF, + DMedgreen = 0x88CC88FF, + DDarkblue = 0x000055FF, + DPalebluegreen= 0xAAFFFFFF, + DPaleblue = 0x0000BBFF, + DBluegreen = 0x008888FF, + DGreygreen = 0x55AAAAFF, + DPalegreygreen = 0x9EEEEEFF, + DYellowgreen = 0x99994CFF, + DMedblue = 0x000099FF, + DGreyblue = 0x005DBBFF, + DPalegreyblue = 0x4993DDFF, + DPurpleblue = 0x8888CCFF, + + DNotacolor = 0xFFFFFF00, + DNofill = DNotacolor, + +}; + +enum +{ + Displaybufsize = 8000, + ICOSSCALE = 1024, + Borderwidth = 4, +}; + +enum +{ + /* refresh methods */ + Refbackup = 0, + Refnone = 1, + Refmesg = 2 +}; +#define NOREFRESH ((void*)-1) + +enum +{ + /* line ends */ + Endsquare = 0, + Enddisc = 1, + Endarrow = 2, + Endmask = 0x1F +}; + +#define ARROW(a, b, c) (Endarrow|((a)<<5)|((b)<<14)|((c)<<23)) + +typedef enum +{ + /* Porter-Duff compositing operators */ + Clear = 0, + + SinD = 8, + DinS = 4, + SoutD = 2, + DoutS = 1, + + S = SinD|SoutD, + SoverD = SinD|SoutD|DoutS, + SatopD = SinD|DoutS, + SxorD = SoutD|DoutS, + + D = DinS|DoutS, + DoverS = DinS|DoutS|SoutD, + DatopS = DinS|SoutD, + DxorS = DoutS|SoutD, /* == SxorD */ + + Ncomp = 12, +} Drawop; + +/* + * image channel descriptors + */ +enum { + CRed = 0, + CGreen, + CBlue, + CGrey, + CAlpha, + CMap, + CIgnore, + NChan, +}; + +#define __DC(type, nbits) ((((type)&15)<<4)|((nbits)&15)) +#define CHAN1(a,b) __DC(a,b) +#define CHAN2(a,b,c,d) (CHAN1((a),(b))<<8|__DC((c),(d))) +#define CHAN3(a,b,c,d,e,f) (CHAN2((a),(b),(c),(d))<<8|__DC((e),(f))) +#define CHAN4(a,b,c,d,e,f,g,h) (CHAN3((a),(b),(c),(d),(e),(f))<<8|__DC((g),(h))) + +#define NBITS(c) ((c)&15) +#define TYPE(c) (((c)>>4)&15) + +enum { + GREY1 = CHAN1(CGrey, 1), + GREY2 = CHAN1(CGrey, 2), + GREY4 = CHAN1(CGrey, 4), + GREY8 = CHAN1(CGrey, 8), + CMAP8 = CHAN1(CMap, 8), + RGB15 = CHAN4(CIgnore, 1, CRed, 5, CGreen, 5, CBlue, 5), + RGB16 = CHAN3(CRed, 5, CGreen, 6, CBlue, 5), + RGB24 = CHAN3(CRed, 8, CGreen, 8, CBlue, 8), + BGR24 = CHAN3(CBlue, 8, CGreen, 8, CRed, 8), + RGBA32 = CHAN4(CRed, 8, CGreen, 8, CBlue, 8, CAlpha, 8), + ARGB32 = CHAN4(CAlpha, 8, CRed, 8, CGreen, 8, CBlue, 8), /* stupid VGAs */ + XRGB32 = CHAN4(CIgnore, 8, CRed, 8, CGreen, 8, CBlue, 8), + XBGR32 = CHAN4(CIgnore, 8, CBlue, 8, CGreen, 8, CRed, 8), +}; + +extern char* chantostr(char*, ulong); +extern ulong strtochan(char*); +extern int chantodepth(ulong); + +struct Point +{ + int x; + int y; +}; + +struct Rectangle +{ + Point min; + Point max; +}; + +typedef void (*Reffn)(Image*, Rectangle, void*); + +struct Screen +{ + Display *display; /* display holding data */ + int id; /* id of system-held Screen */ + Image *image; /* unused; for reference only */ + Image *fill; /* color to paint behind windows */ +}; + +struct Display +{ + QLock qlock; + int locking; /*program is using lockdisplay */ + int dirno; + int fd; + int reffd; + int ctlfd; + int imageid; + int local; + void (*error)(Display*, char*); + char *devdir; + char *windir; + char oldlabel[64]; + ulong dataqid; + Image *white; + Image *black; + Image *opaque; + Image *transparent; + Image *image; + uchar *buf; + int bufsize; + uchar *bufp; + Font *defaultfont; + Subfont *defaultsubfont; + Image *windows; + Image *screenimage; + int _isnewdisplay; +}; + +struct Image +{ + Display *display; /* display holding data */ + int id; /* id of system-held Image */ + Rectangle r; /* rectangle in data area, local coords */ + Rectangle clipr; /* clipping region */ + int depth; /* number of bits per pixel */ + ulong chan; + int repl; /* flag: data replicates to tile clipr */ + Screen *screen; /* 0 if not a window */ + Image *next; /* next in list of windows */ +}; + +struct RGB +{ + ulong red; + ulong green; + ulong blue; +}; + +/* + * Subfonts + * + * given char c, Subfont *f, Fontchar *i, and Point p, one says + * i = f->info+c; + * draw(b, Rect(p.x+i->left, p.y+i->top, + * p.x+i->left+((i+1)->x-i->x), p.y+i->bottom), + * color, f->bits, Pt(i->x, i->top)); + * p.x += i->width; + * to draw characters in the specified color (itself an Image) in Image b. + */ + +struct Fontchar +{ + int x; /* left edge of bits */ + uchar top; /* first non-zero scan-line */ + uchar bottom; /* last non-zero scan-line + 1 */ + char left; /* offset of baseline */ + uchar width; /* width of baseline */ +}; + +struct Subfont +{ + char *name; + short n; /* number of chars in font */ + uchar height; /* height of image */ + char ascent; /* top of image to baseline */ + Fontchar *info; /* n+1 character descriptors */ + Image *bits; /* of font */ + int ref; +}; + +enum +{ + /* starting values */ + LOG2NFCACHE = 6, + NFCACHE = (1<>8)) +#define BPLONG(p, v) (BPSHORT(p, (v)), BPSHORT(p+2, (v)>>16)) + +/* + * Compressed image file parameters and helper routines + */ +#define NMATCH 3 /* shortest match possible */ +#define NRUN (NMATCH+31) /* longest match possible */ +#define NMEM 1024 /* window size */ +#define NDUMP 128 /* maximum length of dump */ +#define NCBLOCK 6000 /* size of compressed blocks */ +extern void _twiddlecompressed(uchar*, int); +extern int _compblocksize(Rectangle, int); + +/* XXX backwards helps; should go */ +extern int log2[]; +extern ulong drawld2chan[]; +extern void drawsetdebug(int); diff --git a/include/dtos.h b/include/dtos.h new file mode 100644 index 0000000..ce2a4ae --- /dev/null +++ b/include/dtos.h @@ -0,0 +1,10 @@ +#if defined(linux) || defined(IRIX) || defined(SOLARIS) || defined(OSF1) || defined(__FreeBSD__) || defined(__APPLE__) +# include "unix.h" +# ifdef __APPLE__ +# define panic dt_panic +# endif +#elif defined(WINDOWS) +# include "winduhz.h" +#else +# error "Define an OS" +#endif diff --git a/include/fcall.h b/include/fcall.h new file mode 100644 index 0000000..3fc3a58 --- /dev/null +++ b/include/fcall.h @@ -0,0 +1,110 @@ +#define VERSION9P "9P2000" + +#define MAXWELEM 16 + +typedef +struct Fcall +{ + uchar type; + u32int fid; + ushort tag; + u32int msize; /* Tversion, Rversion */ + char *version; /* Tversion, Rversion */ + ushort oldtag; /* Tflush */ + char *ename; /* Rerror */ + Qid qid; /* Rattach, Ropen, Rcreate */ + u32int iounit; /* Ropen, Rcreate */ + Qid aqid; /* Rauth */ + u32int afid; /* Tauth, Tattach */ + char *uname; /* Tauth, Tattach */ + char *aname; /* Tauth, Tattach */ + u32int perm; /* Tcreate */ + char *name; /* Tcreate */ + uchar mode; /* Tcreate, Topen */ + u32int newfid; /* Twalk */ + ushort nwname; /* Twalk */ + char *wname[MAXWELEM]; /* Twalk */ + ushort nwqid; /* Rwalk */ + Qid wqid[MAXWELEM]; /* Rwalk */ + vlong offset; /* Tread, Twrite */ + u32int count; /* Tread, Twrite, Rread */ + char *data; /* Twrite, Rread */ + ushort nstat; /* Twstat, Rstat */ + uchar *stat; /* Twstat, Rstat */ +} Fcall; + + +#define GBIT8(p) ((p)[0]) +#define GBIT16(p) ((p)[0]|((p)[1]<<8)) +#define GBIT32(p) ((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24)) +#define GBIT64(p) ((vlong)((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24)) |\ + ((vlong)((p)[4]|((p)[5]<<8)|((p)[6]<<16)|((p)[7]<<24)) << 32)) + +#define PBIT8(p,v) (p)[0]=(v) +#define PBIT16(p,v) (p)[0]=(v);(p)[1]=(v)>>8 +#define PBIT32(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24 +#define PBIT64(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24;\ + (p)[4]=(v)>>32;(p)[5]=(v)>>40;(p)[6]=(v)>>48;(p)[7]=(v)>>56 + +#define BIT8SZ 1 +#define BIT16SZ 2 +#define BIT32SZ 4 +#define BIT64SZ 8 +#define QIDSZ (BIT8SZ+BIT32SZ+BIT64SZ) + +/* STATFIXLEN includes leading 16-bit count */ +/* The count, however, excludes itself; total size is BIT16SZ+count */ +#define STATFIXLEN (BIT16SZ+QIDSZ+5*BIT16SZ+4*BIT32SZ+1*BIT64SZ) /* amount of fixed length data in a stat buffer */ + +#define NOTAG (ushort)~0U /* Dummy tag */ +#define NOFID (u32int)~0U /* Dummy fid */ +#define IOHDRSZ 24 /* ample room for Twrite/Rread header (iounit) */ + +enum +{ + Tversion = 100, + Rversion, + Tauth = 102, + Rauth, + Tattach = 104, + Rattach, + Terror = 106, /* illegal */ + Rerror, + Tflush = 108, + Rflush, + Twalk = 110, + Rwalk, + Topen = 112, + Ropen, + Tcreate = 114, + Rcreate, + Tread = 116, + Rread, + Twrite = 118, + Rwrite, + Tclunk = 120, + Rclunk, + Tremove = 122, + Rremove, + Tstat = 124, + Rstat, + Twstat = 126, + Rwstat, + Tmax, +}; + +uint convM2S(uchar*, uint, Fcall*); +uint convS2M(Fcall*, uchar*, uint); +uint sizeS2M(Fcall*); + +int statcheck(uchar *abuf, uint nbuf); +uint convM2D(uchar*, uint, Dir*, char*); +uint convD2M(Dir*, uchar*, uint); +uint sizeD2M(Dir*); + +int fcallfmt(Fmt*); +int dirfmt(Fmt*); +int dirmodefmt(Fmt*); + +int read9pmsg(int, void*, uint); + diff --git a/include/keyboard.h b/include/keyboard.h new file mode 100644 index 0000000..90b1ca7 --- /dev/null +++ b/include/keyboard.h @@ -0,0 +1,42 @@ +#pragma src "/sys/src/libdraw" +#pragma lib "libdraw.a" + +typedef struct Keyboardctl Keyboardctl; +typedef struct Channel Channel; + +struct Keyboardctl +{ + Channel *c; /* chan(Rune)[20] */ + + char *file; + int consfd; /* to cons file */ + int ctlfd; /* to ctl file */ + int pid; /* of slave proc */ +}; + + +extern Keyboardctl* initkeyboard(char*); +extern int ctlkeyboard(Keyboardctl*, char*); +extern void closekeyboard(Keyboardctl*); + +enum { + KF= 0xF000, /* Rune: beginning of private Unicode space */ + Spec= 0xF800, + /* KF|1, KF|2, ..., KF|0xC is F1, F2, ..., F12 */ + Khome= KF|0x0D, + Kup= KF|0x0E, + Kpgup= KF|0x0F, + Kprint= KF|0x10, + Kleft= KF|0x11, + Kright= KF|0x12, + Kdown= Spec|0x00, + Kview= Spec|0x00, + Kpgdown= KF|0x13, + Kins= KF|0x14, + Kend= KF|0x18, + + Kalt= KF|0x15, + Kshift= KF|0x16, + Kctl= KF|0x17, +}; + diff --git a/include/lib.h b/include/lib.h new file mode 100644 index 0000000..88de366 --- /dev/null +++ b/include/lib.h @@ -0,0 +1,246 @@ +/* avoid name conflicts */ +#define accept pm_accept +#define listen pm_listen +#define sleep ksleep +#define wakeup kwakeup +#define strtod libstrtod +#define pow10 libpow10 + +/* conflicts on some os's */ +#define encrypt libencrypt +#define decrypt libdecrypt +#define oserror liboserror +#define clone libclone +#define atexit libatexit +#define log2 liblog2 +#define log liblog +#define reboot libreboot +#define srand dtsrand +#define rand dtrand +#define nrand dtnrand +#define lrand dtlrand +#define lnrand dtlnrand +#undef timeradd +#define timeradd xtimeradd + + +#define nil ((void*)0) + +typedef unsigned char p9_uchar; +typedef unsigned int p9_uint; +typedef unsigned int p9_ulong; +typedef int p9_long; +typedef signed char p9_schar; +typedef unsigned short p9_ushort; +typedef unsigned short Rune; +typedef unsigned int p9_u32int; +typedef p9_u32int mpdigit; + +/* make sure we don't conflict with predefined types */ +#define schar p9_schar +#define uchar p9_uchar +#define ushort p9_ushort +#define uint p9_uint +#define u32int p9_u32int + +/* #define long int rather than p9_long so that "unsigned long" is valid */ +#define long int +#define ulong p9_ulong +#define vlong p9_vlong +#define uvlong p9_uvlong + +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#define USED(x) if(x);else +#define SET(x) + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80 /* decoding error in UTF */ +}; + +/* + * new rune routines + */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern int runelen(long); +extern int fullrune(char*, int); + +extern int wstrtoutf(char*, Rune*, int); +extern int wstrutflen(Rune*); + +/* + * rune routines from converted str routines + */ +extern long utflen(char*); +extern char* utfrune(char*, long); +extern char* utfrrune(char*, long); + +/* + * Syscall data structures + */ +#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 OEXCL 0x1000 /* or'ed in, exclusive create */ + +#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 */ + +#define ERRMAX 128 /* max length of error string */ +#define KNAMELEN 28 /* max length of name held in kernel */ + +/* 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 QTFILE 0x00 /* plain file */ + +/* bits in Dir.mode */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMAUTH 0x08000000 /* mode bit for authentication files */ +#define DMREAD 0x4 /* mode bit for read permission */ +#define DMWRITE 0x2 /* mode bit for write permission */ +#define DMEXEC 0x1 /* mode bit for execute permission */ + +typedef struct Lock +{ + long key; +} Lock; + +typedef struct QLock +{ + Lock lk; + struct Proc *hold; + struct Proc *first; + struct Proc *last; +} QLock; + +typedef +struct Qid +{ + uvlong path; + ulong vers; + uchar type; +} Qid; + +typedef +struct Dir { + /* system-modified data */ + ushort type; /* server type */ + uint dev; /* server subtype */ + /* file data */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + ulong atime; /* last read time */ + ulong mtime; /* last write time */ + vlong length; /* file length */ + char *name; /* last element of path */ + char *uid; /* owner name */ + char *gid; /* group name */ + char *muid; /* last modifier name */ +} Dir; + +typedef +struct Waitmsg +{ + int pid; /* of loved one */ + ulong time[3]; /* of loved one & descendants */ + char *msg; +} Waitmsg; + +/* + * print routines + */ +typedef struct Fmt Fmt; +struct Fmt{ + uchar runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + 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 (*doquote)(int); +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 int fmtinstall(int, int (*)(Fmt*)); +extern char* fmtstrflush(Fmt*); +extern int runefmtstrinit(Fmt*); +extern Rune* runefmtstrflush(Fmt*); + +extern void* mallocz(ulong, int); + +extern void srand(long); +extern int rand(void); +extern int nrand(int); +extern long lrand(void); +extern long lnrand(long); +extern double frand(void); diff --git a/include/libc.h b/include/libc.h new file mode 100644 index 0000000..9ec90cb --- /dev/null +++ b/include/libc.h @@ -0,0 +1,3 @@ +#include "lib.h" +#include "user.h" + diff --git a/include/libsec.h b/include/libsec.h new file mode 100644 index 0000000..7c187fa --- /dev/null +++ b/include/libsec.h @@ -0,0 +1,340 @@ + +#ifndef _MPINT +typedef struct mpint mpint; +#endif + +///////////////////////////////////////////////////////// +// AES definitions +///////////////////////////////////////////////////////// + +enum +{ + AESbsize= 16, + AESmaxkey= 32, + AESmaxrounds= 14 +}; + +typedef struct AESstate AESstate; +struct AESstate +{ + ulong setup; + int rounds; + int keybytes; + uchar key[AESmaxkey]; /* unexpanded key */ + u32int ekey[4*(AESmaxrounds + 1)]; /* encryption key */ + u32int dkey[4*(AESmaxrounds + 1)]; /* decryption key */ + uchar ivec[AESbsize]; /* initialization vector */ +}; + +void setupAESstate(AESstate *s, uchar key[], int keybytes, uchar *ivec); +void aesCBCencrypt(uchar *p, int len, AESstate *s); +void aesCBCdecrypt(uchar *p, int len, AESstate *s); + +///////////////////////////////////////////////////////// +// Blowfish Definitions +///////////////////////////////////////////////////////// + +enum +{ + BFbsize = 8, + BFrounds = 16 +}; + +// 16-round Blowfish +typedef struct BFstate BFstate; +struct BFstate +{ + ulong setup; + + uchar key[56]; + uchar ivec[8]; + + u32int pbox[BFrounds+2]; + u32int sbox[1024]; +}; + +void setupBFstate(BFstate *s, uchar key[], int keybytes, uchar *ivec); +void bfCBCencrypt(uchar*, int, BFstate*); +void bfCBCdecrypt(uchar*, int, BFstate*); +void bfECBencrypt(uchar*, int, BFstate*); +void bfECBdecrypt(uchar*, int, BFstate*); + +///////////////////////////////////////////////////////// +// DES definitions +///////////////////////////////////////////////////////// + +enum +{ + DESbsize= 8 +}; + +// single des +typedef struct DESstate DESstate; +struct DESstate +{ + ulong setup; + uchar key[8]; /* unexpanded key */ + ulong expanded[32]; /* expanded key */ + uchar ivec[8]; /* initialization vector */ +}; + +void setupDESstate(DESstate *s, uchar key[8], uchar *ivec); +void des_key_setup(uchar[8], ulong[32]); +void block_cipher(ulong*, uchar*, int); +void desCBCencrypt(uchar*, int, DESstate*); +void desCBCdecrypt(uchar*, int, DESstate*); +void desECBencrypt(uchar*, int, DESstate*); +void desECBdecrypt(uchar*, int, DESstate*); + +// for backward compatibility with 7 byte DES key format +void des56to64(uchar *k56, uchar *k64); +void des64to56(uchar *k64, uchar *k56); +void key_setup(uchar[7], ulong[32]); + +// triple des encrypt/decrypt orderings +enum { + DES3E= 0, + DES3D= 1, + DES3EEE= 0, + DES3EDE= 2, + DES3DED= 5, + DES3DDD= 7 +}; + +typedef struct DES3state DES3state; +struct DES3state +{ + ulong setup; + uchar key[3][8]; /* unexpanded key */ + ulong expanded[3][32]; /* expanded key */ + uchar ivec[8]; /* initialization vector */ +}; + +void setupDES3state(DES3state *s, uchar key[3][8], uchar *ivec); +void triple_block_cipher(ulong keys[3][32], uchar*, int); +void des3CBCencrypt(uchar*, int, DES3state*); +void des3CBCdecrypt(uchar*, int, DES3state*); +void des3ECBencrypt(uchar*, int, DES3state*); +void des3ECBdecrypt(uchar*, int, DES3state*); + +///////////////////////////////////////////////////////// +// digests +///////////////////////////////////////////////////////// + +enum +{ + SHA1dlen= 20, /* SHA digest length */ + MD4dlen= 16, /* MD4 digest length */ + MD5dlen= 16 /* MD5 digest length */ +}; + +typedef struct DigestState DigestState; +struct DigestState +{ + ulong len; + u32int state[5]; + uchar buf[128]; + int blen; + char malloced; + char seeded; +}; +typedef struct DigestState SHAstate; /* obsolete name */ +typedef struct DigestState SHA1state; +typedef struct DigestState MD5state; +typedef struct DigestState MD4state; + +DigestState* md4(uchar*, ulong, uchar*, DigestState*); +DigestState* md5(uchar*, ulong, uchar*, DigestState*); +DigestState* sha1(uchar*, ulong, uchar*, DigestState*); +DigestState* hmac_md5(uchar*, ulong, uchar*, ulong, uchar*, DigestState*); +DigestState* hmac_sha1(uchar*, ulong, uchar*, ulong, uchar*, DigestState*); +char* sha1pickle(SHA1state*); +SHA1state* sha1unpickle(char*); + +///////////////////////////////////////////////////////// +// random number generation +///////////////////////////////////////////////////////// +void genrandom(uchar *buf, int nbytes); +void prng(uchar *buf, int nbytes); +ulong fastrand(void); +ulong nfastrand(ulong); + +///////////////////////////////////////////////////////// +// primes +///////////////////////////////////////////////////////// +void genprime(mpint *p, int n, int accuracy); // generate an n bit probable prime +void gensafeprime(mpint *p, mpint *alpha, int n, int accuracy); // prime and generator +void genstrongprime(mpint *p, int n, int accuracy); // generate an n bit strong prime +void DSAprimes(mpint *q, mpint *p, uchar seed[SHA1dlen]); +int probably_prime(mpint *n, int nrep); // miller-rabin test +int smallprimetest(mpint *p); // returns -1 if not prime, 0 otherwise + +///////////////////////////////////////////////////////// +// rc4 +///////////////////////////////////////////////////////// +typedef struct RC4state RC4state; +struct RC4state +{ + uchar state[256]; + uchar x; + uchar y; +}; + +void setupRC4state(RC4state*, uchar*, int); +void rc4(RC4state*, uchar*, int); +void rc4skip(RC4state*, int); +void rc4back(RC4state*, int); + +///////////////////////////////////////////////////////// +// rsa +///////////////////////////////////////////////////////// +typedef struct RSApub RSApub; +typedef struct RSApriv RSApriv; + +// public/encryption key +struct RSApub +{ + mpint *n; // modulus + mpint *ek; // exp (encryption key) +}; + +// private/decryption key +struct RSApriv +{ + RSApub pub; + + mpint *dk; // exp (decryption key) + + // precomputed values to help with chinese remainder theorem calc + mpint *p; + mpint *q; + mpint *kp; // dk mod p-1 + mpint *kq; // dk mod q-1 + mpint *c2; // (inv p) mod q +}; + +RSApriv* rsagen(int nlen, int elen, int rounds); +RSApriv* rsafill(mpint *n, mpint *e, mpint *d, mpint *p, mpint *q); +mpint* rsaencrypt(RSApub *k, mpint *in, mpint *out); +mpint* rsadecrypt(RSApriv *k, mpint *in, mpint *out); +RSApub* rsapuballoc(void); +void rsapubfree(RSApub*); +RSApriv* rsaprivalloc(void); +void rsaprivfree(RSApriv*); +RSApub* rsaprivtopub(RSApriv*); +RSApub* X509toRSApub(uchar*, int, char*, int); +RSApriv* asn1toRSApriv(uchar*, int); +void asn1dump(uchar *der, int len); +uchar* decodepem(char *s, char *type, int *len); +uchar* X509gen(RSApriv *priv, char *subj, ulong valid[2], int *certlen); +uchar* X509req(RSApriv *priv, char *subj, int *certlen); +char* X509verify(uchar *cert, int ncert, RSApub *pk); +void X509dump(uchar *cert, int ncert); +///////////////////////////////////////////////////////// +// elgamal +///////////////////////////////////////////////////////// +typedef struct EGpub EGpub; +typedef struct EGpriv EGpriv; +typedef struct EGsig EGsig; + +// public/encryption key +struct EGpub +{ + mpint *p; // modulus + mpint *alpha; // generator + mpint *key; // (encryption key) alpha**secret mod p +}; + +// private/decryption key +struct EGpriv +{ + EGpub pub; + mpint *secret; // (decryption key) +}; + +// signature +struct EGsig +{ + mpint *r, *s; +}; + +EGpriv* eggen(int nlen, int rounds); +mpint* egencrypt(EGpub *k, mpint *in, mpint *out); +mpint* egdecrypt(EGpriv *k, mpint *in, mpint *out); +EGsig* egsign(EGpriv *k, mpint *m); +int egverify(EGpub *k, EGsig *sig, mpint *m); +EGpub* egpuballoc(void); +void egpubfree(EGpub*); +EGpriv* egprivalloc(void); +void egprivfree(EGpriv*); +EGsig* egsigalloc(void); +void egsigfree(EGsig*); +EGpub* egprivtopub(EGpriv*); + +///////////////////////////////////////////////////////// +// dsa +///////////////////////////////////////////////////////// +typedef struct DSApub DSApub; +typedef struct DSApriv DSApriv; +typedef struct DSAsig DSAsig; + +// public/encryption key +struct DSApub +{ + mpint *p; // modulus + mpint *q; // group order, q divides p-1 + mpint *alpha; // group generator + mpint *key; // (encryption key) alpha**secret mod p +}; + +// private/decryption key +struct DSApriv +{ + DSApub pub; + mpint *secret; // (decryption key) +}; + +// signature +struct DSAsig +{ + mpint *r, *s; +}; + +DSApriv* dsagen(DSApub *opub); +DSAsig* dsasign(DSApriv *k, mpint *m); +int dsaverify(DSApub *k, DSAsig *sig, mpint *m); +DSApub* dsapuballoc(void); +void dsapubfree(DSApub*); +DSApriv* dsaprivalloc(void); +void dsaprivfree(DSApriv*); +DSAsig* dsasigalloc(void); +void dsasigfree(DSAsig*); +DSApub* dsaprivtopub(DSApriv*); + +///////////////////////////////////////////////////////// +// TLS +///////////////////////////////////////////////////////// +typedef struct Thumbprint{ + struct Thumbprint *next; + uchar sha1[SHA1dlen]; +} Thumbprint; + +typedef struct TLSconn{ + char dir[40]; // connection directory + uchar *cert; // certificate (local on input, remote on output) + uchar *sessionID; + int certlen, sessionIDlen; + int (*trace)(char*fmt, ...); +} TLSconn; + +// tlshand.c +extern int tlsClient(int fd, TLSconn *c); +extern int tlsServer(int fd, TLSconn *c); + +// thumb.c +extern Thumbprint* initThumbprints(char *ok, char *crl); +extern void freeThumbprints(Thumbprint *ok); +extern int okThumbprint(uchar *sha1, Thumbprint *ok); + +// readcert.c +extern uchar *readcert(char *filename, int *pcertlen); diff --git a/include/memdraw.h b/include/memdraw.h new file mode 100644 index 0000000..39e2826 --- /dev/null +++ b/include/memdraw.h @@ -0,0 +1,200 @@ +#pragma src "/sys/src/libmemdraw" +#pragma lib "libmemdraw.a" + +typedef struct Memimage Memimage; +typedef struct Memdata Memdata; +typedef struct Memsubfont Memsubfont; +typedef struct Memlayer Memlayer; +typedef struct Memcmap Memcmap; +typedef struct Memdrawparam Memdrawparam; + +/* + * Memdata is allocated from main pool, but .data from the image pool. + * Memdata is allocated separately to permit patching its pointer after + * compaction when windows share the image data. + * The first word of data is a back pointer to the Memdata, to find + * The word to patch. + */ + +struct Memdata +{ + ulong *base; /* allocated data pointer */ + uchar *bdata; /* pointer to first byte of actual data; word-aligned */ + int ref; /* number of Memimages using this data */ + void* imref; + int allocd; /* is this malloc'd? */ +}; + +enum { + Frepl = 1<<0, /* is replicated */ + Fsimple = 1<<1, /* is 1x1 */ + Fgrey = 1<<2, /* is grey */ + Falpha = 1<<3, /* has explicit alpha */ + Fcmap = 1<<4, /* has cmap channel */ + Fbytes = 1<<5, /* has only 8-bit channels */ +}; + +struct Memimage +{ + Rectangle r; /* rectangle in data area, local coords */ + Rectangle clipr; /* clipping region */ + int depth; /* number of bits of storage per pixel */ + int nchan; /* number of channels */ + ulong chan; /* channel descriptions */ + Memcmap *cmap; + + Memdata *data; /* pointer to data; shared by windows in this image */ + int zero; /* data->bdata+zero==&byte containing (0,0) */ + ulong width; /* width in words of a single scan line */ + Memlayer *layer; /* nil if not a layer*/ + ulong flags; + + int shift[NChan]; + int mask[NChan]; + int nbits[NChan]; + + void *X; +}; + +struct Memcmap +{ + uchar cmap2rgb[3*256]; + uchar rgb2cmap[16*16*16]; +}; + +/* + * Subfonts + * + * given char c, Subfont *f, Fontchar *i, and Point p, one says + * i = f->info+c; + * draw(b, Rect(p.x+i->left, p.y+i->top, + * p.x+i->left+((i+1)->x-i->x), p.y+i->bottom), + * color, f->bits, Pt(i->x, i->top)); + * p.x += i->width; + * to draw characters in the specified color (itself a Memimage) in Memimage b. + */ + +struct Memsubfont +{ + char *name; + short n; /* number of chars in font */ + uchar height; /* height of bitmap */ + char ascent; /* top of bitmap to baseline */ + Fontchar *info; /* n+1 character descriptors */ + Memimage *bits; /* of font */ +}; + +/* + * Encapsulated parameters and information for sub-draw routines. + */ +enum { + Simplesrc=1<<0, + Simplemask=1<<1, + Replsrc=1<<2, + Replmask=1<<3, + Fullmask=1<<4, +}; +struct Memdrawparam +{ + Memimage *dst; + Rectangle r; + Memimage *src; + Rectangle sr; + Memimage *mask; + Rectangle mr; + int op; + + ulong state; + ulong mval; /* if Simplemask, the mask pixel in mask format */ + ulong mrgba; /* mval in rgba */ + ulong sval; /* if Simplesrc, the source pixel in src format */ + ulong srgba; /* sval in rgba */ + ulong sdval; /* sval in dst format */ +}; + +/* + * Memimage management + */ + +extern Memimage* allocmemimage(Rectangle, ulong); +extern Memimage* _allocmemimage(Rectangle, ulong); +extern Memimage* allocmemimaged(Rectangle, ulong, Memdata*, void*); +extern Memimage* readmemimage(int); +extern Memimage* creadmemimage(int); +extern int writememimage(int, Memimage*); +extern void freememimage(Memimage*); +extern int _loadmemimage(Memimage*, Rectangle, uchar*, int); +extern int _cloadmemimage(Memimage*, Rectangle, uchar*, int); +extern int _unloadmemimage(Memimage*, Rectangle, uchar*, int); +extern int loadmemimage(Memimage*, Rectangle, uchar*, int); +extern int cloadmemimage(Memimage*, Rectangle, uchar*, int); +extern int unloadmemimage(Memimage*, Rectangle, uchar*, int); +extern ulong* wordaddr(Memimage*, Point); +extern uchar* byteaddr(Memimage*, Point); +extern int drawclip(Memimage*, Rectangle*, Memimage*, Point*, Memimage*, Point*, Rectangle*, Rectangle*); +extern void memfillcolor(Memimage*, ulong); +extern int memsetchan(Memimage*, ulong); + +/* + * Graphics + */ +extern void memdraw(Memimage*, Rectangle, Memimage*, Point, Memimage*, Point, int); +extern void memline(Memimage*, Point, Point, int, int, int, Memimage*, Point, int); +extern void mempoly(Memimage*, Point*, int, int, int, int, Memimage*, Point, int); +extern void memfillpoly(Memimage*, Point*, int, int, Memimage*, Point, int); +extern void _memfillpolysc(Memimage*, Point*, int, int, Memimage*, Point, int, int, int, int); +extern Memdrawparam* _memimagedrawsetup(Memimage*, Rectangle, Memimage*, Point, Memimage*, Point, int); +extern void _memimagedraw(Memdrawparam*); +extern void memimagedraw(Memimage*, Rectangle, Memimage*, Point, Memimage*, Point, int); +extern int hwdraw(Memdrawparam*); +extern void memimageline(Memimage*, Point, Point, int, int, int, Memimage*, Point, int); +extern void _memimageline(Memimage*, Point, Point, int, int, int, Memimage*, Point, Rectangle, int); +extern Point memimagestring(Memimage*, Point, Memimage*, Point, Memsubfont*, char*); +extern void memellipse(Memimage*, Point, int, int, int, Memimage*, Point, int); +extern void memarc(Memimage*, Point, int, int, int, Memimage*, Point, int, int, int); +extern Rectangle memlinebbox(Point, Point, int, int, int); +extern int memlineendsize(int); +extern void _memmkcmap(void); +extern void memimageinit(void); + +/* + * Subfont management + */ +extern Memsubfont* allocmemsubfont(char*, int, int, int, Fontchar*, Memimage*); +extern Memsubfont* openmemsubfont(char*); +extern void freememsubfont(Memsubfont*); +extern Point memsubfontwidth(Memsubfont*, char*); +extern Memsubfont* getmemdefont(void); + +/* + * Predefined + */ +extern Memimage* memwhite; +extern Memimage* memblack; +extern Memimage* memopaque; +extern Memimage* memtransparent; +extern Memcmap *memdefcmap; + +/* + * Kernel interface + */ +void memimagemove(void*, void*); + +/* + * Kernel cruft + */ +extern void rdb(void); +extern int iprint(char*, ...); +#pragma varargck argpos iprint 1 +extern int drawdebug; + +/* + * doprint interface: numbconv bit strings + */ +#pragma varargck type "llb" vlong +#pragma varargck type "llb" uvlong +#pragma varargck type "lb" long +#pragma varargck type "lb" ulong +#pragma varargck type "b" int +#pragma varargck type "b" uint + diff --git a/include/memlayer.h b/include/memlayer.h new file mode 100644 index 0000000..1e0b211 --- /dev/null +++ b/include/memlayer.h @@ -0,0 +1,51 @@ +#pragma src "/sys/src/libmemlayer" +#pragma lib "libmemlayer.a" + +typedef struct Memscreen Memscreen; +typedef void (*Refreshfn)(Memimage*, Rectangle, void*); + +struct Memscreen +{ + Memimage *frontmost; /* frontmost layer on screen */ + Memimage *rearmost; /* rearmost layer on screen */ + Memimage *image; /* upon which all layers are drawn */ + Memimage *fill; /* if non-zero, picture to use when repainting */ +}; + +struct Memlayer +{ + Rectangle screenr; /* true position of layer on screen */ + Point delta; /* add delta to go from image coords to screen */ + Memscreen *screen; /* screen this layer belongs to */ + Memimage *front; /* window in front of this one */ + Memimage *rear; /* window behind this one*/ + int clear; /* layer is fully visible */ + Memimage *save; /* save area for obscured parts */ + Refreshfn refreshfn; /* function to call to refresh obscured parts if save==nil */ + void *refreshptr; /* argument to refreshfn */ +}; + +/* + * These functions accept local coordinates + */ +int memload(Memimage*, Rectangle, uchar*, int, int); +int memunload(Memimage*, Rectangle, uchar*, int); + +/* + * All these functions accept screen coordinates, not local ones. + */ +void _memlayerop(void (*fn)(Memimage*, Rectangle, Rectangle, void*, int), Memimage*, Rectangle, Rectangle, void*); +Memimage* memlalloc(Memscreen*, Rectangle, Refreshfn, void*, ulong); +void memldelete(Memimage*); +void memlfree(Memimage*); +void memltofront(Memimage*); +void memltofrontn(Memimage**, int); +void _memltofrontfill(Memimage*, int); +void memltorear(Memimage*); +void memltorearn(Memimage**, int); +int memlsetrefresh(Memimage*, Refreshfn, void*); +void memlhide(Memimage*, Rectangle); +void memlexpose(Memimage*, Rectangle); +void _memlsetclear(Memscreen*); +int memlorigin(Memimage*, Point, Point); +void memlnorefresh(Memimage*, Rectangle, void*); diff --git a/include/mp.h b/include/mp.h new file mode 100644 index 0000000..fa912ca --- /dev/null +++ b/include/mp.h @@ -0,0 +1,134 @@ +#define _MPINT 1 + +// the code assumes mpdigit to be at least an int +// mpdigit must be an atomic type. mpdigit is defined +// in the architecture specific u.h + +typedef struct mpint mpint; + +struct mpint +{ + int sign; // +1 or -1 + int size; // allocated digits + int top; // significant digits + mpdigit *p; + char flags; +}; + +enum +{ + MPstatic= 0x01, + Dbytes= sizeof(mpdigit), // bytes per digit + Dbits= Dbytes*8 // bits per digit +}; + +// allocation +void mpsetminbits(int n); // newly created mpint's get at least n bits +mpint* mpnew(int n); // create a new mpint with at least n bits +void mpfree(mpint *b); +void mpbits(mpint *b, int n); // ensure that b has at least n bits +void mpnorm(mpint *b); // dump leading zeros +mpint* mpcopy(mpint *b); +void mpassign(mpint *old, mpint *new); + +// random bits +mpint* mprand(int bits, void (*gen)(uchar*, int), mpint *b); + +// conversion +mpint* strtomp(char*, char**, int, mpint*); // ascii +int mpfmt(Fmt*); +char* mptoa(mpint*, int, char*, int); +mpint* letomp(uchar*, uint, mpint*); // byte array, little-endian +int mptole(mpint*, uchar*, uint, uchar**); +mpint* betomp(uchar*, uint, mpint*); // byte array, little-endian +int mptobe(mpint*, uchar*, uint, uchar**); +uint mptoui(mpint*); // unsigned int +mpint* uitomp(uint, mpint*); +int mptoi(mpint*); // int +mpint* itomp(int, mpint*); +uvlong mptouv(mpint*); // unsigned vlong +mpint* uvtomp(uvlong, mpint*); +vlong mptov(mpint*); // vlong +mpint* vtomp(vlong, mpint*); + +// divide 2 digits by one +void mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient); + +// in the following, the result mpint may be +// the same as one of the inputs. +void mpadd(mpint *b1, mpint *b2, mpint *sum); // sum = b1+b2 +void mpsub(mpint *b1, mpint *b2, mpint *diff); // diff = b1-b2 +void mpleft(mpint *b, int shift, mpint *res); // res = b<>shift +void mpmul(mpint *b1, mpint *b2, mpint *prod); // prod = b1*b2 +void mpexp(mpint *b, mpint *e, mpint *m, mpint *res); // res = b**e mod m +void mpmod(mpint *b, mpint *m, mpint *remainder); // remainder = b mod m + +// quotient = dividend/divisor, remainder = dividend % divisor +void mpdiv(mpint *dividend, mpint *divisor, mpint *quotient, mpint *remainder); + +// return neg, 0, pos as b1-b2 is neg, 0, pos +int mpcmp(mpint *b1, mpint *b2); + +// extended gcd return d, x, and y, s.t. d = gcd(a,b) and ax+by = d +void mpextendedgcd(mpint *a, mpint *b, mpint *d, mpint *x, mpint *y); + +// res = b**-1 mod m +void mpinvert(mpint *b, mpint *m, mpint *res); + +// bit counting +int mpsignif(mpint*); // number of sigificant bits in mantissa +int mplowbits0(mpint*); // k, where n = 2**k * q for odd q + +// well known constants +extern mpint *mpzero, *mpone, *mptwo; + +// sum[0:alen] = a[0:alen-1] + b[0:blen-1] +// prereq: alen >= blen, sum has room for alen+1 digits +void mpvecadd(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *sum); + +// diff[0:alen-1] = a[0:alen-1] - b[0:blen-1] +// prereq: alen >= blen, diff has room for alen digits +void mpvecsub(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *diff); + +// p[0:n] += m * b[0:n-1] +// prereq: p has room for n+1 digits +void mpvecdigmuladd(mpdigit *b, int n, mpdigit m, mpdigit *p); + +// p[0:n] -= m * b[0:n-1] +// prereq: p has room for n+1 digits +int mpvecdigmulsub(mpdigit *b, int n, mpdigit m, mpdigit *p); + +// p[0:alen*blen-1] = a[0:alen-1] * b[0:blen-1] +// prereq: alen >= blen, p has room for m*n digits +void mpvecmul(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p); + +// sign of a - b or zero if the same +int mpveccmp(mpdigit *a, int alen, mpdigit *b, int blen); + +// divide the 2 digit dividend by the one digit divisor and stick in quotient +// we assume that the result is one digit - overflow is all 1's +void mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient); + +// playing with magnitudes +int mpmagcmp(mpint *b1, mpint *b2); +void mpmagadd(mpint *b1, mpint *b2, mpint *sum); // sum = b1+b2 +void mpmagsub(mpint *b1, mpint *b2, mpint *sum); // sum = b1+b2 + +// chinese remainder theorem +typedef struct CRTpre CRTpre; // precomputed values for converting + // twixt residues and mpint +typedef struct CRTres CRTres; // residue form of an mpint + +struct CRTres +{ + int n; // number of residues + mpint *r[1]; // residues +}; + +CRTpre* crtpre(int, mpint**); // precompute conversion values +CRTres* crtin(CRTpre*, mpint*); // convert mpint to residues +void crtout(CRTpre*, CRTres*, mpint*); // convert residues to mpint +void crtprefree(CRTpre*); +void crtresfree(CRTres*); + diff --git a/include/u.h b/include/u.h new file mode 100644 index 0000000..724410f --- /dev/null +++ b/include/u.h @@ -0,0 +1,26 @@ +#include "dtos.h" + +/* avoid name conflicts */ +#undef accept +#undef listen + +/* sys calls */ +#undef bind +#undef chdir +#undef close +#undef create +#undef dup +#undef export +#undef fstat +#undef fwstat +#undef mount +#undef open +#undef start +#undef read +#undef remove +#undef seek +#undef stat +#undef write +#undef wstat +#undef unmount +#undef pipe diff --git a/include/unix.h b/include/unix.h new file mode 100644 index 0000000..a196494 --- /dev/null +++ b/include/unix.h @@ -0,0 +1,14 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef long long p9_vlong; +typedef unsigned long long p9_uvlong; diff --git a/include/user.h b/include/user.h new file mode 100644 index 0000000..a3e2685 --- /dev/null +++ b/include/user.h @@ -0,0 +1,65 @@ +/* sys calls */ +#define bind sysbind +#define chdir syschdir +#define close sysclose +#define create syscreate +#define dup sysdup +#define export sysexport +#define fstat sysfstat +#define fwstat sysfwstat +#define mount sysmount +#define open sysopen +#define read sysread +#define remove sysremove +#define seek sysseek +#define stat sysstat +#define write syswrite +#define wstat syswstat +#define unmount sysunmount +#define pipe syspipe +#define rendezvous sysrendezvous +#define getpid sysgetpid +#define time systime +#define nsec sysnsec +#define pread syspread +#define pwrite syspwrite + +extern int bind(char*, char*, int); +extern int chdir(char*); +extern int close(int); +extern int create(char*, int, ulong); +extern int dup(int, int); +extern int export(int); +extern int fstat(int, uchar*, int); +extern int fwstat(int, uchar*, int); +extern int mount(int, int, char*, int, char*); +extern int unmount(char*, char*); +extern int open(char*, int); +extern int pipe(int*); +extern long read(int, void*, long); +extern long readn(int, void*, long); +extern int remove(char*); +extern vlong seek(int, vlong, int); +extern int stat(char*, uchar*, int); +extern long write(int, void*, long); +extern int wstat(char*, uchar*, int); + +extern Dir *dirstat(char*); +extern Dir *dirfstat(int); +extern int dirwstat(char*, Dir*); +extern int dirfwstat(int, Dir*); +extern long dirread(int, Dir*, long); + +/* + * network dialing and authentication + */ +#define NETPATHLEN 40 +extern int accept(int, char*); +extern int announce(char*, char*); +extern int dial(char*, char*, char*, int*); +extern int hangup(int); +extern int listen(char*, char*); +extern char *netmkaddr(char*, char*, char*); +extern int reject(int, char*, char*); + +extern char* argv0; diff --git a/include/x b/include/x new file mode 100644 index 0000000..e69de29 diff --git a/include/x.c b/include/x.c new file mode 100644 index 0000000..02995e2 --- /dev/null +++ b/include/x.c @@ -0,0 +1,6 @@ +#include + +void +main(void) +{ +} diff --git a/kern/Makefile b/kern/Makefile new file mode 100644 index 0000000..589bbb0 --- /dev/null +++ b/kern/Makefile @@ -0,0 +1,50 @@ +LIB=libkern.a +CC=gcc +CFLAGS=-I../include -I. -c -ggdb -D_THREAD_SAFE -pthread +O=o +#CC=cl +#CFLAGS=-c -nologo -W3 -YX -Zi -MT -Zl -I../include -DWINDOWS +#O=obj + +OFILES=\ + allocb.$O\ + cache.$O\ + chan.$O\ + data.$O\ + dev.$O\ + devcons.$O\ + devdraw.$O\ + devfs.$O\ + devip.$O\ + devip-posix.$O\ + devmnt.$O\ + devmouse.$O\ + devpipe.$O\ + devroot.$O\ + devssl.$O\ + devtab.$O\ + error.$O\ + parse.$O\ + pgrp.$O\ + posix.$O\ + procinit.$O\ + rwlock.$O\ + sleep.$O\ + smalloc.$O\ + stub.$O\ + sysfile.$O\ + sysproc.$O\ + qio.$O\ + qlock.$O\ + term.$O\ + todo.$O\ + uart.$O\ + waserror.$O + +$(LIB): $(OFILES) + ar r $(LIB) $(OFILES) + ranlib $(LIB) + +%.$O: %.c + $(CC) $(CFLAGS) $*.c + diff --git a/kern/allocb.c b/kern/allocb.c new file mode 100644 index 0000000..dbc91e1 --- /dev/null +++ b/kern/allocb.c @@ -0,0 +1,165 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Hdrspc = 64, /* leave room for high-level headers */ + Bdead = 0x51494F42, /* "QIOB" */ +}; + +struct +{ + Lock lk; + ulong bytes; +} ialloc; + +static Block* +_allocb(int size) +{ + Block *b; + ulong addr; + + if((b = mallocz(sizeof(Block)+size+Hdrspc, 0)) == nil) + return nil; + + b->next = nil; + b->list = nil; + b->free = 0; + b->flag = 0; + + /* align start of data portion by rounding up */ + addr = (ulong)b; + addr = ROUND(addr + sizeof(Block), BLOCKALIGN); + b->base = (uchar*)addr; + + /* align end of data portion by rounding down */ + b->lim = ((uchar*)b) + sizeof(Block)+size+Hdrspc; + addr = (ulong)(b->lim); + addr = addr & ~(BLOCKALIGN-1); + b->lim = (uchar*)addr; + + /* leave sluff at beginning for added headers */ + b->rp = b->lim - ROUND(size, BLOCKALIGN); + if(b->rp < b->base) + panic("_allocb"); + b->wp = b->rp; + + return b; +} + +Block* +allocb(int size) +{ + Block *b; + + /* + * Check in a process and wait until successful. + * Can still error out of here, though. + */ + if(up == nil) + panic("allocb without up: %luX\n", getcallerpc(&size)); + if((b = _allocb(size)) == nil){ + panic("allocb: no memory for %d bytes\n", size); + } + setmalloctag(b, getcallerpc(&size)); + + return b; +} + +Block* +iallocb(int size) +{ + Block *b; + static int m1, m2; + + if(ialloc.bytes > conf.ialloc){ + if((m1++%10000)==0) + print("iallocb: limited %lud/%lud\n", + ialloc.bytes, conf.ialloc); + return 0; + } + + if((b = _allocb(size)) == nil){ + if((m2++%10000)==0) + print("iallocb: no memory %lud/%lud\n", + ialloc.bytes, conf.ialloc); + return nil; + } + setmalloctag(b, getcallerpc(&size)); + b->flag = BINTR; + + ilock(&ialloc.lk); + ialloc.bytes += b->lim - b->base; + iunlock(&ialloc.lk); + + return b; +} + +void +freeb(Block *b) +{ + void *dead = (void*)Bdead; + + if(b == nil) + return; + + /* + * drivers which perform non cache coherent DMA manage their own buffer + * pool of uncached buffers and provide their own free routine. + */ + if(b->free) { + b->free(b); + return; + } + if(b->flag & BINTR) { + ilock(&ialloc.lk); + ialloc.bytes -= b->lim - b->base; + iunlock(&ialloc.lk); + } + + /* poison the block in case someone is still holding onto it */ + b->next = dead; + b->rp = dead; + b->wp = dead; + b->lim = dead; + b->base = dead; + + free(b); +} + +void +checkb(Block *b, char *msg) +{ + void *dead = (void*)Bdead; + + if(b == dead) + panic("checkb b %s %lux", msg, b); + if(b->base == dead || b->lim == dead || b->next == dead + || b->rp == dead || b->wp == dead){ + print("checkb: base 0x%8.8luX lim 0x%8.8luX next 0x%8.8luX\n", + b->base, b->lim, b->next); + print("checkb: rp 0x%8.8luX wp 0x%8.8luX\n", b->rp, b->wp); + panic("checkb dead: %s\n", msg); + } + + if(b->base > b->lim) + panic("checkb 0 %s %lux %lux", msg, b->base, b->lim); + if(b->rp < b->base) + panic("checkb 1 %s %lux %lux", msg, b->base, b->rp); + if(b->wp < b->base) + panic("checkb 2 %s %lux %lux", msg, b->base, b->wp); + if(b->rp > b->lim) + panic("checkb 3 %s %lux %lux", msg, b->rp, b->lim); + if(b->wp > b->lim) + panic("checkb 4 %s %lux %lux", msg, b->wp, b->lim); + +} + +void +iallocsummary(void) +{ + print("ialloc %lud/%lud\n", ialloc.bytes, conf.ialloc); +} diff --git a/kern/cache.c b/kern/cache.c new file mode 100644 index 0000000..56ee23c --- /dev/null +++ b/kern/cache.c @@ -0,0 +1,46 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + + +void +cinit(void) +{ +} + +void +copen(Chan *c) +{ + USED(c); +} + +int +cread(Chan *c, uchar *buf, int len, vlong off) +{ + USED(c); + USED(buf); + USED(len); + USED(off); + + return 0; +} + +void +cupdate(Chan *c, uchar *buf, int len, vlong off) +{ + USED(c); + USED(buf); + USED(len); + USED(off); +} + +void +cwrite(Chan* c, uchar *buf, int len, vlong off) +{ + USED(c); + USED(buf); + USED(len); + USED(off); +} diff --git a/kern/chan.c b/kern/chan.c new file mode 100644 index 0000000..243c603 --- /dev/null +++ b/kern/chan.c @@ -0,0 +1,1496 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +int chandebug=0; /* toggled by sysr1 */ +QLock chanprint; /* probably asking for trouble (deadlocks) -rsc */ + +int domount(Chan**, Mhead**); + +void +dumpmount(void) /* DEBUGGING */ +{ + Pgrp *pg; + Mount *t; + Mhead **h, **he, *f; + + if(up == nil){ + print("no process for dumpmount\n"); + return; + } + pg = up->pgrp; + if(pg == nil){ + print("no pgrp for dumpmount\n"); + return; + } + rlock(&pg->ns); + if(waserror()) { + runlock(&pg->ns); + nexterror(); + } + + he = &pg->mnthash[MNTHASH]; + for(h = pg->mnthash; h < he; h++) { + for(f = *h; f; f = f->hash) { + print("head: %p: %s 0x%llux.%lud %C %lud -> \n", f, + f->from->name->s, f->from->qid.path, + f->from->qid.vers, devtab[f->from->type]->dc, + f->from->dev); + for(t = f->mount; t; t = t->next) + print("\t%p: %s (umh %p) (path %.8llux dev %C %lud)\n", t, t->to->name->s, t->to->umh, t->to->qid.path, devtab[t->to->type]->dc, t->to->dev); + } + } + poperror(); + runlock(&pg->ns); +} + + +char* +c2name(Chan *c) /* DEBUGGING */ +{ + if(c == nil) + return ""; + if(c->name == nil) + return ""; + if(c->name->s == nil) + return ""; + return c->name->s; +} + +enum +{ + CNAMESLOP = 20 +}; + +struct +{ + Lock lk; + int fid; + Chan *free; + Chan *list; +}chanalloc; + +typedef struct Elemlist Elemlist; + +struct Elemlist +{ + char *name; /* copy of name, so '/' can be overwritten */ + int nelems; + char **elems; + int *off; + int mustbedir; +}; + +#define SEP(c) ((c) == 0 || (c) == '/') +void cleancname(Cname*); + +int +isdotdot(char *p) +{ + return p[0]=='.' && p[1]=='.' && p[2]=='\0'; +} + +int +incref(Ref *r) +{ + int x; + + lock(&r->lk); + x = ++r->ref; + unlock(&r->lk); + return x; +} + +int +decref(Ref *r) +{ + int x; + + lock(&r->lk); + x = --r->ref; + unlock(&r->lk); + if(x < 0) + panic("decref, pc=0x%lux", getcallerpc(&r)); + + return x; +} + +/* + * Rather than strncpy, which zeros the rest of the buffer, kstrcpy + * truncates if necessary, always zero terminates, does not zero fill, + * and puts ... at the end of the string if it's too long. Usually used to + * save a string in up->genbuf; + */ +void +kstrcpy(char *s, char *t, int ns) +{ + int nt; + + nt = strlen(t); + if(nt+1 <= ns){ + memmove(s, t, nt+1); + return; + } + /* too long */ + if(ns < 4){ + /* but very short! */ + strncpy(s, t, ns); + return; + } + /* truncate with ... at character boundary (very rare case) */ + memmove(s, t, ns-4); + ns -= 4; + s[ns] = '\0'; + /* look for first byte of UTF-8 sequence by skipping continuation bytes */ + while(ns>0 && (s[--ns]&0xC0)==0x80) + ; + strcpy(s+ns, "..."); +} + +int +emptystr(char *s) +{ + if(s == nil) + return 1; + if(s[0] == '\0') + return 1; + return 0; +} + +/* + * Atomically replace *p with copy of s + */ +void +kstrdup(char **p, char *s) +{ + int n; + char *t, *prev; + static Lock l; + + n = strlen(s)+1; + /* if it's a user, we can wait for memory; if not, something's very wrong */ + if(up){ + t = smalloc(n); + setmalloctag(t, getcallerpc(&p)); + }else{ + t = malloc(n); + if(t == nil) + panic("kstrdup: no memory"); + } + memmove(t, s, n); + prev = *p; + *p = t; + free(prev); +} + +void +chandevreset(void) +{ + int i; + + for(i=0; devtab[i] != nil; i++) + devtab[i]->reset(); +} + +void +chandevinit(void) +{ + int i; + + for(i=0; devtab[i] != nil; i++) + devtab[i]->init(); +} + +void +chandevshutdown(void) +{ + int i; + + /* shutdown in reverse order */ + for(i=0; devtab[i] != nil; i++) + ; + for(i--; i >= 0; i--) + devtab[i]->shutdown(); +} + +Chan* +newchan(void) +{ + Chan *c; + + lock(&chanalloc.lk); + c = chanalloc.free; + if(c != 0) + chanalloc.free = c->next; + unlock(&chanalloc.lk); + + if(c == nil) { + c = smalloc(sizeof(Chan)); + lock(&chanalloc.lk); + c->fid = ++chanalloc.fid; + c->link = chanalloc.list; + chanalloc.list = c; + unlock(&chanalloc.lk); + } + + /* if you get an error before associating with a dev, + close calls rootclose, a nop */ + c->type = 0; + c->flag = 0; + c->ref.ref = 1; + c->dev = 0; + c->offset = 0; + c->iounit = 0; + c->umh = 0; + c->uri = 0; + c->dri = 0; + c->aux = 0; + c->mchan = 0; + c->mcp = 0; + c->mux = 0; + memset(&c->mqid, 0, sizeof(c->mqid)); + c->name = 0; + return c; +} + +static Ref ncname; + +Cname* +newcname(char *s) +{ + Cname *n; + int i; + + n = smalloc(sizeof(Cname)); + i = strlen(s); + n->len = i; + n->alen = i+CNAMESLOP; + n->s = smalloc(n->alen); + memmove(n->s, s, i+1); + n->ref.ref = 1; + incref(&ncname); + return n; +} + +void +cnameclose(Cname *n) +{ + if(n == nil) + return; + if(decref(&n->ref)) + return; + decref(&ncname); + free(n->s); + free(n); +} + +Cname* +addelem(Cname *n, char *s) +{ + int i, a; + char *t; + Cname *new; + + if(s[0]=='.' && s[1]=='\0') + return n; + + if(n->ref.ref > 1){ + /* copy on write */ + new = newcname(n->s); + cnameclose(n); + n = new; + } + + i = strlen(s); + if(n->len+1+i+1 > n->alen){ + a = n->len+1+i+1 + CNAMESLOP; + t = smalloc(a); + memmove(t, n->s, n->len+1); + free(n->s); + n->s = t; + n->alen = a; + } + if(n->len>0 && n->s[n->len-1]!='/' && s[0]!='/') /* don't insert extra slash if one is present */ + n->s[n->len++] = '/'; + memmove(n->s+n->len, s, i+1); + n->len += i; + if(isdotdot(s)) + cleancname(n); + return n; +} + +void +chanfree(Chan *c) +{ + c->flag = CFREE; + + if(c->umh != nil){ + putmhead(c->umh); + c->umh = nil; + } + if(c->umc != nil){ + cclose(c->umc); + c->umc = nil; + } + if(c->mux != nil){ + muxclose(c->mux); + c->mux = nil; + } + if(c->mchan != nil){ + cclose(c->mchan); + c->mchan = nil; + } + + cnameclose(c->name); + + lock(&chanalloc.lk); + c->next = chanalloc.free; + chanalloc.free = c; + unlock(&chanalloc.lk); +} + +void +cclose(Chan *c) +{ + if(c->flag&CFREE) + panic("cclose %lux", getcallerpc(&c)); + + if(decref(&c->ref)) + return; + + if(!waserror()){ + devtab[c->type]->close(c); + poperror(); + } + chanfree(c); +} + +/* + * Make sure we have the only copy of c. (Copy on write.) + */ +Chan* +cunique(Chan *c) +{ + Chan *nc; + + if(c->ref.ref != 1) { + nc = cclone(c); + cclose(c); + c = nc; + } + + return c; +} + +int +eqqid(Qid a, Qid b) +{ + return a.path==b.path && a.vers==b.vers; +} + +int +eqchan(Chan *a, Chan *b, int pathonly) +{ + if(a->qid.path != b->qid.path) + return 0; + if(!pathonly && a->qid.vers!=b->qid.vers) + return 0; + if(a->type != b->type) + return 0; + if(a->dev != b->dev) + return 0; + return 1; +} + +int +eqchantdqid(Chan *a, int type, int dev, Qid qid, int pathonly) +{ + if(a->qid.path != qid.path) + return 0; + if(!pathonly && a->qid.vers!=qid.vers) + return 0; + if(a->type != type) + return 0; + if(a->dev != dev) + return 0; + return 1; +} + +Mhead* +newmhead(Chan *from) +{ + Mhead *mh; + + mh = smalloc(sizeof(Mhead)); + mh->ref.ref = 1; + mh->from = from; + incref(&from->ref); + +/* + n = from->name->len; + if(n >= sizeof(mh->fromname)) + n = sizeof(mh->fromname)-1; + memmove(mh->fromname, from->name->s, n); + mh->fromname[n] = 0; +*/ + return mh; +} + +int +cmount(Chan **newp, Chan *old, int flag, char *spec) +{ + Pgrp *pg; + int order, flg; + Mhead *m, **l, *mh; + Mount *nm, *f, *um, **h; + Chan *new; + + if(QTDIR & (old->qid.type^(*newp)->qid.type)) + error(Emount); + +if(old->umh)print("cmount old extra umh\n"); + + order = flag&MORDER; + + if((old->qid.type&QTDIR)==0 && order != MREPL) + error(Emount); + + new = *newp; + mh = new->umh; + + /* + * Not allowed to bind when the old directory + * is itself a union. (Maybe it should be allowed, but I don't see + * what the semantics would be.) + * + * We need to check mh->mount->next to tell unions apart from + * simple mount points, so that things like + * mount -c fd /root + * bind -c /root / + * work. The check of mount->mflag catches things like + * mount fd /root + * bind -c /root / + * + * This is far more complicated than it should be, but I don't + * see an easier way at the moment. -rsc + */ + if((flag&MCREATE) && mh && mh->mount + && (mh->mount->next || !(mh->mount->mflag&MCREATE))) + error(Emount); + + pg = up->pgrp; + wlock(&pg->ns); + + l = &MOUNTH(pg, old->qid); + for(m = *l; m; m = m->hash) { + if(eqchan(m->from, old, 1)) + break; + l = &m->hash; + } + + if(m == nil) { + /* + * nothing mounted here yet. create a mount + * head and add to the hash table. + */ + m = newmhead(old); + *l = m; + + /* + * if this is a union mount, add the old + * node to the mount chain. + */ + if(order != MREPL) + m->mount = newmount(m, old, 0, 0); + } + wlock(&m->lock); + if(waserror()){ + wunlock(&m->lock); + nexterror(); + } + wunlock(&pg->ns); + + nm = newmount(m, new, flag, spec); + if(mh != nil && mh->mount != nil) { + /* + * copy a union when binding it onto a directory + */ + flg = order; + if(order == MREPL) + flg = MAFTER; + h = &nm->next; + um = mh->mount; + for(um = um->next; um; um = um->next) { + f = newmount(m, um->to, flg, um->spec); + *h = f; + h = &f->next; + } + } + + if(m->mount && order == MREPL) { + mountfree(m->mount); + m->mount = 0; + } + + if(flag & MCREATE) + nm->mflag |= MCREATE; + + if(m->mount && order == MAFTER) { + for(f = m->mount; f->next; f = f->next) + ; + f->next = nm; + } + else { + for(f = nm; f->next; f = f->next) + ; + f->next = m->mount; + m->mount = nm; + } + + wunlock(&m->lock); + poperror(); + return nm->mountid; +} + +void +cunmount(Chan *mnt, Chan *mounted) +{ + Pgrp *pg; + Mhead *m, **l; + Mount *f, **p; + + if(mnt->umh) /* should not happen */ + print("cunmount newp extra umh %p has %p\n", mnt, mnt->umh); + + /* + * It _can_ happen that mounted->umh is non-nil, + * because mounted is the result of namec(Aopen) + * (see sysfile.c:/^sysunmount). + * If we open a union directory, it will have a umh. + * Although surprising, this is okay, since the + * cclose will take care of freeing the umh. + */ + + pg = up->pgrp; + wlock(&pg->ns); + + l = &MOUNTH(pg, mnt->qid); + for(m = *l; m; m = m->hash) { + if(eqchan(m->from, mnt, 1)) + break; + l = &m->hash; + } + + if(m == 0) { + wunlock(&pg->ns); + error(Eunmount); + } + + wlock(&m->lock); + if(mounted == 0) { + *l = m->hash; + wunlock(&pg->ns); + mountfree(m->mount); + m->mount = nil; + cclose(m->from); + wunlock(&m->lock); + putmhead(m); + return; + } + + p = &m->mount; + for(f = *p; f; f = f->next) { + /* BUG: Needs to be 2 pass */ + if(eqchan(f->to, mounted, 1) || + (f->to->mchan && eqchan(f->to->mchan, mounted, 1))) { + *p = f->next; + f->next = 0; + mountfree(f); + if(m->mount == nil) { + *l = m->hash; + cclose(m->from); + wunlock(&m->lock); + wunlock(&pg->ns); + putmhead(m); + return; + } + wunlock(&m->lock); + wunlock(&pg->ns); + return; + } + p = &f->next; + } + wunlock(&m->lock); + wunlock(&pg->ns); + error(Eunion); +} + +Chan* +cclone(Chan *c) +{ + Chan *nc; + Walkqid *wq; + + wq = devtab[c->type]->walk(c, nil, nil, 0); + if(wq == nil) + error("clone failed"); + nc = wq->clone; + free(wq); + nc->name = c->name; + if(c->name) + incref(&c->name->ref); + return nc; +} + +int +findmount(Chan **cp, Mhead **mp, int type, int dev, Qid qid) +{ + Pgrp *pg; + Mhead *m; + + pg = up->pgrp; + rlock(&pg->ns); + for(m = MOUNTH(pg, qid); m; m = m->hash){ + rlock(&m->lock); +if(m->from == nil){ + print("m %p m->from 0\n", m); + runlock(&m->lock); + continue; +} + if(eqchantdqid(m->from, type, dev, qid, 1)) { + runlock(&pg->ns); + if(mp != nil){ + incref(&m->ref); + if(*mp != nil) + putmhead(*mp); + *mp = m; + } + if(*cp != nil) + cclose(*cp); + incref(&m->mount->to->ref); + *cp = m->mount->to; + runlock(&m->lock); + return 1; + } + runlock(&m->lock); + } + + runlock(&pg->ns); + return 0; +} + +int +domount(Chan **cp, Mhead **mp) +{ + return findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid); +} + +Chan* +undomount(Chan *c, Cname *name) +{ + Chan *nc; + Pgrp *pg; + Mount *t; + Mhead **h, **he, *f; + + pg = up->pgrp; + rlock(&pg->ns); + if(waserror()) { + runlock(&pg->ns); + nexterror(); + } + + he = &pg->mnthash[MNTHASH]; + for(h = pg->mnthash; h < he; h++) { + for(f = *h; f; f = f->hash) { + if(strcmp(f->from->name->s, name->s) != 0) + continue; + for(t = f->mount; t; t = t->next) { + if(eqchan(c, t->to, 1)) { + /* + * We want to come out on the left hand side of the mount + * point using the element of the union that we entered on. + * To do this, find the element that has a from name of + * c->name->s. + */ + if(strcmp(t->head->from->name->s, name->s) != 0) + continue; + nc = t->head->from; + incref(&nc->ref); + cclose(c); + c = nc; + break; + } + } + } + } + poperror(); + runlock(&pg->ns); + return c; +} + +/* + * Either walks all the way or not at all. No partial results in *cp. + * *nerror is the number of names to display in an error message. + */ +static char Edoesnotexist[] = "does not exist"; +int +walk(Chan **cp, char **names, int nnames, int nomount, int *nerror) +{ + int dev, dotdot, i, n, nhave, ntry, type; + Chan *c, *nc; + Cname *cname; + Mount *f; + Mhead *mh, *nmh; + Walkqid *wq; + + c = *cp; + incref(&c->ref); + cname = c->name; + incref(&cname->ref); + mh = nil; + + /* + * While we haven't gotten all the way down the path: + * 1. step through a mount point, if any + * 2. send a walk request for initial dotdot or initial prefix without dotdot + * 3. move to the first mountpoint along the way. + * 4. repeat. + * + * An invariant is that each time through the loop, c is on the undomount + * side of the mount point, and c's name is cname. + */ + for(nhave=0; nhaveqid.type&QTDIR)==0){ + if(nerror) + *nerror = nhave; + cnameclose(cname); + cclose(c); + strcpy(up->errstr, Enotdir); + if(mh != nil) +{print("walk 1\n"); + putmhead(mh); +} + return -1; + } + ntry = nnames - nhave; + if(ntry > MAXWELEM) + ntry = MAXWELEM; + dotdot = 0; + for(i=0; itype; + dev = c->dev; + + if((wq = devtab[type]->walk(c, nil, names+nhave, ntry)) == nil){ + /* try a union mount, if any */ + if(mh && !nomount){ + /* + * mh->mount == c, so start at mh->mount->next + */ + rlock(&mh->lock); + for(f = mh->mount->next; f; f = f->next) + if((wq = devtab[f->to->type]->walk(f->to, nil, names+nhave, ntry)) != nil) + break; + runlock(&mh->lock); + if(f != nil){ + type = f->to->type; + dev = f->to->dev; + } + } + if(wq == nil){ + cclose(c); + cnameclose(cname); + if(nerror) + *nerror = nhave+1; + if(mh != nil) + putmhead(mh); + return -1; + } + } + + nmh = nil; + if(dotdot) { + assert(wq->nqid == 1); + assert(wq->clone != nil); + + cname = addelem(cname, ".."); + nc = undomount(wq->clone, cname); + n = 1; + } else { + nc = nil; + if(!nomount) + for(i=0; inqid && iqid[i])) + break; + if(nc == nil){ /* no mount points along path */ + if(wq->clone == nil){ + cclose(c); + cnameclose(cname); + if(wq->nqid==0 || (wq->qid[wq->nqid-1].type&QTDIR)){ + if(nerror) + *nerror = nhave+wq->nqid+1; + strcpy(up->errstr, Edoesnotexist); + }else{ + if(nerror) + *nerror = nhave+wq->nqid; + strcpy(up->errstr, Enotdir); + } + free(wq); + if(mh != nil) + putmhead(mh); + return -1; + } + n = wq->nqid; + nc = wq->clone; + }else{ /* stopped early, at a mount point */ + if(wq->clone != nil){ + cclose(wq->clone); + wq->clone = nil; + } + n = i+1; + } + for(i=0; iumh != nil){ //BUG + print("walk umh\n"); + putmhead(c->umh); + c->umh = nil; + } + + cnameclose(c->name); + c->name = cname; + + cclose(*cp); + *cp = c; + if(nerror) + *nerror = 0; + return 0; +} + +/* + * c is a mounted non-creatable directory. find a creatable one. + */ +Chan* +createdir(Chan *c, Mhead *m) +{ + Chan *nc; + Mount *f; + + rlock(&m->lock); + if(waserror()) { + runlock(&m->lock); + nexterror(); + } + for(f = m->mount; f; f = f->next) { + if(f->mflag&MCREATE) { + nc = cclone(f->to); + runlock(&m->lock); + poperror(); + cclose(c); + return nc; + } + } + error(Enocreate); + return 0; +} + +void +saveregisters(void) +{ +} + +/* + * In place, rewrite name to compress multiple /, eliminate ., and process .. + */ +void +cleancname(Cname *n) +{ + char *p; + + if(n->s[0] == '#'){ + p = strchr(n->s, '/'); + if(p == nil) + return; + cleanname(p); + + /* + * The correct name is #i rather than #i/, + * but the correct name of #/ is #/. + */ + if(strcmp(p, "/")==0 && n->s[1] != '/') + *p = '\0'; + }else + cleanname(n->s); + n->len = strlen(n->s); +} + +static void +growparse(Elemlist *e) +{ + char **new; + int *inew; + enum { Delta = 8 }; + + if(e->nelems % Delta == 0){ + new = smalloc((e->nelems+Delta) * sizeof(char*)); + memmove(new, e->elems, e->nelems*sizeof(char*)); + free(e->elems); + e->elems = new; + inew = smalloc((e->nelems+Delta+1) * sizeof(int)); + memmove(inew, e->off, e->nelems*sizeof(int)); + free(e->off); + e->off = inew; + } +} + +/* + * The name is known to be valid. + * Copy the name so slashes can be overwritten. + * An empty string will set nelem=0. + * A path ending in / or /. or /.//./ etc. will have + * e.mustbedir = 1, so that we correctly + * reject, e.g., "/adm/users/." when /adm/users is a file + * rather than a directory. + */ +static void +parsename(char *name, Elemlist *e) +{ + char *slash; + + kstrdup(&e->name, name); + name = e->name; + e->nelems = 0; + e->elems = nil; + e->off = smalloc(sizeof(int)); + e->off[0] = skipslash(name) - name; + for(;;){ + name = skipslash(name); + if(*name=='\0'){ + e->mustbedir = 1; + break; + } + growparse(e); + e->elems[e->nelems++] = name; + slash = utfrune(name, '/'); + if(slash == nil){ + e->off[e->nelems] = name+strlen(name) - e->name; + e->mustbedir = 0; + break; + } + e->off[e->nelems] = slash - e->name; + *slash++ = '\0'; + name = slash; + } +} + +void* +memrchr(void *va, int c, long n) +{ + uchar *a, *e; + + a = va; + for(e=a+n-1; e>a; e--) + if(*e == c) + return e; + return nil; +} + +/* + * Turn a name into a channel. + * &name[0] is known to be a valid address. It may be a kernel address. + * + * Opening with amode Aopen, Acreate, or Aremove guarantees + * that the result will be the only reference to that particular fid. + * This is necessary since we might pass the result to + * devtab[]->remove(). + * + * Opening Atodir, Amount, or Aaccess does not guarantee this. + * + * Opening Aaccess can, under certain conditions, return a + * correct Chan* but with an incorrect Cname attached. + * Since the functions that open Aaccess (sysstat, syswstat, sys_stat) + * do not use the Cname*, this avoids an unnecessary clone. + */ +Chan* +namec(char *aname, int amode, int omode, ulong perm) +{ + int n, prefix, len, t, nomount, npath; + Chan *c, *cnew; + Cname *cname; + Elemlist e; + Rune r; + Mhead *m; + char *createerr, tmperrbuf[ERRMAX]; + char *name; + + name = aname; + if(name[0] == '\0') + error("empty file name"); + validname(name, 1); + + /* + * Find the starting off point (the current slash, the root of + * a device tree, or the current dot) as well as the name to + * evaluate starting there. + */ + nomount = 0; + switch(name[0]){ + case '/': + c = up->slash; + incref(&c->ref); + break; + + case '#': + nomount = 1; + up->genbuf[0] = '\0'; + n = 0; + while(*name!='\0' && (*name != '/' || n < 2)){ + if(n >= sizeof(up->genbuf)-1) + error(Efilename); + up->genbuf[n++] = *name++; + } + up->genbuf[n] = '\0'; + /* + * noattach is sandboxing. + * + * the OK exceptions are: + * | it only gives access to pipes you create + * d this process's file descriptors + * e this process's environment + * the iffy exceptions are: + * c time and pid, but also cons and consctl + * p control of your own processes (and unfortunately + * any others left unprotected) + */ + n = chartorune(&r, up->genbuf+1)+1; + /* actually / is caught by parsing earlier */ + if(utfrune("M", r)) + error(Enoattach); + if(up->pgrp->noattach && utfrune("|decp", r)==nil) + error(Enoattach); + t = devno(r, 1); + if(t == -1) + error(Ebadsharp); + c = devtab[t]->attach(up->genbuf+n); + break; + + default: + c = up->dot; + incref(&c->ref); + break; + } + prefix = name - aname; + + e.name = nil; + e.elems = nil; + e.off = nil; + e.nelems = 0; + if(waserror()){ + cclose(c); + free(e.name); + free(e.elems); + free(e.off); +//dumpmount(); + nexterror(); + } + + /* + * Build a list of elements in the path. + */ + parsename(name, &e); + + /* + * On create, .... + */ + if(amode == Acreate){ + /* perm must have DMDIR if last element is / or /. */ + if(e.mustbedir && !(perm&DMDIR)){ + npath = e.nelems; + strcpy(tmperrbuf, "create without DMDIR"); + goto NameError; + } + + /* don't try to walk the last path element just yet. */ + if(e.nelems == 0) + error(Eexist); + e.nelems--; + } + + if(walk(&c, e.elems, e.nelems, nomount, &npath) < 0){ + if(npath < 0 || npath > e.nelems){ + print("namec %s walk error npath=%d\n", aname, npath); + nexterror(); + } + strcpy(tmperrbuf, up->errstr); + NameError: + len = prefix+e.off[npath]; + if(len < ERRMAX/3 || (name=memrchr(aname, '/', len))==nil || name==aname) + snprint(up->genbuf, sizeof up->genbuf, "%.*s", len, aname); + else + snprint(up->genbuf, sizeof up->genbuf, "...%.*s", (int)(len-(name-aname)), name); + snprint(up->errstr, ERRMAX, "%#q %s", up->genbuf, tmperrbuf); + nexterror(); + } + + if(e.mustbedir && !(c->qid.type&QTDIR)){ + npath = e.nelems; + strcpy(tmperrbuf, "not a directory"); + goto NameError; + } + + if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR)){ + npath = e.nelems; + error("cannot exec directory"); + } + + switch(amode){ + case Aaccess: + if(!nomount) + domount(&c, nil); + break; + + case Abind: + m = nil; + if(!nomount) + domount(&c, &m); + if(c->umh != nil) + putmhead(c->umh); + c->umh = m; + break; + + case Aremove: + case Aopen: + Open: + /* save the name; domount might change c */ + cname = c->name; + incref(&cname->ref); + m = nil; + if(!nomount) + domount(&c, &m); + + /* our own copy to open or remove */ + c = cunique(c); + + /* now it's our copy anyway, we can put the name back */ + cnameclose(c->name); + c->name = cname; + + switch(amode){ + case Aremove: + putmhead(m); + break; + + case Aopen: + case Acreate: +if(c->umh != nil){ + print("cunique umh Open\n"); + putmhead(c->umh); + c->umh = nil; +} + + /* only save the mount head if it's a multiple element union */ + if(m && m->mount && m->mount->next) + c->umh = m; + else + putmhead(m); + + /* save registers else error() in open has wrong value of c saved */ + saveregisters(); + + if(omode == OEXEC) + c->flag &= ~CCACHE; + + c = devtab[c->type]->open(c, omode&~OCEXEC); + + if(omode & OCEXEC) + c->flag |= CCEXEC; + if(omode & ORCLOSE) + c->flag |= CRCLOSE; + break; + } + break; + + case Atodir: + /* + * Directories (e.g. for cd) are left before the mount point, + * so one may mount on / or . and see the effect. + */ + if(!(c->qid.type & QTDIR)) + error(Enotdir); + break; + + case Amount: + /* + * When mounting on an already mounted upon directory, + * one wants subsequent mounts to be attached to the + * original directory, not the replacement. Don't domount. + */ + break; + + case Acreate: + /* + * We've already walked all but the last element. + * If the last exists, try to open it OTRUNC. + * If omode&OEXCL is set, just give up. + */ + e.nelems++; + if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) == 0){ + if(omode&OEXCL) + error(Eexist); + omode |= OTRUNC; + goto Open; + } + + /* + * The semantics of the create(2) system call are that if the + * file exists and can be written, it is to be opened with truncation. + * On the other hand, the create(5) message fails if the file exists. + * If we get two create(2) calls happening simultaneously, + * they might both get here and send create(5) messages, but only + * one of the messages will succeed. To provide the expected create(2) + * semantics, the call with the failed message needs to try the above + * walk again, opening for truncation. This correctly solves the + * create/create race, in the sense that any observable outcome can + * be explained as one happening before the other. + * The create/create race is quite common. For example, it happens + * when two rc subshells simultaneously update the same + * environment variable. + * + * The implementation still admits a create/create/remove race: + * (A) walk to file, fails + * (B) walk to file, fails + * (A) create file, succeeds, returns + * (B) create file, fails + * (A) remove file, succeeds, returns + * (B) walk to file, return failure. + * + * This is hardly as common as the create/create race, and is really + * not too much worse than what might happen if (B) got a hold of a + * file descriptor and then the file was removed -- either way (B) can't do + * anything with the result of the create call. So we don't care about this race. + * + * Applications that care about more fine-grained decision of the races + * can use the OEXCL flag to get at the underlying create(5) semantics; + * by default we provide the common case. + * + * We need to stay behind the mount point in case we + * need to do the first walk again (should the create fail). + * + * We also need to cross the mount point and find the directory + * in the union in which we should be creating. + * + * The channel staying behind is c, the one moving forward is cnew. + */ + m = nil; + cnew = nil; /* is this assignment necessary? */ + if(!waserror()){ /* try create */ + if(!nomount && findmount(&cnew, &m, c->type, c->dev, c->qid)) + cnew = createdir(cnew, m); + else{ + cnew = c; + incref(&cnew->ref); + } + + /* + * We need our own copy of the Chan because we're + * about to send a create, which will move it. Once we have + * our own copy, we can fix the name, which might be wrong + * if findmount gave us a new Chan. + */ + cnew = cunique(cnew); + cnameclose(cnew->name); + cnew->name = c->name; + incref(&cnew->name->ref); + + devtab[cnew->type]->create(cnew, e.elems[e.nelems-1], omode&~(OEXCL|OCEXEC), perm); + poperror(); + if(omode & OCEXEC) + cnew->flag |= CCEXEC; + if(omode & ORCLOSE) + cnew->flag |= CRCLOSE; + if(m) + putmhead(m); + cclose(c); + c = cnew; + c->name = addelem(c->name, e.elems[e.nelems-1]); + break; + }else{ /* create failed */ + cclose(cnew); + if(m) + putmhead(m); + if(omode & OEXCL) + nexterror(); + /* save error */ + createerr = up->errstr; + up->errstr = tmperrbuf; + /* note: we depend that walk does not error */ + if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0){ + up->errstr = createerr; + error(createerr); /* report true error */ + } + up->errstr = createerr; + omode |= OTRUNC; + goto Open; + } + panic("namec: not reached"); + + default: + panic("unknown namec access %d\n", amode); + } + + poperror(); + + /* place final element in genbuf for e.g. exec */ + if(e.nelems > 0) + kstrcpy(up->genbuf, e.elems[e.nelems-1], sizeof up->genbuf); + else + kstrcpy(up->genbuf, ".", sizeof up->genbuf); + free(e.name); + free(e.elems); + free(e.off); + + return c; +} + +/* + * name is valid. skip leading / and ./ as much as possible + */ +char* +skipslash(char *name) +{ + while(name[0]=='/' || (name[0]=='.' && (name[1]==0 || name[1]=='/'))) + name++; + return name; +} + +char isfrog[256]={ + /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1, /* 0 */ + /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1, /* 0x08 */ + /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1, /* 0x10 */ + /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1, /* 0x18 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 */ + 0, 0, 0, 0, 0, 0, 0, 1, /* 0x28 (1 is '/', 0x2F) */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x38 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x48 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x58 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x68 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 */ + 0, 0, 0, 0, 0, 0, 0, 1, /* 0x78 (1 is DEL, 0x7F) */ +}; + +/* + * Check that the name + * a) is in valid memory. + * b) is shorter than 2^16 bytes, so it can fit in a 9P string field. + * c) contains no frogs. + * The first byte is known to be addressible by the requester, so the + * routine works for kernel and user memory both. + * The parameter slashok flags whether a slash character is an error + * or a valid character. + */ +void +validname(char *aname, int slashok) +{ + char *p, *ename, *name; + uint t; + int c; + Rune r; + + name = aname; +/* + if(((ulong)name & KZERO) != KZERO) { + p = name; + t = BY2PG-((ulong)p&(BY2PG-1)); + while((ename=vmemchr(p, 0, t)) == nil) { + p += t; + t = BY2PG; + } + }else +*/ + ename = memchr(name, 0, (1<<16)); + + if(ename==nil || ename-name>=(1<<16)) + error("name too long"); + + while(*name){ + /* all characters above '~' are ok */ + c = *(uchar*)name; + if(c >= Runeself) + name += chartorune(&r, name); + else{ + if(isfrog[c]) + if(!slashok || c!='/'){ + snprint(up->genbuf, sizeof(up->genbuf), "%s: %q", Ebadchar, aname); + error(up->genbuf); + } + name++; + } + } +} + +void +isdir(Chan *c) +{ + if(c->qid.type & QTDIR) + return; + error(Enotdir); +} + +/* + * This is necessary because there are many + * pointers to the top of a given mount list: + * + * - the mhead in the namespace hash table + * - the mhead in chans returned from findmount: + * used in namec and then by unionread. + * - the mhead in chans returned from createdir: + * used in the open/create race protect, which is gone. + * + * The RWlock in the Mhead protects the mount list it contains. + * The mount list is deleted when we cunmount. + * The RWlock ensures that nothing is using the mount list at that time. + * + * It is okay to replace c->mh with whatever you want as + * long as you are sure you have a unique reference to it. + * + * This comment might belong somewhere else. + */ +void +putmhead(Mhead *m) +{ + if(m && decref(&m->ref) == 0){ + m->mount = (Mount*)0xCafeBeef; + free(m); + } +} diff --git a/kern/dat.h b/kern/dat.h new file mode 100644 index 0000000..0c16814 --- /dev/null +++ b/kern/dat.h @@ -0,0 +1,519 @@ +#define KNAMELEN 28 /* max length of name held in kernel */ +#define DOMLEN 64 + +#define BLOCKALIGN 8 + +typedef struct Alarms Alarms; +typedef struct Block Block; +typedef struct CSN CSN; +typedef struct Chan Chan; +typedef struct Cmdbuf Cmdbuf; +typedef struct Cmdtab Cmdtab; +typedef struct Cname Cname; +typedef struct Conf Conf; +typedef struct Dev Dev; +typedef struct Dirtab Dirtab; +typedef struct Edfinterface Edfinterface; +typedef struct Egrp Egrp; +typedef struct Evalue Evalue; +typedef struct Fgrp Fgrp; +typedef struct FPsave FPsave; +typedef struct DevConf DevConf; +typedef struct Label Label; +typedef struct List List; +typedef struct Log Log; +typedef struct Logflag Logflag; +typedef struct Mntcache Mntcache; +typedef struct Mount Mount; +typedef struct Mntrpc Mntrpc; +typedef struct Mntwalk Mntwalk; +typedef struct Mnt Mnt; +typedef struct Mhead Mhead; +typedef struct Note Note; +typedef struct Page Page; +typedef struct Palloc Palloc; +typedef struct Perf Perf; +typedef struct Pgrps Pgrps; +typedef struct PhysUart PhysUart; +typedef struct Pgrp Pgrp; +typedef struct Physseg Physseg; +typedef struct Proc Proc; +typedef struct Pte Pte; +typedef struct Pthash Pthash; +typedef struct Queue Queue; +typedef struct Ref Ref; +typedef struct Rendez Rendez; +typedef struct Rgrp Rgrp; +typedef struct RWlock RWlock; +typedef struct Schedq Schedq; +typedef struct Segment Segment; +typedef struct Session Session; +typedef struct Task Task; +typedef struct Talarm Talarm; +typedef struct Timer Timer; +typedef struct Uart Uart; +typedef struct Ureg Ureg; +typedef struct Waitq Waitq; +typedef struct Walkqid Walkqid; +typedef int Devgen(Chan*, char*, Dirtab*, int, int, Dir*); + +#include "fcall.h" + +enum +{ + SnarfSize = 64*1024, +}; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong monitor; /* has monitor? */ + ulong npage0; /* total physical pages of memory */ + ulong npage1; /* total physical pages of memory */ + ulong npage; /* total physical pages of memory */ + ulong upages; /* user page pool */ + ulong nimage; /* number of page cache image headers */ + ulong nswap; /* number of swap pages */ + int nswppo; /* max # of pageouts per segment pass */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + ulong copymode; /* 0 is copy on write, 1 is copy on reference */ + ulong ialloc; /* max interrupt time allocation in bytes */ + ulong pipeqsize; /* size in bytes of pipe queues */ + int nuart; /* number of uart devices */ +}; + +struct Label +{ + jmp_buf buf; +}; + +struct Ref +{ + Lock lk; + long ref; +}; + +struct Rendez +{ + Lock lk; + Proc *p; +}; + +struct RWlock /* changed from kernel */ +{ + int readers; + Lock lk; + QLock x; + QLock k; +}; + +struct Talarm +{ + Lock lk; + Proc *list; +}; + +struct Alarms +{ + QLock lk; + Proc *head; +}; + +/* + * Access types in namec & channel flags + */ +enum +{ + Aaccess, /* as in stat, wstat */ + Abind, /* for left-hand-side of bind */ + Atodir, /* as in chdir */ + Aopen, /* for i/o */ + Amount, /* to be mounted or mounted upon */ + Acreate, /* is to be created */ + Aremove, /* will be removed by caller */ + + COPEN = 0x0001, /* for i/o */ + CMSG = 0x0002, /* the message channel for a mount */ +/*rsc CCREATE = 0x0004, /* permits creation if c->mnt */ + CCEXEC = 0x0008, /* close on exec */ + CFREE = 0x0010, /* not in use */ + CRCLOSE = 0x0020, /* remove on close */ + CCACHE = 0x0080, /* client cache */ +}; + +/* flag values */ +enum +{ + BINTR = (1<<0), + BFREE = (1<<1), + Bipck = (1<<2), /* ip checksum */ + Budpck = (1<<3), /* udp checksum */ + Btcpck = (1<<4), /* tcp checksum */ + Bpktck = (1<<5), /* packet checksum */ +}; + +struct Block +{ + Block* next; + Block* list; + uchar* rp; /* first unconsumed byte */ + uchar* wp; /* first empty byte */ + uchar* lim; /* 1 past the end of the buffer */ + uchar* base; /* start of the buffer */ + void (*free)(Block*); + ushort flag; + ushort checksum; /* IP checksum of complete packet (minus media header) */ +}; +#define BLEN(s) ((s)->wp - (s)->rp) +#define BALLOC(s) ((s)->lim - (s)->base) + +struct Chan +{ + Ref ref; + Chan* next; /* allocation */ + Chan* link; + vlong offset; /* in file */ + ushort type; + ulong dev; + ushort mode; /* read/write */ + ushort flag; + Qid qid; + int fid; /* for devmnt */ + ulong iounit; /* chunk size for i/o; 0==default */ + Mhead* umh; /* mount point that derived Chan; used in unionread */ + Chan* umc; /* channel in union; held for union read */ + QLock umqlock; /* serialize unionreads */ + int uri; /* union read index */ + int dri; /* devdirread index */ + ulong mountid; + Mntcache *mcp; /* Mount cache pointer */ + Mnt *mux; /* Mnt for clients using me for messages */ + void* aux; + Qid pgrpid; /* for #p/notepg */ + ulong mid; /* for ns in devproc */ + Chan* mchan; /* channel to mounted server */ + Qid mqid; /* qid of root of mount point */ + Session*session; + Cname *name; +}; + +struct Cname +{ + Ref ref; + int alen; /* allocated length */ + int len; /* strlen(s) */ + char *s; +}; + +struct Dev +{ + int dc; + char* name; + + void (*reset)(void); + void (*init)(void); + void (*shutdown)(void); + Chan* (*attach)(char*); + Walkqid* (*walk)(Chan*, Chan*, char**, int); + int (*stat)(Chan*, uchar*, int); + Chan* (*open)(Chan*, int); + void (*create)(Chan*, char*, int, ulong); + void (*close)(Chan*); + long (*read)(Chan*, void*, long, vlong); + Block* (*bread)(Chan*, long, ulong); + long (*write)(Chan*, void*, long, vlong); + long (*bwrite)(Chan*, Block*, ulong); + void (*remove)(Chan*); + int (*wstat)(Chan*, uchar*, int); + void (*power)(int); /* power mgt: power(1) => on, power (0) => off */ + int (*config)(int, char*, DevConf*); // returns nil on error +}; + +struct Dirtab +{ + char name[KNAMELEN]; + Qid qid; + vlong length; + long perm; +}; + +struct Walkqid +{ + Chan *clone; + int nqid; + Qid qid[1]; +}; + +enum +{ + NSMAX = 1000, + NSLOG = 7, + NSCACHE = (1<ref; channels on this mount point incref(c->mchan) == Mnt.c */ + Chan *c; /* Channel to file service */ + Proc *rip; /* Reader in progress */ + Mntrpc *queue; /* Queue of pending requests on this channel */ + ulong id; /* Multiplexer id for channel check */ + Mnt *list; /* Free list */ + int flags; /* cache */ + int msize; /* data + IOHDRSZ */ + char *version; /* 9P version */ + Queue *q; /* input queue */ +}; + +enum +{ + NUser, /* note provided externally */ + NExit, /* deliver note quietly */ + NDebug, /* print debug message */ +}; + +struct Note +{ + char msg[ERRMAX]; + int flag; /* whether system posted it */ +}; + +enum +{ + RENDLOG = 5, + RENDHASH = 1<rendhash[(s)&((1<mnthash[(qid).path&((1< variadic */ +}; + +/* queue state bits, Qmsg, Qcoalesce, and Qkick can be set in qopen */ +enum +{ + /* Queue.state */ + Qstarve = (1<<0), /* consumer starved */ + Qmsg = (1<<1), /* message stream */ + Qclosed = (1<<2), /* queue has been closed/hungup */ + Qflow = (1<<3), /* producer flow controlled */ + Qcoalesce = (1<<4), /* coallesce packets on read */ + Qkick = (1<<5), /* always call the kick routine after qwrite */ +}; + +#define DEVDOTDOT -1 + +extern Proc *_getproc(void); +extern void _setproc(Proc*); +#define up (_getproc()) diff --git a/kern/data.c b/kern/data.c new file mode 100644 index 0000000..735e5e0 --- /dev/null +++ b/kern/data.c @@ -0,0 +1,31 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +Proc *up; +Conf conf = +{ + 1, + 100, + 0, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 1024*1024*1024, + 0, +}; + +char *eve = "eve"; +ulong kerndate; +int cpuserver; +char hostdomain[] = "drawterm.net"; diff --git a/kern/dev.c b/kern/dev.c new file mode 100644 index 0000000..83bf624 --- /dev/null +++ b/kern/dev.c @@ -0,0 +1,468 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +extern ulong kerndate; + +void +mkqid(Qid *q, vlong path, ulong vers, int type) +{ + q->type = type; + q->vers = vers; + q->path = path; +} + +int +devno(int c, int user) +{ + int i; + + for(i = 0; devtab[i] != nil; i++) { + if(devtab[i]->dc == c) + return i; + } + if(user == 0) + panic("devno %C 0x%ux", c, c); + + return -1; +} + +void +devdir(Chan *c, Qid qid, char *n, vlong length, char *user, long perm, Dir *db) +{ + db->name = n; + if(c->flag&CMSG) + qid.type |= QTMOUNT; + db->qid = qid; + db->type = devtab[c->type]->dc; + db->dev = c->dev; + db->mode = perm; + db->mode |= qid.type << 24; + db->atime = seconds(); + db->mtime = kerndate; + db->length = length; + db->uid = user; + db->gid = eve; + db->muid = user; +} + +/* + * (here, Devgen is the prototype; devgen is the function in dev.c.) + * + * a Devgen is expected to return the directory entry for ".." + * if you pass it s==DEVDOTDOT (-1). otherwise... + * + * there are two contradictory rules. + * + * (i) if c is a directory, a Devgen is expected to list its children + * as you iterate s. + * + * (ii) whether or not c is a directory, a Devgen is expected to list + * its siblings as you iterate s. + * + * devgen always returns the list of children in the root + * directory. thus it follows (i) when c is the root and (ii) otherwise. + * many other Devgens follow (i) when c is a directory and (ii) otherwise. + * + * devwalk assumes (i). it knows that devgen breaks (i) + * for children that are themselves directories, and explicitly catches them. + * + * devstat assumes (ii). if the Devgen in question follows (i) + * for this particular c, devstat will not find the necessary info. + * with our particular Devgen functions, this happens only for + * directories, so devstat makes something up, assuming + * c->name, c->qid, eve, DMDIR|0555. + * + * devdirread assumes (i). the callers have to make sure + * that the Devgen satisfies (i) for the chan being read. + */ +/* + * the zeroth element of the table MUST be the directory itself for .. +*/ +int +devgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp) +{ + if(tab == 0) + return -1; + if(i == DEVDOTDOT){ + /* nothing */ + }else if(name){ + for(i=1; i= ntab) + return -1; + tab += i; + } + devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); + return 1; +} + +void +devreset(void) +{ +} + +void +devinit(void) +{ +} + +void +devshutdown(void) +{ +} + +Chan* +devattach(int tc, char *spec) +{ + Chan *c; + char *buf; + + c = newchan(); + mkqid(&c->qid, 0, 0, QTDIR); + c->type = devno(tc, 0); + if(spec == nil) + spec = ""; + buf = smalloc(4+strlen(spec)+1); + sprint(buf, "#%C%s", tc, spec); + c->name = newcname(buf); + free(buf); + return c; +} + + +Chan* +devclone(Chan *c) +{ + Chan *nc; + + if(c->flag & COPEN) + panic("clone of open file type %C\n", devtab[c->type]->dc); + + nc = newchan(); + + nc->type = c->type; + nc->dev = c->dev; + nc->mode = c->mode; + nc->qid = c->qid; + nc->offset = c->offset; + nc->umh = nil; + nc->mountid = c->mountid; + nc->aux = c->aux; + nc->pgrpid = c->pgrpid; + nc->mid = c->mid; + nc->mqid = c->mqid; + nc->mcp = c->mcp; + return nc; +} + +Walkqid* +devwalk(Chan *c, Chan *nc, char **name, int nname, Dirtab *tab, int ntab, Devgen *gen) +{ + int i, j, alloc; + Walkqid *wq; + char *n; + Dir dir; + + if(nname > 0) + isdir(c); + + alloc = 0; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone!=nil) + cclose(wq->clone); + free(wq); + return nil; + } + if(nc == nil){ + nc = devclone(c); + nc->type = 0; /* device doesn't know about this channel yet */ + alloc = 1; + } + wq->clone = nc; + + for(j=0; jqid.type&QTDIR)){ + if(j==0) + error(Enotdir); + goto Done; + } + n = name[j]; + if(strcmp(n, ".") == 0){ + Accept: + wq->qid[wq->nqid++] = nc->qid; + continue; + } + if(strcmp(n, "..") == 0){ + if((*gen)(nc, nil, tab, ntab, DEVDOTDOT, &dir) != 1){ + print("devgen walk .. in dev%s %llux broken\n", + devtab[nc->type]->name, nc->qid.path); + error("broken devgen"); + } + nc->qid = dir.qid; + goto Accept; + } + /* + * Ugly problem: If we're using devgen, make sure we're + * walking the directory itself, represented by the first + * entry in the table, and not trying to step into a sub- + * directory of the table, e.g. /net/net. Devgen itself + * should take care of the problem, but it doesn't have + * the necessary information (that we're doing a walk). + */ + if(gen==devgen && nc->qid.path!=tab[0].qid.path) + goto Notfound; + for(i=0;; i++) { + switch((*gen)(nc, n, tab, ntab, i, &dir)){ + case -1: + Notfound: + if(j == 0) + error(Enonexist); + kstrcpy(up->errstr, Enonexist, ERRMAX); + goto Done; + case 0: + continue; + case 1: + if(strcmp(n, dir.name) == 0){ + nc->qid = dir.qid; + goto Accept; + } + continue; + } + } + } + /* + * We processed at least one name, so will return some data. + * If we didn't process all nname entries succesfully, we drop + * the cloned channel and return just the Qids of the walks. + */ +Done: + poperror(); + if(wq->nqid < nname){ + if(alloc) + cclose(wq->clone); + wq->clone = nil; + }else if(wq->clone){ + /* attach cloned channel to same device */ + wq->clone->type = c->type; + } + return wq; +} + +int +devstat(Chan *c, uchar *db, int n, Dirtab *tab, int ntab, Devgen *gen) +{ + int i; + Dir dir; + char *p, *elem; + + for(i=0;; i++) + switch((*gen)(c, nil, tab, ntab, i, &dir)){ + case -1: + if(c->qid.type & QTDIR){ + if(c->name == nil) + elem = "???"; + else if(strcmp(c->name->s, "/") == 0) + elem = "/"; + else + for(elem=p=c->name->s; *p; p++) + if(*p == '/') + elem = p+1; + devdir(c, c->qid, elem, 0, eve, DMDIR|0555, &dir); + n = convD2M(&dir, db, n); + if(n == 0) + error(Ebadarg); + return n; + } + print("devstat %C %llux\n", devtab[c->type]->dc, c->qid.path); + + error(Enonexist); + case 0: + break; + case 1: + if(c->qid.path == dir.qid.path) { + if(c->flag&CMSG) + dir.mode |= DMMOUNT; + n = convD2M(&dir, db, n); + if(n == 0) + error(Ebadarg); + return n; + } + break; + } + error(Egreg); /* not reached? */ + return -1; +} + +long +devdirread(Chan *c, char *d, long n, Dirtab *tab, int ntab, Devgen *gen) +{ + long m, dsz; + struct{ + Dir d; + char slop[100]; + }dir; + + for(m=0; mdri++) { + switch((*gen)(c, nil, tab, ntab, c->dri, &dir.d)){ + case -1: + return m; + + case 0: + break; + + case 1: + dsz = convD2M(&dir.d, (uchar*)d, n-m); + if(dsz <= BIT16SZ){ /* <= not < because this isn't stat; read is stuck */ + if(m == 0) + error(Eshort); + return m; + } + m += dsz; + d += dsz; + break; + } + } + + return m; +} + +/* + * error(Eperm) if open permission not granted for up->user. + */ +void +devpermcheck(char *fileuid, ulong perm, int omode) +{ + ulong t; + static int access[] = { 0400, 0200, 0600, 0100 }; + + if(strcmp(up->user, fileuid) == 0) + perm <<= 0; + else + if(strcmp(up->user, eve) == 0) + perm <<= 3; + else + perm <<= 6; + + t = access[omode&3]; + if((t&perm) != t) + error(Eperm); +} + +Chan* +devopen(Chan *c, int omode, Dirtab *tab, int ntab, Devgen *gen) +{ + int i; + Dir dir; + + for(i=0;; i++) { + switch((*gen)(c, nil, tab, ntab, i, &dir)){ + case -1: + goto Return; + case 0: + break; + case 1: + if(c->qid.path == dir.qid.path) { + devpermcheck(dir.uid, dir.mode, omode); + goto Return; + } + break; + } + } +Return: + c->offset = 0; + if((c->qid.type&QTDIR) && omode!=OREAD) + error(Eperm); + c->mode = openmode(omode); + c->flag |= COPEN; + return c; +} + +void +devcreate(Chan *c, char *name, int mode, ulong perm) +{ + USED(c); + USED(name); + USED(mode); + USED(perm); + + error(Eperm); +} + +Block* +devbread(Chan *c, long n, ulong offset) +{ + Block *bp; + + bp = allocb(n); + if(bp == 0) + error(Enomem); + if(waserror()) { + freeb(bp); + nexterror(); + } + bp->wp += devtab[c->type]->read(c, bp->wp, n, offset); + poperror(); + return bp; +} + +long +devbwrite(Chan *c, Block *bp, ulong offset) +{ + long n; + + if(waserror()) { + freeb(bp); + nexterror(); + } + n = devtab[c->type]->write(c, bp->rp, BLEN(bp), offset); + poperror(); + freeb(bp); + + return n; +} + +void +devremove(Chan *c) +{ + USED(c); + error(Eperm); +} + +int +devwstat(Chan *c, uchar *a, int n) +{ + USED(c); + USED(a); + USED(n); + + error(Eperm); + return 0; +} + +void +devpower(int a) +{ + USED(a); + error(Eperm); +} + +int +devconfig(int a, char *b, DevConf *c) +{ + USED(a); + USED(b); + USED(c); + error(Eperm); + return 0; +} diff --git a/kern/devcons.c b/kern/devcons.c new file mode 100644 index 0000000..d60d100 --- /dev/null +++ b/kern/devcons.c @@ -0,0 +1,1238 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "keyboard.h" + +void (*consdebug)(void) = nil; +void (*screenputs)(char*, int) = nil; + +Queue* kbdq; /* unprocessed console input */ +Queue* lineq; /* processed console input */ +Queue* serialoq; /* serial console output */ +Queue* kprintoq; /* console output, for /dev/kprint */ +ulong kprintinuse; /* test and set whether /dev/kprint is open */ +int iprintscreenputs = 0; + +int panicking; + +struct +{ + int exiting; + int machs; +} active; + +static struct +{ + QLock lk; + + int raw; /* true if we shouldn't process input */ + int ctl; /* number of opens to the control file */ + int x; /* index into line */ + char line[1024]; /* current input line */ + + int count; + int ctlpoff; + + /* a place to save up characters at interrupt time before dumping them in the queue */ + Lock lockputc; + char istage[1024]; + char *iw; + char *ir; + char *ie; +} kbd = { + { 0 }, + 0, + 0, + 0, + { 0 }, + 0, + 0, + { 0 }, + { 0 }, + kbd.istage, + kbd.istage, + kbd.istage + sizeof(kbd.istage), +}; + +char *sysname; +vlong fasthz; + +static void seedrand(void); +static int readtime(ulong, char*, int); +static int readbintime(char*, int); +static int writetime(char*, int); +static int writebintime(char*, int); + +enum +{ + CMreboot, + CMpanic, +}; + +Cmdtab rebootmsg[] = +{ + CMreboot, "reboot", 0, + CMpanic, "panic", 0, +}; + +int +return0(void *v) +{ + return 0; +} + +void +printinit(void) +{ + lineq = qopen(2*1024, 0, nil, nil); + if(lineq == nil) + panic("printinit"); + qnoblock(lineq, 1); + + kbdq = qopen(4*1024, 0, 0, 0); + if(kbdq == nil) + panic("kbdinit"); + qnoblock(kbdq, 1); +} + +int +consactive(void) +{ + if(serialoq) + return qlen(serialoq) > 0; + return 0; +} + +void +prflush(void) +{ +/* + ulong now; + + now = m->ticks; + while(consactive()) + if(m->ticks - now >= HZ) + break; +*/ +} + +/* + * Print a string on the console. Convert \n to \r\n for serial + * line consoles. Locking of the queues is left up to the screen + * or uart code. Multi-line messages to serial consoles may get + * interspersed with other messages. + */ +static void +putstrn0(char *str, int n, int usewrite) +{ + int m; + char *t; + + /* + * if someone is reading /dev/kprint, + * put the message there. + * if not and there's an attached bit mapped display, + * put the message there. + * + * if there's a serial line being used as a console, + * put the message there. + */ + if(kprintoq != nil && !qisclosed(kprintoq)){ + if(usewrite) + qwrite(kprintoq, str, n); + else + qiwrite(kprintoq, str, n); + }else if(screenputs != nil) + screenputs(str, n); + + if(serialoq == nil){ + uartputs(str, n); + return; + } + + while(n > 0) { + t = memchr(str, '\n', n); + if(t && !kbd.raw) { + m = t-str; + if(usewrite){ + qwrite(serialoq, str, m); + qwrite(serialoq, "\r\n", 2); + } else { + qiwrite(serialoq, str, m); + qiwrite(serialoq, "\r\n", 2); + } + n -= m+1; + str = t+1; + } else { + if(usewrite) + qwrite(serialoq, str, n); + else + qiwrite(serialoq, str, n); + break; + } + } +} + +void +putstrn(char *str, int n) +{ + putstrn0(str, n, 0); +} + +int noprint; + +int +print(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + if(noprint) + return -1; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + putstrn(buf, n); + + return n; +} + +int +iprint(char *fmt, ...) +{ + int n, s; + va_list arg; + char buf[PRINTSIZE]; + + s = splhi(); + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + if(screenputs != nil && iprintscreenputs) + screenputs(buf, n); + uartputs(buf, n); + splx(s); + + return n; +} + +void +panic(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + kprintoq = nil; /* don't try to write to /dev/kprint */ + + if(panicking) + for(;;); + panicking = 1; + + splhi(); + strcpy(buf, "panic: "); + va_start(arg, fmt); + n = vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + buf[n] = '\n'; + uartputs(buf, n+1); + if(consdebug) + (*consdebug)(); + spllo(); + prflush(); + putstrn(buf, n+1); + dumpstack(); + + exit(1); +} + +int +pprint(char *fmt, ...) +{ + int n; + Chan *c; + va_list arg; + char buf[2*PRINTSIZE]; + + if(up == nil || up->fgrp == nil) + return 0; + + c = up->fgrp->fd[2]; + if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR)) + return 0; + n = sprint(buf, "%s %lud: ", up->text, up->pid); + va_start(arg, fmt); + n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + if(waserror()) + return 0; + devtab[c->type]->write(c, buf, n, c->offset); + poperror(); + + lock(&c->ref.lk); + c->offset += n; + unlock(&c->ref.lk); + + return n; +} + +static void +echoscreen(char *buf, int n) +{ + char *e, *p; + char ebuf[128]; + int x; + + p = ebuf; + e = ebuf + sizeof(ebuf) - 4; + while(n-- > 0){ + if(p >= e){ + screenputs(ebuf, p - ebuf); + p = ebuf; + } + x = *buf++; + if(x == 0x15){ + *p++ = '^'; + *p++ = 'U'; + *p++ = '\n'; + } else + *p++ = x; + } + if(p != ebuf) + screenputs(ebuf, p - ebuf); +} + +static void +echoserialoq(char *buf, int n) +{ + char *e, *p; + char ebuf[128]; + int x; + + p = ebuf; + e = ebuf + sizeof(ebuf) - 4; + while(n-- > 0){ + if(p >= e){ + qiwrite(serialoq, ebuf, p - ebuf); + p = ebuf; + } + x = *buf++; + if(x == '\n'){ + *p++ = '\r'; + *p++ = '\n'; + } else if(x == 0x15){ + *p++ = '^'; + *p++ = 'U'; + *p++ = '\n'; + } else + *p++ = x; + } + if(p != ebuf) + qiwrite(serialoq, ebuf, p - ebuf); +} + +static void +echo(char *buf, int n) +{ + static int ctrlt, pid; + extern ulong etext; + int x; + char *e, *p; + + e = buf+n; + for(p = buf; p < e; p++){ + switch(*p){ + case 0x10: /* ^P */ + if(cpuserver && !kbd.ctlpoff){ + active.exiting = 1; + return; + } + break; + case 0x14: /* ^T */ + ctrlt++; + if(ctrlt > 2) + ctrlt = 2; + continue; + } + + if(ctrlt != 2) + continue; + + /* ^T escapes */ + ctrlt = 0; + switch(*p){ + case 'S': + x = splhi(); + dumpstack(); + procdump(); + splx(x); + return; + case 's': + dumpstack(); + return; + case 'x': + xsummary(); + ixsummary(); + mallocsummary(); + pagersummary(); + return; + case 'd': + if(consdebug == nil) + consdebug = rdb; + else + consdebug = nil; + print("consdebug now 0x%p\n", consdebug); + return; + case 'D': + if(consdebug == nil) + consdebug = rdb; + consdebug(); + return; + case 'p': + x = spllo(); + procdump(); + splx(x); + return; + case 'q': + scheddump(); + return; + case 'k': + killbig(); + return; + case 'r': + exit(0); + return; + } + } + + qproduce(kbdq, buf, n); + if(kbd.raw) + return; + if(screenputs != nil) + echoscreen(buf, n); + if(serialoq) + echoserialoq(buf, n); +} + +/* + * Called by a uart interrupt for console input. + * + * turn '\r' into '\n' before putting it into the queue. + */ +int +kbdcr2nl(Queue *q, int ch) +{ + char *next; + + USED(q); + ilock(&kbd.lockputc); /* just a mutex */ + if(ch == '\r' && !kbd.raw) + ch = '\n'; + next = kbd.iw+1; + if(next >= kbd.ie) + next = kbd.istage; + if(next != kbd.ir){ + *kbd.iw = ch; + kbd.iw = next; + } + iunlock(&kbd.lockputc); + return 0; +} +static +void +_kbdputc(int c) +{ + Rune r; + char buf[UTFmax]; + int n; + + r = c; + n = runetochar(buf, &r); + if(n == 0) + return; + echo(buf, n); +// kbd.c = r; +// qproduce(kbdq, buf, n); +} + +/* _kbdputc, but with compose translation */ +int +kbdputc(Queue *q, int c) +{ + int i; + static int collecting, nk; + static Rune kc[5]; + + if(c == Kalt){ + collecting = 1; + nk = 0; + return; + } + + if(!collecting){ + _kbdputc(c); + return; + } + + kc[nk++] = c; + c = latin1(kc, nk); + if(c < -1) /* need more keystrokes */ + return; + if(c != -1) /* valid sequence */ + _kbdputc(c); + else + for(i=0; i= size) + return 0; + if(off+n > size) + n = size-off; + memmove(buf, tmp+off, n); + return n; +} + +int +readstr(ulong off, char *buf, ulong n, char *str) +{ + int size; + + size = strlen(str); + if(off >= size) + return 0; + if(off+n > size) + n = size-off; + memmove(buf, str+off, n); + return n; +} + +static void +consinit(void) +{ + todinit(); + randominit(); + /* + * at 115200 baud, the 1024 char buffer takes 56 ms to process, + * processing it every 22 ms should be fine + */ +/* addclock0link(kbdputcclock, 22); */ +} + +static Chan* +consattach(char *spec) +{ + return devattach('c', spec); +} + +static Walkqid* +conswalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name,nname, consdir, nelem(consdir), devgen); +} + +static int +consstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, consdir, nelem(consdir), devgen); +} + +static Chan* +consopen(Chan *c, int omode) +{ + c->aux = nil; + c = devopen(c, omode, consdir, nelem(consdir), devgen); + switch((ulong)c->qid.path){ + case Qconsctl: + qlock(&kbd.lk); + kbd.ctl++; + qunlock(&kbd.lk); + break; + + case Qkprint: + if(tas(&kprintinuse) != 0){ + c->flag &= ~COPEN; + error(Einuse); + } + if(kprintoq == nil){ + kprintoq = qopen(8*1024, Qcoalesce, 0, 0); + if(kprintoq == nil){ + c->flag &= ~COPEN; + error(Enomem); + } + qnoblock(kprintoq, 1); + }else + qreopen(kprintoq); + c->iounit = qiomaxatomic; + break; + + case Qsecstore: + if(omode == ORDWR) + error(Eperm); + if(omode != OREAD) + memset(secstorebuf, 0, sizeof secstorebuf); + break; + + case Qsnarf: + if(omode == ORDWR) + error(Eperm); + if(omode == OREAD) + c->aux = strdup(""); + else + c->aux = mallocz(SnarfSize, 1); + break; + } + return c; +} + +static void +consclose(Chan *c) +{ + switch((ulong)c->qid.path){ + /* last close of control file turns off raw */ + case Qconsctl: + if(c->flag&COPEN){ + qlock(&kbd.lk); + if(--kbd.ctl == 0) + kbd.raw = 0; + qunlock(&kbd.lk); + } + break; + + /* close of kprint allows other opens */ + case Qkprint: + if(c->flag & COPEN){ + kprintinuse = 0; + qhangup(kprintoq, nil); + } + break; + + case Qsnarf: + if(c->mode == OWRITE) + clipwrite(c->aux); + free(c->aux); + break; + } +} + +static long +consread(Chan *c, void *buf, long n, vlong off) +{ + ulong l; + char *b, *bp; + char tmp[128]; /* must be >= 6*NUMSIZE */ + char *cbuf = buf; + int ch, i, k, id, eol; + vlong offset = off; + + if(n <= 0) + return n; + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, buf, n, consdir, nelem(consdir), devgen); + + case Qcons: + qlock(&kbd.lk); + if(waserror()) { + qunlock(&kbd.lk); + nexterror(); + } + if(kbd.raw) { + if(qcanread(lineq)) + n = qread(lineq, buf, n); + else { + /* read as much as possible */ + do { + i = qread(kbdq, cbuf, n); + cbuf += i; + n -= i; + } while (n>0 && qcanread(kbdq)); + n = cbuf - (char*)buf; + } + } else { + while(!qcanread(lineq)) { + qread(kbdq, &kbd.line[kbd.x], 1); + ch = kbd.line[kbd.x]; + eol = 0; + switch(ch){ + case '\b': + if(kbd.x) + kbd.x--; + break; + case 0x15: + kbd.x = 0; + break; + case '\n': + case 0x04: + eol = 1; + default: + kbd.line[kbd.x++] = ch; + break; + } + if(kbd.x == sizeof(kbd.line) || eol){ + if(ch == 0x04) + kbd.x--; + qwrite(lineq, kbd.line, kbd.x); + kbd.x = 0; + } + } + n = qread(lineq, buf, n); + } + qunlock(&kbd.lk); + poperror(); + return n; + + case Qcpunote: + sleep(&up->sleep, return0, nil); + + case Qcputime: + return 0; + + case Qkprint: + return qread(kprintoq, buf, n); + + case Qpgrpid: + return readnum((ulong)offset, buf, n, up->pgrp->pgrpid, NUMSIZE); + + case Qpid: + return readnum((ulong)offset, buf, n, up->pid, NUMSIZE); + + case Qppid: + return readnum((ulong)offset, buf, n, up->parentpid, NUMSIZE); + + case Qtime: + return readtime((ulong)offset, buf, n); + + case Qbintime: + return readbintime(buf, n); + + case Qhostowner: + return readstr((ulong)offset, buf, n, eve); + + case Qhostdomain: + return readstr((ulong)offset, buf, n, hostdomain); + + case Quser: + return readstr((ulong)offset, buf, n, up->user); + + case Qnull: + return 0; + + case Qsnarf: + if(offset == 0){ + free(c->aux); + c->aux = clipread(); + } + if(c->aux == nil) + return 0; + return readstr(offset, buf, n, c->aux); + + case Qsecstore: + return readstr(offset, buf, n, secstorebuf); + + case Qsysstat: + return 0; + + case Qswap: + return 0; + + case Qsysname: + if(sysname == nil) + return 0; + return readstr((ulong)offset, buf, n, sysname); + + case Qrandom: + return randomread(buf, n); + + case Qdrivers: + b = malloc(READSTR); + if(b == nil) + error(Enomem); + n = 0; + for(i = 0; devtab[i] != nil; i++) + n += snprint(b+n, READSTR-n, "#%C %s\n", devtab[i]->dc, devtab[i]->name); + if(waserror()){ + free(b); + nexterror(); + } + n = readstr((ulong)offset, buf, n, b); + free(b); + poperror(); + return n; + + case Qzero: + memset(buf, 0, n); + return n; + + case Qosversion: + snprint(tmp, sizeof tmp, "2000"); + n = readstr((ulong)offset, buf, n, tmp); + return n; + + default: + print("consread 0x%llux\n", c->qid.path); + error(Egreg); + } + return -1; /* never reached */ +} + +static long +conswrite(Chan *c, void *va, long n, vlong off) +{ + char buf[256]; + long l, bp; + char *a = va; + int id, fd; + Chan *swc; + ulong offset = off; + Cmdbuf *cb; + Cmdtab *ct; + + switch((ulong)c->qid.path){ + case Qcons: + /* + * Can't page fault in putstrn, so copy the data locally. + */ + l = n; + while(l > 0){ + bp = l; + if(bp > sizeof buf) + bp = sizeof buf; + memmove(buf, a, bp); + putstrn0(buf, bp, 1); + a += bp; + l -= bp; + } + break; + + case Qconsctl: + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, a, n); + buf[n] = 0; + for(a = buf; a;){ + if(strncmp(a, "rawon", 5) == 0){ + qlock(&kbd.lk); + if(kbd.x){ + qwrite(kbdq, kbd.line, kbd.x); + kbd.x = 0; + } + kbd.raw = 1; + qunlock(&kbd.lk); + } else if(strncmp(a, "rawoff", 6) == 0){ + qlock(&kbd.lk); + kbd.raw = 0; + kbd.x = 0; + qunlock(&kbd.lk); + } else if(strncmp(a, "ctlpon", 6) == 0){ + kbd.ctlpoff = 0; + } else if(strncmp(a, "ctlpoff", 7) == 0){ + kbd.ctlpoff = 1; + } + if(a = strchr(a, ' ')) + a++; + } + break; + + case Qtime: + if(!iseve()) + error(Eperm); + return writetime(a, n); + + case Qbintime: + if(!iseve()) + error(Eperm); + return writebintime(a, n); + + case Qhostowner: + return hostownerwrite(a, n); + + case Qhostdomain: + return hostdomainwrite(a, n); + + case Quser: + return userwrite(a, n); + + case Qnull: + break; + + case Qreboot: + if(!iseve()) + error(Eperm); + cb = parsecmd(a, n); + + if(waserror()) { + free(cb); + nexterror(); + } + ct = lookupcmd(cb, rebootmsg, nelem(rebootmsg)); + switch(ct->index) { + case CMreboot: + rebootcmd(cb->nf-1, cb->f+1); + break; + case CMpanic: + panic("/dev/reboot"); + } + poperror(); + free(cb); + break; + + case Qsecstore: + if(offset >= sizeof secstorebuf || offset+n+1 >= sizeof secstorebuf) + error(Etoobig); + secstoretab->qid.vers++; + memmove(secstorebuf+offset, va, n); + return n; + + case Qsnarf: + if(offset >= SnarfSize || offset+n >= SnarfSize) + error(Etoobig); + snarftab->qid.vers++; + memmove((uchar*)c->aux+offset, va, n); + return n; + + case Qsysstat: + n = 0; + break; + + case Qswap: + if(n >= sizeof buf) + error(Egreg); + memmove(buf, va, n); /* so we can NUL-terminate */ + buf[n] = 0; + /* start a pager if not already started */ + if(strncmp(buf, "start", 5) == 0){ + kickpager(); + break; + } + if(cpuserver && !iseve()) + error(Eperm); + if(buf[0]<'0' || '9'= sizeof buf) + error(Ebadarg); + strncpy(buf, a, n); + buf[n] = 0; + if(buf[n-1] == '\n') + buf[n-1] = 0; + kstrdup(&sysname, buf); + break; + + default: + print("conswrite: 0x%llux\n", c->qid.path); + error(Egreg); + } + return n; +} + +Dev consdevtab = { + 'c', + "cons", + + devreset, + consinit, + devshutdown, + consattach, + conswalk, + consstat, + consopen, + devcreate, + consclose, + consread, + devbread, + conswrite, + devbwrite, + devremove, + devwstat, +}; + +static ulong randn; + +static void +seedrand(void) +{ + randomread((void*)&randn, sizeof(randn)); +} + +// int +// nrand(int n) +// { +// if(randn == 0) +// seedrand(); +// randn = randn*1103515245 + 12345 + fastticks(0); +// return (randn>>16) % n; +// } + +int +rand(void) +{ + nrand(1); + return randn; +} + +/* static uvlong uvorder = 0x0001020304050607ULL; */ +static uvlong uvorder = (uvlong)0x0001020304050607; + +static uchar* +le2vlong(vlong *to, uchar *f) +{ + uchar *t, *o; + int i; + + t = (uchar*)to; + o = (uchar*)&uvorder; + for(i = 0; i < sizeof(vlong); i++) + t[o[i]] = f[i]; + return f+sizeof(vlong); +} + +static uchar* +vlong2le(uchar *t, vlong from) +{ + uchar *f, *o; + int i; + + f = (uchar*)&from; + o = (uchar*)&uvorder; + for(i = 0; i < sizeof(vlong); i++) + t[i] = f[o[i]]; + return t+sizeof(vlong); +} + +static long order = 0x00010203; + +static uchar* +le2long(long *to, uchar *f) +{ + uchar *t, *o; + int i; + + t = (uchar*)to; + o = (uchar*)ℴ + for(i = 0; i < sizeof(long); i++) + t[o[i]] = f[i]; + return f+sizeof(long); +} + +static uchar* +long2le(uchar *t, long from) +{ + uchar *f, *o; + int i; + + f = (uchar*)&from; + o = (uchar*)ℴ + for(i = 0; i < sizeof(long); i++) + t[i] = f[o[i]]; + return t+sizeof(long); +} + +char *Ebadtimectl = "bad time control"; + +/* + * like the old #c/time but with added info. Return + * + * secs nanosecs fastticks fasthz + */ +static int +readtime(ulong off, char *buf, int n) +{ + vlong nsec, ticks; + long sec; + char str[7*NUMSIZE]; + + nsec = todget(&ticks); + if(fasthz == (vlong)0) + fastticks((uvlong*)&fasthz); + sec = nsec/((uvlong) 1000000000); + snprint(str, sizeof(str), "%*.0lud %*.0llud %*.0llud %*.0llud ", + NUMSIZE-1, sec, + VLNUMSIZE-1, nsec, + VLNUMSIZE-1, ticks, + VLNUMSIZE-1, fasthz); + return readstr(off, buf, n, str); +} + +/* + * set the time in seconds + */ +static int +writetime(char *buf, int n) +{ + char b[13]; + long i; + vlong now; + + if(n >= sizeof(b)) + error(Ebadtimectl); + strncpy(b, buf, n); + b[n] = 0; + i = strtol(b, 0, 0); + if(i <= 0) + error(Ebadtimectl); + now = i*((vlong) 1000000000); + todset(now, 0, 0); + return n; +} + +/* + * read binary time info. all numbers are little endian. + * ticks and nsec are syncronized. + */ +static int +readbintime(char *buf, int n) +{ + int i; + vlong nsec, ticks; + uchar *b = (uchar*)buf; + + i = 0; + if(fasthz == (vlong)0) + fastticks((uvlong*)&fasthz); + nsec = todget(&ticks); + if(n >= 3*sizeof(uvlong)){ + vlong2le(b+2*sizeof(uvlong), fasthz); + i += sizeof(uvlong); + } + if(n >= 2*sizeof(uvlong)){ + vlong2le(b+sizeof(uvlong), ticks); + i += sizeof(uvlong); + } + if(n >= 8){ + vlong2le(b, nsec); + i += sizeof(vlong); + } + return i; +} + +/* + * set any of the following + * - time in nsec + * - nsec trim applied over some seconds + * - clock frequency + */ +static int +writebintime(char *buf, int n) +{ + uchar *p; + vlong delta; + long period; + + n--; + p = (uchar*)buf + 1; + switch(*buf){ + case 'n': + if(n < sizeof(vlong)) + error(Ebadtimectl); + le2vlong(&delta, p); + todset(delta, 0, 0); + break; + case 'd': + if(n < sizeof(vlong)+sizeof(long)) + error(Ebadtimectl); + p = le2vlong(&delta, p); + le2long(&period, p); + todset(-1, delta, period); + break; + case 'f': + if(n < sizeof(uvlong)) + error(Ebadtimectl); + le2vlong(&fasthz, p); + todsetfreq(fasthz); + break; + } + return n; +} + + diff --git a/kern/devdraw.c b/kern/devdraw.c new file mode 100644 index 0000000..5c848f3 --- /dev/null +++ b/kern/devdraw.c @@ -0,0 +1,2100 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#define Image IMAGE +#include +#include +#include +#include +#include "screen.h" + +enum +{ + Qtopdir = 0, + Qnew, + Q3rd, + Q2nd, + Qcolormap, + Qctl, + Qdata, + Qrefresh, +}; + +/* + * Qid path is: + * 4 bits of file type (qids above) + * 24 bits of mux slot number +1; 0 means not attached to client + */ +#define QSHIFT 4 /* location in qid of client # */ + +#define QID(q) ((((ulong)(q).path)&0x0000000F)>>0) +#define CLIENTPATH(q) ((((ulong)q)&0x7FFFFFF0)>>QSHIFT) +#define CLIENT(q) CLIENTPATH((q).path) + +#define NHASH (1<<5) +#define HASHMASK (NHASH-1) +#define IOUNIT (64*1024) + +typedef struct Client Client; +typedef struct Draw Draw; +typedef struct DImage DImage; +typedef struct DScreen DScreen; +typedef struct CScreen CScreen; +typedef struct FChar FChar; +typedef struct Refresh Refresh; +typedef struct Refx Refx; +typedef struct DName DName; + +ulong blanktime = 30; /* in minutes; a half hour */ + +struct Draw +{ + QLock lk; + int clientid; + int nclient; + Client** client; + int nname; + DName* name; + int vers; + int softscreen; + int blanked; /* screen turned off */ + ulong blanktime; /* time of last operation */ + ulong savemap[3*256]; +}; + +struct Client +{ + Ref r; + DImage* dimage[NHASH]; + CScreen* cscreen; + Refresh* refresh; + Rendez refrend; + uchar* readdata; + int nreaddata; + int busy; + int clientid; + int slot; + int refreshme; + int infoid; + int op; +}; + +struct Refresh +{ + DImage* dimage; + Rectangle r; + Refresh* next; +}; + +struct Refx +{ + Client* client; + DImage* dimage; +}; + +struct DName +{ + char *name; + Client *client; + DImage* dimage; + int vers; +}; + +struct FChar +{ + int minx; /* left edge of bits */ + int maxx; /* right edge of bits */ + uchar miny; /* first non-zero scan-line */ + uchar maxy; /* last non-zero scan-line + 1 */ + schar left; /* offset of baseline */ + uchar width; /* width of baseline */ +}; + +/* + * Reference counts in DImages: + * one per open by original client + * one per screen image or fill + * one per image derived from this one by name + */ +struct DImage +{ + int id; + int ref; + char *name; + int vers; + Memimage* image; + int ascent; + int nfchar; + FChar* fchar; + DScreen* dscreen; /* 0 if not a window */ + DImage* fromname; /* image this one is derived from, by name */ + DImage* next; +}; + +struct CScreen +{ + DScreen* dscreen; + CScreen* next; +}; + +struct DScreen +{ + int id; + int public; + int ref; + DImage *dimage; + DImage *dfill; + Memscreen* screen; + Client* owner; + DScreen* next; +}; + +static Draw sdraw; +static Memimage *screenimage; +static Memdata screendata; +static Rectangle flushrect; +static int waste; +static DScreen* dscreen; +extern void flushmemscreen(Rectangle); + void drawmesg(Client*, void*, int); + void drawuninstall(Client*, int); + void drawfreedimage(DImage*); + Client* drawclientofpath(ulong); + +static char Enodrawimage[] = "unknown id for draw image"; +static char Enodrawscreen[] = "unknown id for draw screen"; +static char Eshortdraw[] = "short draw message"; +static char Eshortread[] = "draw read too short"; +static char Eimageexists[] = "image id in use"; +static char Escreenexists[] = "screen id in use"; +static char Edrawmem[] = "image memory allocation failed"; +static char Ereadoutside[] = "readimage outside image"; +static char Ewriteoutside[] = "writeimage outside image"; +static char Enotfont[] = "image not a font"; +static char Eindex[] = "character index out of range"; +static char Enoclient[] = "no such draw client"; +static char Edepth[] = "image has bad depth"; +static char Enameused[] = "image name in use"; +static char Enoname[] = "no image with that name"; +static char Eoldname[] = "named image no longer valid"; +static char Enamed[] = "image already has name"; +static char Ewrongname[] = "wrong name for image"; + +static int +drawgen(Chan *c, char *name, Dirtab *dt, int ndt, int s, Dir *dp) +{ + int t; + Qid q; + ulong path; + Client *cl; + + USED(name); + USED(dt); + USED(ndt); + + q.vers = 0; + + if(s == DEVDOTDOT){ + switch(QID(c->qid)){ + case Qtopdir: + case Q2nd: + mkqid(&q, Qtopdir, 0, QTDIR); + devdir(c, q, "#i", 0, eve, 0500, dp); + break; + case Q3rd: + cl = drawclientofpath(c->qid.path); + if(cl == nil) + strcpy(up->genbuf, "??"); + else + sprint(up->genbuf, "%d", cl->clientid); + mkqid(&q, Q2nd, 0, QTDIR); + devdir(c, q, up->genbuf, 0, eve, 0500, dp); + break; + default: + panic("drawwalk %llux", c->qid.path); + } + return 1; + } + + /* + * Top level directory contains the name of the device. + */ + t = QID(c->qid); + if(t == Qtopdir){ + switch(s){ + case 0: + mkqid(&q, Q2nd, 0, QTDIR); + devdir(c, q, "draw", 0, eve, 0555, dp); + break; + default: + return -1; + } + return 1; + } + + /* + * Second level contains "new" plus all the clients. + */ + if(t == Q2nd || t == Qnew){ + if(s == 0){ + mkqid(&q, Qnew, 0, QTFILE); + devdir(c, q, "new", 0, eve, 0666, dp); + } + else if(s <= sdraw.nclient){ + cl = sdraw.client[s-1]; + if(cl == 0) + return 0; + sprint(up->genbuf, "%d", cl->clientid); + mkqid(&q, (s<genbuf, 0, eve, 0555, dp); + return 1; + } + else + return -1; + return 1; + } + + /* + * Third level. + */ + path = c->qid.path&~((1<qid.vers; + q.type = QTFILE; + switch(s){ + case 0: + q.path = path|Qcolormap; + devdir(c, q, "colormap", 0, eve, 0600, dp); + break; + case 1: + q.path = path|Qctl; + devdir(c, q, "ctl", 0, eve, 0600, dp); + break; + case 2: + q.path = path|Qdata; + devdir(c, q, "data", 0, eve, 0600, dp); + break; + case 3: + q.path = path|Qrefresh; + devdir(c, q, "refresh", 0, eve, 0400, dp); + break; + default: + return -1; + } + return 1; +} + +static +int +drawrefactive(void *a) +{ + Client *c; + + c = a; + return c->refreshme || c->refresh!=0; +} + +static +void +drawrefreshscreen(DImage *l, Client *client) +{ + while(l != nil && l->dscreen == nil) + l = l->fromname; + if(l != nil && l->dscreen->owner != client) + l->dscreen->owner->refreshme = 1; +} + +static +void +drawrefresh(Memimage *m, Rectangle r, void *v) +{ + Refx *x; + DImage *d; + Client *c; + Refresh *ref; + + USED(m); + + if(v == 0) + return; + x = v; + c = x->client; + d = x->dimage; + for(ref=c->refresh; ref; ref=ref->next) + if(ref->dimage == d){ + combinerect(&ref->r, r); + return; + } + ref = malloc(sizeof(Refresh)); + if(ref){ + ref->dimage = d; + ref->r = r; + ref->next = c->refresh; + c->refresh = ref; + } +} + +static void +addflush(Rectangle r) +{ + int abb, ar, anbb; + Rectangle nbb; + + if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r)) + return; + + if(flushrect.min.x >= flushrect.max.x){ + flushrect = r; + waste = 0; + return; + } + nbb = flushrect; + combinerect(&nbb, r); + ar = Dx(r)*Dy(r); + abb = Dx(flushrect)*Dy(flushrect); + anbb = Dx(nbb)*Dy(nbb); + /* + * Area of new waste is area of new bb minus area of old bb, + * less the area of the new segment, which we assume is not waste. + * This could be negative, but that's OK. + */ + waste += anbb-abb - ar; + if(waste < 0) + waste = 0; + /* + * absorb if: + * total area is small + * waste is less than half total area + * rectangles touch + */ + if(anbb<=1024 || waste*2layer; + if(l == nil) + return; + do{ + if(l->screen->image->data != screenimage->data) + return; + r = rectaddpt(r, l->delta); + l = l->screen->image->layer; + }while(l); + addflush(r); +} + +void +drawflush(void) +{ + if(flushrect.min.x < flushrect.max.x) + flushmemscreen(flushrect); + flushrect = Rect(10000, 10000, -10000, -10000); +} + +void +drawflushr(Rectangle r) +{ + qlock(&sdraw.lk); + flushmemscreen(r); + qunlock(&sdraw.lk); +} + +static +int +drawcmp(char *a, char *b, int n) +{ + if(strlen(a) != n) + return 1; + return memcmp(a, b, n); +} + +DName* +drawlookupname(int n, char *str) +{ + DName *name, *ename; + + name = sdraw.name; + ename = &name[sdraw.nname]; + for(; namename, str, n) == 0) + return name; + return 0; +} + +int +drawgoodname(DImage *d) +{ + DName *n; + + /* if window, validate the screen's own images */ + if(d->dscreen) + if(drawgoodname(d->dscreen->dimage) == 0 + || drawgoodname(d->dscreen->dfill) == 0) + return 0; + if(d->name == nil) + return 1; + n = drawlookupname(strlen(d->name), d->name); + if(n==nil || n->vers!=d->vers) + return 0; + return 1; +} + +DImage* +drawlookup(Client *client, int id, int checkname) +{ + DImage *d; + + d = client->dimage[id&HASHMASK]; + while(d){ + if(d->id == id){ + if(checkname && !drawgoodname(d)) + error(Eoldname); + return d; + } + d = d->next; + } + return 0; +} + +DScreen* +drawlookupdscreen(int id) +{ + DScreen *s; + + s = dscreen; + while(s){ + if(s->id == id) + return s; + s = s->next; + } + return 0; +} + +DScreen* +drawlookupscreen(Client *client, int id, CScreen **cs) +{ + CScreen *s; + + s = client->cscreen; + while(s){ + if(s->dscreen->id == id){ + *cs = s; + return s->dscreen; + } + s = s->next; + } + error(Enodrawscreen); + return 0; +} + +Memimage* +drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen) +{ + DImage *d; + + d = malloc(sizeof(DImage)); + if(d == 0) + return 0; + d->id = id; + d->ref = 1; + d->name = 0; + d->vers = 0; + d->image = i; + d->nfchar = 0; + d->fchar = 0; + d->fromname = 0; + d->dscreen = dscreen; + d->next = client->dimage[id&HASHMASK]; + client->dimage[id&HASHMASK] = d; + return i; +} + +Memscreen* +drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public) +{ + Memscreen *s; + CScreen *c; + + c = malloc(sizeof(CScreen)); + if(dimage && dimage->image && dimage->image->chan == 0) + panic("bad image %p in drawinstallscreen", dimage->image); + + if(c == 0) + return 0; + if(d == 0){ + d = malloc(sizeof(DScreen)); + if(d == 0){ + free(c); + return 0; + } + s = malloc(sizeof(Memscreen)); + if(s == 0){ + free(c); + free(d); + return 0; + } + s->frontmost = 0; + s->rearmost = 0; + d->dimage = dimage; + if(dimage){ + s->image = dimage->image; + dimage->ref++; + } + d->dfill = dfill; + if(dfill){ + s->fill = dfill->image; + dfill->ref++; + } + d->ref = 0; + d->id = id; + d->screen = s; + d->public = public; + d->next = dscreen; + d->owner = client; + dscreen = d; + } + c->dscreen = d; + d->ref++; + c->next = client->cscreen; + client->cscreen = c; + return d->screen; +} + +void +drawdelname(DName *name) +{ + int i; + + i = name-sdraw.name; + memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName)); + sdraw.nname--; +} + +void +drawfreedscreen(DScreen *this) +{ + DScreen *ds, *next; + + this->ref--; + if(this->ref < 0) + print("negative ref in drawfreedscreen\n"); + if(this->ref > 0) + return; + ds = dscreen; + if(ds == this){ + dscreen = this->next; + goto Found; + } + while(next = ds->next){ /* assign = */ + if(next == this){ + ds->next = this->next; + goto Found; + } + ds = next; + } + error(Enodrawimage); + + Found: + if(this->dimage) + drawfreedimage(this->dimage); + if(this->dfill) + drawfreedimage(this->dfill); + free(this->screen); + free(this); +} + +void +drawfreedimage(DImage *dimage) +{ + int i; + Memimage *l; + DScreen *ds; + + dimage->ref--; + if(dimage->ref < 0) + print("negative ref in drawfreedimage\n"); + if(dimage->ref > 0) + return; + + /* any names? */ + for(i=0; ifromname){ /* acquired by name; owned by someone else*/ + drawfreedimage(dimage->fromname); + goto Return; + } + if(dimage->image == screenimage) /* don't free the display */ + goto Return; + ds = dimage->dscreen; + if(ds){ + l = dimage->image; + if(l->data == screenimage->data) + addflush(l->layer->screenr); + if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */ + free(l->layer->refreshptr); + l->layer->refreshptr = nil; + if(drawgoodname(dimage)) + memldelete(l); + else + memlfree(l); + drawfreedscreen(ds); + }else + freememimage(dimage->image); + Return: + free(dimage->fchar); + free(dimage); +} + +void +drawuninstallscreen(Client *client, CScreen *this) +{ + CScreen *cs, *next; + + cs = client->cscreen; + if(cs == this){ + client->cscreen = this->next; + drawfreedscreen(this->dscreen); + free(this); + return; + } + while(next = cs->next){ /* assign = */ + if(next == this){ + cs->next = this->next; + drawfreedscreen(this->dscreen); + free(this); + return; + } + cs = next; + } +} + +void +drawuninstall(Client *client, int id) +{ + DImage *d, *next; + + d = client->dimage[id&HASHMASK]; + if(d == 0) + error(Enodrawimage); + if(d->id == id){ + client->dimage[id&HASHMASK] = d->next; + drawfreedimage(d); + return; + } + while(next = d->next){ /* assign = */ + if(next->id == id){ + d->next = next->next; + drawfreedimage(next); + return; + } + d = next; + } + error(Enodrawimage); +} + +void +drawaddname(Client *client, DImage *di, int n, char *str) +{ + DName *name, *ename, *new, *t; + + name = sdraw.name; + ename = &name[sdraw.nname]; + for(; namename, str, n) == 0) + error(Enameused); + t = smalloc((sdraw.nname+1)*sizeof(DName)); + memmove(t, sdraw.name, sdraw.nname*sizeof(DName)); + free(sdraw.name); + sdraw.name = t; + new = &sdraw.name[sdraw.nname++]; + new->name = smalloc(n+1); + memmove(new->name, str, n); + new->name[n] = 0; + new->dimage = di; + new->client = client; + new->vers = ++sdraw.vers; +} + +Client* +drawnewclient(void) +{ + Client *cl, **cp; + int i; + + for(i=0; islot = i; + cl->clientid = ++sdraw.clientid; + cl->op = SoverD; + sdraw.client[i] = cl; + return cl; +} + +static int +drawclientop(Client *cl) +{ + int op; + + op = cl->op; + cl->op = SoverD; + return op; +} + +int +drawhasclients(void) +{ + /* + * if draw has ever been used, we can't resize the frame buffer, + * even if all clients have exited (nclients is cumulative); it's too + * hard to make work. + */ + return sdraw.nclient != 0; +} + +Client* +drawclientofpath(ulong path) +{ + Client *cl; + int slot; + + slot = CLIENTPATH(path); + if(slot == 0) + return nil; + cl = sdraw.client[slot-1]; + if(cl==0 || cl->clientid==0) + return nil; + return cl; +} + + +Client* +drawclient(Chan *c) +{ + Client *client; + + client = drawclientofpath(c->qid.path); + if(client == nil) + error(Enoclient); + return client; +} + +Memimage* +drawimage(Client *client, uchar *a) +{ + DImage *d; + + d = drawlookup(client, BGLONG(a), 1); + if(d == nil) + error(Enodrawimage); + return d->image; +} + +void +drawrectangle(Rectangle *r, uchar *a) +{ + r->min.x = BGLONG(a+0*4); + r->min.y = BGLONG(a+1*4); + r->max.x = BGLONG(a+2*4); + r->max.y = BGLONG(a+3*4); +} + +void +drawpoint(Point *p, uchar *a) +{ + p->x = BGLONG(a+0*4); + p->y = BGLONG(a+1*4); +} + +Point +drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op) +{ + FChar *fc; + Rectangle r; + Point sp1; + + fc = &font->fchar[index]; + r.min.x = p.x+fc->left; + r.min.y = p.y-(font->ascent-fc->miny); + r.max.x = r.min.x+(fc->maxx-fc->minx); + r.max.y = r.min.y+(fc->maxy-fc->miny); + sp1.x = sp->x+fc->left; + sp1.y = sp->y+fc->miny; + memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op); + p.x += fc->width; + sp->x += fc->width; + return p; +} + +static int +initscreenimage(void) +{ + int width, depth; + ulong chan; + void *X; + Rectangle r; + + if(screenimage != nil) + return 1; + + screendata.base = nil; + screendata.bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen, &X); + if(screendata.bdata == nil && X == nil) + return 0; + screendata.ref = 1; + + screenimage = allocmemimaged(r, chan, &screendata, X); + if(screenimage == nil){ + /* RSC: BUG: detach screen */ + return 0; + } + + screenimage->width = width; + screenimage->clipr = r; + return 1; +} + +void +deletescreenimage(void) +{ + qlock(&sdraw.lk); + /* RSC: BUG: detach screen */ + if(screenimage) + freememimage(screenimage); + screenimage = nil; + qunlock(&sdraw.lk); +} + +static Chan* +drawattach(char *spec) +{ + qlock(&sdraw.lk); + if(!initscreenimage()){ + qunlock(&sdraw.lk); + error("no frame buffer"); + } + qunlock(&sdraw.lk); + return devattach('i', spec); +} + +static Walkqid* +drawwalk(Chan *c, Chan *nc, char **name, int nname) +{ + if(screendata.bdata == nil) + error("no frame buffer"); + return devwalk(c, nc, name, nname, 0, 0, drawgen); +} + +static int +drawstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, drawgen); +} + +static Chan* +drawopen(Chan *c, int omode) +{ + Client *cl; + + if(c->qid.type & QTDIR){ + c = devopen(c, omode, 0, 0, drawgen); + c->iounit = IOUNIT; + } + + qlock(&sdraw.lk); + if(waserror()){ + qunlock(&sdraw.lk); + nexterror(); + } + + if(QID(c->qid) == Qnew){ + cl = drawnewclient(); + if(cl == 0) + error(Enodev); + c->qid.path = Qctl|((cl->slot+1)<qid)){ + case Qnew: + break; + + case Qctl: + cl = drawclient(c); + if(cl->busy) + error(Einuse); + cl->busy = 1; + flushrect = Rect(10000, 10000, -10000, -10000); + drawinstall(cl, 0, screenimage, 0); + incref(&cl->r); + break; + case Qcolormap: + case Qdata: + case Qrefresh: + cl = drawclient(c); + incref(&cl->r); + break; + } + qunlock(&sdraw.lk); + poperror(); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = IOUNIT; + return c; +} + +static void +drawclose(Chan *c) +{ + int i; + DImage *d, **dp; + Client *cl; + Refresh *r; + + if(QID(c->qid) < Qcolormap) /* Qtopdir, Qnew, Q3rd, Q2nd have no client */ + return; + qlock(&sdraw.lk); + if(waserror()){ + qunlock(&sdraw.lk); + nexterror(); + } + + cl = drawclient(c); + if(QID(c->qid) == Qctl) + cl->busy = 0; + if((c->flag&COPEN) && (decref(&cl->r)==0)){ + while(r = cl->refresh){ /* assign = */ + cl->refresh = r->next; + free(r); + } + /* free names */ + for(i=0; icscreen) + drawuninstallscreen(cl, cl->cscreen); + /* all screens are freed, so now we can free images */ + dp = cl->dimage; + for(i=0; inext; + drawfreedimage(d); + } + dp++; + } + sdraw.client[cl->slot] = 0; + drawflush(); /* to erase visible, now dead windows */ + free(cl); + } + qunlock(&sdraw.lk); + poperror(); +} + +long +drawread(Chan *c, void *a, long n, vlong off) +{ + int index, m; + ulong red, green, blue; + Client *cl; + uchar *p; + Refresh *r; + DImage *di; + Memimage *i; + ulong offset = off; + char buf[16]; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, drawgen); + cl = drawclient(c); + qlock(&sdraw.lk); + if(waserror()){ + qunlock(&sdraw.lk); + nexterror(); + } + switch(QID(c->qid)){ + case Qctl: + if(n < 12*12) + error(Eshortread); + if(cl->infoid < 0) + error(Enodrawimage); + if(cl->infoid == 0){ + i = screenimage; + if(i == nil) + error(Enodrawimage); + }else{ + di = drawlookup(cl, cl->infoid, 1); + if(di == nil) + error(Enodrawimage); + i = di->image; + } + n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ", + cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl, + i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y, + i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y); + cl->infoid = -1; + break; + + case Qcolormap: + drawactive(1); /* to restore map from backup */ + p = malloc(4*12*256+1); + if(p == 0) + error(Enomem); + m = 0; + for(index = 0; index < 256; index++){ + getcolor(index, &red, &green, &blue); + m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n", index, red>>24, green>>24, blue>>24); + } + n = readstr(offset, a, n, (char*)p); + free(p); + break; + + case Qdata: + if(cl->readdata == nil) + error("no draw data"); + if(n < cl->nreaddata) + error(Eshortread); + n = cl->nreaddata; + memmove(a, cl->readdata, cl->nreaddata); + free(cl->readdata); + cl->readdata = nil; + break; + + case Qrefresh: + if(n < 5*4) + error(Ebadarg); + for(;;){ + if(cl->refreshme || cl->refresh) + break; + qunlock(&sdraw.lk); + if(waserror()){ + qlock(&sdraw.lk); /* restore lock for waserror() above */ + nexterror(); + } + sleep(&cl->refrend, drawrefactive, cl); + poperror(); + qlock(&sdraw.lk); + } + p = a; + while(cl->refresh && n>=5*4){ + r = cl->refresh; + BPLONG(p+0*4, r->dimage->id); + BPLONG(p+1*4, r->r.min.x); + BPLONG(p+2*4, r->r.min.y); + BPLONG(p+3*4, r->r.max.x); + BPLONG(p+4*4, r->r.max.y); + cl->refresh = r->next; + free(r); + p += 5*4; + n -= 5*4; + } + cl->refreshme = 0; + n = p-(uchar*)a; + } + qunlock(&sdraw.lk); + poperror(); + return n; +} + +void +drawwakeall(void) +{ + Client *cl; + int i; + + for(i=0; irefreshme || cl->refresh)) + wakeup(&cl->refrend); + } +} + +static long +drawwrite(Chan *c, void *a, long n, vlong offset) +{ + char buf[128], *fields[4], *q; + Client *cl; + int i, m, red, green, blue, x; + + USED(offset); + + if(c->qid.type & QTDIR) + error(Eisdir); + cl = drawclient(c); + qlock(&sdraw.lk); + if(waserror()){ + drawwakeall(); + qunlock(&sdraw.lk); + nexterror(); + } + switch(QID(c->qid)){ + case Qctl: + if(n != 4) + error("unknown draw control request"); + cl->infoid = BGLONG((uchar*)a); + break; + + case Qcolormap: + drawactive(1); /* to restore map from backup */ + m = n; + n = 0; + while(m > 0){ + x = m; + if(x > sizeof(buf)-1) + x = sizeof(buf)-1; + q = memccpy(buf, a, '\n', x); + if(q == 0) + break; + i = q-buf; + n += i; + a = (char*)a + i; + m -= i; + *q = 0; + if(tokenize(buf, fields, nelem(fields)) != 4) + error(Ebadarg); + i = strtoul(fields[0], 0, 0); + red = strtoul(fields[1], 0, 0); + green = strtoul(fields[2], 0, 0); + blue = strtoul(fields[3], &q, 0); + if(fields[3] == q) + error(Ebadarg); + if(red>255 || green>255 || blue>255 || i<0 || i>255) + error(Ebadarg); + red |= red<<8; + red |= red<<16; + green |= green<<8; + green |= green<<16; + blue |= blue<<8; + blue |= blue<<16; + setcolor(i, red, green, blue); + } + break; + + case Qdata: + drawmesg(cl, a, n); + drawwakeall(); + break; + + default: + error(Ebadusefd); + } + qunlock(&sdraw.lk); + poperror(); + return n; +} + +uchar* +drawcoord(uchar *p, uchar *maxp, int oldx, int *newx) +{ + int b, x; + + if(p >= maxp) + error(Eshortdraw); + b = *p++; + x = b & 0x7F; + if(b & 0x80){ + if(p+1 >= maxp) + error(Eshortdraw); + x |= *p++ << 7; + x |= *p++ << 15; + if(x & (1<<22)) + x |= ~0<<23; + }else{ + if(b & 0x40) + x |= ~0<<7; + x += oldx; + } + *newx = x; + return p; +} + +static void +printmesg(char *fmt, uchar *a, int plsprnt) +{ + char buf[256]; + char *p, *q; + int s; + + if(1|| plsprnt==0){ + SET(s); + SET(q); + SET(p); + USED(fmt); + USED(a); + USED(buf); + USED(p); + USED(q); + USED(s); + return; + } + q = buf; + *q++ = *a++; + for(p=fmt; *p; p++){ + switch(*p){ + case 'l': + q += sprint(q, " %ld", (long)BGLONG(a)); + a += 4; + break; + case 'L': + q += sprint(q, " %.8lux", (ulong)BGLONG(a)); + a += 4; + break; + case 'R': + q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12)); + a += 16; + break; + case 'P': + q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4)); + a += 8; + break; + case 'b': + q += sprint(q, " %d", *a++); + break; + case 's': + q += sprint(q, " %d", BGSHORT(a)); + a += 2; + break; + case 'S': + q += sprint(q, " %.4ux", BGSHORT(a)); + a += 2; + break; + } + } + *q++ = '\n'; + *q = 0; + iprint("%.*s", (int)(q-buf), buf); +} + +void +drawmesg(Client *client, void *av, int n) +{ + int c, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, op, ox, oy, oesize, esize, doflush; + uchar *u, *a, refresh; + char *fmt; + ulong value, chan; + Rectangle r, clipr; + Point p, q, *pp, sp; + Memimage *i, *dst, *src, *mask; + Memimage *l, **lp; + Memscreen *scrn; + DImage *font, *ll, *di, *ddst, *dsrc; + DName *dn; + DScreen *dscrn; + FChar *fc; + Refx *refx; + CScreen *cs; + Refreshfn reffn; + + a = av; + m = 0; + fmt = nil; + if(waserror()){ + if(fmt) printmesg(fmt, a, 1); + /* iprint("error: %s\n", up->errstr); */ + nexterror(); + } + while((n-=m) > 0){ + USED(fmt); + a += m; + switch(*a){ + default: + error("bad draw command"); + /* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */ + case 'b': + printmesg(fmt="LLbLbRRL", a, 0); + m = 1+4+4+1+4+1+4*4+4*4+4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + scrnid = BGSHORT(a+5); + refresh = a[9]; + chan = BGLONG(a+10); + repl = a[14]; + drawrectangle(&r, a+15); + drawrectangle(&clipr, a+31); + value = BGLONG(a+47); + if(drawlookup(client, dstid, 0)) + error(Eimageexists); + if(scrnid){ + dscrn = drawlookupscreen(client, scrnid, &cs); + scrn = dscrn->screen; + if(repl || chan!=scrn->image->chan) + error("image parameters incompatible with screen"); + reffn = nil; + switch(refresh){ + case Refbackup: + break; + case Refnone: + reffn = memlnorefresh; + break; + case Refmesg: + reffn = drawrefresh; + break; + default: + error("unknown refresh method"); + } + l = memlalloc(scrn, r, reffn, 0, value); + if(l == 0) + error(Edrawmem); + addflush(l->layer->screenr); + l->clipr = clipr; + rectclip(&l->clipr, r); + if(drawinstall(client, dstid, l, dscrn) == 0){ + memldelete(l); + error(Edrawmem); + } + dscrn->ref++; + if(reffn){ + refx = nil; + if(reffn == drawrefresh){ + refx = malloc(sizeof(Refx)); + if(refx == 0){ + drawuninstall(client, dstid); + error(Edrawmem); + } + refx->client = client; + refx->dimage = drawlookup(client, dstid, 1); + } + memlsetrefresh(l, reffn, refx); + } + continue; + } + i = allocmemimage(r, chan); + if(i == 0) + error(Edrawmem); + if(repl) + i->flags |= Frepl; + i->clipr = clipr; + if(!repl) + rectclip(&i->clipr, r); + if(drawinstall(client, dstid, i, 0) == 0){ + freememimage(i); + error(Edrawmem); + } + memfillcolor(i, value); + continue; + + /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */ + case 'A': + printmesg(fmt="LLLb", a, 1); + m = 1+4+4+4+1; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error(Ebadarg); + if(drawlookupdscreen(dstid)) + error(Escreenexists); + ddst = drawlookup(client, BGLONG(a+5), 1); + dsrc = drawlookup(client, BGLONG(a+9), 1); + if(ddst==0 || dsrc==0) + error(Enodrawimage); + if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0) + error(Edrawmem); + continue; + + /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */ + case 'c': + printmesg(fmt="LbR", a, 0); + m = 1+4+1+4*4; + if(n < m) + error(Eshortdraw); + ddst = drawlookup(client, BGLONG(a+1), 1); + if(ddst == nil) + error(Enodrawimage); + if(ddst->name) + error("can't change repl/clipr of shared image"); + dst = ddst->image; + if(a[5]) + dst->flags |= Frepl; + drawrectangle(&dst->clipr, a+6); + continue; + + /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */ + case 'd': + printmesg(fmt="LLLRPP", a, 0); + m = 1+4+4+4+4*4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + dstid = BGLONG(a+1); + src = drawimage(client, a+5); + mask = drawimage(client, a+9); + drawrectangle(&r, a+13); + drawpoint(&p, a+29); + drawpoint(&q, a+37); + op = drawclientop(client); + memdraw(dst, r, src, p, mask, q, op); + dstflush(dstid, dst, r); + continue; + + /* toggle debugging: 'D' val[1] */ + case 'D': + printmesg(fmt="b", a, 0); + m = 1+1; + if(n < m) + error(Eshortdraw); + drawdebug = a[1]; + continue; + + /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/ + case 'e': + case 'E': + printmesg(fmt="LLPlllPll", a, 0); + m = 1+4+4+2*4+4+4+4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + dstid = BGLONG(a+1); + src = drawimage(client, a+5); + drawpoint(&p, a+9); + e0 = BGLONG(a+17); + e1 = BGLONG(a+21); + if(e0<0 || e1<0) + error("invalid ellipse semidiameter"); + j = BGLONG(a+25); + if(j < 0) + error("negative ellipse thickness"); + drawpoint(&sp, a+29); + c = j; + if(*a == 'E') + c = -1; + ox = BGLONG(a+37); + oy = BGLONG(a+41); + op = drawclientop(client); + /* high bit indicates arc angles are present */ + if(ox & (1<<31)){ + if((ox & (1<<30)) == 0) + ox &= ~(1<<31); + memarc(dst, p, e0, e1, c, src, sp, ox, oy, op); + }else + memellipse(dst, p, e0, e1, c, src, sp, op); + dstflush(dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1)); + continue; + + /* free: 'f' id[4] */ + case 'f': + printmesg(fmt="L", a, 1); + m = 1+4; + if(n < m) + error(Eshortdraw); + ll = drawlookup(client, BGLONG(a+1), 0); + if(ll && ll->dscreen && ll->dscreen->owner != client) + ll->dscreen->owner->refreshme = 1; + drawuninstall(client, BGLONG(a+1)); + continue; + + /* free screen: 'F' id[4] */ + case 'F': + printmesg(fmt="L", a, 1); + m = 1+4; + if(n < m) + error(Eshortdraw); + drawlookupscreen(client, BGLONG(a+1), &cs); + drawuninstallscreen(client, cs); + continue; + + /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */ + case 'i': + printmesg(fmt="Llb", a, 1); + m = 1+4+4+1; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error("can't use display as font"); + font = drawlookup(client, dstid, 1); + if(font == 0) + error(Enodrawimage); + if(font->image->layer) + error("can't use window as font"); + ni = BGLONG(a+5); + if(ni<=0 || ni>4096) + error("bad font size (4096 chars max)"); + free(font->fchar); /* should we complain if non-zero? */ + font->fchar = malloc(ni*sizeof(FChar)); + if(font->fchar == 0) + error("no memory for font"); + memset(font->fchar, 0, ni*sizeof(FChar)); + font->nfchar = ni; + font->ascent = a[9]; + continue; + + /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */ + case 'l': + printmesg(fmt="LLSRPbb", a, 0); + m = 1+4+4+2+4*4+2*4+1+1; + if(n < m) + error(Eshortdraw); + font = drawlookup(client, BGLONG(a+1), 1); + if(font == 0) + error(Enodrawimage); + if(font->nfchar == 0) + error(Enotfont); + src = drawimage(client, a+5); + ci = BGSHORT(a+9); + if(ci >= font->nfchar) + error(Eindex); + drawrectangle(&r, a+11); + drawpoint(&p, a+27); + memdraw(font->image, r, src, p, memopaque, p, S); + fc = &font->fchar[ci]; + fc->minx = r.min.x; + fc->maxx = r.max.x; + fc->miny = r.min.y; + fc->maxy = r.max.y; + fc->left = a[35]; + fc->width = a[36]; + continue; + + /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */ + case 'L': + printmesg(fmt="LPPlllLP", a, 0); + m = 1+4+2*4+2*4+4+4+4+4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + dstid = BGLONG(a+1); + drawpoint(&p, a+5); + drawpoint(&q, a+13); + e0 = BGLONG(a+21); + e1 = BGLONG(a+25); + j = BGLONG(a+29); + if(j < 0) + error("negative line width"); + src = drawimage(client, a+33); + drawpoint(&sp, a+37); + op = drawclientop(client); + memline(dst, p, q, e0, e1, j, src, sp, op); + /* avoid memlinebbox if possible */ + if(dstid==0 || dst->layer!=nil){ + /* BUG: this is terribly inefficient: update maximal containing rect*/ + r = memlinebbox(p, q, e0, e1, j); + dstflush(dstid, dst, insetrect(r, -(1+1+j))); + } + continue; + + /* create image mask: 'm' newid[4] id[4] */ +/* + * + case 'm': + printmesg("LL", a, 0); + m = 4+4; + if(n < m) + error(Eshortdraw); + break; + * + */ + + /* attach to a named image: 'n' dstid[4] j[1] name[j] */ + case 'n': + printmesg(fmt="Lz", a, 0); + m = 1+4+1; + if(n < m) + error(Eshortdraw); + j = a[5]; + if(j == 0) /* give me a non-empty name please */ + error(Eshortdraw); + m += j; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(drawlookup(client, dstid, 0)) + error(Eimageexists); + dn = drawlookupname(j, (char*)a+6); + if(dn == nil) + error(Enoname); + if(drawinstall(client, dstid, dn->dimage->image, 0) == 0) + error(Edrawmem); + di = drawlookup(client, dstid, 0); + if(di == 0) + error("draw: can't happen"); + di->vers = dn->vers; + di->name = smalloc(j+1); + di->fromname = dn->dimage; + di->fromname->ref++; + memmove(di->name, a+6, j); + di->name[j] = 0; + client->infoid = dstid; + continue; + + /* name an image: 'N' dstid[4] in[1] j[1] name[j] */ + case 'N': + printmesg(fmt="Lbz", a, 0); + m = 1+4+1+1; + if(n < m) + error(Eshortdraw); + c = a[5]; + j = a[6]; + if(j == 0) /* give me a non-empty name please */ + error(Eshortdraw); + m += j; + if(n < m) + error(Eshortdraw); + di = drawlookup(client, BGLONG(a+1), 0); + if(di == 0) + error(Enodrawimage); + if(di->name) + error(Enamed); + if(c) + drawaddname(client, di, j, (char*)a+7); + else{ + dn = drawlookupname(j, (char*)a+7); + if(dn == nil) + error(Enoname); + if(dn->dimage != di) + error(Ewrongname); + drawdelname(dn); + } + continue; + + /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */ + case 'o': + printmesg(fmt="LPP", a, 0); + m = 1+4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + if(dst->layer){ + drawpoint(&p, a+5); + drawpoint(&q, a+13); + r = dst->layer->screenr; + ni = memlorigin(dst, p, q); + if(ni < 0) + error("image origin failed"); + if(ni > 0){ + addflush(r); + addflush(dst->layer->screenr); + ll = drawlookup(client, BGLONG(a+1), 1); + drawrefreshscreen(ll, client); + } + } + continue; + + /* set compositing operator for next draw operation: 'O' op */ + case 'O': + printmesg(fmt="b", a, 0); + m = 1+1; + if(n < m) + error(Eshortdraw); + client->op = a[1]; + continue; + + /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ + /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ + case 'p': + case 'P': + printmesg(fmt="LslllLPP", a, 0); + m = 1+4+2+4+4+4+4+2*4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + dst = drawimage(client, a+1); + ni = BGSHORT(a+5); + if(ni < 0) + error("negative count in polygon"); + e0 = BGLONG(a+7); + e1 = BGLONG(a+11); + j = 0; + if(*a == 'p'){ + j = BGLONG(a+15); + if(j < 0) + error("negative polygon line width"); + } + src = drawimage(client, a+19); + drawpoint(&sp, a+23); + drawpoint(&p, a+31); + ni++; + pp = malloc(ni*sizeof(Point)); + if(pp == nil) + error(Enomem); + doflush = 0; + if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data)) + doflush = 1; /* simplify test in loop */ + ox = oy = 0; + esize = 0; + u = a+m; + for(y=0; y esize) + esize = c; + } + if(y == ni-1){ + c = memlineendsize(e1); + if(c > esize) + esize = c; + } + } + if(*a=='P' && e0!=1 && e0 !=~0) + r = dst->clipr; + else if(y > 0){ + r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1); + combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + } + if(rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */ + dstflush(dstid, dst, r); + } + pp[y] = p; + } + if(y == 1) + dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + op = drawclientop(client); + if(*a == 'p') + mempoly(dst, pp, ni, e0, e1, j, src, sp, op); + else + memfillpoly(dst, pp, ni, e0, src, sp, op); + free(pp); + m = u-a; + continue; + + /* read: 'r' id[4] R[4*4] */ + case 'r': + printmesg(fmt="LR", a, 0); + m = 1+4+4*4; + if(n < m) + error(Eshortdraw); + i = drawimage(client, a+1); + drawrectangle(&r, a+5); + if(!rectinrect(r, i->r)) + error(Ereadoutside); + c = bytesperline(r, i->depth); + c *= Dy(r); + free(client->readdata); + client->readdata = mallocz(c, 0); + if(client->readdata == nil) + error("readimage malloc failed"); + client->nreaddata = memunload(i, r, client->readdata, c); + if(client->nreaddata < 0){ + free(client->readdata); + client->readdata = nil; + error("bad readimage call"); + } + continue; + + /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */ + /* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */ + case 's': + case 'x': + printmesg(fmt="LLLPRPs", a, 0); + m = 1+4+4+4+2*4+4*4+2*4+2; + if(*a == 'x') + m += 4+2*4; + if(n < m) + error(Eshortdraw); + + dst = drawimage(client, a+1); + dstid = BGLONG(a+1); + src = drawimage(client, a+5); + font = drawlookup(client, BGLONG(a+9), 1); + if(font == 0) + error(Enodrawimage); + if(font->nfchar == 0) + error(Enotfont); + drawpoint(&p, a+13); + drawrectangle(&r, a+21); + drawpoint(&sp, a+37); + ni = BGSHORT(a+45); + u = a+m; + m += ni*2; + if(n < m) + error(Eshortdraw); + clipr = dst->clipr; + dst->clipr = r; + op = drawclientop(client); + if(*a == 'x'){ + /* paint background */ + l = drawimage(client, a+47); + drawpoint(&q, a+51); + r.min.x = p.x; + r.min.y = p.y-font->ascent; + r.max.x = p.x; + r.max.y = r.min.y+Dy(font->image->r); + j = ni; + while(--j >= 0){ + ci = BGSHORT(u); + if(ci<0 || ci>=font->nfchar){ + dst->clipr = clipr; + error(Eindex); + } + r.max.x += font->fchar[ci].width; + u += 2; + } + memdraw(dst, r, l, q, memopaque, ZP, op); + u -= 2*ni; + } + q = p; + while(--ni >= 0){ + ci = BGSHORT(u); + if(ci<0 || ci>=font->nfchar){ + dst->clipr = clipr; + error(Eindex); + } + q = drawchar(dst, q, src, &sp, font, ci, op); + u += 2; + } + dst->clipr = clipr; + p.y -= font->ascent; + dstflush(dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r))); + continue; + + /* use public screen: 'S' id[4] chan[4] */ + case 'S': + printmesg(fmt="Ll", a, 0); + m = 1+4+4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error(Ebadarg); + dscrn = drawlookupdscreen(dstid); + if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client)) + error(Enodrawscreen); + if(dscrn->screen->image->chan != BGLONG(a+5)) + error("inconsistent chan"); + if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0) + error(Edrawmem); + continue; + + /* top or bottom windows: 't' top[1] nw[2] n*id[4] */ + case 't': + printmesg(fmt="bsL", a, 0); + m = 1+1+2; + if(n < m) + error(Eshortdraw); + nw = BGSHORT(a+2); + if(nw < 0) + error(Ebadarg); + if(nw == 0) + continue; + m += nw*4; + if(n < m) + error(Eshortdraw); + lp = malloc(nw*sizeof(Memimage*)); + if(lp == 0) + error(Enomem); + if(waserror()){ + free(lp); + nexterror(); + } + for(j=0; jlayer == 0) + error("images are not windows"); + for(j=1; jlayer->screen != lp[0]->layer->screen) + error("images not on same screen"); + if(a[1]) + memltofrontn(lp, nw); + else + memltorearn(lp, nw); + if(lp[0]->layer->screen->image->data == screenimage->data) + for(j=0; jlayer->screenr); + ll = drawlookup(client, BGLONG(a+1+1+2), 1); + drawrefreshscreen(ll, client); + poperror(); + free(lp); + continue; + + /* visible: 'v' */ + case 'v': + printmesg(fmt="", a, 0); + m = 1; + drawflush(); + continue; + + /* write: 'y' id[4] R[4*4] data[x*1] */ + /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */ + case 'y': + case 'Y': + printmesg(fmt="LR", a, 0); + // iprint("load %c\n", *a); + m = 1+4+4*4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + dst = drawimage(client, a+1); + drawrectangle(&r, a+5); + if(!rectinrect(r, dst->r)) + error(Ewriteoutside); + y = memload(dst, r, a+m, n-m, *a=='Y'); + if(y < 0) + error("bad writeimage call"); + dstflush(dstid, dst, r); + m += y; + continue; + } + } + poperror(); +} + +Dev drawdevtab = { + 'i', + "draw", + + devreset, + devinit, + devshutdown, + drawattach, + drawwalk, + drawstat, + drawopen, + devcreate, + drawclose, + drawread, + devbread, + drawwrite, + devbwrite, + devremove, + devwstat, +}; + +/* + * On 8 bit displays, load the default color map + */ +void +drawcmap(void) +{ + int r, g, b, cr, cg, cb, v; + int num, den; + int i, j; + + drawactive(1); /* to restore map from backup */ + for(r=0,i=0; r!=4; r++) + for(v=0; v!=4; v++,i+=16){ + for(g=0,j=v-r; g!=4; g++) + for(b=0;b!=4;b++,j++){ + den = r; + if(g > den) + den = g; + if(b > den) + den = b; + if(den == 0) /* divide check -- pick grey shades */ + cr = cg = cb = v*17; + else{ + num = 17*(4*den+v); + cr = r*num/den; + cg = g*num/den; + cb = b*num/den; + } + setcolor(i+(j&15), + cr*0x01010101, cg*0x01010101, cb*0x01010101); + } + } +} + +void +drawblankscreen(int blank) +{ + int i, nc; + ulong *p; + + if(blank == sdraw.blanked) + return; + if(!canqlock(&sdraw.lk)) + return; + if(!initscreenimage()){ + qunlock(&sdraw.lk); + return; + } + p = sdraw.savemap; + nc = screenimage->depth > 8 ? 256 : 1<depth; + + /* + * blankscreen uses the hardware to blank the screen + * when possible. to help in cases when it is not possible, + * we set the color map to be all black. + */ + if(blank == 0){ /* turn screen on */ + for(i=0; iticks; + }else{ + if(blanktime && sdraw.blanktime && TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60 >= blanktime) + drawblankscreen(1); + } +*/ +} + +int +drawidletime(void) +{ + return 0; +/* return TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60; */ +} + diff --git a/kern/devfs.c b/kern/devfs.c new file mode 100644 index 0000000..03200d0 --- /dev/null +++ b/kern/devfs.c @@ -0,0 +1,638 @@ +#include +#include +#include +#include +#include +#include /* for remove, rename */ +#include + +#ifndef NAME_MAX +# define NAME_MAX 256 +#endif +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + + +typedef struct Ufsinfo Ufsinfo; + +enum +{ + NUID = 256, + NGID = 256, + MAXPATH = 1024, + MAXCOMP = 128 +}; + +struct Ufsinfo +{ + int mode; + int fd; + int uid; + int gid; + DIR* dir; + ulong offset; + QLock oq; + char nextname[NAME_MAX]; +}; + +char *base = "/"; + +static Qid fsqid(char*, struct stat *); +static void fspath(Chan*, char*, char*); +static ulong fsdirread(Chan*, uchar*, int, ulong); +static int fsomode(int); + +/* clumsy hack, but not worse than the Path stuff in the last one */ +static char* +uc2name(Chan *c) +{ + char *s; + + if(c->name == nil) + return "/"; + s = c2name(c); + if(s[0]=='#' && s[1]=='U') + return s+2; + return s; +} + +static char* +lastelem(Chan *c) +{ + char *s, *t; + + s = uc2name(c); + if((t = strrchr(s, '/')) == nil) + return s; + if(t[1] == 0) + return t; + return t+1; +} + +static Chan* +fsattach(char *spec) +{ + Chan *c; + struct stat stbuf; + static int devno; + Ufsinfo *uif; + + if(stat(base, &stbuf) < 0) + error(strerror(errno)); + + c = devattach('U', spec); + + uif = mallocz(sizeof(Ufsinfo), 1); + uif->mode = stbuf.st_mode; + uif->uid = stbuf.st_uid; + uif->gid = stbuf.st_gid; + + c->aux = uif; + c->dev = devno++; + c->qid.type = QTDIR; +/*print("fsattach %s\n", c2name(c));*/ + + return c; +} + +static Chan* +fsclone(Chan *c, Chan *nc) +{ + Ufsinfo *uif; + + uif = mallocz(sizeof(Ufsinfo), 1); + *uif = *(Ufsinfo*)c->aux; + nc->aux = uif; + + return nc; +} + +static int +fswalk1(Chan *c, char *name) +{ + struct stat stbuf; + char path[MAXPATH]; + Ufsinfo *uif; + + fspath(c, name, path); + + /*print("** fs walk '%s' -> %s\n", path, name); */ + + if(stat(path, &stbuf) < 0) + return 0; + + uif = c->aux; + + uif->mode = stbuf.st_mode; + uif->uid = stbuf.st_uid; + uif->gid = stbuf.st_gid; + + c->qid = fsqid(path, &stbuf); + + return 1; +} + +extern Cname* addelem(Cname*, char*); + +static Walkqid* +fswalk(Chan *c, Chan *nc, char **name, int nname) +{ + int i; + Cname *cname; + Walkqid *wq; + + if(nc != nil) + panic("fswalk: nc != nil"); + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + nc = devclone(c); + cname = c->name; + incref(&cname->ref); + + fsclone(c, nc); + wq->clone = nc; + for(i=0; iname = cname; + if(fswalk1(nc, name[i]) == 0) + break; + cname = addelem(cname, name[i]); + wq->qid[i] = nc->qid; + } + nc->name = nil; + cnameclose(cname); + if(i != nname){ + cclose(nc); + wq->clone = nil; + } + wq->nqid = i; + return wq; +} + +static int +fsstat(Chan *c, uchar *buf, int n) +{ + Dir d; + struct stat stbuf; + char path[MAXPATH]; + + if(n < BIT16SZ) + error(Eshortstat); + + fspath(c, 0, path); + if(stat(path, &stbuf) < 0) + error(strerror(errno)); + + d.name = lastelem(c); + d.uid = "unknown"; + d.gid = "unknown"; + d.muid = "unknown"; + d.qid = c->qid; + d.mode = (c->qid.type<<24)|(stbuf.st_mode&0777); + d.atime = stbuf.st_atime; + d.mtime = stbuf.st_mtime; + d.length = stbuf.st_size; + d.type = 'U'; + d.dev = c->dev; + return convD2M(&d, buf, n); +} + +static Chan* +fsopen(Chan *c, int mode) +{ + char path[MAXPATH]; + int m, isdir; + Ufsinfo *uif; + +/*print("fsopen %s\n", c2name(c));*/ + m = mode & (OTRUNC|3); + switch(m) { + case 0: + break; + case 1: + case 1|16: + break; + case 2: + case 0|16: + case 2|16: + break; + case 3: + break; + default: + error(Ebadarg); + } + + isdir = c->qid.type & QTDIR; + + if(isdir && mode != OREAD) + error(Eperm); + + m = fsomode(m & 3); + c->mode = openmode(mode); + + uif = c->aux; + + fspath(c, 0, path); + if(isdir) { + uif->dir = opendir(path); + if(uif->dir == 0) + error(strerror(errno)); + } + else { + if(mode & OTRUNC) + m |= O_TRUNC; + uif->fd = open(path, m, 0666); + + if(uif->fd < 0) + error(strerror(errno)); + } + uif->offset = 0; + + c->offset = 0; + c->flag |= COPEN; + return c; +} + +static void +fscreate(Chan *c, char *name, int mode, ulong perm) +{ + int fd, m; + char path[MAXPATH]; + struct stat stbuf; + Ufsinfo *uif; + + m = fsomode(mode&3); + + fspath(c, name, path); + + uif = c->aux; + + if(perm & DMDIR) { + if(m) + error(Eperm); + + if(mkdir(path, perm & 0777) < 0) + error(strerror(errno)); + + fd = open(path, 0); + if(fd >= 0) { + chmod(path, perm & 0777); + chown(path, uif->uid, uif->uid); + } + close(fd); + + uif->dir = opendir(path); + if(uif->dir == 0) + error(strerror(errno)); + } + else { + fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666); + if(fd >= 0) { + if(m != 1) { + close(fd); + fd = open(path, m); + } + chmod(path, perm & 0777); + chown(path, uif->uid, uif->gid); + } + if(fd < 0) + error(strerror(errno)); + uif->fd = fd; + } + + if(stat(path, &stbuf) < 0) + error(strerror(errno)); + c->qid = fsqid(path, &stbuf); + c->offset = 0; + c->flag |= COPEN; + c->mode = openmode(mode); +} + +static void +fsclose(Chan *c) +{ + Ufsinfo *uif; + + uif = c->aux; + + if(c->flag & COPEN) { + if(c->qid.type & QTDIR) + closedir(uif->dir); + else + close(uif->fd); + } + + free(uif); +} + +static long +fsread(Chan *c, void *va, long n, vlong offset) +{ + int fd, r; + Ufsinfo *uif; + +/*print("fsread %s\n", c2name(c));*/ + if(c->qid.type & QTDIR) + return fsdirread(c, va, n, offset); + + uif = c->aux; + qlock(&uif->oq); + if(waserror()) { + qunlock(&uif->oq); + nexterror(); + } + fd = uif->fd; + if(uif->offset != offset) { + r = lseek(fd, offset, 0); + if(r < 0) + error(strerror(errno)); + uif->offset = offset; + } + + n = read(fd, va, n); + if(n < 0) + error(strerror(errno)); + + uif->offset += n; + qunlock(&uif->oq); + poperror(); + + return n; +} + +static long +fswrite(Chan *c, void *va, long n, vlong offset) +{ + int fd, r; + Ufsinfo *uif; + + uif = c->aux; + + qlock(&uif->oq); + if(waserror()) { + qunlock(&uif->oq); + nexterror(); + } + fd = uif->fd; + if(uif->offset != offset) { + r = lseek(fd, offset, 0); + if(r < 0) + error(strerror(errno)); + uif->offset = offset; + } + + n = write(fd, va, n); + if(n < 0) + error(strerror(errno)); + + uif->offset += n; + qunlock(&uif->oq); + poperror(); + + return n; +} + +static void +fsremove(Chan *c) +{ + int n; + char path[MAXPATH]; + + fspath(c, 0, path); + if(c->qid.type & QTDIR) + n = rmdir(path); + else + n = remove(path); + if(n < 0) + error(strerror(errno)); +} + +int +fswstat(Chan *c, uchar *buf, int n) +{ + Dir d; + struct stat stbuf; + char old[MAXPATH], new[MAXPATH], dir[MAXPATH]; + char strs[MAXPATH*3], *p; + Ufsinfo *uif; + + if(convM2D(buf, n, &d, strs) != n) + error(Ebadstat); + + fspath(c, 0, old); + if(stat(old, &stbuf) < 0) + error(strerror(errno)); + + uif = c->aux; + + if(d.name[0] && strcmp(d.name, lastelem(c)) != 0) { + fspath(c, 0, old); + strcpy(new, old); + p = strrchr(new, '/'); + strcpy(p+1, d.name); + if(rename(old, new) < 0) + error(strerror(errno)); + } + + fspath(c, 0, old); + if(~d.mode != 0 && (int)(d.mode&0777) != (int)(stbuf.st_mode&0777)) { + if(chmod(old, d.mode&0777) < 0) + error(strerror(errno)); + uif->mode &= ~0777; + uif->mode |= d.mode&0777; + } +/* + p = name2pass(gid, d.gid); + if(p == 0) + error(Eunknown); + + if(p->id != stbuf.st_gid) { + if(chown(old, stbuf.st_uid, p->id) < 0) + error(strerror(errno)); + + uif->gid = p->id; + } +*/ + return n; +} + +static Qid +fsqid(char *p, struct stat *st) +{ + Qid q; + int dev; + ulong h; + static int nqdev; + static uchar *qdev; + + if(qdev == 0) + qdev = mallocz(65536U, 1); + + q.type = 0; + if((st->st_mode&S_IFMT) == S_IFDIR) + q.type = QTDIR; + + dev = st->st_dev & 0xFFFFUL; + if(qdev[dev] == 0) + qdev[dev] = ++nqdev; + + h = 0; + while(*p != '\0') + h += *p++ * 13; + + q.path = (vlong)qdev[dev]<<32; + q.path |= h; + q.vers = st->st_mtime; + + return q; +} + +static void +fspath(Chan *c, char *ext, char *path) +{ + int i, n; + char *comp[MAXCOMP]; + + strcpy(path, base); + strcat(path, "/"); + strcat(path, uc2name(c)); + if(ext){ + strcat(path, "/"); + strcat(path, ext); + } + cleanname(path); +} + +static int +isdots(char *name) +{ + if(name[0] != '.') + return 0; + if(name[1] == '\0') + return 1; + if(name[1] != '.') + return 0; + if(name[2] == '\0') + return 1; + return 0; +} + +static int +p9readdir(char *name, Ufsinfo *uif) +{ + struct dirent *de; + + if(uif->nextname[0]){ + strcpy(name, uif->nextname); + uif->nextname[0] = 0; + return 1; + } + + de = readdir(uif->dir); + if(de == NULL) + return 0; + + strcpy(name, de->d_name); + return 1; +} + +static ulong +fsdirread(Chan *c, uchar *va, int count, ulong offset) +{ + int i; + Dir d; + long n; + char de[NAME_MAX]; + struct stat stbuf; + char path[MAXPATH], dirpath[MAXPATH]; + Ufsinfo *uif; + +/*print("fsdirread %s\n", c2name(c));*/ + i = 0; + uif = c->aux; + + errno = 0; + if(uif->offset != offset) { + if(offset != 0) + error("bad offset in fsdirread"); + uif->offset = offset; /* sync offset */ + uif->nextname[0] = 0; + rewinddir(uif->dir); + } + + fspath(c, 0, dirpath); + + while(i+BIT16SZ < count) { + if(!p9readdir(de, uif)) + break; + + if(de[0]==0 || isdots(de)) + continue; + + d.name = de; + sprint(path, "%s/%s", dirpath, de); + memset(&stbuf, 0, sizeof stbuf); + + if(stat(path, &stbuf) < 0) { + print("dir: bad path %s\n", path); + /* but continue... probably a bad symlink */ + } + + d.uid = "unknown"; + d.gid = "unknown"; + d.qid = fsqid(path, &stbuf); + d.mode = (d.qid.type<<24)|(stbuf.st_mode&0777); + d.atime = stbuf.st_atime; + d.mtime = stbuf.st_mtime; + d.length = stbuf.st_size; + d.type = 'U'; + d.dev = c->dev; + n = convD2M(&d, (char*)va+i, count-i); + if(n == BIT16SZ){ + strcpy(uif->nextname, de); + break; + } + i += n; + } +/*print("got %d\n", i);*/ + uif->offset += i; + return i; +} + +static int +fsomode(int m) +{ + switch(m) { + case 0: /* OREAD */ + case 3: /* OEXEC */ + return 0; + case 1: /* OWRITE */ + return 1; + case 2: /* ORDWR */ + return 2; + } + error(Ebadarg); + return 0; +} + +Dev fsdevtab = { + 'U', + "fs", + + devreset, + devinit, + devshutdown, + fsattach, + fswalk, + fsstat, + fsopen, + fscreate, + fsclose, + fsread, + devbread, + fswrite, + devbwrite, + fsremove, + fswstat, +}; diff --git a/kern/devip-posix.c b/kern/devip-posix.c new file mode 100644 index 0000000..64bf2f5 --- /dev/null +++ b/kern/devip-posix.c @@ -0,0 +1,209 @@ +#include +#include +#include +#include +#include + +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "devip.h" + +#undef listen +#undef accept +#undef bind + +void +osipinit(void) +{ + char buf[1024]; + gethostname(buf, sizeof(buf)); + kstrdup(&sysname, buf); + +} + +int +so_socket(int type) +{ + int fd, one; + + switch(type) { + default: + error("bad protocol type"); + case S_TCP: + type = SOCK_STREAM; + break; + case S_UDP: + type = SOCK_DGRAM; + break; + } + + fd = socket(AF_INET, type, 0); + if(fd < 0) + oserror(); + + one = 1; + if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one)) > 0){ + oserrstr(); + print("setsockopt: %r"); + } + + return fd; +} + + +void +so_connect(int fd, unsigned long raddr, unsigned short rport) +{ + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + hnputs(&sin.sin_port, rport); + hnputl(&sin.sin_addr.s_addr, raddr); + + if(connect(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) + oserror(); +} + +void +so_getsockname(int fd, unsigned long *laddr, unsigned short *lport) +{ + int len; + struct sockaddr_in sin; + + len = sizeof(sin); + if(getsockname(fd, (struct sockaddr*)&sin, &len) < 0) + oserror(); + + if(sin.sin_family != AF_INET || len != sizeof(sin)) + error("not AF_INET"); + + *laddr = nhgetl(&sin.sin_addr.s_addr); + *lport = nhgets(&sin.sin_port); +} + +void +so_listen(int fd) +{ + if(listen(fd, 5) < 0) + oserror(); +} + +int +so_accept(int fd, unsigned long *raddr, unsigned short *rport) +{ + int nfd, len; + struct sockaddr_in sin; + + len = sizeof(sin); + nfd = accept(fd, (struct sockaddr*)&sin, &len); + if(nfd < 0) + oserror(); + + if(sin.sin_family != AF_INET || len != sizeof(sin)) + error("not AF_INET"); + + *raddr = nhgetl(&sin.sin_addr.s_addr); + *rport = nhgets(&sin.sin_port); + return nfd; +} + +void +so_bind(int fd, int su, unsigned short port) +{ + int i, one; + struct sockaddr_in sin; + + one = 1; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0){ + oserrstr(); + print("setsockopt: %r"); + } + + if(su) { + for(i = 600; i < 1024; i++) { + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = i; + + if(bind(fd, (struct sockaddr*)&sin, sizeof(sin)) >= 0) + return; + } + oserror(); + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + hnputs(&sin.sin_port, port); + + if(bind(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) + oserror(); +} + +int +so_gethostbyname(char *host, char**hostv, int n) +{ + int i; + char buf[32]; + unsigned char *p; + struct hostent *hp; + + hp = gethostbyname(host); + if(hp == 0) + return 0; + + for(i = 0; hp->h_addr_list[i] && i < n; i++) { + p = (unsigned char*)hp->h_addr_list[i]; + sprint(buf, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + hostv[i] = strdup(buf); + if(hostv[i] == 0) + break; + } + return i; +} + +char* +hostlookup(char *host) +{ + char buf[100]; + uchar *p; + struct hostent *he; + + he = gethostbyname(host); + if(he != 0 && he->h_addr_list[0]) { + p = (uchar*)he->h_addr_list[0]; + sprint(buf, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]); + } else + strcpy(buf, host); + + return strdup(buf); +} + +int +so_getservbyname(char *service, char *net, char *port) +{ + struct servent *s; + + s = getservbyname(service, net); + if(s == 0) + return -1; + + sprint(port, "%d", nhgets(&s->s_port)); + return 0; +} + +int +so_send(int fd, void *d, int n, int f) +{ + send(fd, d, n, f); +} + +int +so_recv(int fd, void *d, int n, int f) +{ + return recv(fd, d, n, f); +} diff --git a/kern/devip-win.c b/kern/devip-win.c new file mode 100644 index 0000000..239035f --- /dev/null +++ b/kern/devip-win.c @@ -0,0 +1,208 @@ +#include +#include "winduhz.h" +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "devip.h" + +#pragma comment(lib, "wsock32.lib") + +#undef listen +#undef accept +#undef bind + +void +osipinit(void) +{ + WSADATA wasdat; + char buf[1024]; + + if(WSAStartup(MAKEWORD(1, 1), &wasdat) != 0) + panic("no winsock.dll"); + + gethostname(buf, sizeof(buf)); + kstrdup(&sysname, buf); +} + +int +so_socket(int type) +{ + int fd, one; + + switch(type) { + default: + error("bad protocol type"); + case S_TCP: + type = SOCK_STREAM; + break; + case S_UDP: + type = SOCK_DGRAM; + break; + } + + fd = socket(AF_INET, type, 0); + if(fd < 0) + error(sys_errlist[errno]); + + one = 1; + if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one)) > 0) + print("setsockopt: %s", sys_errlist[errno]); + + + return fd; +} + + +void +so_connect(int fd, unsigned long raddr, unsigned short rport) +{ + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + hnputs(&sin.sin_port, rport); + hnputl(&sin.sin_addr.s_addr, raddr); + + if(connect(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) + error(sys_errlist[errno]); +} + +void +so_getsockname(int fd, unsigned long *laddr, unsigned short *lport) +{ + int len; + struct sockaddr_in sin; + + len = sizeof(sin); + if(getsockname(fd, (struct sockaddr*)&sin, &len) < 0) + error(sys_errlist[errno]); + + if(sin.sin_family != AF_INET || len != sizeof(sin)) + error("not AF_INET"); + + *laddr = nhgetl(&sin.sin_addr.s_addr); + *lport = nhgets(&sin.sin_port); +} + +void +so_listen(int fd) +{ + if(listen(fd, 5) < 0) + error(sys_errlist[errno]); +} + +int +so_accept(int fd, unsigned long *raddr, unsigned short *rport) +{ + int nfd, len; + struct sockaddr_in sin; + + len = sizeof(sin); + nfd = accept(fd, (struct sockaddr*)&sin, &len); + if(nfd < 0) + error(sys_errlist[errno]); + + if(sin.sin_family != AF_INET || len != sizeof(sin)) + error("not AF_INET"); + + *raddr = nhgetl(&sin.sin_addr.s_addr); + *rport = nhgets(&sin.sin_port); + return nfd; +} + +void +so_bind(int fd, int su, unsigned short port) +{ + int i, one; + struct sockaddr_in sin; + + one = 1; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) + print("setsockopt: %s", sys_errlist[errno]); + + if(su) { + for(i = 600; i < 1024; i++) { + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = i; + + if(bind(fd, (struct sockaddr*)&sin, sizeof(sin)) >= 0) + return; + } + error(sys_errlist[errno]); + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + hnputs(&sin.sin_port, port); + + if(bind(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) + error(sys_errlist[errno]); +} + +int +so_gethostbyname(char *host, char**hostv, int n) +{ + int i; + char buf[32]; + unsigned char *p; + struct hostent *hp; + + hp = gethostbyname(host); + if(hp == 0) + return 0; + + for(i = 0; hp->h_addr_list[i] && i < n; i++) { + p = (unsigned char*)hp->h_addr_list[i]; + sprint(buf, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + hostv[i] = strdup(buf); + if(hostv[i] == 0) + break; + } + return i; +} + +char* +hostlookup(char *host) +{ + char buf[100]; + uchar *p; + HOSTENT *he; + + he = gethostbyname(host); + if(he != 0 && he->h_addr_list[0]) { + p = he->h_addr_list[0]; + sprint(buf, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]); + } else + strcpy(buf, host); + + return strdup(buf); +} + +int +so_getservbyname(char *service, char *net, char *port) +{ + struct servent *s; + + s = getservbyname(service, net); + if(s == 0) + return -1; + + sprint(port, "%d", nhgets(&s->s_port)); + return 0; +} + +int +so_send(int fd, void *d, int n, int f) +{ + return send(fd, d, n, f); +} + +int +so_recv(int fd, void *d, int n, int f) +{ + return recv(fd, d, n, f); +} diff --git a/kern/devip.c b/kern/devip.c new file mode 100644 index 0000000..eb95aa3 --- /dev/null +++ b/kern/devip.c @@ -0,0 +1,936 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "devip.h" + +void hnputl(void *p, unsigned long v); +void hnputs(void *p, unsigned short v); +unsigned long nhgetl(void *p); +unsigned short nhgets(void *p); +unsigned long parseip(char *to, char *from); +void csclose(Chan*); +long csread(Chan*, void*, long, vlong); +long cswrite(Chan*, void*, long, vlong); + +void osipinit(void); + +enum +{ + Qtopdir = 1, /* top level directory */ + Qcs, + Qprotodir, /* directory for a protocol */ + Qclonus, + Qconvdir, /* directory for a conversation */ + Qdata, + Qctl, + Qstatus, + Qremote, + Qlocal, + Qlisten, + + MAXPROTO = 4 +}; +#define TYPE(x) ((int)((x).path & 0xf)) +#define CONV(x) ((int)(((x).path >> 4)&0xfff)) +#define PROTO(x) ((int)(((x).path >> 16)&0xff)) +#define QID(p, c, y) (((p)<<16) | ((c)<<4) | (y)) + +typedef struct Proto Proto; +typedef struct Conv Conv; +struct Conv +{ + int x; + Ref r; + int sfd; + int perm; + char owner[KNAMELEN]; + char* state; + ulong laddr; + ushort lport; + ulong raddr; + ushort rport; + int restricted; + char cerr[KNAMELEN]; + Proto* p; +}; + +struct Proto +{ + Lock l; + int x; + int stype; + char name[KNAMELEN]; + int nc; + int maxconv; + Conv** conv; + Qid qid; +}; + +static int np; +static Proto proto[MAXPROTO]; +int eipfmt(Fmt*); + +static Conv* protoclone(Proto*, char*, int); +static void setladdr(Conv*); + +int +ipgen(Chan *c, char *nname, Dirtab *d, int nd, int s, Dir *dp) +{ + Qid q; + Conv *cv; + char *p; + + USED(nname); + q.vers = 0; + q.type = 0; + switch(TYPE(c->qid)) { + case Qtopdir: + if(s >= 1+np) + return -1; + + if(s == 0){ + q.path = QID(s, 0, Qcs); + devdir(c, q, "cs", 0, "network", 0666, dp); + }else{ + s--; + q.path = QID(s, 0, Qprotodir); + q.type = QTDIR; + devdir(c, q, proto[s].name, 0, "network", DMDIR|0555, dp); + } + return 1; + case Qprotodir: + if(s < proto[PROTO(c->qid)].nc) { + cv = proto[PROTO(c->qid)].conv[s]; + sprint(up->genbuf, "%d", s); + q.path = QID(PROTO(c->qid), s, Qconvdir); + q.type = QTDIR; + devdir(c, q, up->genbuf, 0, cv->owner, DMDIR|0555, dp); + return 1; + } + s -= proto[PROTO(c->qid)].nc; + switch(s) { + default: + return -1; + case 0: + p = "clone"; + q.path = QID(PROTO(c->qid), 0, Qclonus); + break; + } + devdir(c, q, p, 0, "network", 0555, dp); + return 1; + case Qconvdir: + cv = proto[PROTO(c->qid)].conv[CONV(c->qid)]; + switch(s) { + default: + return -1; + case 0: + q.path = QID(PROTO(c->qid), CONV(c->qid), Qdata); + devdir(c, q, "data", 0, cv->owner, cv->perm, dp); + return 1; + case 1: + q.path = QID(PROTO(c->qid), CONV(c->qid), Qctl); + devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp); + return 1; + case 2: + p = "status"; + q.path = QID(PROTO(c->qid), CONV(c->qid), Qstatus); + break; + case 3: + p = "remote"; + q.path = QID(PROTO(c->qid), CONV(c->qid), Qremote); + break; + case 4: + p = "local"; + q.path = QID(PROTO(c->qid), CONV(c->qid), Qlocal); + break; + case 5: + p = "listen"; + q.path = QID(PROTO(c->qid), CONV(c->qid), Qlisten); + break; + } + devdir(c, q, p, 0, cv->owner, 0444, dp); + return 1; + } + return -1; +} + +static void +newproto(char *name, int type, int maxconv) +{ + int l; + Proto *p; + + if(np >= MAXPROTO) { + print("no %s: increase MAXPROTO", name); + return; + } + + p = &proto[np]; + strcpy(p->name, name); + p->stype = type; + p->qid.path = QID(np, 0, Qprotodir); + p->qid.type = QTDIR; + p->x = np++; + p->maxconv = maxconv; + l = sizeof(Conv*)*(p->maxconv+1); + p->conv = mallocz(l, 1); + if(p->conv == 0) + panic("no memory"); +} + +void +ipinit(void) +{ + osipinit(); + + newproto("udp", S_UDP, 10); + newproto("tcp", S_TCP, 30); + + fmtinstall('I', eipfmt); + fmtinstall('E', eipfmt); + +} + +Chan * +ipattach(char *spec) +{ + Chan *c; + + c = devattach('I', spec); + c->qid.path = QID(0, 0, Qtopdir); + c->qid.type = QTDIR; + c->qid.vers = 0; + return c; +} + +static Walkqid* +ipwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, ipgen); +} + +int +ipstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, 0, 0, ipgen); +} + +Chan * +ipopen(Chan *c, int omode) +{ + Proto *p; + ulong raddr; + ushort rport; + int perm, sfd; + Conv *cv, *lcv; + + omode &= 3; + switch(omode) { + case OREAD: + perm = 4; + break; + case OWRITE: + perm = 2; + break; + case ORDWR: + perm = 6; + break; + } + + switch(TYPE(c->qid)) { + default: + break; + case Qtopdir: + case Qprotodir: + case Qconvdir: + case Qstatus: + case Qremote: + case Qlocal: + if(omode != OREAD) + error(Eperm); + break; + case Qclonus: + p = &proto[PROTO(c->qid)]; + cv = protoclone(p, up->user, -1); + if(cv == 0) + error(Enodev); + c->qid.path = QID(p->x, cv->x, Qctl); + c->qid.vers = 0; + break; + case Qdata: + case Qctl: + p = &proto[PROTO(c->qid)]; + lock(&p->l); + cv = p->conv[CONV(c->qid)]; + lock(&cv->r.lk); + if((perm & (cv->perm>>6)) != perm) { + if(strcmp(up->user, cv->owner) != 0 || + (perm & cv->perm) != perm) { + unlock(&cv->r.lk); + unlock(&p->l); + error(Eperm); + } + } + cv->r.ref++; + if(cv->r.ref == 1) { + memmove(cv->owner, up->user, KNAMELEN); + cv->perm = 0660; + } + unlock(&cv->r.lk); + unlock(&p->l); + break; + case Qlisten: + p = &proto[PROTO(c->qid)]; + lcv = p->conv[CONV(c->qid)]; + sfd = so_accept(lcv->sfd, &raddr, &rport); + cv = protoclone(p, up->user, sfd); + if(cv == 0) { + close(sfd); + error(Enodev); + } + cv->raddr = raddr; + cv->rport = rport; + setladdr(cv); + cv->state = "Established"; + c->qid.path = QID(p->x, cv->x, Qctl); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +void +ipclose(Chan *c) +{ + Conv *cc; + + switch(TYPE(c->qid)) { + case Qcs: + csclose(c); + break; + case Qdata: + case Qctl: + if((c->flag & COPEN) == 0) + break; + cc = proto[PROTO(c->qid)].conv[CONV(c->qid)]; + if(decref(&cc->r) != 0) + break; + strcpy(cc->owner, "network"); + cc->perm = 0666; + cc->state = "Closed"; + cc->laddr = 0; + cc->raddr = 0; + cc->lport = 0; + cc->rport = 0; + close(cc->sfd); + break; + } +} + +long +ipread(Chan *ch, void *a, long n, vlong offset) +{ + int r; + Conv *c; + Proto *x; + uchar ip[4]; + char buf[128], *p; + +/*print("ipread %s %lux\n", c2name(ch), (long)ch->qid.path);*/ + p = a; + switch(TYPE(ch->qid)) { + default: + error(Eperm); + case Qcs: + return csread(ch, a, n, offset); + case Qprotodir: + case Qtopdir: + case Qconvdir: + return devdirread(ch, a, n, 0, 0, ipgen); + case Qctl: + sprint(buf, "%d", CONV(ch->qid)); + return readstr(offset, p, n, buf); + case Qremote: + c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)]; + hnputl(ip, c->raddr); + sprint(buf, "%I!%d\n", ip, c->rport); + return readstr(offset, p, n, buf); + case Qlocal: + c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)]; + hnputl(ip, c->laddr); + sprint(buf, "%I!%d\n", ip, c->lport); + return readstr(offset, p, n, buf); + case Qstatus: + x = &proto[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + sprint(buf, "%s/%d %d %s \n", + c->p->name, c->x, c->r.ref, c->state); + return readstr(offset, p, n, buf); + case Qdata: + c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)]; + r = so_recv(c->sfd, a, n, 0); + if(r < 0){ + oserrstr(); + nexterror(); + } + return r; + } +} + +static void +setladdr(Conv *c) +{ + so_getsockname(c->sfd, &c->laddr, &c->lport); +} + +static void +setlport(Conv *c) +{ + if(c->restricted == 0 && c->lport == 0) + return; + + so_bind(c->sfd, c->restricted, c->lport); +} + +static void +setladdrport(Conv *c, char *str) +{ + char *p, addr[4]; + + p = strchr(str, '!'); + if(p == 0) { + p = str; + c->laddr = 0; + } + else { + *p++ = 0; + parseip(addr, str); + c->laddr = nhgetl((uchar*)addr); + } + if(*p == '*') + c->lport = 0; + else + c->lport = atoi(p); + + setlport(c); +} + +static char* +setraddrport(Conv *c, char *str) +{ + char *p, addr[4]; + + p = strchr(str, '!'); + if(p == 0) + return "malformed address"; + *p++ = 0; + parseip(addr, str); + c->raddr = nhgetl((uchar*)addr); + c->rport = atoi(p); + p = strchr(p, '!'); + if(p) { + if(strcmp(p, "!r") == 0) + c->restricted = 1; + } + return 0; +} + +long +ipwrite(Chan *ch, void *a, long n, vlong offset) +{ + Conv *c; + Proto *x; + int r, nf; + char *p, *fields[3], buf[128]; + + switch(TYPE(ch->qid)) { + default: + error(Eperm); + case Qcs: + return cswrite(ch, a, n, offset); + case Qctl: + x = &proto[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + if(n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = '\0'; + + nf = tokenize(buf, fields, 3); + if(strcmp(fields[0], "connect") == 0){ + switch(nf) { + default: + error("bad args to connect"); + case 2: + p = setraddrport(c, fields[1]); + if(p != 0) + error(p); + break; + case 3: + p = setraddrport(c, fields[1]); + if(p != 0) + error(p); + c->lport = atoi(fields[2]); + setlport(c); + break; + } + so_connect(c->sfd, c->raddr, c->rport); + setladdr(c); + c->state = "Established"; + return n; + } + if(strcmp(fields[0], "announce") == 0) { + switch(nf){ + default: + error("bad args to announce"); + case 2: + setladdrport(c, fields[1]); + break; + } + so_listen(c->sfd); + c->state = "Announced"; + return n; + } + if(strcmp(fields[0], "bind") == 0){ + switch(nf){ + default: + error("bad args to bind"); + case 2: + c->lport = atoi(fields[1]); + break; + } + setlport(c); + return n; + } + error("bad control message"); + case Qdata: + x = &proto[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + r = so_send(c->sfd, a, n, 0); + if(r < 0){ + oserrstr(); + nexterror(); + } + return r; + } + return n; +} + +static Conv* +protoclone(Proto *p, char *user, int nfd) +{ + Conv *c, **pp, **ep; + + c = 0; + lock(&p->l); + if(waserror()) { + unlock(&p->l); + nexterror(); + } + ep = &p->conv[p->maxconv]; + for(pp = p->conv; pp < ep; pp++) { + c = *pp; + if(c == 0) { + c = mallocz(sizeof(Conv), 1); + if(c == 0) + error(Enomem); + lock(&c->r.lk); + c->r.ref = 1; + c->p = p; + c->x = pp - p->conv; + p->nc++; + *pp = c; + break; + } + lock(&c->r.lk); + if(c->r.ref == 0) { + c->r.ref++; + break; + } + unlock(&c->r.lk); + } + if(pp >= ep) { + unlock(&p->l); + poperror(); + return 0; + } + + strcpy(c->owner, user); + c->perm = 0660; + c->state = "Closed"; + c->restricted = 0; + c->laddr = 0; + c->raddr = 0; + c->lport = 0; + c->rport = 0; + c->sfd = nfd; + if(nfd == -1) + c->sfd = so_socket(p->stype); + + unlock(&c->r.lk); + unlock(&p->l); + poperror(); + return c; +} + +enum +{ + Isprefix= 16, +}; + +uchar prefixvals[256] = +{ +/*0x00*/ 0 | Isprefix, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x30*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x40*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x50*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x80*/ 1 | Isprefix, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0x90*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0xA0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0xB0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0xC0*/ 2 | Isprefix, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0xD0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0xE0*/ 3 | Isprefix, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*0xF0*/ 4 | Isprefix, + 0, 0, 0, 0, 0, 0, 0, +/*0xF8*/ 5 | Isprefix, + 0, 0, 0, +/*0xFC*/ 6 | Isprefix, + 0, +/*0xFE*/ 7 | Isprefix, +/*0xFF*/ 8 | Isprefix, +}; + +int +eipfmt(Fmt *f) +{ + char buf[5*8]; + static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux"; + static char *ifmt = "%d.%d.%d.%d"; + uchar *p, ip[16]; + ulong *lp; + ushort s; + int i, j, n, eln, eli; + ulong ul; + + switch(f->r) { + case 'E': /* Ethernet address */ + p = va_arg(f->args, uchar*); + snprint(buf, sizeof buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]); + return fmtstrcpy(f, buf); + + case 'I': + ul = va_arg(f->args, ulong); + hnputl(ip, ul); + snprint(buf, sizeof buf, ifmt, ip[0], ip[1], ip[2], ip[3]); + return fmtstrcpy(f, buf); + } + return fmtstrcpy(f, "(eipfmt)"); +} + +void +hnputl(void *p, unsigned long v) +{ + unsigned char *a; + + a = p; + a[0] = v>>24; + a[1] = v>>16; + a[2] = v>>8; + a[3] = v; +} + +void +hnputs(void *p, unsigned short v) +{ + unsigned char *a; + + a = p; + a[0] = v>>8; + a[1] = v; +} + +unsigned long +nhgetl(void *p) +{ + unsigned char *a; + a = p; + return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0); +} + +unsigned short +nhgets(void *p) +{ + unsigned char *a; + a = p; + return (a[0]<<8)|(a[1]<<0); +} + +#define CLASS(p) ((*(unsigned char*)(p))>>6) + +unsigned long +parseip(char *to, char *from) +{ + int i; + char *p; + + p = from; + memset(to, 0, 4); + for(i = 0; i < 4 && *p; i++){ + to[i] = strtoul(p, &p, 0); + if(*p == '.') + p++; + } + switch(CLASS(to)){ + case 0: /* class A - 1 byte net */ + case 1: + if(i == 3){ + to[3] = to[2]; + to[2] = to[1]; + to[1] = 0; + } else if (i == 2){ + to[3] = to[1]; + to[1] = 0; + } + break; + case 2: /* class B - 2 byte net */ + if(i == 3){ + to[3] = to[2]; + to[2] = 0; + } + break; + } + return nhgetl(to); +} + +void +csclose(Chan *c) +{ + free(c->aux); +} + +long +csread(Chan *c, void *a, long n, vlong offset) +{ + if(c->aux == nil) + return 0; + return readstr(offset, a, n, c->aux); +} + +static struct +{ + char *name; + uint num; +} tab[] = { + "cs", 1, + "echo", 7, + "discard", 9, + "systat", 11, + "daytime", 13, + "netstat", 15, + "chargen", 19, + "ftp-data", 20, + "ftp", 21, + "ssh", 22, + "telnet", 23, + "smtp", 25, + "time", 37, + "whois", 43, + "dns", 53, + "domain", 53, + "uucp", 64, + "gopher", 70, + "rje", 77, + "finger", 79, + "http", 80, + "link", 87, + "supdup", 95, + "hostnames", 101, + "iso-tsap", 102, + "x400", 103, + "x400-snd", 104, + "csnet-ns", 105, + "pop-2", 109, + "pop3", 110, + "portmap", 111, + "uucp-path", 117, + "nntp", 119, + "netbios", 139, + "imap4", 143, + "NeWS", 144, + "print-srv", 170, + "z39.50", 210, + "fsb", 400, + "sysmon", 401, + "proxy", 402, + "proxyd", 404, + "https", 443, + "cifs", 445, + "ssmtp", 465, + "rexec", 512, + "login", 513, + "shell", 514, + "printer", 515, + "courier", 530, + "cscan", 531, + "uucp", 540, + "snntp", 563, + "9fs", 564, + "whoami", 565, + "guard", 566, + "ticket", 567, + "dlsftp", 666, + "fmclient", 729, + "imaps", 993, + "pop3s", 995, + "ingreslock", 1524, + "pptp", 1723, + "nfs", 2049, + "webster", 2627, + "weather", 3000, + "secstore", 5356, + "Xdisplay", 6000, + "styx", 6666, + "mpeg", 6667, + "rstyx", 6668, + "infdb", 6669, + "infsigner", 6671, + "infcsigner", 6672, + "inflogin", 6673, + "bandt", 7330, + "face", 32000, + "dhashgate", 11978, + "exportfs", 17007, + "rexexec", 17009, + "ncpu", 17010, + "cpu", 17013, + "glenglenda1", 17020, + "glenglenda2", 17021, + "glenglenda3", 17022, + "glenglenda4", 17023, + "glenglenda5", 17024, + "glenglenda6", 17025, + "glenglenda7", 17026, + "glenglenda8", 17027, + "glenglenda9", 17028, + "glenglenda10", 17029, + "flyboy", 17032, + "dlsftp", 17033, + "venti", 17034, + "wiki", 17035, + "vica", 17036, + 0 +}; + +static int +lookupport(char *s) +{ + int i; + char buf[10], *p; + + i = strtol(s, &p, 0); + if(*s && *p == 0) + return i; + + i = so_getservbyname(s, "tcp", buf); + if(i != -1) + return atoi(buf); + for(i=0; tab[i].name; i++) + if(strcmp(s, tab[i].name) == 0) + return tab[i].num; + return 0; +} + +static ulong +lookuphost(char *s) +{ + char to[4]; + ulong ip; + + memset(to, 0, sizeof to); + parseip(to, s); + ip = nhgetl(to); + if(ip != 0) + return ip; + if((s = hostlookup(s)) == nil) + return 0; + parseip(to, s); + ip = nhgetl(to); + free(s); + return ip; +} + +long +cswrite(Chan *c, void *a, long n, vlong offset) +{ + char *f[4]; + char *s, *ns; + ulong ip; + int nf, port; + + s = malloc(n+1); + if(s == nil) + error(Enomem); + if(waserror()){ + free(s); + nexterror(); + } + memmove(s, a, n); + s[n] = 0; + nf = getfields(s, f, nelem(f), 0, "!"); + if(nf != 3) + error("can't translate"); + + port = lookupport(f[2]); + if(port <= 0) + error("no translation for port found"); + + ip = lookuphost(f[1]); + if(ip == 0) + error("no translation for host found"); + + ns = smprint("/net/%s/clone %I!%d", f[0], ip, port); + if(ns == nil) + error(Enomem); + free(c->aux); + c->aux = ns; + poperror(); + free(s); + return n; +} + +Dev ipdevtab = +{ + 'I', + "ip", + + devreset, + ipinit, + devshutdown, + ipattach, + ipwalk, + ipstat, + ipopen, + devcreate, + ipclose, + ipread, + devbread, + ipwrite, + devbwrite, + devremove, + devwstat, +}; + diff --git a/kern/devip.h b/kern/devip.h new file mode 100644 index 0000000..6a20f3c --- /dev/null +++ b/kern/devip.h @@ -0,0 +1,17 @@ +enum +{ + S_TCP, + S_UDP +}; + +int so_socket(int type); +void so_connect(int, unsigned long, unsigned short); +void so_getsockname(int, unsigned long*, unsigned short*); +void so_bind(int, int, unsigned short); +void so_listen(int); +int so_accept(int, unsigned long*, unsigned short*); +int so_getservbyname(char*, char*, char*); +int so_gethostbyname(char*, char**, int); + +char* hostlookup(char*); + diff --git a/kern/devmnt.c b/kern/devmnt.c new file mode 100644 index 0000000..d7ce367 --- /dev/null +++ b/kern/devmnt.c @@ -0,0 +1,1214 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +/* + * References are managed as follows: + * The channel to the server - a network connection or pipe - has one + * reference for every Chan open on the server. The server channel has + * c->mux set to the Mnt used for muxing control to that server. Mnts + * have no reference count; they go away when c goes away. + * Each channel derived from the mount point has mchan set to c, + * and increfs/decrefs mchan to manage references on the server + * connection. + */ + +#define MAXRPC (IOHDRSZ+8192) + +struct Mntrpc +{ + Chan* c; /* Channel for whom we are working */ + Mntrpc* list; /* Free/pending list */ + Fcall request; /* Outgoing file system protocol message */ + Fcall reply; /* Incoming reply */ + Mnt* m; /* Mount device during rpc */ + Rendez r; /* Place to hang out */ + uchar* rpc; /* I/O Data buffer */ + uint rpclen; /* len of buffer */ + Block *b; /* reply blocks */ + char done; /* Rpc completed */ + uvlong stime; /* start time for mnt statistics */ + ulong reqlen; /* request length for mnt statistics */ + ulong replen; /* reply length for mnt statistics */ + Mntrpc* flushed; /* message this one flushes */ +}; + +enum +{ + TAGSHIFT = 5, /* ulong has to be 32 bits */ + TAGMASK = (1<>TAGSHIFT, +}; + +struct Mntalloc +{ + Lock lk; + Mnt* list; /* Mount devices in use */ + Mnt* mntfree; /* Free list */ + Mntrpc* rpcfree; + int nrpcfree; + int nrpcused; + ulong id; + ulong tagmask[NMASK]; +}mntalloc; + +void mattach(Mnt*, Chan*, char*); +Mnt* mntchk(Chan*); +void mntdirfix(uchar*, Chan*); +Mntrpc* mntflushalloc(Mntrpc*, ulong); +void mntflushfree(Mnt*, Mntrpc*); +void mntfree(Mntrpc*); +void mntgate(Mnt*); +void mntpntfree(Mnt*); +void mntqrm(Mnt*, Mntrpc*); +Mntrpc* mntralloc(Chan*, ulong); +long mntrdwr(int, Chan*, void*, long, vlong); +int mntrpcread(Mnt*, Mntrpc*); +void mountio(Mnt*, Mntrpc*); +void mountmux(Mnt*, Mntrpc*); +void mountrpc(Mnt*, Mntrpc*); +int rpcattn(void*); +Chan* mntchan(void); + +char Esbadstat[] = "invalid directory entry received from server"; +char Enoversion[] = "version not established for mount channel"; + + +void (*mntstats)(int, Chan*, uvlong, ulong); + +static void +mntreset(void) +{ + mntalloc.id = 1; + mntalloc.tagmask[0] = 1; /* don't allow 0 as a tag */ + mntalloc.tagmask[NMASK-1] = 0x80000000UL; /* don't allow NOTAG */ + fmtinstall('F', fcallfmt); + fmtinstall('D', dirfmt); +/* We can't install %M since eipfmt does and is used in the kernel [sape] */ + + cinit(); +} + +/* + * Version is not multiplexed: message sent only once per connection. + */ +long +mntversion(Chan *c, char *version, int msize, int returnlen) +{ + Fcall f; + uchar *msg; + Mnt *m; + char *v; + long k, l; + uvlong oo; + char buf[128]; + + qlock(&c->umqlock); /* make sure no one else does this until we've established ourselves */ + if(waserror()){ + qunlock(&c->umqlock); + nexterror(); + } + + /* defaults */ + if(msize == 0) + msize = MAXRPC; + if(msize > c->iounit && c->iounit != 0) + msize = c->iounit; + v = version; + if(v == nil || v[0] == '\0') + v = VERSION9P; + + /* validity */ + if(msize < 0) + error("bad iounit in version call"); + if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0) + error("bad 9P version specification"); + + m = c->mux; + + if(m != nil){ + qunlock(&c->umqlock); + poperror(); + + strecpy(buf, buf+sizeof buf, m->version); + k = strlen(buf); + if(strncmp(buf, v, k) != 0){ + snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v); + error(buf); + } + if(returnlen > 0){ + if(returnlen < k) + error(Eshort); + memmove(version, buf, k); + } + return k; + } + + f.type = Tversion; + f.tag = NOTAG; + f.msize = msize; + f.version = v; + msg = malloc(8192+IOHDRSZ); + if(msg == nil) + exhausted("version memory"); + if(waserror()){ + free(msg); + nexterror(); + } + k = convS2M(&f, msg, 8192+IOHDRSZ); + if(k == 0) + error("bad fversion conversion on send"); + + lock(&c->ref.lk); + oo = c->offset; + c->offset += k; + unlock(&c->ref.lk); + + l = devtab[c->type]->write(c, msg, k, oo); + + if(l < k){ + lock(&c->ref.lk); + c->offset -= k - l; + unlock(&c->ref.lk); + error("short write in fversion"); + } + + /* message sent; receive and decode reply */ + k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset); + if(k <= 0) + error("EOF receiving fversion reply"); + + lock(&c->ref.lk); + c->offset += k; + unlock(&c->ref.lk); + + l = convM2S(msg, k, &f); + if(l != k) + error("bad fversion conversion on reply"); + if(f.type != Rversion){ + if(f.type == Rerror) + error(f.ename); + error("unexpected reply type in fversion"); + } + if(f.msize > msize) + error("server tries to increase msize in fversion"); + if(f.msize<256 || f.msize>1024*1024) + error("nonsense value of msize in fversion"); + if(strncmp(f.version, v, strlen(f.version)) != 0) + error("bad 9P version returned from server"); + + /* now build Mnt associated with this connection */ + lock(&mntalloc.lk); + m = mntalloc.mntfree; + if(m != 0) + mntalloc.mntfree = m->list; + else { + m = malloc(sizeof(Mnt)); + if(m == 0) { + unlock(&mntalloc.lk); + exhausted("mount devices"); + } + } + m->list = mntalloc.list; + mntalloc.list = m; + m->version = nil; + kstrdup(&m->version, f.version); + m->id = mntalloc.id++; + m->q = qopen(10*MAXRPC, 0, nil, nil); + m->msize = f.msize; + unlock(&mntalloc.lk); + + poperror(); /* msg */ + free(msg); + + lock(&m->lk); + m->queue = 0; + m->rip = 0; + + c->flag |= CMSG; + c->mux = m; + m->c = c; + unlock(&m->lk); + + poperror(); /* c */ + qunlock(&c->umqlock); + + k = strlen(f.version); + if(returnlen > 0){ + if(returnlen < k) + error(Eshort); + memmove(version, f.version, k); + } + + return k; +} + +Chan* +mntauth(Chan *c, char *spec) +{ + Mnt *m; + Mntrpc *r; + + m = c->mux; + + if(m == nil){ + mntversion(c, VERSION9P, MAXRPC, 0); + m = c->mux; + if(m == nil) + error(Enoversion); + } + + c = mntchan(); + if(waserror()) { + /* Close must not be called since it will + * call mnt recursively + */ + chanfree(c); + nexterror(); + } + + r = mntralloc(0, m->msize); + + if(waserror()) { + mntfree(r); + nexterror(); + } + + r->request.type = Tauth; + r->request.afid = c->fid; + r->request.uname = up->user; + r->request.aname = spec; + mountrpc(m, r); + + c->qid = r->reply.aqid; + c->mchan = m->c; + incref(&m->c->ref); + c->mqid = c->qid; + c->mode = ORDWR; + + poperror(); /* r */ + mntfree(r); + + poperror(); /* c */ + + return c; + +} + +static Chan* +mntattach(char *muxattach) +{ + Mnt *m; + Chan *c; + Mntrpc *r; + struct bogus{ + Chan *chan; + Chan *authchan; + char *spec; + int flags; + }bogus; + + bogus = *((struct bogus *)muxattach); + c = bogus.chan; + + m = c->mux; + + if(m == nil){ + mntversion(c, nil, 0, 0); + m = c->mux; + if(m == nil) + error(Enoversion); + } + + c = mntchan(); + if(waserror()) { + /* Close must not be called since it will + * call mnt recursively + */ + chanfree(c); + nexterror(); + } + + r = mntralloc(0, m->msize); + + if(waserror()) { + mntfree(r); + nexterror(); + } + + r->request.type = Tattach; + r->request.fid = c->fid; + if(bogus.authchan == nil) + r->request.afid = NOFID; + else + r->request.afid = bogus.authchan->fid; + r->request.uname = up->user; + r->request.aname = bogus.spec; + mountrpc(m, r); + + c->qid = r->reply.qid; + c->mchan = m->c; + incref(&m->c->ref); + c->mqid = c->qid; + + poperror(); /* r */ + mntfree(r); + + poperror(); /* c */ + + if(bogus.flags&MCACHE) + c->flag |= CCACHE; + return c; +} + +Chan* +mntchan(void) +{ + Chan *c; + + c = devattach('M', 0); + lock(&mntalloc.lk); + c->dev = mntalloc.id++; + unlock(&mntalloc.lk); + + if(c->mchan) + panic("mntchan non-zero %p", c->mchan); + return c; +} + +static Walkqid* +mntwalk(Chan *c, Chan *nc, char **name, int nname) +{ + int i, alloc; + Mnt *m; + Mntrpc *r; + Walkqid *wq; + + if(nc != nil) + print("mntwalk: nc != nil\n"); + if(nname > MAXWELEM) + error("devmnt: too many name elements"); + alloc = 0; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone!=nil) + cclose(wq->clone); + free(wq); + return nil; + } + + alloc = 0; + m = mntchk(c); + r = mntralloc(c, m->msize); + if(nc == nil){ + nc = devclone(c); + /* + * Until the other side accepts this fid, we can't mntclose it. + * Therefore set type to 0 for now; rootclose is known to be safe. + */ + nc->type = 0; + alloc = 1; + } + wq->clone = nc; + + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Twalk; + r->request.fid = c->fid; + r->request.newfid = nc->fid; + r->request.nwname = nname; + memmove(r->request.wname, name, nname*sizeof(char*)); + + mountrpc(m, r); + + if(r->reply.nwqid > nname) + error("too many QIDs returned by walk"); + if(r->reply.nwqid < nname){ + if(alloc) + cclose(nc); + wq->clone = nil; + if(r->reply.nwqid == 0){ + free(wq); + wq = nil; + goto Return; + } + } + + /* move new fid onto mnt device and update its qid */ + if(wq->clone != nil){ + if(wq->clone != c){ + wq->clone->type = c->type; + wq->clone->mchan = c->mchan; + incref(&c->mchan->ref); + } + if(r->reply.nwqid > 0) + wq->clone->qid = r->reply.wqid[r->reply.nwqid-1]; + } + wq->nqid = r->reply.nwqid; + for(i=0; inqid; i++) + wq->qid[i] = r->reply.wqid[i]; + + Return: + poperror(); + mntfree(r); + poperror(); + return wq; +} + +static int +mntstat(Chan *c, uchar *dp, int n) +{ + Mnt *m; + Mntrpc *r; + + if(n < BIT16SZ) + error(Eshortstat); + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Tstat; + r->request.fid = c->fid; + mountrpc(m, r); + + if(r->reply.nstat >= 1<<(8*BIT16SZ)) + error("returned stat buffer count too large"); + + if(r->reply.nstat > n){ + /* + * 12/31/2002 RSC + * + * This should be nstat-2, which is the first two + * bytes of the stat buffer. But dirstat and dirfstat + * depended on getting the full nstat (they didn't + * add BIT16SZ themselves). I fixed dirstat and dirfstat + * but am leaving this unchanged for now. After a + * few months, once enough of the relevant binaries + * have been recompiled for other reasons, we can + * change this to nstat-2. Devstat gets this right + * (via convD2M). + */ + /* doesn't fit; just patch the count and return */ + PBIT16((uchar*)dp, r->reply.nstat); + n = BIT16SZ; + }else{ + n = r->reply.nstat; + memmove(dp, r->reply.stat, n); + validstat(dp, n); + mntdirfix(dp, c); + } + poperror(); + mntfree(r); + return n; +} + +static Chan* +mntopencreate(int type, Chan *c, char *name, int omode, ulong perm) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = type; + r->request.fid = c->fid; + r->request.mode = omode; + if(type == Tcreate){ + r->request.perm = perm; + r->request.name = name; + } + mountrpc(m, r); + + c->qid = r->reply.qid; + c->offset = 0; + c->mode = openmode(omode); + c->iounit = r->reply.iounit; + if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ) + c->iounit = m->msize-IOHDRSZ; + c->flag |= COPEN; + poperror(); + mntfree(r); + + if(c->flag & CCACHE) + copen(c); + + return c; +} + +static Chan* +mntopen(Chan *c, int omode) +{ + return mntopencreate(Topen, c, nil, omode, 0); +} + +static void +mntcreate(Chan *c, char *name, int omode, ulong perm) +{ + mntopencreate(Tcreate, c, name, omode, perm); +} + +static void +mntclunk(Chan *c, int t) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()){ + mntfree(r); + nexterror(); + } + + r->request.type = t; + r->request.fid = c->fid; + mountrpc(m, r); + mntfree(r); + poperror(); +} + +void +muxclose(Mnt *m) +{ + Mntrpc *q, *r; + + for(q = m->queue; q; q = r) { + r = q->list; + mntfree(q); + } + m->id = 0; + free(m->version); + m->version = nil; + mntpntfree(m); +} + +void +mntpntfree(Mnt *m) +{ + Mnt *f, **l; + Queue *q; + + lock(&mntalloc.lk); + l = &mntalloc.list; + for(f = *l; f; f = f->list) { + if(f == m) { + *l = m->list; + break; + } + l = &f->list; + } + m->list = mntalloc.mntfree; + mntalloc.mntfree = m; + q = m->q; + unlock(&mntalloc.lk); + + qfree(q); +} + +static void +mntclose(Chan *c) +{ + mntclunk(c, Tclunk); +} + +static void +mntremove(Chan *c) +{ + mntclunk(c, Tremove); +} + +static int +mntwstat(Chan *c, uchar *dp, int n) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Twstat; + r->request.fid = c->fid; + r->request.nstat = n; + r->request.stat = dp; + mountrpc(m, r); + poperror(); + mntfree(r); + return n; +} + +static long +mntread(Chan *c, void *buf, long n, vlong off) +{ + uchar *p, *e; + int nc, cache, isdir, dirlen; + + isdir = 0; + cache = c->flag & CCACHE; + if(c->qid.type & QTDIR) { + cache = 0; + isdir = 1; + } + + p = buf; + if(cache) { + nc = cread(c, buf, n, off); + if(nc > 0) { + n -= nc; + if(n == 0) + return nc; + p += nc; + off += nc; + } + n = mntrdwr(Tread, c, p, n, off); + cupdate(c, p, n, off); + return n + nc; + } + + n = mntrdwr(Tread, c, buf, n, off); + if(isdir) { + for(e = &p[n]; p+BIT16SZ < e; p += dirlen){ + dirlen = BIT16SZ+GBIT16(p); + if(p+dirlen > e) + break; + validstat(p, dirlen); + mntdirfix(p, c); + } + if(p != e) + error(Esbadstat); + } + return n; +} + +static long +mntwrite(Chan *c, void *buf, long n, vlong off) +{ + return mntrdwr(Twrite, c, buf, n, off); +} + +long +mntrdwr(int type, Chan *c, void *buf, long n, vlong off) +{ + Mnt *m; + Mntrpc *r; + char *uba; + int cache; + ulong cnt, nr, nreq; + + m = mntchk(c); + uba = buf; + cnt = 0; + cache = c->flag & CCACHE; + if(c->qid.type & QTDIR) + cache = 0; + for(;;) { + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = type; + r->request.fid = c->fid; + r->request.offset = off; + r->request.data = uba; + nr = n; + if(nr > m->msize-IOHDRSZ) + nr = m->msize-IOHDRSZ; + r->request.count = nr; + mountrpc(m, r); + nreq = r->request.count; + nr = r->reply.count; + if(nr > nreq) + nr = nreq; + + if(type == Tread) + r->b = bl2mem((uchar*)uba, r->b, nr); + else if(cache) + cwrite(c, (uchar*)uba, nr, off); + + poperror(); + mntfree(r); + off += nr; + uba += nr; + cnt += nr; + n -= nr; + if(nr != nreq || n == 0) + break; + } + return cnt; +} + +void +mountrpc(Mnt *m, Mntrpc *r) +{ + char *sn, *cn; + int t; + + r->reply.tag = 0; + r->reply.type = Tmax; /* can't ever be a valid message type */ + + mountio(m, r); + + t = r->reply.type; + switch(t) { + case Rerror: + error(r->reply.ename); + case Rflush: + error(Eintr); + default: + if(t == r->request.type+1) + break; + sn = "?"; + if(m->c->name != nil) + sn = m->c->name->s; + cn = "?"; + if(r->c != nil && r->c->name != nil) + cn = r->c->name->s; + print("mnt: proc %lud: mismatch from %s %s rep 0x%lux tag %d fid %d T%d R%d rp %d\n", + up->pid, sn, cn, + r, r->request.tag, r->request.fid, r->request.type, + r->reply.type, r->reply.tag); + error(Emountrpc); + } +} + +void +mountio(Mnt *m, Mntrpc *r) +{ + int n; + + while(waserror()) { + if(m->rip == up) + mntgate(m); + if(strcmp(up->errstr, Eintr) != 0){ + mntflushfree(m, r); + nexterror(); + } + r = mntflushalloc(r, m->msize); + } + + lock(&m->lk); + r->m = m; + r->list = m->queue; + m->queue = r; + unlock(&m->lk); + + /* Transmit a file system rpc */ + if(m->msize == 0) + panic("msize"); + n = convS2M(&r->request, r->rpc, m->msize); + if(n < 0) + panic("bad message type in mountio"); + if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n) + error(Emountrpc); + r->stime = fastticks(nil); + r->reqlen = n; + + /* Gate readers onto the mount point one at a time */ + for(;;) { + lock(&m->lk); + if(m->rip == 0) + break; + unlock(&m->lk); + sleep(&r->r, rpcattn, r); + if(r->done){ + poperror(); + mntflushfree(m, r); + return; + } + } + m->rip = up; + unlock(&m->lk); + while(r->done == 0) { + if(mntrpcread(m, r) < 0) + error(Emountrpc); + mountmux(m, r); + } + mntgate(m); + poperror(); + mntflushfree(m, r); +} + +static int +doread(Mnt *m, int len) +{ + Block *b; + + while(qlen(m->q) < len){ + b = devtab[m->c->type]->bread(m->c, m->msize, 0); + if(b == nil) + return -1; + if(BLEN(b) == 0){ + freeblist(b); + return -1; + } + qaddlist(m->q, b); + } + return 0; +} + +int +mntrpcread(Mnt *m, Mntrpc *r) +{ + int i, t, len, hlen; + Block *b, **l, *nb; + + r->reply.type = 0; + r->reply.tag = 0; + + /* read at least length, type, and tag and pullup to a single block */ + if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0) + return -1; + nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ); + + /* read in the rest of the message, avoid rediculous (for now) message sizes */ + len = GBIT32(nb->rp); + if(len > m->msize){ + qdiscard(m->q, qlen(m->q)); + return -1; + } + if(doread(m, len) < 0) + return -1; + + /* pullup the header (i.e. everything except data) */ + t = nb->rp[BIT32SZ]; + switch(t){ + case Rread: + hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ; + break; + default: + hlen = len; + break; + } + nb = pullupqueue(m->q, hlen); + + if(convM2S(nb->rp, len, &r->reply) <= 0){ + /* bad message, dump it */ + print("mntrpcread: convM2S failed\n"); + qdiscard(m->q, len); + return -1; + } + + /* hang the data off of the fcall struct */ + l = &r->b; + *l = nil; + do { + b = qremove(m->q); + if(hlen > 0){ + b->rp += hlen; + len -= hlen; + hlen = 0; + } + i = BLEN(b); + if(i <= len){ + len -= i; + *l = b; + l = &(b->next); + } else { + /* split block and put unused bit back */ + nb = allocb(i-len); + memmove(nb->wp, b->rp+len, i-len); + b->wp = b->rp+len; + nb->wp += i-len; + qputback(m->q, nb); + *l = b; + return 0; + } + }while(len > 0); + + return 0; +} + +void +mntgate(Mnt *m) +{ + Mntrpc *q; + + lock(&m->lk); + m->rip = 0; + for(q = m->queue; q; q = q->list) { + if(q->done == 0) + if(wakeup(&q->r)) + break; + } + unlock(&m->lk); +} + +void +mountmux(Mnt *m, Mntrpc *r) +{ + Mntrpc **l, *q; + + lock(&m->lk); + l = &m->queue; + for(q = *l; q; q = q->list) { + /* look for a reply to a message */ + if(q->request.tag == r->reply.tag) { + *l = q->list; + if(q != r) { + /* + * Completed someone else. + * Trade pointers to receive buffer. + */ + q->reply = r->reply; + q->b = r->b; + r->b = nil; + } + q->done = 1; + unlock(&m->lk); + if(mntstats != nil) + (*mntstats)(q->request.type, + m->c, q->stime, + q->reqlen + r->replen); + if(q != r) + wakeup(&q->r); + return; + } + l = &q->list; + } + unlock(&m->lk); + print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type); +} + +/* + * Create a new flush request and chain the previous + * requests from it + */ +Mntrpc* +mntflushalloc(Mntrpc *r, ulong iounit) +{ + Mntrpc *fr; + + fr = mntralloc(0, iounit); + + fr->request.type = Tflush; + if(r->request.type == Tflush) + fr->request.oldtag = r->request.oldtag; + else + fr->request.oldtag = r->request.tag; + fr->flushed = r; + + return fr; +} + +/* + * Free a chain of flushes. Remove each unanswered + * flush and the original message from the unanswered + * request queue. Mark the original message as done + * and if it hasn't been answered set the reply to to + * Rflush. + */ +void +mntflushfree(Mnt *m, Mntrpc *r) +{ + Mntrpc *fr; + + while(r){ + fr = r->flushed; + if(!r->done){ + r->reply.type = Rflush; + mntqrm(m, r); + } + if(fr) + mntfree(r); + r = fr; + } +} + +int +alloctag(void) +{ + int i, j; + ulong v; + + for(i = 0; i < NMASK; i++){ + v = mntalloc.tagmask[i]; + if(v == ~0UL) + continue; + for(j = 0; j < 1<>TAGSHIFT] &= ~(1<<(t&TAGMASK)); +} + +Mntrpc* +mntralloc(Chan *c, ulong msize) +{ + Mntrpc *new; + + lock(&mntalloc.lk); + new = mntalloc.rpcfree; + if(new == nil){ + new = malloc(sizeof(Mntrpc)); + if(new == nil) { + unlock(&mntalloc.lk); + exhausted("mount rpc header"); + } + /* + * The header is split from the data buffer as + * mountmux may swap the buffer with another header. + */ + new->rpc = mallocz(msize, 0); + if(new->rpc == nil){ + free(new); + unlock(&mntalloc.lk); + exhausted("mount rpc buffer"); + } + new->rpclen = msize; + new->request.tag = alloctag(); + } + else { + mntalloc.rpcfree = new->list; + mntalloc.nrpcfree--; + if(new->rpclen < msize){ + free(new->rpc); + new->rpc = mallocz(msize, 0); + if(new->rpc == nil){ + free(new); + mntalloc.nrpcused--; + unlock(&mntalloc.lk); + exhausted("mount rpc buffer"); + } + new->rpclen = msize; + } + } + mntalloc.nrpcused++; + unlock(&mntalloc.lk); + new->c = c; + new->done = 0; + new->flushed = nil; + new->b = nil; + return new; +} + +void +mntfree(Mntrpc *r) +{ + if(r->b != nil) + freeblist(r->b); + lock(&mntalloc.lk); + if(mntalloc.nrpcfree >= 10){ + free(r->rpc); + free(r); + freetag(r->request.tag); + } + else{ + r->list = mntalloc.rpcfree; + mntalloc.rpcfree = r; + mntalloc.nrpcfree++; + } + mntalloc.nrpcused--; + unlock(&mntalloc.lk); +} + +void +mntqrm(Mnt *m, Mntrpc *r) +{ + Mntrpc **l, *f; + + lock(&m->lk); + r->done = 1; + + l = &m->queue; + for(f = *l; f; f = f->list) { + if(f == r) { + *l = r->list; + break; + } + l = &f->list; + } + unlock(&m->lk); +} + +Mnt* +mntchk(Chan *c) +{ + Mnt *m; + + /* This routine is mostly vestiges of prior lives; now it's just sanity checking */ + + if(c->mchan == nil) + panic("mntchk 1: nil mchan c %s\n", c2name(c)); + + m = c->mchan->mux; + + if(m == nil) + print("mntchk 2: nil mux c %s c->mchan %s \n", c2name(c), c2name(c->mchan)); + + /* + * Was it closed and reused (was error(Eshutdown); now, it can't happen) + */ + if(m->id == 0 || m->id >= c->dev) + panic("mntchk 3: can't happen"); + + return m; +} + +/* + * Rewrite channel type and dev for in-flight data to + * reflect local values. These entries are known to be + * the first two in the Dir encoding after the count. + */ +void +mntdirfix(uchar *dirbuf, Chan *c) +{ + uint r; + + r = devtab[c->type]->dc; + dirbuf += BIT16SZ; /* skip count */ + PBIT16(dirbuf, r); + dirbuf += BIT16SZ; + PBIT32(dirbuf, c->dev); +} + +int +rpcattn(void *v) +{ + Mntrpc *r; + + r = v; + return r->done || r->m->rip == 0; +} + +Dev mntdevtab = { + 'M', + "mnt", + + mntreset, + devinit, + devshutdown, + mntattach, + mntwalk, + mntstat, + mntopen, + mntcreate, + mntclose, + mntread, + devbread, + mntwrite, + devbwrite, + mntremove, + mntwstat, +}; diff --git a/kern/devmouse.c b/kern/devmouse.c new file mode 100644 index 0000000..e799417 --- /dev/null +++ b/kern/devmouse.c @@ -0,0 +1,237 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "draw.h" +#include "memdraw.h" +#include "screen.h" + +int mousequeue; + +Mouseinfo mouse; +Cursorinfo cursor; + +static int mousechanged(void*); + +enum{ + Qdir, + Qcursor, + Qmouse +}; + +Dirtab mousedir[]={ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "cursor", {Qcursor}, 0, 0666, + "mouse", {Qmouse}, 0, 0666, +}; + +#define NMOUSE (sizeof(mousedir)/sizeof(Dirtab)) + +static Chan* +mouseattach(char *spec) +{ + return devattach('m', spec); +} + +static Walkqid* +mousewalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, mousedir, NMOUSE, devgen); +} + +static int +mousestat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, mousedir, NMOUSE, devgen); +} + +static Chan* +mouseopen(Chan *c, int omode) +{ + switch((long)c->qid.path){ + case Qdir: + if(omode != OREAD) + error(Eperm); + break; + case Qmouse: + lock(&mouse.lk); + if(mouse.open){ + unlock(&mouse.lk); + error(Einuse); + } + mouse.open = 1; + unlock(&mouse.lk); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +void +mouseclose(Chan *c) +{ + if(!(c->flag&COPEN)) + return; + + switch((long)c->qid.path) { + case Qmouse: + lock(&mouse.lk); + mouse.open = 0; + unlock(&mouse.lk); + cursorarrow(); + } +} + + +long +mouseread(Chan *c, void *va, long n, vlong offset) +{ + char buf[4*12+1]; + uchar *p; + int i, nn; + ulong msec; +/* static int map[8] = {0, 4, 2, 6, 1, 5, 3, 7 }; */ + + p = va; + switch((long)c->qid.path){ + case Qdir: + return devdirread(c, va, n, mousedir, NMOUSE, devgen); + + case Qcursor: + if(offset != 0) + return 0; + if(n < 2*4+2*2*16) + error(Eshort); + n = 2*4+2*2*16; + lock(&cursor.lk); + BPLONG(p+0, cursor.offset.x); + BPLONG(p+4, cursor.offset.y); + memmove(p+8, cursor.clr, 2*16); + memmove(p+40, cursor.set, 2*16); + unlock(&cursor.lk); + return n; + + case Qmouse: + while(mousechanged(0) == 0) + sleep(&mouse.r, mousechanged, 0); + + lock(&screen.lk); + if(screen.reshaped) { + screen.reshaped = 0; + sprint(buf, "t%11d %11d", 0, ticks()); + if(n > 1+2*12) + n = 1+2*12; + memmove(va, buf, n); + unlock(&screen.lk); + return n; + } + unlock(&screen.lk); + + lock(&mouse.lk); + i = mouse.ri; + nn = (mouse.wi + Mousequeue - i) % Mousequeue; + if(nn < 1) + panic("empty mouse queue"); + msec = ticks(); + while(nn > 1) { + if(mouse.queue[i].msec + Mousewindow > msec) + break; + i = (i+1)%Mousequeue; + nn--; + } + sprint(buf, "m%11d %11d %11d %11d", + mouse.queue[i].xy.x, + mouse.queue[i].xy.y, + mouse.queue[i].buttons, + mouse.queue[i].msec); + mouse.ri = (i+1)%Mousequeue; + unlock(&mouse.lk); + if(n > 1+4*12) + n = 1+4*12; + memmove(va, buf, n); + return n; + } + return 0; +} + +long +mousewrite(Chan *c, void *va, long n, vlong offset) +{ + char *p; + Point pt; + char buf[64]; + + USED(offset); + + p = va; + switch((long)c->qid.path){ + case Qdir: + error(Eisdir); + + case Qcursor: + if(n < 2*4+2*2*16){ + cursorarrow(); + }else{ + n = 2*4+2*2*16; + lock(&cursor.lk); + cursor.offset.x = BGLONG(p+0); + cursor.offset.y = BGLONG(p+4); + memmove(cursor.clr, p+8, 2*16); + memmove(cursor.set, p+40, 2*16); + unlock(&cursor.lk); + setcursor(); + } + return n; + + case Qmouse: + if(n > sizeof buf-1) + n = sizeof buf -1; + memmove(buf, va, n); + buf[n] = 0; + p = 0; + pt.x = strtoul(buf+1, &p, 0); + if(p == 0) + error(Eshort); + pt.y = strtoul(p, 0, 0); + if(ptinrect(pt, gscreen->r)) + mouseset(pt); + return n; + } + + error(Egreg); + return -1; +} + +int +mousechanged(void *a) +{ + USED(a); + + return mouse.ri != mouse.wi || screen.reshaped; +} + +Dev mousedevtab = { + 'm', + "mouse", + + devreset, + devinit, + devshutdown, + mouseattach, + mousewalk, + mousestat, + mouseopen, + devcreate, + mouseclose, + mouseread, + devbread, + mousewrite, + devbwrite, + devremove, + devwstat, +}; + diff --git a/kern/devntfs.c b/kern/devntfs.c new file mode 100644 index 0000000..8608e47 --- /dev/null +++ b/kern/devntfs.c @@ -0,0 +1,704 @@ +#include +#include +#include +#include + +#ifndef NAME_MAX +# define NAME_MAX 256 +#endif +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +typedef struct DIR DIR; +typedef struct Ufsinfo Ufsinfo; + +enum +{ + NUID = 256, + NGID = 256, + MAXPATH = 1024, + MAXCOMP = 128 +}; + +struct DIR +{ + HANDLE handle; + char* path; + int index; + WIN32_FIND_DATA wfd; +}; + +struct Ufsinfo +{ + int mode; + int fd; + int uid; + int gid; + DIR* dir; + ulong offset; + QLock oq; + char nextname[NAME_MAX]; +}; + +DIR* opendir(char*); +int readdir(char*, DIR*); +void closedir(DIR*); +void rewinddir(DIR*); + +char *base = "c:/."; + +static Qid fsqid(char*, struct stat *); +static void fspath(Chan*, char*, char*); +// static void fsperm(Chan*, int); +static ulong fsdirread(Chan*, uchar*, int, ulong); +static int fsomode(int); +static int chown(char *path, int uid, int); +static int link(char *path, char *next); + +/* clumsy hack, but not worse than the Path stuff in the last one */ +static char* +uc2name(Chan *c) +{ + char *s; + + if(c->name == nil) + return "/"; + s = c2name(c); + if(s[0]=='#' && s[1]=='U') + return s+2; + return s; +} + +static char* +lastelem(Chan *c) +{ + char *s, *t; + + s = uc2name(c); + if((t = strrchr(s, '/')) == nil) + return s; + if(t[1] == 0) + return t; + return t+1; +} + +static Chan* +fsattach(void *spec) +{ + Chan *c; + struct stat stbuf; + static int devno; + Ufsinfo *uif; + + if(stat(base, &stbuf) < 0) + error(strerror(errno)); + + c = devattach('U', spec); + + uif = mallocz(sizeof(Ufsinfo), 1); + uif->gid = stbuf.st_gid; + uif->uid = stbuf.st_uid; + uif->mode = stbuf.st_mode; + + c->aux = uif; + c->dev = devno++; + c->qid.type = QTDIR; +/*print("fsattach %s\n", c2name(c));*/ + + return c; +} + +static Chan* +fsclone(Chan *c, Chan *nc) +{ + Ufsinfo *uif; + + uif = mallocz(sizeof(Ufsinfo), 1); + *uif = *(Ufsinfo*)c->aux; + nc->aux = uif; + + return nc; +} + +static int +fswalk1(Chan *c, char *name) +{ + struct stat stbuf; + char path[MAXPATH]; + Ufsinfo *uif; + + fspath(c, name, path); + + /* print("** fs walk '%s' -> %s\n", path, name); /**/ + + if(stat(path, &stbuf) < 0) + return 0; + + uif = c->aux; + + uif->gid = stbuf.st_gid; + uif->uid = stbuf.st_uid; + uif->mode = stbuf.st_mode; + + c->qid = fsqid(path, &stbuf); + + return 1; +} + +extern Cname* addelem(Cname*, char*); + +static Walkqid* +fswalk(Chan *c, Chan *nc, char **name, int nname) +{ + int i; + Cname *cname; + Walkqid *wq; + + if(nc != nil) + panic("fswalk: nc != nil"); + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + nc = devclone(c); + cname = c->name; + incref(&cname->ref); + + fsclone(c, nc); + wq->clone = nc; + for(i=0; iname = cname; + if(fswalk1(nc, name[i]) == 0) + break; + cname = addelem(cname, name[i]); + wq->qid[i] = nc->qid; + } + nc->name = nil; + cnameclose(cname); + if(i != nname){ + cclose(nc); + wq->clone = nil; + } + wq->nqid = i; + return wq; +} + +static int +fsstat(Chan *c, uchar *buf, int n) +{ + Dir d; + struct stat stbuf; + char path[MAXPATH]; + + if(n < BIT16SZ) + error(Eshortstat); + + fspath(c, 0, path); + if(stat(path, &stbuf) < 0) + error(strerror(errno)); + + d.name = lastelem(c); + d.uid = "unknown"; + d.gid = "unknown"; + d.muid = "unknown"; + d.qid = c->qid; + d.mode = (c->qid.type<<24)|(stbuf.st_mode&0777); + d.atime = stbuf.st_atime; + d.mtime = stbuf.st_mtime; + d.length = stbuf.st_size; + d.type = 'U'; + d.dev = c->dev; + return convD2M(&d, buf, n); +} + +static Chan* +fsopen(Chan *c, int mode) +{ + char path[MAXPATH]; + int m, isdir; + Ufsinfo *uif; + +/*print("fsopen %s\n", c2name(c));*/ + m = mode & (OTRUNC|3); + switch(m) { + case 0: + break; + case 1: + case 1|16: + break; + case 2: + case 0|16: + case 2|16: + break; + case 3: + break; + default: + error(Ebadarg); + } + + isdir = c->qid.type & QTDIR; + + if(isdir && mode != OREAD) + error(Eperm); + + m = fsomode(m & 3); + c->mode = openmode(mode); + + uif = c->aux; + + fspath(c, 0, path); + if(isdir) { + uif->dir = opendir(path); + if(uif->dir == 0) + error(strerror(errno)); + } + else { + if(mode & OTRUNC) + m |= O_TRUNC; + uif->fd = open(path, m|_O_BINARY, 0666); + + if(uif->fd < 0) + error(strerror(errno)); + } + uif->offset = 0; + + c->offset = 0; + c->flag |= COPEN; + return c; +} + +static void +fscreate(Chan *c, char *name, int mode, ulong perm) +{ + int fd, m; + char path[MAXPATH]; + struct stat stbuf; + Ufsinfo *uif; + + m = fsomode(mode&3); + + fspath(c, name, path); + + uif = c->aux; + + if(perm & DMDIR) { + if(m) + error(Eperm); + + if(mkdir(path) < 0) + error(strerror(errno)); + + fd = open(path, 0); + if(fd >= 0) { + chmod(path, perm & 0777); + chown(path, uif->uid, uif->uid); + } + close(fd); + + uif->dir = opendir(path); + if(uif->dir == 0) + error(strerror(errno)); + } + else { + fd = open(path, _O_WRONLY|_O_BINARY|_O_CREAT|_O_TRUNC, 0666); + if(fd >= 0) { + if(m != 1) { + close(fd); + fd = open(path, m|_O_BINARY); + } + chmod(path, perm & 0777); + chown(path, uif->uid, uif->gid); + } + if(fd < 0) + error(strerror(errno)); + uif->fd = fd; + } + + if(stat(path, &stbuf) < 0) + error(strerror(errno)); + c->qid = fsqid(path, &stbuf); + c->offset = 0; + c->flag |= COPEN; + c->mode = openmode(mode); +} + +static void +fsclose(Chan *c) +{ + Ufsinfo *uif; + + uif = c->aux; + + if(c->flag & COPEN) { + if(c->qid.type & QTDIR) + closedir(uif->dir); + else + close(uif->fd); + } + + free(uif); +} + +static long +fsread(Chan *c, void *va, long n, ulong offset) +{ + int fd, r; + Ufsinfo *uif; + +/*print("fsread %s\n", c2name(c));*/ + if(c->qid.type & QTDIR) + return fsdirread(c, va, n, offset); + + uif = c->aux; + qlock(&uif->oq); + if(waserror()) { + qunlock(&uif->oq); + nexterror(); + } + fd = uif->fd; + if(uif->offset != offset) { + r = lseek(fd, offset, 0); + if(r < 0) + error(strerror(errno)); + uif->offset = offset; + } + + n = read(fd, va, n); + if(n < 0) + error(strerror(errno)); + + uif->offset += n; + qunlock(&uif->oq); + poperror(); + + return n; +} + +static long +fswrite(Chan *c, void *va, long n, ulong offset) +{ + int fd, r; + Ufsinfo *uif; + + uif = c->aux; + + qlock(&uif->oq); + if(waserror()) { + qunlock(&uif->oq); + nexterror(); + } + fd = uif->fd; + if(uif->offset != offset) { + r = lseek(fd, offset, 0); + if(r < 0) + error(strerror(errno)); + uif->offset = offset; + } + + n = write(fd, va, n); + if(n < 0) + error(strerror(errno)); + + uif->offset += n; + qunlock(&uif->oq); + poperror(); + + return n; +} + +static void +fsremove(Chan *c) +{ + int n; + char path[MAXPATH]; + + fspath(c, 0, path); + if(c->qid.type & QTDIR) + n = rmdir(path); + else + n = remove(path); + if(n < 0) + error(strerror(errno)); +} + +static int +fswstat(Chan *c, uchar *buf, int n) +{ + Dir d; + struct stat stbuf; + char old[MAXPATH], new[MAXPATH]; + char strs[MAXPATH*3], *p; + Ufsinfo *uif; + + if (convM2D(buf, n, &d, strs) != n) + error(Ebadstat); + + fspath(c, 0, old); + if(stat(old, &stbuf) < 0) + error(strerror(errno)); + + uif = c->aux; + +// if(uif->uid != stbuf.st_uid) +// error(Eowner); + + if(d.name[0] && strcmp(d.name, lastelem(c)) != 0) { + fspath(c, 0, old); + strcpy(new, old); + p = strrchr(new, '/'); + strcpy(p+1, d.name); + if(rename(old, new) < 0) + error(strerror(errno)); + } + + fspath(c, 0, old); + if(~d.mode != 0 && (int)(d.mode&0777) != (int)(stbuf.st_mode&0777)) { + if(chmod(old, d.mode&0777) < 0) + error(strerror(errno)); + uif->mode &= ~0777; + uif->mode |= d.mode&0777; + } +/* + p = name2pass(gid, d.gid); + if(p == 0) + error(Eunknown); + + if(p->id != stbuf.st_gid) { + if(chown(old, stbuf.st_uid, p->id) < 0) + error(sys_errlist[errno]); + + uif->gid = p->id; + } +*/ + return n; +} + +static Qid +fsqid(char *p, struct stat *st) +{ + Qid q; + int dev; + ulong h; + static int nqdev; + static uchar *qdev; + + if(qdev == 0) + qdev = mallocz(65536U, 1); + + q.type = 0; + if((st->st_mode&S_IFMT) == S_IFDIR) + q.type = QTDIR; + + dev = st->st_dev & 0xFFFFUL; + if(qdev[dev] == 0) + qdev[dev] = ++nqdev; + + h = 0; + while(*p != '\0') + h += *p++ * 13; + + q.path = (vlong)qdev[dev]<<32; + q.path |= h; + q.vers = st->st_mtime; + + return q; +} + +static void +fspath(Chan *c, char *ext, char *path) +{ + strcpy(path, base); + strcat(path, "/"); + strcat(path, uc2name(c)); + if(ext) { + strcat(path, "/"); + strcat(path, ext); + } + cleanname(path); +} + +static int +isdots(char *name) +{ + if(name[0] != '.') + return 0; + if(name[1] == '\0') + return 1; + if(name[1] != '.') + return 0; + if(name[2] == '\0') + return 1; + return 0; +} + +static int +p9readdir(char *name, Ufsinfo *uif) +{ + if(uif->nextname[0]){ + strcpy(name, uif->nextname); + uif->nextname[0] = 0; + return 1; + } + + return readdir(name, uif->dir); +} + +static ulong +fsdirread(Chan *c, uchar *va, int count, ulong offset) +{ + int i; + Dir d; + long n; + char de[NAME_MAX]; + struct stat stbuf; + char path[MAXPATH], dirpath[MAXPATH]; + Ufsinfo *uif; + +/*print("fsdirread %s\n", c2name(c));*/ + i = 0; + uif = c->aux; + + errno = 0; + if(uif->offset != offset) { + if(offset != 0) + error("bad offset in fsdirread"); + uif->offset = offset; /* sync offset */ + uif->nextname[0] = 0; + rewinddir(uif->dir); + } + + fspath(c, 0, dirpath); + + while(i+BIT16SZ < count) { + if(!p9readdir(de, uif)) + break; + + if(de[0]==0 || isdots(de)) + continue; + + d.name = de; + sprint(path, "%s/%s", dirpath, de); + memset(&stbuf, 0, sizeof stbuf); + + if(stat(path, &stbuf) < 0) { + print("dir: bad path %s\n", path); + /* but continue... probably a bad symlink */ + } + + d.uid = "unknown"; + d.gid = "unknown"; + d.muid = "unknown"; + d.qid = fsqid(path, &stbuf); + d.mode = (d.qid.type<<24)|(stbuf.st_mode&0777); + d.atime = stbuf.st_atime; + d.mtime = stbuf.st_mtime; + d.length = stbuf.st_size; + d.type = 'U'; + d.dev = c->dev; + n = convD2M(&d, (char*)va+i, count-i); + if(n == BIT16SZ){ + strcpy(uif->nextname, de); + break; + } + i += n; + } +/*print("got %d\n", i);*/ + uif->offset += i; + return i; +} + +static int +fsomode(int m) +{ + switch(m) { + case 0: /* OREAD */ + case 3: /* OEXEC */ + return 0; + case 1: /* OWRITE */ + return 1; + case 2: /* ORDWR */ + return 2; + } + error(Ebadarg); + return 0; +} +void +closedir(DIR *d) +{ + FindClose(d->handle); + free(d->path); +} + +int +readdir(char *name, DIR *d) +{ + if(d->index != 0) { + if(FindNextFile(d->handle, &d->wfd) == FALSE) + return 0; + } + strcpy(name, d->wfd.cFileName); + d->index++; + + return 1; +} + +void +rewinddir(DIR *d) +{ + FindClose(d->handle); + d->handle = FindFirstFile(d->path, &d->wfd); + d->index = 0; +} + +static int +chown(char *path, int uid, int perm) +{ +/* panic("chown"); */ + return 0; +} + +DIR* +opendir(char *p) +{ + DIR *d; + char path[MAX_PATH]; + + + snprint(path, sizeof(path), "%s/*.*", p); + + d = mallocz(sizeof(DIR), 1); + if(d == 0) + return 0; + + d->index = 0; + + d->handle = FindFirstFile(path, &d->wfd); + if(d->handle == INVALID_HANDLE_VALUE) { + free(d); + return 0; + } + + d->path = strdup(path); + return d; +} + +Dev fsdevtab = { + 'U', + "fs", + + devreset, + devinit, + devshutdown, + fsattach, + fswalk, + fsstat, + fsopen, + fscreate, + fsclose, + fsread, + devbread, + fswrite, + devbwrite, + fsremove, + fswstat, +}; diff --git a/kern/devpipe.c b/kern/devpipe.c new file mode 100644 index 0000000..73401f9 --- /dev/null +++ b/kern/devpipe.c @@ -0,0 +1,398 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "netif.h" + +typedef struct Pipe Pipe; +struct Pipe +{ + QLock lk; + Pipe *next; + int ref; + ulong path; + Queue *q[2]; + int qref[2]; +}; + +struct +{ + Lock lk; + ulong path; +} pipealloc; + +enum +{ + Qdir, + Qdata0, + Qdata1, +}; + +Dirtab pipedir[] = +{ + ".", {Qdir,0,QTDIR}, 0, DMDIR|0500, + "data", {Qdata0}, 0, 0600, + "data1", {Qdata1}, 0, 0600, +}; +#define NPIPEDIR 3 + +static void +pipeinit(void) +{ + if(conf.pipeqsize == 0){ + if(conf.nmach > 1) + conf.pipeqsize = 256*1024; + else + conf.pipeqsize = 32*1024; + } +} + +/* + * create a pipe, no streams are created until an open + */ +static Chan* +pipeattach(char *spec) +{ + Pipe *p; + Chan *c; + + c = devattach('|', spec); + p = malloc(sizeof(Pipe)); + if(p == 0) + exhausted("memory"); + p->ref = 1; + + p->q[0] = qopen(conf.pipeqsize, 0, 0, 0); + if(p->q[0] == 0){ + free(p); + exhausted("memory"); + } + p->q[1] = qopen(conf.pipeqsize, 0, 0, 0); + if(p->q[1] == 0){ + free(p->q[0]); + free(p); + exhausted("memory"); + } + + lock(&pipealloc.lk); + p->path = ++pipealloc.path; + unlock(&pipealloc.lk); + + mkqid(&c->qid, NETQID(2*p->path, Qdir), 0, QTDIR); + c->aux = p; + c->dev = 0; + return c; +} + +static int +pipegen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp) +{ + Qid q; + int len; + Pipe *p; + + USED(name); + + if(i == DEVDOTDOT){ + devdir(c, c->qid, "#|", 0, eve, DMDIR|0555, dp); + return 1; + } + i++; /* skip . */ + if(tab==0 || i>=ntab) + return -1; + + tab += i; + p = c->aux; + switch((ulong)tab->qid.path){ + case Qdata0: + len = qlen(p->q[0]); + break; + case Qdata1: + len = qlen(p->q[1]); + break; + default: + len = tab->length; + break; + } + mkqid(&q, NETQID(NETID(c->qid.path), tab->qid.path), 0, QTFILE); + devdir(c, q, tab->name, len, eve, tab->perm, dp); + return 1; +} + + +static Walkqid* +pipewalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + Pipe *p; + + wq = devwalk(c, nc, name, nname, pipedir, NPIPEDIR, pipegen); + if(wq != nil && wq->clone != nil && wq->clone != c){ + p = c->aux; + qlock(&p->lk); + p->ref++; + if(c->flag & COPEN){ + print("channel open in pipewalk\n"); + switch(NETTYPE(c->qid.path)){ + case Qdata0: + p->qref[0]++; + break; + case Qdata1: + p->qref[1]++; + break; + } + } + qunlock(&p->lk); + } + return wq; +} + +static int +pipestat(Chan *c, uchar *db, int n) +{ + Pipe *p; + Dir dir; + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdir: + devdir(c, c->qid, ".", 0, eve, DMDIR|0555, &dir); + break; + case Qdata0: + devdir(c, c->qid, "data", qlen(p->q[0]), eve, 0600, &dir); + break; + case Qdata1: + devdir(c, c->qid, "data1", qlen(p->q[1]), eve, 0600, &dir); + break; + default: + panic("pipestat"); + } + n = convD2M(&dir, db, n); + if(n < BIT16SZ) + error(Eshortstat); + return n; +} + +/* + * if the stream doesn't exist, create it + */ +static Chan* +pipeopen(Chan *c, int omode) +{ + Pipe *p; + + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Ebadarg); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + p = c->aux; + qlock(&p->lk); + switch(NETTYPE(c->qid.path)){ + case Qdata0: + p->qref[0]++; + break; + case Qdata1: + p->qref[1]++; + break; + } + qunlock(&p->lk); + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = qiomaxatomic; + return c; +} + +static void +pipeclose(Chan *c) +{ + Pipe *p; + + p = c->aux; + qlock(&p->lk); + + if(c->flag & COPEN){ + /* + * closing either side hangs up the stream + */ + switch(NETTYPE(c->qid.path)){ + case Qdata0: + p->qref[0]--; + if(p->qref[0] == 0){ + qhangup(p->q[1], 0); + qclose(p->q[0]); + } + break; + case Qdata1: + p->qref[1]--; + if(p->qref[1] == 0){ + qhangup(p->q[0], 0); + qclose(p->q[1]); + } + break; + } + } + + + /* + * if both sides are closed, they are reusable + */ + if(p->qref[0] == 0 && p->qref[1] == 0){ + qreopen(p->q[0]); + qreopen(p->q[1]); + } + + /* + * free the structure on last close + */ + p->ref--; + if(p->ref == 0){ + qunlock(&p->lk); + free(p->q[0]); + free(p->q[1]); + free(p); + } else + qunlock(&p->lk); +} + +static long +piperead(Chan *c, void *va, long n, vlong offset) +{ + Pipe *p; + + USED(offset); + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdir: + return devdirread(c, va, n, pipedir, NPIPEDIR, pipegen); + case Qdata0: + return qread(p->q[0], va, n); + case Qdata1: + return qread(p->q[1], va, n); + default: + panic("piperead"); + } + return -1; /* not reached */ +} + +static Block* +pipebread(Chan *c, long n, ulong offset) +{ + Pipe *p; + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdata0: + return qbread(p->q[0], n); + case Qdata1: + return qbread(p->q[1], n); + } + + return devbread(c, n, offset); +} + +/* + * a write to a closed pipe causes a note to be sent to + * the process. + */ +static long +pipewrite(Chan *c, void *va, long n, vlong offset) +{ + Pipe *p; + + USED(offset); + if(!islo()) + print("pipewrite hi %lux\n", getcallerpc(&c)); + + if(waserror()) { + /* avoid notes when pipe is a mounted queue */ + if((c->flag & CMSG) == 0) + postnote(up, 1, "sys: write on closed pipe", NUser); + nexterror(); + } + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdata0: + n = qwrite(p->q[1], va, n); + break; + + case Qdata1: + n = qwrite(p->q[0], va, n); + break; + + default: + panic("pipewrite"); + } + + poperror(); + return n; +} + +static long +pipebwrite(Chan *c, Block *bp, ulong offset) +{ + long n; + Pipe *p; + + USED(offset); + + if(waserror()) { + /* avoid notes when pipe is a mounted queue */ + if((c->flag & CMSG) == 0) + postnote(up, 1, "sys: write on closed pipe", NUser); + nexterror(); + } + + p = c->aux; + switch(NETTYPE(c->qid.path)){ + case Qdata0: + n = qbwrite(p->q[1], bp); + break; + + case Qdata1: + n = qbwrite(p->q[0], bp); + break; + + default: + n = 0; + panic("pipebwrite"); + } + + poperror(); + return n; +} + +Dev pipedevtab = { + '|', + "pipe", + + devreset, + pipeinit, + devshutdown, + pipeattach, + pipewalk, + pipestat, + pipeopen, + devcreate, + pipeclose, + piperead, + pipebread, + pipewrite, + pipebwrite, + devremove, + devwstat, +}; diff --git a/kern/devroot.c b/kern/devroot.c new file mode 100644 index 0000000..7821556 --- /dev/null +++ b/kern/devroot.c @@ -0,0 +1,268 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Qdir = 0, + Qboot = 0x1000, + + Nrootfiles = 32, + Nbootfiles = 32, +}; + +typedef struct Dirlist Dirlist; +struct Dirlist +{ + uint base; + Dirtab *dir; + uchar **data; + int ndir; + int mdir; +}; + +static Dirtab rootdir[Nrootfiles] = { + "#/", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "boot", {Qboot, 0, QTDIR}, 0, DMDIR|0555, +}; +static uchar *rootdata[Nrootfiles]; +static Dirlist rootlist = +{ + 0, + rootdir, + rootdata, + 2, + Nrootfiles +}; + +static Dirtab bootdir[Nbootfiles] = { + "boot", {Qboot, 0, QTDIR}, 0, DMDIR|0555, +}; +static uchar *bootdata[Nbootfiles]; +static Dirlist bootlist = +{ + Qboot, + bootdir, + bootdata, + 1, + Nbootfiles +}; + +/* + * add a file to the list + */ +static void +addlist(Dirlist *l, char *name, uchar *contents, ulong len, int perm) +{ + Dirtab *d; + + if(l->ndir >= l->mdir) + panic("too many root files"); + l->data[l->ndir] = contents; + d = &l->dir[l->ndir]; + strcpy(d->name, name); + d->length = len; + d->perm = perm; + d->qid.type = 0; + d->qid.vers = 0; + d->qid.path = ++l->ndir + l->base; + if(perm & DMDIR) + d->qid.type |= QTDIR; +} + +/* + * add a root file + */ +void +addbootfile(char *name, uchar *contents, ulong len) +{ + addlist(&bootlist, name, contents, len, 0555); +} + +/* + * add a root directory + */ +static void +addrootdir(char *name) +{ + addlist(&rootlist, name, nil, 0, DMDIR|0555); +} + +static void +rootreset(void) +{ + addrootdir("bin"); + addrootdir("dev"); + addrootdir("env"); + addrootdir("fd"); + addrootdir("mnt"); + addrootdir("net"); + addrootdir("net.alt"); + addrootdir("proc"); + addrootdir("root"); + addrootdir("srv"); +} + +static Chan* +rootattach(char *spec) +{ + return devattach('/', spec); +} + +static int +rootgen(Chan *c, char *name, Dirtab *dirt, int ndirt, int s, Dir *dp) +{ + int t; + Dirtab *d; + Dirlist *l; + + USED(dirt); + USED(ndirt); + + switch((int)c->qid.path){ + case Qdir: + if(s == DEVDOTDOT){ + Qid tqiddir = {Qdir, 0, QTDIR}; + devdir(c, tqiddir, "#/", 0, eve, 0555, dp); + return 1; + } + return devgen(c, name, rootlist.dir, rootlist.ndir, s, dp); + case Qboot: + if(s == DEVDOTDOT){ + Qid tqiddir = {Qdir, 0, QTDIR}; + devdir(c, tqiddir, "#/", 0, eve, 0555, dp); + return 1; + } + return devgen(c, name, bootlist.dir, bootlist.ndir, s, dp); + default: + if(s == DEVDOTDOT){ + Qid tqiddir = {Qdir, 0, QTDIR}; + if((int)c->qid.path < Qboot) + devdir(c, tqiddir, "#/", 0, eve, 0555, dp); + else { + tqiddir.path = Qboot; + devdir(c, tqiddir, "#/", 0, eve, 0555, dp); + } + return 1; + } + if(s != 0) + return -1; + if((int)c->qid.path < Qboot){ + t = c->qid.path-1; + l = &rootlist; + }else{ + t = c->qid.path - Qboot - 1; + l = &bootlist; + } + if(t >= l->ndir) + return -1; +if(t < 0){ +print("rootgen %llud %d %d\n", c->qid.path, s, t); +panic("whoops"); +} + d = &l->dir[t]; + devdir(c, d->qid, d->name, d->length, eve, d->perm, dp); + return 1; + } + return -1; +} + +static Walkqid* +rootwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, rootgen); +} + +static int +rootstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, nil, 0, rootgen); +} + +static Chan* +rootopen(Chan *c, int omode) +{ + return devopen(c, omode, nil, 0, devgen); +} + +/* + * sysremove() knows this is a nop + */ +static void +rootclose(Chan *c) +{ + USED(c); +} + +static long +rootread(Chan *c, void *buf, long n, vlong off) +{ + ulong t; + Dirtab *d; + Dirlist *l; + uchar *data; + ulong offset = off; + + t = c->qid.path; + switch(t){ + case Qdir: + case Qboot: + return devdirread(c, buf, n, nil, 0, rootgen); + } + + if(t= l->ndir) + error(Egreg); + + d = &l->dir[t]; + data = l->data[t]; + if(offset >= d->length) + return 0; + if(offset+n > d->length) + n = d->length - offset; + memmove(buf, data+offset, n); + return n; +} + +static long +rootwrite(Chan *c, void *v, long n, vlong o) +{ + USED(c); + USED(v); + USED(n); + USED(o); + + error(Egreg); + return 0; +} + +Dev rootdevtab = { + '/', + "root", + + rootreset, + devinit, + devshutdown, + rootattach, + rootwalk, + rootstat, + rootopen, + devcreate, + rootclose, + rootread, + devbread, + rootwrite, + devbwrite, + devremove, + devwstat, +}; + diff --git a/kern/devssl.c b/kern/devssl.c new file mode 100644 index 0000000..d9d9bac --- /dev/null +++ b/kern/devssl.c @@ -0,0 +1,1512 @@ +/* + * devssl - secure sockets layer + */ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "libsec.h" + +#define NOSPOOKS 1 + +typedef struct OneWay OneWay; +struct OneWay +{ + QLock q; + QLock ctlq; + + void *state; /* encryption state */ + int slen; /* hash data length */ + uchar *secret; /* secret */ + ulong mid; /* message id */ +}; + +enum +{ + /* connection states */ + Sincomplete= 0, + Sclear= 1, + Sencrypting= 2, + Sdigesting= 4, + Sdigenc= Sencrypting|Sdigesting, + + /* encryption algorithms */ + Noencryption= 0, + DESCBC= 1, + DESECB= 2, + RC4= 3 +}; + +typedef struct Dstate Dstate; +struct Dstate +{ + Chan *c; /* io channel */ + uchar state; /* state of connection */ + int ref; /* serialized by dslock for atomic destroy */ + + uchar encryptalg; /* encryption algorithm */ + ushort blocklen; /* blocking length */ + + ushort diglen; /* length of digest */ + DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); /* hash func */ + + /* for SSL format */ + int max; /* maximum unpadded data per msg */ + int maxpad; /* maximum padded data per msg */ + + /* input side */ + OneWay in; + Block *processed; + Block *unprocessed; + + /* output side */ + OneWay out; + + /* protections */ + char *user; + int perm; +}; + +enum +{ + Maxdmsg= 1<<16, + Maxdstate= 128, /* must be a power of 2 */ +}; + +Lock dslock; +int dshiwat; +char *dsname[Maxdstate]; +Dstate *dstate[Maxdstate]; +char *encalgs; +char *hashalgs; + +enum{ + Qtopdir = 1, /* top level directory */ + Qprotodir, + Qclonus, + Qconvdir, /* directory for a conversation */ + Qdata, + Qctl, + Qsecretin, + Qsecretout, + Qencalgs, + Qhashalgs, +}; + +#define TYPE(x) ((x).path & 0xf) +#define CONV(x) (((x).path >> 5)&(Maxdstate-1)) +#define QID(c, y) (((c)<<5) | (y)) + +static void ensure(Dstate*, Block**, int); +static void consume(Block**, uchar*, int); +static void setsecret(OneWay*, uchar*, int); +static Block* encryptb(Dstate*, Block*, int); +static Block* decryptb(Dstate*, Block*); +static Block* digestb(Dstate*, Block*, int); +static void checkdigestb(Dstate*, Block*); +static Chan* buftochan(char*); +static void sslhangup(Dstate*); +static Dstate* dsclone(Chan *c); +static void dsnew(Chan *c, Dstate **); +static long sslput(Dstate *s, Block * volatile b); + +char *sslnames[] = { + /* unused */ 0, + /* topdir */ 0, + /* protodir */ 0, + "clone", + /* convdir */ 0, + "data", + "ctl", + "secretin", + "secretout", + "encalgs", + "hashalgs", +}; + +static int +sslgen(Chan *c, char *n, Dirtab *d, int nd, int s, Dir *dp) +{ + Qid q; + Dstate *ds; + char name[16], *p, *nm; + int ft; + + USED(n); + USED(nd); + USED(d); + + q.type = QTFILE; + q.vers = 0; + + ft = TYPE(c->qid); + switch(ft) { + case Qtopdir: + if(s == DEVDOTDOT){ + q.path = QID(0, Qtopdir); + q.type = QTDIR; + devdir(c, q, "#D", 0, eve, 0555, dp); + return 1; + } + if(s > 0) + return -1; + q.path = QID(0, Qprotodir); + q.type = QTDIR; + devdir(c, q, "ssl", 0, eve, 0555, dp); + return 1; + case Qprotodir: + if(s == DEVDOTDOT){ + q.path = QID(0, Qtopdir); + q.type = QTDIR; + devdir(c, q, ".", 0, eve, 0555, dp); + return 1; + } + if(s < dshiwat) { + q.path = QID(s, Qconvdir); + q.type = QTDIR; + ds = dstate[s]; + if(ds != 0) + nm = ds->user; + else + nm = eve; + if(dsname[s] == nil){ + sprint(name, "%d", s); + kstrdup(&dsname[s], name); + } + devdir(c, q, dsname[s], 0, nm, 0555, dp); + return 1; + } + if(s > dshiwat) + return -1; + q.path = QID(0, Qclonus); + devdir(c, q, "clone", 0, eve, 0555, dp); + return 1; + case Qconvdir: + if(s == DEVDOTDOT){ + q.path = QID(0, Qprotodir); + q.type = QTDIR; + devdir(c, q, "ssl", 0, eve, 0555, dp); + return 1; + } + ds = dstate[CONV(c->qid)]; + if(ds != 0) + nm = ds->user; + else + nm = eve; + switch(s) { + default: + return -1; + case 0: + q.path = QID(CONV(c->qid), Qctl); + p = "ctl"; + break; + case 1: + q.path = QID(CONV(c->qid), Qdata); + p = "data"; + break; + case 2: + q.path = QID(CONV(c->qid), Qsecretin); + p = "secretin"; + break; + case 3: + q.path = QID(CONV(c->qid), Qsecretout); + p = "secretout"; + break; + case 4: + q.path = QID(CONV(c->qid), Qencalgs); + p = "encalgs"; + break; + case 5: + q.path = QID(CONV(c->qid), Qhashalgs); + p = "hashalgs"; + break; + } + devdir(c, q, p, 0, nm, 0660, dp); + return 1; + case Qclonus: + devdir(c, c->qid, sslnames[TYPE(c->qid)], 0, eve, 0555, dp); + return 1; + default: + ds = dstate[CONV(c->qid)]; + if(ds != 0) + nm = ds->user; + else + nm = eve; + devdir(c, c->qid, sslnames[TYPE(c->qid)], 0, nm, 0660, dp); + return 1; + } + return -1; +} + +static Chan* +sslattach(char *spec) +{ + Chan *c; + + c = devattach('D', spec); + c->qid.path = QID(0, Qtopdir); + c->qid.vers = 0; + c->qid.type = QTDIR; + return c; +} + +static Walkqid* +sslwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, sslgen); +} + +static int +sslstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, nil, 0, sslgen); +} + +static Chan* +sslopen(Chan *c, int omode) +{ + Dstate *s, **pp; + int perm; + int ft; + + perm = 0; + omode &= 3; + switch(omode) { + case OREAD: + perm = 4; + break; + case OWRITE: + perm = 2; + break; + case ORDWR: + perm = 6; + break; + } + + ft = TYPE(c->qid); + switch(ft) { + default: + panic("sslopen"); + case Qtopdir: + case Qprotodir: + case Qconvdir: + if(omode != OREAD) + error(Eperm); + break; + case Qclonus: + s = dsclone(c); + if(s == 0) + error(Enodev); + break; + case Qctl: + case Qdata: + case Qsecretin: + case Qsecretout: + if(waserror()) { + unlock(&dslock); + nexterror(); + } + lock(&dslock); + pp = &dstate[CONV(c->qid)]; + s = *pp; + if(s == 0) + dsnew(c, pp); + else { + if((perm & (s->perm>>6)) != perm + && (strcmp(up->user, s->user) != 0 + || (perm & s->perm) != perm)) + error(Eperm); + + s->ref++; + } + unlock(&dslock); + poperror(); + break; + case Qencalgs: + case Qhashalgs: + if(omode != OREAD) + error(Eperm); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static int +sslwstat(Chan *c, uchar *db, int n) +{ + Dir *dir; + Dstate *s; + int m; + + s = dstate[CONV(c->qid)]; + if(s == 0) + error(Ebadusefd); + if(strcmp(s->user, up->user) != 0) + error(Eperm); + + dir = smalloc(sizeof(Dir)+n); + m = convM2D(db, n, &dir[0], (char*)&dir[1]); + if(m == 0){ + free(dir); + error(Eshortstat); + } + + if(!emptystr(dir->uid)) + kstrdup(&s->user, dir->uid); + if(dir->mode != ~0UL) + s->perm = dir->mode; + + free(dir); + return m; +} + +static void +sslclose(Chan *c) +{ + Dstate *s; + int ft; + + ft = TYPE(c->qid); + switch(ft) { + case Qctl: + case Qdata: + case Qsecretin: + case Qsecretout: + if((c->flag & COPEN) == 0) + break; + + s = dstate[CONV(c->qid)]; + if(s == 0) + break; + + lock(&dslock); + if(--s->ref > 0) { + unlock(&dslock); + break; + } + dstate[CONV(c->qid)] = 0; + unlock(&dslock); + + if(s->user != nil) + free(s->user); + sslhangup(s); + if(s->c) + cclose(s->c); + if(s->in.secret) + free(s->in.secret); + if(s->out.secret) + free(s->out.secret); + if(s->in.state) + free(s->in.state); + if(s->out.state) + free(s->out.state); + free(s); + + } +} + +/* + * make sure we have at least 'n' bytes in list 'l' + */ +static void +ensure(Dstate *s, Block **l, int n) +{ + int sofar, i; + Block *b, *bl; + + sofar = 0; + for(b = *l; b; b = b->next){ + sofar += BLEN(b); + if(sofar >= n) + return; + l = &b->next; + } + + while(sofar < n){ + bl = devtab[s->c->type]->bread(s->c, Maxdmsg, 0); + if(bl == 0) + nexterror(); + *l = bl; + i = 0; + for(b = bl; b; b = b->next){ + i += BLEN(b); + l = &b->next; + } + if(i == 0) + error(Ehungup); + sofar += i; + } +} + +/* + * copy 'n' bytes from 'l' into 'p' and free + * the bytes in 'l' + */ +static void +consume(Block **l, uchar *p, int n) +{ + Block *b; + int i; + + for(; *l && n > 0; n -= i){ + b = *l; + i = BLEN(b); + if(i > n) + i = n; + memmove(p, b->rp, i); + b->rp += i; + p += i; + if(BLEN(b) < 0) + panic("consume"); + if(BLEN(b)) + break; + *l = b->next; + freeb(b); + } +} + +/* + * give back n bytes +static void +regurgitate(Dstate *s, uchar *p, int n) +{ + Block *b; + + if(n <= 0) + return; + b = s->unprocessed; + if(s->unprocessed == nil || b->rp - b->base < n) { + b = allocb(n); + memmove(b->wp, p, n); + b->wp += n; + b->next = s->unprocessed; + s->unprocessed = b; + } else { + b->rp -= n; + memmove(b->rp, p, n); + } +} + */ + +/* + * remove at most n bytes from the queue, if discard is set + * dump the remainder + */ +static Block* +qtake(Block **l, int n, int discard) +{ + Block *nb, *b, *first; + int i; + + first = *l; + for(b = first; b; b = b->next){ + i = BLEN(b); + if(i == n){ + if(discard){ + freeblist(b->next); + *l = 0; + } else + *l = b->next; + b->next = 0; + return first; + } else if(i > n){ + i -= n; + if(discard){ + freeblist(b->next); + b->wp -= i; + *l = 0; + } else { + nb = allocb(i); + memmove(nb->wp, b->rp+n, i); + nb->wp += i; + b->wp -= i; + nb->next = b->next; + *l = nb; + } + b->next = 0; + if(BLEN(b) < 0) + panic("qtake"); + return first; + } else + n -= i; + if(BLEN(b) < 0) + panic("qtake"); + } + *l = 0; + return first; +} + +/* + * We can't let Eintr's lose data since the program + * doing the read may be able to handle it. The only + * places Eintr is possible is during the read's in consume. + * Therefore, we make sure we can always put back the bytes + * consumed before the last ensure. + */ +static Block* +sslbread(Chan *c, long n, ulong o) +{ + Dstate * volatile s; + Block *b; + uchar consumed[3], *p; + int toconsume; + int len, pad; + + USED(o); + s = dstate[CONV(c->qid)]; + if(s == 0) + panic("sslbread"); + if(s->state == Sincomplete) + error(Ebadusefd); + + qlock(&s->in.q); + if(waserror()){ + qunlock(&s->in.q); + nexterror(); + } + + if(s->processed == 0){ + /* + * Read in the whole message. Until we've got it all, + * it stays on s->unprocessed, so that if we get Eintr, + * we'll pick up where we left off. + */ + ensure(s, &s->unprocessed, 3); + s->unprocessed = pullupblock(s->unprocessed, 2); + p = s->unprocessed->rp; + if(p[0] & 0x80){ + len = ((p[0] & 0x7f)<<8) | p[1]; + ensure(s, &s->unprocessed, len); + pad = 0; + toconsume = 2; + } else { + s->unprocessed = pullupblock(s->unprocessed, 3); + len = ((p[0] & 0x3f)<<8) | p[1]; + pad = p[2]; + if(pad > len){ + print("pad %d buf len %d\n", pad, len); + error("bad pad in ssl message"); + } + toconsume = 3; + } + ensure(s, &s->unprocessed, toconsume+len); + + /* skip header */ + consume(&s->unprocessed, consumed, toconsume); + + /* grab the next message and decode/decrypt it */ + b = qtake(&s->unprocessed, len, 0); + + if(blocklen(b) != len) + print("devssl: sslbread got wrong count %d != %d", blocklen(b), len); + + if(waserror()){ + qunlock(&s->in.ctlq); + if(b != nil) + freeb(b); + nexterror(); + } + qlock(&s->in.ctlq); + switch(s->state){ + case Sencrypting: + if(b == nil) + error("ssl message too short (encrypting)"); + b = decryptb(s, b); + break; + case Sdigesting: + b = pullupblock(b, s->diglen); + if(b == nil) + error("ssl message too short (digesting)"); + checkdigestb(s, b); + b->rp += s->diglen; + break; + case Sdigenc: + b = decryptb(s, b); + b = pullupblock(b, s->diglen); + if(b == nil) + error("ssl message too short (dig+enc)"); + checkdigestb(s, b); + b->rp += s->diglen; + len -= s->diglen; + break; + } + + /* remove pad */ + if(pad) + s->processed = qtake(&b, len - pad, 1); + else + s->processed = b; + b = nil; + s->in.mid++; + qunlock(&s->in.ctlq); + poperror(); + } + + /* return at most what was asked for */ + b = qtake(&s->processed, n, 0); + + qunlock(&s->in.q); + poperror(); + + return b; +} + +static long +sslread(Chan *c, void *a, long n, vlong off) +{ + Block * volatile b; + Block *nb; + uchar *va; + int i; + char buf[128]; + ulong offset = off; + int ft; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, sslgen); + + ft = TYPE(c->qid); + switch(ft) { + default: + error(Ebadusefd); + case Qctl: + ft = CONV(c->qid); + sprint(buf, "%d", ft); + return readstr(offset, a, n, buf); + case Qdata: + b = sslbread(c, n, offset); + break; + case Qencalgs: + return readstr(offset, a, n, encalgs); + break; + case Qhashalgs: + return readstr(offset, a, n, hashalgs); + break; + } + + if(waserror()){ + freeblist(b); + nexterror(); + } + + n = 0; + va = a; + for(nb = b; nb; nb = nb->next){ + i = BLEN(nb); + memmove(va+n, nb->rp, i); + n += i; + } + + freeblist(b); + poperror(); + + return n; +} + +/* + * this algorithm doesn't have to be great since we're just + * trying to obscure the block fill + */ +static void +randfill(uchar *buf, int len) +{ + while(len-- > 0) + *buf++ = nrand(256); +} + +static long +sslbwrite(Chan *c, Block *b, ulong o) +{ + Dstate * volatile s; + long rv; + + USED(o); + s = dstate[CONV(c->qid)]; + if(s == nil) + panic("sslbwrite"); + + if(s->state == Sincomplete){ + freeb(b); + error(Ebadusefd); + } + + /* lock so split writes won't interleave */ + if(waserror()){ + qunlock(&s->out.q); + nexterror(); + } + qlock(&s->out.q); + + rv = sslput(s, b); + + poperror(); + qunlock(&s->out.q); + + return rv; +} + +/* + * use SSL record format, add in count, digest and/or encrypt. + * the write is interruptable. if it is interrupted, we'll + * get out of sync with the far side. not much we can do about + * it since we don't know if any bytes have been written. + */ +static long +sslput(Dstate *s, Block * volatile b) +{ + Block *nb; + int h, n, m, pad, rv; + uchar *p; + int offset; + + if(waserror()){ + if(b != nil) + free(b); + nexterror(); + } + + rv = 0; + while(b != nil){ + m = n = BLEN(b); + h = s->diglen + 2; + + /* trim to maximum block size */ + pad = 0; + if(m > s->max){ + m = s->max; + } else if(s->blocklen != 1){ + pad = (m + s->diglen)%s->blocklen; + if(pad){ + if(m > s->maxpad){ + pad = 0; + m = s->maxpad; + } else { + pad = s->blocklen - pad; + h++; + } + } + } + + rv += m; + if(m != n){ + nb = allocb(m + h + pad); + memmove(nb->wp + h, b->rp, m); + nb->wp += m + h; + b->rp += m; + } else { + /* add header space */ + nb = padblock(b, h); + b = 0; + } + m += s->diglen; + + /* SSL style count */ + if(pad){ + nb = padblock(nb, -pad); + randfill(nb->wp, pad); + nb->wp += pad; + m += pad; + + p = nb->rp; + p[0] = (m>>8); + p[1] = m; + p[2] = pad; + offset = 3; + } else { + p = nb->rp; + p[0] = (m>>8) | 0x80; + p[1] = m; + offset = 2; + } + + switch(s->state){ + case Sencrypting: + nb = encryptb(s, nb, offset); + break; + case Sdigesting: + nb = digestb(s, nb, offset); + break; + case Sdigenc: + nb = digestb(s, nb, offset); + nb = encryptb(s, nb, offset); + break; + } + + s->out.mid++; + + m = BLEN(nb); + devtab[s->c->type]->bwrite(s->c, nb, s->c->offset); + s->c->offset += m; + } + + poperror(); + return rv; +} + +static void +setsecret(OneWay *w, uchar *secret, int n) +{ + if(w->secret) + free(w->secret); + + w->secret = smalloc(n); + memmove(w->secret, secret, n); + w->slen = n; +} + +static void +initDESkey(OneWay *w) +{ + if(w->state){ + free(w->state); + w->state = 0; + } + + w->state = smalloc(sizeof(DESstate)); + if(w->slen >= 16) + setupDESstate(w->state, w->secret, w->secret+8); + else if(w->slen >= 8) + setupDESstate(w->state, w->secret, 0); + else + error("secret too short"); +} + +/* + * 40 bit DES is the same as 56 bit DES. However, + * 16 bits of the key are masked to zero. + */ +static void +initDESkey_40(OneWay *w) +{ + uchar key[8]; + + if(w->state){ + free(w->state); + w->state = 0; + } + + if(w->slen >= 8){ + memmove(key, w->secret, 8); + key[0] &= 0x0f; + key[2] &= 0x0f; + key[4] &= 0x0f; + key[6] &= 0x0f; + } + + w->state = malloc(sizeof(DESstate)); + if(w->slen >= 16) + setupDESstate(w->state, key, w->secret+8); + else if(w->slen >= 8) + setupDESstate(w->state, key, 0); + else + error("secret too short"); +} + +static void +initRC4key(OneWay *w) +{ + if(w->state){ + free(w->state); + w->state = 0; + } + + w->state = smalloc(sizeof(RC4state)); + setupRC4state(w->state, w->secret, w->slen); +} + +/* + * 40 bit RC4 is the same as n-bit RC4. However, + * we ignore all but the first 40 bits of the key. + */ +static void +initRC4key_40(OneWay *w) +{ + if(w->state){ + free(w->state); + w->state = 0; + } + + if(w->slen > 5) + w->slen = 5; + + w->state = malloc(sizeof(RC4state)); + setupRC4state(w->state, w->secret, w->slen); +} + +/* + * 128 bit RC4 is the same as n-bit RC4. However, + * we ignore all but the first 128 bits of the key. + */ +static void +initRC4key_128(OneWay *w) +{ + if(w->state){ + free(w->state); + w->state = 0; + } + + if(w->slen > 16) + w->slen = 16; + + w->state = malloc(sizeof(RC4state)); + setupRC4state(w->state, w->secret, w->slen); +} + + +typedef struct Hashalg Hashalg; +struct Hashalg +{ + char *name; + int diglen; + DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); +}; + +Hashalg hashtab[] = +{ + { "md4", MD4dlen, md4, }, + { "md5", MD5dlen, md5, }, + { "sha1", SHA1dlen, sha1, }, + { "sha", SHA1dlen, sha1, }, + { 0 } +}; + +static int +parsehashalg(char *p, Dstate *s) +{ + Hashalg *ha; + + for(ha = hashtab; ha->name; ha++){ + if(strcmp(p, ha->name) == 0){ + s->hf = ha->hf; + s->diglen = ha->diglen; + s->state &= ~Sclear; + s->state |= Sdigesting; + return 0; + } + } + return -1; +} + +typedef struct Encalg Encalg; +struct Encalg +{ + char *name; + int blocklen; + int alg; + void (*keyinit)(OneWay*); +}; + +#ifdef NOSPOOKS +Encalg encrypttab[] = +{ + { "descbc", 8, DESCBC, initDESkey, }, /* DEPRECATED -- use des_56_cbc */ + { "desecb", 8, DESECB, initDESkey, }, /* DEPRECATED -- use des_56_ecb */ + { "des_56_cbc", 8, DESCBC, initDESkey, }, + { "des_56_ecb", 8, DESECB, initDESkey, }, + { "des_40_cbc", 8, DESCBC, initDESkey_40, }, + { "des_40_ecb", 8, DESECB, initDESkey_40, }, + { "rc4", 1, RC4, initRC4key_40, }, /* DEPRECATED -- use rc4_X */ + { "rc4_256", 1, RC4, initRC4key, }, + { "rc4_128", 1, RC4, initRC4key_128, }, + { "rc4_40", 1, RC4, initRC4key_40, }, + { 0 } +}; +#else +Encalg encrypttab[] = +{ + { "des_40_cbc", 8, DESCBC, initDESkey_40, }, + { "des_40_ecb", 8, DESECB, initDESkey_40, }, + { "rc4", 1, RC4, initRC4key_40, }, /* DEPRECATED -- use rc4_X */ + { "rc4_40", 1, RC4, initRC4key_40, }, + { 0 } +}; +#endif NOSPOOKS + +static int +parseencryptalg(char *p, Dstate *s) +{ + Encalg *ea; + + for(ea = encrypttab; ea->name; ea++){ + if(strcmp(p, ea->name) == 0){ + s->encryptalg = ea->alg; + s->blocklen = ea->blocklen; + (*ea->keyinit)(&s->in); + (*ea->keyinit)(&s->out); + s->state &= ~Sclear; + s->state |= Sencrypting; + return 0; + } + } + return -1; +} + +static long +sslwrite(Chan *c, void *a, long n, vlong o) +{ + Dstate * volatile s; + Block * volatile b; + int m, t; + char *p, *np, *e, buf[128]; + uchar *x; + + USED(o); + s = dstate[CONV(c->qid)]; + if(s == 0) + panic("sslwrite"); + + t = TYPE(c->qid); + if(t == Qdata){ + if(s->state == Sincomplete) + error(Ebadusefd); + + /* lock should a write gets split over multiple records */ + if(waserror()){ + qunlock(&s->out.q); + nexterror(); + } + qlock(&s->out.q); + + p = a; + e = p + n; + do { + m = e - p; + if(m > s->max) + m = s->max; + + b = allocb(m); + if(waserror()){ + freeb(b); + nexterror(); + } + memmove(b->wp, p, m); + poperror(); + b->wp += m; + + sslput(s, b); + + p += m; + } while(p < e); + + poperror(); + qunlock(&s->out.q); + return n; + } + + /* mutex with operations using what we're about to change */ + if(waserror()){ + qunlock(&s->in.ctlq); + qunlock(&s->out.q); + nexterror(); + } + qlock(&s->in.ctlq); + qlock(&s->out.q); + + switch(t){ + default: + panic("sslwrite"); + case Qsecretin: + setsecret(&s->in, a, n); + goto out; + case Qsecretout: + setsecret(&s->out, a, n); + goto out; + case Qctl: + break; + } + + if(n >= sizeof(buf)) + error("arg too long"); + strncpy(buf, a, n); + buf[n] = 0; + p = strchr(buf, '\n'); + if(p) + *p = 0; + p = strchr(buf, ' '); + if(p) + *p++ = 0; + + if(strcmp(buf, "fd") == 0){ + s->c = buftochan(p); + + /* default is clear (msg delimiters only) */ + s->state = Sclear; + s->blocklen = 1; + s->diglen = 0; + s->maxpad = s->max = (1<<15) - s->diglen - 1; + s->in.mid = 0; + s->out.mid = 0; + } else if(strcmp(buf, "alg") == 0 && p != 0){ + s->blocklen = 1; + s->diglen = 0; + + if(s->c == 0) + error("must set fd before algorithm"); + + s->state = Sclear; + s->maxpad = s->max = (1<<15) - s->diglen - 1; + if(strcmp(p, "clear") == 0){ + goto out; + } + + if(s->in.secret && s->out.secret == 0) + setsecret(&s->out, s->in.secret, s->in.slen); + if(s->out.secret && s->in.secret == 0) + setsecret(&s->in, s->out.secret, s->out.slen); + if(s->in.secret == 0 || s->out.secret == 0) + error("algorithm but no secret"); + + s->hf = 0; + s->encryptalg = Noencryption; + s->blocklen = 1; + + for(;;){ + np = strchr(p, ' '); + if(np) + *np++ = 0; + + if(parsehashalg(p, s) < 0) + if(parseencryptalg(p, s) < 0) + error("bad algorithm"); + + if(np == 0) + break; + p = np; + } + + if(s->hf == 0 && s->encryptalg == Noencryption) + error("bad algorithm"); + + if(s->blocklen != 1){ + s->max = (1<<15) - s->diglen - 1; + s->max -= s->max % s->blocklen; + s->maxpad = (1<<14) - s->diglen - 1; + s->maxpad -= s->maxpad % s->blocklen; + } else + s->maxpad = s->max = (1<<15) - s->diglen - 1; + } else if(strcmp(buf, "secretin") == 0 && p != 0) { + m = (strlen(p)*3)/2; + x = smalloc(m); + t = dec64(x, m, p, strlen(p)); + setsecret(&s->in, x, t); + free(x); + } else if(strcmp(buf, "secretout") == 0 && p != 0) { + m = (strlen(p)*3)/2 + 1; + x = smalloc(m); + t = dec64(x, m, p, strlen(p)); + setsecret(&s->out, x, t); + free(x); + } else + error(Ebadarg); + +out: + qunlock(&s->in.ctlq); + qunlock(&s->out.q); + poperror(); + return n; +} + +static void +sslinit(void) +{ + struct Encalg *e; + struct Hashalg *h; + int n; + char *cp; + + n = 1; + for(e = encrypttab; e->name != nil; e++) + n += strlen(e->name) + 1; + cp = encalgs = smalloc(n); + for(e = encrypttab;;){ + strcpy(cp, e->name); + cp += strlen(e->name); + e++; + if(e->name == nil) + break; + *cp++ = ' '; + } + *cp = 0; + + n = 1; + for(h = hashtab; h->name != nil; h++) + n += strlen(h->name) + 1; + cp = hashalgs = smalloc(n); + for(h = hashtab;;){ + strcpy(cp, h->name); + cp += strlen(h->name); + h++; + if(h->name == nil) + break; + *cp++ = ' '; + } + *cp = 0; +} + +Dev ssldevtab = { + 'D', + "ssl", + + devreset, + sslinit, + devshutdown, + sslattach, + sslwalk, + sslstat, + sslopen, + devcreate, + sslclose, + sslread, + sslbread, + sslwrite, + sslbwrite, + devremove, + sslwstat, +}; + +static Block* +encryptb(Dstate *s, Block *b, int offset) +{ + uchar *p, *ep, *p2, *ip, *eip; + DESstate *ds; + + switch(s->encryptalg){ + case DESECB: + ds = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8) + block_cipher(ds->expanded, p, 0); + break; + case DESCBC: + ds = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8){ + p2 = p; + ip = ds->ivec; + for(eip = ip+8; ip < eip; ) + *p2++ ^= *ip++; + block_cipher(ds->expanded, p, 0); + memmove(ds->ivec, p, 8); + } + break; + case RC4: + rc4(s->out.state, b->rp + offset, BLEN(b) - offset); + break; + } + return b; +} + +static Block* +decryptb(Dstate *s, Block *bin) +{ + Block *b, **l; + uchar *p, *ep, *tp, *ip, *eip; + DESstate *ds; + uchar tmp[8]; + int i; + + l = &bin; + for(b = bin; b; b = b->next){ + /* make sure we have a multiple of s->blocklen */ + if(s->blocklen > 1){ + i = BLEN(b); + if(i % s->blocklen){ + *l = b = pullupblock(b, i + s->blocklen - (i%s->blocklen)); + if(b == 0) + error("ssl encrypted message too short"); + } + } + l = &b->next; + + /* decrypt */ + switch(s->encryptalg){ + case DESECB: + ds = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep; p += 8) + block_cipher(ds->expanded, p, 1); + break; + case DESCBC: + ds = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep;){ + memmove(tmp, p, 8); + block_cipher(ds->expanded, p, 1); + tp = tmp; + ip = ds->ivec; + for(eip = ip+8; ip < eip; ){ + *p++ ^= *ip; + *ip++ = *tp++; + } + } + break; + case RC4: + rc4(s->in.state, b->rp, BLEN(b)); + break; + } + } + return bin; +} + +static Block* +digestb(Dstate *s, Block *b, int offset) +{ + uchar *p; + DigestState ss; + uchar msgid[4]; + ulong n, h; + OneWay *w; + + w = &s->out; + + memset(&ss, 0, sizeof(ss)); + h = s->diglen + offset; + n = BLEN(b) - h; + + /* hash secret + message */ + (*s->hf)(w->secret, w->slen, 0, &ss); + (*s->hf)(b->rp + h, n, 0, &ss); + + /* hash message id */ + p = msgid; + n = w->mid; + *p++ = n>>24; + *p++ = n>>16; + *p++ = n>>8; + *p = n; + (*s->hf)(msgid, 4, b->rp + offset, &ss); + + return b; +} + +static void +checkdigestb(Dstate *s, Block *bin) +{ + uchar *p; + DigestState ss; + uchar msgid[4]; + int n, h; + OneWay *w; + uchar digest[128]; + Block *b; + + w = &s->in; + + memset(&ss, 0, sizeof(ss)); + + /* hash secret */ + (*s->hf)(w->secret, w->slen, 0, &ss); + + /* hash message */ + h = s->diglen; + for(b = bin; b; b = b->next){ + n = BLEN(b) - h; + if(n < 0) + panic("checkdigestb"); + (*s->hf)(b->rp + h, n, 0, &ss); + h = 0; + } + + /* hash message id */ + p = msgid; + n = w->mid; + *p++ = n>>24; + *p++ = n>>16; + *p++ = n>>8; + *p = n; + (*s->hf)(msgid, 4, digest, &ss); + + if(memcmp(digest, bin->rp, s->diglen) != 0) + error("bad digest"); +} + +/* get channel associated with an fd */ +static Chan* +buftochan(char *p) +{ + Chan *c; + int fd; + + if(p == 0) + error(Ebadarg); + fd = strtoul(p, 0, 0); + if(fd < 0) + error(Ebadarg); + c = fdtochan(fd, -1, 0, 1); /* error check and inc ref */ + if(devtab[c->type] == &ssldevtab){ + cclose(c); + error("cannot ssl encrypt devssl files"); + } + return c; +} + +/* hand up a digest connection */ +static void +sslhangup(Dstate *s) +{ + Block *b; + + qlock(&s->in.q); + for(b = s->processed; b; b = s->processed){ + s->processed = b->next; + freeb(b); + } + if(s->unprocessed){ + freeb(s->unprocessed); + s->unprocessed = 0; + } + s->state = Sincomplete; + qunlock(&s->in.q); +} + +static Dstate* +dsclone(Chan *ch) +{ + int i; + Dstate *ret; + + if(waserror()) { + unlock(&dslock); + nexterror(); + } + lock(&dslock); + ret = nil; + for(i=0; i= dshiwat) + dshiwat++; + memset(s, 0, sizeof(*s)); + s->state = Sincomplete; + s->ref = 1; + kstrdup(&s->user, up->user); + s->perm = 0660; + t = TYPE(ch->qid); + if(t == Qclonus) + t = Qctl; + ch->qid.path = QID(pp - dstate, t); + ch->qid.vers = 0; +} diff --git a/kern/devtab.c b/kern/devtab.c new file mode 100644 index 0000000..b371d26 --- /dev/null +++ b/kern/devtab.c @@ -0,0 +1,27 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +extern Dev consdevtab; +extern Dev rootdevtab; +extern Dev pipedevtab; +extern Dev ssldevtab; +extern Dev mousedevtab; +extern Dev drawdevtab; +extern Dev ipdevtab; +extern Dev fsdevtab; + +Dev *devtab[] = { + &rootdevtab, + &consdevtab, + &pipedevtab, + &ssldevtab, + &mousedevtab, + &drawdevtab, + &ipdevtab, + &fsdevtab, + 0 +}; + diff --git a/kern/error.c b/kern/error.c new file mode 100644 index 0000000..465de47 --- /dev/null +++ b/kern/error.c @@ -0,0 +1,50 @@ +char Enoerror[] = "no error"; +char Emount[] = "inconsistent mount"; +char Eunmount[] = "not mounted"; +char Eunion[] = "not in union"; +char Emountrpc[] = "mount rpc error"; +char Eshutdown[] = "device shut down"; +char Enocreate[] = "mounted directory forbids creation"; +char Enonexist[] = "file does not exist"; +char Eexist[] = "file already exists"; +char Ebadsharp[] = "unknown device in # filename"; +char Enotdir[] = "not a directory"; +char Eisdir[] = "file is a directory"; +char Ebadchar[] = "bad character in file name"; +char Efilename[] = "file name syntax"; +char Eperm[] = "permission denied"; +char Ebadusefd[] = "inappropriate use of fd"; +char Ebadarg[] = "bad arg in system call"; +char Einuse[] = "device or object already in use"; +char Eio[] = "i/o error"; +char Etoobig[] = "read or write too large"; +char Etoosmall[] = "read or write too small"; +char Enoport[] = "network port not available"; +char Ehungup[] = "i/o on hungup channel"; +char Ebadctl[] = "bad process or channel control request"; +char Enodev[] = "no free devices"; +char Eprocdied[] = "process exited"; +char Enochild[] = "no living children"; +char Eioload[] = "i/o error in demand load"; +char Enovmem[] = "virtual memory allocation failed"; +char Ebadfd[] = "fd out of range or not open"; +char Enofd[] = "no free file descriptors"; +char Eisstream[] = "seek on a stream"; +char Ebadexec[] = "exec header invalid"; +char Etimedout[] = "connection timed out"; +char Econrefused[] = "connection refused"; +char Econinuse[] = "connection in use"; +char Eintr[] = "interrupted"; +char Enomem[] = "kernel allocate failed"; +char Enoswap[] = "swap space full"; +char Esoverlap[] = "segments overlap"; +char Emouseset[] = "mouse type already set"; +char Eshort[] = "i/o count too small"; +char Egreg[] = "ken has left the building"; +char Ebadspec[] = "bad attach specifier"; +char Enoreg[] = "process has no saved registers"; +char Enoattach[] = "mount/attach disallowed"; +char Eshortstat[] = "stat buffer too small"; +char Ebadstat[] = "malformed stat buffer"; +char Enegoff[] = "negative i/o offset"; +char Ecmdargs[] = "wrong #args in control message"; diff --git a/kern/error.h b/kern/error.h new file mode 100644 index 0000000..6bb8762 --- /dev/null +++ b/kern/error.h @@ -0,0 +1,50 @@ +extern char Enoerror[]; /* no error */ +extern char Emount[]; /* inconsistent mount */ +extern char Eunmount[]; /* not mounted */ +extern char Eunion[]; /* not in union */ +extern char Emountrpc[]; /* mount rpc error */ +extern char Eshutdown[]; /* device shut down */ +extern char Enocreate[]; /* mounted directory forbids creation */ +extern char Enonexist[]; /* file does not exist */ +extern char Eexist[]; /* file already exists */ +extern char Ebadsharp[]; /* unknown device in # filename */ +extern char Enotdir[]; /* not a directory */ +extern char Eisdir[]; /* file is a directory */ +extern char Ebadchar[]; /* bad character in file name */ +extern char Efilename[]; /* file name syntax */ +extern char Eperm[]; /* permission denied */ +extern char Ebadusefd[]; /* inappropriate use of fd */ +extern char Ebadarg[]; /* bad arg in system call */ +extern char Einuse[]; /* device or object already in use */ +extern char Eio[]; /* i/o error */ +extern char Etoobig[]; /* read or write too large */ +extern char Etoosmall[]; /* read or write too small */ +extern char Enoport[]; /* network port not available */ +extern char Ehungup[]; /* i/o on hungup channel */ +extern char Ebadctl[]; /* bad process or channel control request */ +extern char Enodev[]; /* no free devices */ +extern char Eprocdied[]; /* process exited */ +extern char Enochild[]; /* no living children */ +extern char Eioload[]; /* i/o error in demand load */ +extern char Enovmem[]; /* virtual memory allocation failed */ +extern char Ebadfd[]; /* fd out of range or not open */ +extern char Enofd[]; /* no free file descriptors */ +extern char Eisstream[]; /* seek on a stream */ +extern char Ebadexec[]; /* exec header invalid */ +extern char Etimedout[]; /* connection timed out */ +extern char Econrefused[]; /* connection refused */ +extern char Econinuse[]; /* connection in use */ +extern char Eintr[]; /* interrupted */ +extern char Enomem[]; /* kernel allocate failed */ +extern char Enoswap[]; /* swap space full */ +extern char Esoverlap[]; /* segments overlap */ +extern char Emouseset[]; /* mouse type already set */ +extern char Eshort[]; /* i/o count too small */ +extern char Egreg[]; /* ken has left the building */ +extern char Ebadspec[]; /* bad attach specifier */ +extern char Enoreg[]; /* process has no saved registers */ +extern char Enoattach[]; /* mount/attach disallowed */ +extern char Eshortstat[]; /* stat buffer too small */ +extern char Ebadstat[]; /* malformed stat buffer */ +extern char Enegoff[]; /* negative i/o offset */ +extern char Ecmdargs[]; /* wrong #args in control message */ diff --git a/kern/exportfs.c b/kern/exportfs.c new file mode 100644 index 0000000..46cb90d --- /dev/null +++ b/kern/exportfs.c @@ -0,0 +1,821 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +typedef struct Fid Fid; +typedef struct Export Export; +typedef struct Exq Exq; + +#define nil ((void*)0) + +enum +{ + Nfidhash = 1, + MAXRPC = MAXMSG+MAXFDATA, + MAXDIRREAD = (MAXFDATA/DIRLEN)*DIRLEN +}; + +struct Export +{ + Ref r; + Exq* work; + Lock fidlock; + Fid* fid[Nfidhash]; + Chan* root; + Chan* io; + Pgrp* pgrp; + int npart; + char part[MAXRPC]; +}; + +struct Fid +{ + Fid* next; + Fid** last; + Chan* chan; + long offset; + int fid; + int ref; /* fcalls using the fid; locked by Export.Lock */ + int attached; /* fid attached or cloned but not clunked */ +}; + +struct Exq +{ + Lock lk; + int nointr; + int noresponse; /* don't respond to this one */ + Exq* next; + int shut; /* has been noted for shutdown */ + Export* export; + void* slave; + Fcall rpc; + char buf[MAXRPC]; +}; + +struct +{ + Lock l; + Qlock qwait; + Rendez rwait; + Exq *head; /* work waiting for a slave */ + Exq *tail; +}exq; + +static void exshutdown(Export*); +static void exflush(Export*, int, int); +static void exslave(void*); +static void exfree(Export*); +static void exportproc(Export*); + +static char* Exauth(Export*, Fcall*); +static char* Exattach(Export*, Fcall*); +static char* Exclunk(Export*, Fcall*); +static char* Excreate(Export*, Fcall*); +static char* Exopen(Export*, Fcall*); +static char* Exread(Export*, Fcall*); +static char* Exremove(Export*, Fcall*); +static char* Exstat(Export*, Fcall*); +static char* Exwalk(Export*, Fcall*); +static char* Exwrite(Export*, Fcall*); +static char* Exwstat(Export*, Fcall*); +static char* Exversion(Export*, Fcall*); + +static char *(*fcalls[Tmax])(Export*, Fcall*); + +static char Enofid[] = "no such fid"; +static char Eseekdir[] = "can't seek on a directory"; +static char Ereaddir[] = "unaligned read of a directory"; +static int exdebug = 0; + +int +sysexport(int fd) +{ + Chan *c; + Export *fs; + + if(waserror()) + return -1; + + c = fdtochan(fd, ORDWR, 1, 1); + poperror(); + c->flag |= CMSG; + + fs = mallocz(sizeof(Export)); + fs->r.ref = 1; + fs->pgrp = up->pgrp; + refinc(&fs->pgrp->r); + refinc(&up->slash->r); + fs->root = up->slash; + refinc(&fs->root->r); + fs->root = domount(fs->root); + fs->io = c; + + exportproc(fs); + + return 0; +} + +static void +exportinit(void) +{ + lock(&exq.l); + if(fcalls[Tversion] != nil) { + unlock(&exq.l); + return; + } + + fmtinstall('F', fcallfmt); + fmtinstall('D', dirfmt); + fmtinstall('M', dirmodefmt); + fcalls[Tversion] = Exversion; + fcalls[Tauth] = Exauth; + fcalls[Tattach] = Exattach; + fcalls[Twalk] = Exwalk; + fcalls[Topen] = Exopen; + fcalls[Tcreate] = Excreate; + fcalls[Tread] = Exread; + fcalls[Twrite] = Exwrite; + fcalls[Tclunk] = Exclunk; + fcalls[Tremove] = Exremove; + fcalls[Tstat] = Exstat; + fcalls[Twstat] = Exwstat; + unlock(&exq.l); +} + +void +exportproc(Export *fs) +{ + Exq *q; + char *buf; + int n, cn, len; + + exportinit(); + + for(;;){ + q = mallocz(sizeof(Exq)); + if(q == 0) + panic("no memory"); + + q->rpc.data = q->buf + MAXMSG; + + buf = q->buf; + len = MAXRPC; + if(fs->npart) { + memmove(buf, fs->part, fs->npart); + buf += fs->npart; + len -= fs->npart; + goto chk; + } + for(;;) { + if(waserror()) + goto bad; + + n = (*devtab[fs->io->type].read)(fs->io, buf, len, 0); + poperror(); + + if(n <= 0) + goto bad; + + buf += n; + len -= n; + chk: + n = buf - q->buf; + + /* convM2S returns size of correctly decoded message */ + cn = convM2S(q->buf, &q->rpc, n); + if(cn < 0){ + iprint("bad message type in devmnt\n"); + goto bad; + } + if(cn > 0) { + n -= cn; + if(n < 0){ + iprint("negative size in devmnt"); + goto bad; + } + fs->npart = n; + if(n != 0) + memmove(fs->part, q->buf+cn, n); + break; + } + } + if(exdebug) + iprint("export %d <- %F\n", getpid(), &q->rpc); + + if(q->rpc.type == Tflush){ + exflush(fs, q->rpc.tag, q->rpc.oldtag); + free(q); + continue; + } + + q->export = fs; + refinc(&fs->r); + + lock(&exq.l); + if(exq.head == nil) + exq.head = q; + else + exq.tail->next = q; + q->next = nil; + exq.tail = q; + unlock(&exq.l); + if(exq.qwait.first == nil) { + n = thread("exportfs", exslave, nil); +/* iprint("launch export (pid=%ux)\n", n); */ + } + rendwakeup(&exq.rwait); + } +bad: + free(q); + exshutdown(fs); + exfree(fs); +} + +void +exflush(Export *fs, int flushtag, int tag) +{ + Exq *q, **last; + int n; + Fcall fc; + char buf[MAXMSG]; + + /* hasn't been started? */ + lock(&exq.l); + last = &exq.head; + for(q = exq.head; q != nil; q = q->next){ + if(q->export == fs && q->rpc.tag == tag){ + *last = q->next; + unlock(&exq.l); + exfree(fs); + free(q); + goto Respond; + } + last = &q->next; + } + unlock(&exq.l); + + /* in progress? */ + lock(&fs->r.l); + for(q = fs->work; q != nil; q = q->next){ + if(q->rpc.tag == tag && !q->noresponse){ + lock(&q->lk); + q->noresponse = 1; + if(!q->nointr) + intr(q->slave); + unlock(&q->lk); + unlock(&fs->r.l); + goto Respond; + return; + } + } + unlock(&fs->r.l); + +if(exdebug) iprint("exflush: did not find rpc: %d\n", tag); + +Respond: + fc.type = Rflush; + fc.tag = flushtag; + n = convS2M(&fc, buf); +if(exdebug) iprint("exflush -> %F\n", &fc); + if(!waserror()){ + (*devtab[fs->io->type].write)(fs->io, buf, n, 0); + poperror(); + } +} + +void +exshutdown(Export *fs) +{ + Exq *q, **last; + + lock(&exq.l); + last = &exq.head; + for(q = exq.head; q != nil; q = *last){ + if(q->export == fs){ + *last = q->next; + exfree(fs); + free(q); + continue; + } + last = &q->next; + } + unlock(&exq.l); + + lock(&fs->r.l); + q = fs->work; + while(q != nil){ + if(q->shut){ + q = q->next; + continue; + } + q->shut = 1; + unlock(&fs->r.l); + /* postnote(q->slave, 1, "interrupted", NUser); */ + iprint("postnote 2!\n"); + lock(&fs->r.l); + q = fs->work; + } + unlock(&fs->r.l); +} + +void +exfree(Export *fs) +{ + Fid *f, *n; + int i; + + if(refdec(&fs->r) != 0) + return; + closepgrp(fs->pgrp); + cclose(fs->root); + cclose(fs->io); + for(i = 0; i < Nfidhash; i++){ + for(f = fs->fid[i]; f != nil; f = n){ + if(f->chan != nil) + cclose(f->chan); + n = f->next; + free(f); + } + } + free(fs); +} + +int +exwork(void *a) +{ + return exq.head != nil; +} + +void +exslave(void *a) +{ + Export *fs; + Exq *q, *t, **last; + char *err; + int n; +/* + closepgrp(up->pgrp); + up->pgrp = nil; +*/ + for(;;){ + qlock(&exq.qwait); + rendsleep(&exq.rwait, exwork, nil); + + lock(&exq.l); + q = exq.head; + if(q == nil) { + unlock(&exq.l); + qunlock(&exq.qwait); + continue; + } + exq.head = q->next; + q->slave = curthread(); + unlock(&exq.l); + + qunlock(&exq.qwait); + + q->noresponse = 0; + q->nointr = 0; + fs = q->export; + lock(&fs->r.l); + q->next = fs->work; + fs->work = q; + unlock(&fs->r.l); + + up->pgrp = q->export->pgrp; + + if(exdebug > 1) + iprint("exslave dispatch %d %F\n", getpid(), &q->rpc); + + if(waserror()){ + iprint("exslave err %r\n"); + err = up->errstr; + goto Err; + } + if(q->rpc.type >= Tmax || !fcalls[q->rpc.type]) + err = "bad fcall type"; + else + err = (*fcalls[q->rpc.type])(fs, &q->rpc); + + poperror(); + Err:; + q->rpc.type++; + if(err){ + q->rpc.type = Rerror; + strncpy(q->rpc.ename, err, ERRLEN); + } + n = convS2M(&q->rpc, q->buf); + + if(exdebug) + iprint("exslve %d -> %F\n", getpid(), &q->rpc); + + lock(&q->lk); + if(q->noresponse == 0){ + q->nointr = 1; + clearintr(); + if(!waserror()){ + (*devtab[fs->io->type].write)(fs->io, q->buf, n, 0); + poperror(); + } + } + unlock(&q->lk); + + /* + * exflush might set noresponse at this point, but + * setting noresponse means don't send a response now; + * it's okay that we sent a response already. + */ + if(exdebug > 1) + iprint("exslave %d written %d\n", getpid(), q->rpc.tag); + + lock(&fs->r.l); + last = &fs->work; + for(t = fs->work; t != nil; t = t->next){ + if(t == q){ + *last = q->next; + break; + } + last = &t->next; + } + unlock(&fs->r.l); + + exfree(q->export); + free(q); + } + iprint("exslave shut down"); + threadexit(); +} + +Fid* +Exmkfid(Export *fs, int fid) +{ + ulong h; + Fid *f, *nf; + + nf = mallocz(sizeof(Fid)); + if(nf == nil) + return nil; + lock(&fs->fidlock); + h = fid % Nfidhash; + for(f = fs->fid[h]; f != nil; f = f->next){ + if(f->fid == fid){ + unlock(&fs->fidlock); + free(nf); + return nil; + } + } + + nf->next = fs->fid[h]; + if(nf->next != nil) + nf->next->last = &nf->next; + nf->last = &fs->fid[h]; + fs->fid[h] = nf; + + nf->fid = fid; + nf->ref = 1; + nf->attached = 1; + nf->offset = 0; + nf->chan = nil; + unlock(&fs->fidlock); + return nf; +} + +Fid* +Exgetfid(Export *fs, int fid) +{ + Fid *f; + ulong h; + + lock(&fs->fidlock); + h = fid % Nfidhash; + for(f = fs->fid[h]; f; f = f->next) { + if(f->fid == fid){ + if(f->attached == 0) + break; + f->ref++; + unlock(&fs->fidlock); + return f; + } + } + unlock(&fs->fidlock); + return nil; +} + +void +Exputfid(Export *fs, Fid *f) +{ + lock(&fs->fidlock); + f->ref--; + if(f->ref == 0 && f->attached == 0){ + if(f->chan != nil) + cclose(f->chan); + f->chan = nil; + *f->last = f->next; + if(f->next != nil) + f->next->last = f->last; + unlock(&fs->fidlock); + free(f); + return; + } + unlock(&fs->fidlock); +} + +char* +Exsession(Export *e, Fcall *rpc) +{ + memset(rpc->authid, 0, sizeof(rpc->authid)); + memset(rpc->authdom, 0, sizeof(rpc->authdom)); + memset(rpc->chal, 0, sizeof(rpc->chal)); + return nil; +} + +char* +Exauth(Export *e, Fcall *f) +{ + return "authentication not required"; +} + +char* +Exattach(Export *fs, Fcall *rpc) +{ + Fid *f; + + f = Exmkfid(fs, rpc->fid); + if(f == nil) + return Einuse; + if(waserror()){ + f->attached = 0; + Exputfid(fs, f); + return up->errstr; + } + f->chan = clone(fs->root, nil); + poperror(); + rpc->qid = f->chan->qid; + Exputfid(fs, f); + return nil; +} + +char* +Exclone(Export *fs, Fcall *rpc) +{ + Fid *f, *nf; + + if(rpc->fid == rpc->newfid) + return Einuse; + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + nf = Exmkfid(fs, rpc->newfid); + if(nf == nil){ + Exputfid(fs, f); + return Einuse; + } + if(waserror()){ + Exputfid(fs, f); + Exputfid(fs, nf); + return up->errstr; + } + nf->chan = clone(f->chan, nil); + poperror(); + Exputfid(fs, f); + Exputfid(fs, nf); + return nil; +} + +char* +Exclunk(Export *fs, Fcall *rpc) +{ + Fid *f; + + f = Exgetfid(fs, rpc->fid); + if(f != nil){ + f->attached = 0; + Exputfid(fs, f); + } + return nil; +} + +char* +Exwalk(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = walk(f->chan, rpc->name, 1); + if(c == nil) + error(Enonexist); + poperror(); + + f->chan = c; + rpc->qid = c->qid; + Exputfid(fs, f); + return nil; +} + +char* +Exopen(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + c = (*devtab[c->type].open)(c, rpc->mode); + poperror(); + + f->chan = c; + f->offset = 0; + rpc->qid = f->chan->qid; + Exputfid(fs, f); + return nil; +} + +char* +Excreate(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + if(c->mnt && !(c->flag&CCREATE)) + c = createdir(c); + (*devtab[c->type].create)(c, rpc->name, rpc->mode, rpc->perm); + poperror(); + + f->chan = c; + rpc->qid = f->chan->qid; + Exputfid(fs, f); + return nil; +} + +char* +Exread(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + long off; + int dir, n, seek; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + + c = f->chan; + dir = c->qid.path & CHDIR; + if(dir){ + rpc->count -= rpc->count%DIRLEN; + if(rpc->offset%DIRLEN || rpc->count==0){ + Exputfid(fs, f); + return Ereaddir; + } + if(f->offset > rpc->offset){ + Exputfid(fs, f); + return Eseekdir; + } + } + + if(waserror()) { + Exputfid(fs, f); + return up->errstr; + } + + for(;;){ + n = rpc->count; + seek = 0; + off = rpc->offset; + if(dir && f->offset != off){ + off = f->offset; + n = rpc->offset - off; + if(n > MAXDIRREAD) + n = MAXDIRREAD; + seek = 1; + } + if(dir && c->mnt != nil) + n = unionread(c, rpc->data, n); + else{ + c->offset = off; + n = (*devtab[c->type].read)(c, rpc->data, n, off); + } + if(n == 0 || !seek) + break; + f->offset = off + n; + c->offset += n; + } + rpc->count = n; + poperror(); + Exputfid(fs, f); + return nil; +} + +char* +Exwrite(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + if(c->qid.path & CHDIR) + error(Eisdir); + rpc->count = (*devtab[c->type].write)(c, rpc->data, rpc->count, rpc->offset); + poperror(); + Exputfid(fs, f); + return nil; +} + +char* +Exstat(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + (*devtab[c->type].stat)(c, rpc->stat); + poperror(); + Exputfid(fs, f); + return nil; +} + +char* +Exwstat(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + (*devtab[c->type].wstat)(c, rpc->stat); + poperror(); + Exputfid(fs, f); + return nil; +} + +char* +Exremove(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + (*devtab[c->type].remove)(c); + poperror(); + + /* + * chan is already clunked by remove. + * however, we need to recover the chan, + * and follow sysremove's lead in making to point to root. + */ + c->type = 0; + + f->attached = 0; + Exputfid(fs, f); + return nil; +} diff --git a/kern/fns.h b/kern/fns.h new file mode 100644 index 0000000..eabc631 --- /dev/null +++ b/kern/fns.h @@ -0,0 +1,377 @@ +#define ROUND(s, sz) (((s)+((sz)-1))&~((sz)-1)) + +void accounttime(void); +void addclock0link(void (*)(void), int); +int addphysseg(Physseg*); +void addbootfile(char*, uchar*, ulong); +Block* adjustblock(Block*, int); +void alarmkproc(void*); +Block* allocb(int); +int anyhigher(void); +int anyready(void); +Page* auxpage(void); +Block* bl2mem(uchar*, Block*, int); +int blocklen(Block*); +void callwithureg(void(*)(Ureg*)); +char* c2name(Chan*); +int cangetc(void*); +int canlock(Lock*); +int canpage(Proc*); +int canputc(void*); +int canqlock(QLock*); +int canrlock(RWlock*); +void chandevinit(void); +void chandevreset(void); +void chandevshutdown(void); +void chanfree(Chan*); +void chanrec(Mnt*); +void checkalarms(void); +void checkb(Block*, char*); +void cinit(void); +Chan* cclone(Chan*); +void cclose(Chan*); +char* clipread(void); +int clipwrite(char*); +void closeegrp(Egrp*); +void closefgrp(Fgrp*); +void closemount(Mount*); +void closepgrp(Pgrp*); +void closergrp(Rgrp*); +long clrfpintr(void); +void cmderror(Cmdbuf*, char*); +int cmount(Chan**, Chan*, int, char*); +void cnameclose(Cname*); +void confinit(void); +void confinit1(int); +int consactive(void); +void (*consdebug)(void); +void copen(Chan*); +Block* concatblock(Block*); +Block* copyblock(Block*, int); +void copypage(Page*, Page*); +int cread(Chan*, uchar*, int, vlong); +void cunmount(Chan*, Chan*); +void cupdate(Chan*, uchar*, int, vlong); +void cwrite(Chan*, uchar*, int, vlong); +ulong dbgpc(Proc*); +int decref(Ref*); +int decrypt(void*, void*, int); +void delay(int); +Chan* devattach(int, char*); +Block* devbread(Chan*, long, ulong); +long devbwrite(Chan*, Block*, ulong); +Chan* devclone(Chan*); +int devconfig(int, char *, DevConf *); +void devcreate(Chan*, char*, int, ulong); +void devdir(Chan*, Qid, char*, vlong, char*, long, Dir*); +long devdirread(Chan*, char*, long, Dirtab*, int, Devgen*); +Devgen devgen; +void devinit(void); +int devno(int, int); +Chan* devopen(Chan*, int, Dirtab*, int, Devgen*); +void devpermcheck(char*, ulong, int); +void devpower(int); +void devremove(Chan*); +void devreset(void); +void devshutdown(void); +int devstat(Chan*, uchar*, int, Dirtab*, int, Devgen*); +Walkqid* devwalk(Chan*, Chan*, char**, int, Dirtab*, int, Devgen*); +int devwstat(Chan*, uchar*, int); +void drawactive(int); +void drawcmap(void); +void dumpaproc(Proc*); +void dumpqueues(void); +void dumpregs(Ureg*); +void dumpstack(void); +Fgrp* dupfgrp(Fgrp*); +void duppage(Page*); +void dupswap(Page*); +int emptystr(char*); +int encrypt(void*, void*, int); +void envcpy(Egrp*, Egrp*); +int eqchan(Chan*, Chan*, int); +int eqqid(Qid, Qid); +void error(char*); +long execregs(ulong, ulong, ulong); +void exhausted(char*); +void exit(int); +uvlong fastticks(uvlong*); +int fault(ulong, int); +void fdclose(int, int); +Chan* fdtochan(int, int, int, int); +int fixfault(Segment*, ulong, int, int); +void flushmmu(void); +void forkchild(Proc*, Ureg*); +void forkret(void); +void free(void*); +void freeb(Block*); +void freeblist(Block*); +int freebroken(void); +void freepte(Segment*, Pte*); +void freesegs(int); +void freesession(Session*); +ulong getmalloctag(void*); +ulong getrealloctag(void*); +void gotolabel(Label*); +char* getconfenv(void); +int haswaitq(void*); +long hostdomainwrite(char*, int); +long hostownerwrite(char*, int); +void hzsched(void); +void iallocinit(void); +Block* iallocb(int); +void iallocsummary(void); +long ibrk(ulong, int); +void ilock(Lock*); +void iunlock(Lock*); +int incref(Ref*); +void initseg(void); +int iprint(char*, ...); +void isdir(Chan*); +int iseve(void); +#define islo() (0) +Segment* isoverlap(Proc*, ulong, int); +int ispages(void*); +int isphysseg(char*); +void ixsummary(void); +void kbdclock(void); +int kbdcr2nl(Queue*, int); +int kbdputc(Queue*, int); +void kbdrepeat(int); +long keyread(char*, int, long); +void kickpager(void); +void killbig(void); +int kproc(char*, void(*)(void*), void*); +void kprocchild(Proc*, void (*)(void*), void*); +void (*kproftimer)(ulong); +void ksetenv(char*, char*, int); +void kstrcpy(char*, char*, int); +void kstrdup(char**, char*); +long latin1(Rune*, int); +int lock(Lock*); +void lockinit(void); +void logopen(Log*); +void logclose(Log*); +char* logctl(Log*, int, char**, Logflag*); +void logn(Log*, int, void*, int); +long logread(Log*, void*, ulong, long); +void log(Log*, int, char*, ...); +Cmdtab* lookupcmd(Cmdbuf*, Cmdtab*, int); +void machinit(void); +void* mallocz(ulong, int); +#define malloc kmalloc +void* malloc(ulong); +void mallocsummary(void); +Block* mem2bl(uchar*, int); +void mfreeseg(Segment*, ulong, int); +void microdelay(int); +void mkqid(Qid*, vlong, ulong, int); +void mmurelease(Proc*); +void mmuswitch(Proc*); +Chan* mntauth(Chan*, char*); +void mntdump(void); +long mntversion(Chan*, char*, int, int); +void mountfree(Mount*); +ulong ms2tk(ulong); +ulong msize(void*); +ulong ms2tk(ulong); +uvlong ms2fastticks(ulong); +void muxclose(Mnt*); +Chan* namec(char*, int, int, ulong); +Chan* newchan(void); +int newfd(Chan*); +Mhead* newmhead(Chan*); +Mount* newmount(Mhead*, Chan*, int, char*); +Page* newpage(int, Segment **, ulong); +Pgrp* newpgrp(void); +Rgrp* newrgrp(void); +Proc* newproc(void); +char* nextelem(char*, char*); +void nexterror(void); +Cname* newcname(char*); +int notify(Ureg*); +int nrand(int); +int okaddr(ulong, ulong, int); +int openmode(ulong); +Block* packblock(Block*); +Block* padblock(Block*, int); +void pagechainhead(Page*); +void pageinit(void); +void pagersummary(void); +void panic(char*, ...); +Cmdbuf* parsecmd(char *a, int n); +ulong perfticks(void); +void pexit(char*, int); +int preempted(void); +void printinit(void); +int procindex(ulong); +void pgrpcpy(Pgrp*, Pgrp*); +void pgrpnote(ulong, char*, long, int); +Pgrp* pgrptab(int); +void pio(Segment *, ulong, ulong, Page **); +#define poperror() up->nerrlab-- +void portclock(Ureg*); +int postnote(Proc*, int, char*, int); +int pprint(char*, ...); +void prflush(void); +ulong procalarm(ulong); +int proccounter(char *name); +void procctl(Proc*); +void procdump(void); +int procfdprint(Chan*, int, int, char*, int); +void procinit0(void); +void procflushseg(Segment*); +void procpriority(Proc*, int, int); +Proc* proctab(int); +void procwired(Proc*, int); +Pte* ptealloc(void); +Pte* ptecpy(Pte*); +int pullblock(Block**, int); +Block* pullupblock(Block*, int); +Block* pullupqueue(Queue*, int); +void putmhead(Mhead*); +void putmmu(ulong, ulong, Page*); +void putpage(Page*); +void putseg(Segment*); +void putstr(char*); +void putstrn(char*, int); +void putswap(Page*); +ulong pwait(Waitmsg*); +Label* pwaserror(void); +void qaddlist(Queue*, Block*); +Block* qbread(Queue*, int); +long qbwrite(Queue*, Block*); +Queue* qbypass(void (*)(void*, Block*), void*); +int qcanread(Queue*); +void qclose(Queue*); +int qconsume(Queue*, void*, int); +Block* qcopy(Queue*, int, ulong); +int qdiscard(Queue*, int); +void qflush(Queue*); +void qfree(Queue*); +int qfull(Queue*); +Block* qget(Queue*); +void qhangup(Queue*, char*); +int qisclosed(Queue*); +void qinit(void); +int qiwrite(Queue*, void*, int); +int qlen(Queue*); +void qlock(QLock*); +Queue* qopen(int, int, void (*)(void*), void*); +int qpass(Queue*, Block*); +int qpassnolim(Queue*, Block*); +int qproduce(Queue*, void*, int); +void qputback(Queue*, Block*); +long qread(Queue*, void*, int); +Block* qremove(Queue*); +void qreopen(Queue*); +void qsetlimit(Queue*, int); +void qunlock(QLock*); +int qwindow(Queue*); +int qwrite(Queue*, void*, int); +void qnoblock(Queue*, int); +int rand(void); +void randominit(void); +ulong randomread(void*, ulong); +void rdb(void); +int readnum(ulong, char*, ulong, ulong, int); +int readstr(ulong, char*, ulong, char*); +void ready(Proc*); +void rebootcmd(int, char**); +void reboot(void*, void*, ulong); +void relocateseg(Segment*, ulong); +void renameuser(char*, char*); +void resched(char*); +void resrcwait(char*); +int return0(void*); +void rlock(RWlock*); +long rtctime(void); +void runlock(RWlock*); +Proc* runproc(void); +void savefpregs(FPsave*); +void (*saveintrts)(void); +void sched(void); +void scheddump(void); +void schedinit(void); +void (*screenputs)(char*, int); +long seconds(void); +ulong segattach(Proc*, ulong, char *, ulong, ulong); +void segclock(ulong); +void segpage(Segment*, Page*); +void setkernur(Ureg*, Proc*); +int setlabel(Label*); +void setmalloctag(void*, ulong); +void setrealloctag(void*, ulong); +void setregisters(Ureg*, char*, char*, int); +void setswapchan(Chan*); +char* skipslash(char*); +void sleep(Rendez*, int(*)(void*), void*); +void* smalloc(ulong); +int splhi(void); +int spllo(void); +void splx(int); +void splxpc(int); +char* srvname(Chan*); +int swapcount(ulong); +int swapfull(void); +void swapinit(void); +void timeradd(Timer*); +void timerdel(Timer*); +void timersinit(void); +void timerintr(Ureg*, uvlong); +void timerset(uvlong); +ulong tk2ms(ulong); +#define TK2MS(x) ((x)*(1000/HZ)) +vlong todget(vlong*); +void todfix(void); +void todsetfreq(vlong); +void todinit(void); +void todset(vlong, vlong, int); +Block* trimblock(Block*, int, int); +void tsleep(Rendez*, int (*)(void*), void*, int); +int uartctl(Uart*, char*); +int uartgetc(void); +void uartkick(void*); +void uartmouse(Uart*, int (*)(Queue*, int), int); +void uartputc(int); +void uartputs(char*, int); +void uartrecv(Uart*, char); +Uart* uartsetup(Uart*); +int uartstageoutput(Uart*); +void unbreak(Proc*); +void uncachepage(Page*); +long unionread(Chan*, void*, long); +void unlock(Lock*); +Proc** uploc(void); +void userinit(void); +ulong userpc(void); +long userwrite(char*, int); +#define validaddr(a, b, c) +void validname(char*, int); +void validstat(uchar*, int); +void vcacheinval(Page*, ulong); +void* vmemchr(void*, int, int); +Proc* wakeup(Rendez*); +int walk(Chan**, char**, int, int, int*); +#define waserror() (setjmp(pwaserror()->buf)) +void wlock(RWlock*); +void wunlock(RWlock*); +void* xalloc(ulong); +void* xallocz(ulong, int); +void xfree(void*); +void xhole(ulong, ulong); +void xinit(void); +int xmerge(void*, void*); +void* xspanalloc(ulong, int, ulong); +void xsummary(void); +void yield(void); +Segment* data2txt(Segment*); +Segment* dupseg(Segment**, int, int); +Segment* newseg(int, ulong, ulong); +Segment* seg(Proc*, ulong, int); +void hnputv(void*, vlong); +void hnputl(void*, ulong); +void hnputs(void*, ushort); +vlong nhgetv(void*); +ulong nhgetl(void*); +ushort nhgets(void*); diff --git a/kern/mkfile b/kern/mkfile new file mode 100644 index 0000000..dea81ff --- /dev/null +++ b/kern/mkfile @@ -0,0 +1,47 @@ +<$DSRC/mkfile-$CONF +TARG=libkern.$L + +OFILES=\ + allocb.$O\ + cache.$O\ + chan.$O\ + data.$O\ + dev.$O\ + devcons.$O\ + devdraw.$O\ + devip.$O\ + devmnt.$O\ + devmouse.$O\ + devpipe.$O\ + devroot.$O\ + devssl.$O\ + devtab.$O\ + error.$O\ + parse.$O\ + pgrp.$O\ + procinit.$O\ + rwlock.$O\ + sleep.$O\ + smalloc.$O\ + stub.$O\ + sysfile.$O\ + sysproc.$O\ + qio.$O\ + qlock.$O\ + term.$O\ + todo.$O\ + uart.$O\ + waserror.$O\ + $DEVIP.$O\ + $OSHOOKS.$O\ + $DEVFS.$O + +HFILE=\ + dat.h\ + devip.h\ + error.h\ + fns.h\ + netif.h\ + screen.h + +<$DSRC/mklib-$CONF diff --git a/kern/netif.h b/kern/netif.h new file mode 100644 index 0000000..06c42ae --- /dev/null +++ b/kern/netif.h @@ -0,0 +1,133 @@ +typedef struct Etherpkt Etherpkt; +typedef struct Netaddr Netaddr; +typedef struct Netfile Netfile; +typedef struct Netif Netif; + +enum +{ + Nmaxaddr= 64, + Nmhash= 31, + + Ncloneqid= 1, + Naddrqid, + N2ndqid, + N3rdqid, + Ndataqid, + Nctlqid, + Nstatqid, + Ntypeqid, + Nifstatqid, +}; + +/* + * Macros to manage Qid's used for multiplexed devices + */ +#define NETTYPE(x) (((ulong)x)&0x1f) +#define NETID(x) ((((ulong)x))>>5) +#define NETQID(i,t) ((((ulong)i)<<5)|(t)) + +/* + * one per multiplexed connection + */ +struct Netfile +{ + QLock lk; + + int inuse; + ulong mode; + char owner[KNAMELEN]; + + int type; /* multiplexor type */ + int prom; /* promiscuous mode */ + int scan; /* base station scanning interval */ + int bridge; /* bridge mode */ + int headersonly; /* headers only - no data */ + uchar maddr[8]; /* bitmask of multicast addresses requested */ + int nmaddr; /* number of multicast addresses */ + + Queue *in; /* input buffer */ +}; + +/* + * a network address + */ +struct Netaddr +{ + Netaddr *next; /* allocation chain */ + Netaddr *hnext; + uchar addr[Nmaxaddr]; + int ref; +}; + +/* + * a network interface + */ +struct Netif +{ + QLock lk; + + /* multiplexing */ + char name[KNAMELEN]; /* for top level directory */ + int nfile; /* max number of Netfiles */ + Netfile **f; + + /* about net */ + int limit; /* flow control */ + int alen; /* address length */ + int mbps; /* megabits per sec */ + uchar addr[Nmaxaddr]; + uchar bcast[Nmaxaddr]; + Netaddr *maddr; /* known multicast addresses */ + int nmaddr; /* number of known multicast addresses */ + Netaddr *mhash[Nmhash]; /* hash table of multicast addresses */ + int prom; /* number of promiscuous opens */ + int scan; /* number of base station scanners */ + int all; /* number of -1 multiplexors */ + + /* statistics */ + int misses; + int inpackets; + int outpackets; + int crcs; /* input crc errors */ + int oerrs; /* output errors */ + int frames; /* framing errors */ + int overflows; /* packet overflows */ + int buffs; /* buffering errors */ + int soverflows; /* software overflow */ + + /* routines for touching the hardware */ + void *arg; + void (*promiscuous)(void*, int); + void (*multicast)(void*, uchar*, int); + void (*scanbs)(void*, uint); /* scan for base stations */ +}; + +void netifinit(Netif*, char*, int, ulong); +Walkqid* netifwalk(Netif*, Chan*, Chan*, char **, int); +Chan* netifopen(Netif*, Chan*, int); +void netifclose(Netif*, Chan*); +long netifread(Netif*, Chan*, void*, long, ulong); +Block* netifbread(Netif*, Chan*, long, ulong); +long netifwrite(Netif*, Chan*, void*, long); +int netifwstat(Netif*, Chan*, uchar*, int); +int netifstat(Netif*, Chan*, uchar*, int); +int activemulti(Netif*, uchar*, int); + +/* + * Ethernet specific + */ +enum +{ + Eaddrlen= 6, + ETHERMINTU = 60, /* minimum transmit size */ + ETHERMAXTU = 1514, /* maximum transmit size */ + ETHERHDRSIZE = 14, /* size of an ethernet header */ +}; + +struct Etherpkt +{ + uchar d[Eaddrlen]; + uchar s[Eaddrlen]; + uchar type[2]; + uchar data[1500]; +}; diff --git a/kern/os-windows.c b/kern/os-windows.c new file mode 100644 index 0000000..6afd0d0 --- /dev/null +++ b/kern/os-windows.c @@ -0,0 +1,184 @@ +#include +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" + +typedef struct Oproc Oproc; +struct Oproc { + int tid; + HANDLE *sema; +}; + +char *argv0; +_declspec(thread) Proc *CT; + +Proc* +_getproc(void) +{ + return CT; +} + +void +_setproc(Proc *p) +{ + CT = p; +} + +void +oserrstr(void) +{ + char *p; + char buf[ERRMAX]; + + if((p = strerror(errno)) != nil) + strecpy(up->errstr, up->errstr+ERRMAX, p); + else + snprint(up->errstr, ERRMAX, "unix error %d", errno); +} + +void +oserror(void) +{ + oserrstr(); + nexterror(); +} + +void +osinit(void) +{ + Oproc *t; + static Proc firstprocCTstore; + + CT = &firstprocCTstore; + t = (Oproc*) CT->oproc; + assert(t != 0); + + t->tid = GetCurrentThreadId(); + t->sema = CreateSemaphore(0, 0, 1000, 0); + if(t->sema == 0) { + oserror(); + fatal("could not create semaphore: %r"); + } +} + +void +osnewproc(Proc *p) +{ + Oproc *op; + + op = (Oproc*)p->oproc; + op->sema = CreateSemaphore(0, 0, 1000, 0); + if (op->sema == 0) { + oserror(); + fatal("could not create semaphore: %r"); + } +} + +void +osmsleep(int ms) +{ + Sleep((DWORD) ms); +} + +void +osyield(void) +{ + Sleep(0); +} + +static DWORD WINAPI tramp(LPVOID vp); + +void +osproc(Proc *p) +{ + int tid; + + if(CreateThread(0, 0, tramp, p, 0, &tid) == 0) { + oserror(); + fatal("osproc: %r"); + } + + Sleep(0); +} + +static DWORD WINAPI +tramp(LPVOID vp) +{ + Proc *p = (Proc *) vp; + Oproc *op = (Oproc*) p->oproc; + + CT = p; + op->tid = GetCurrentThreadId(); + op->sema = CreateSemaphore(0, 0, 1000, 0); + if(op->sema == 0) { + oserror(); + fatal("could not create semaphore: %r"); + } + + (*p->fn)(p->arg); + ExitThread(0); + return 0; +} + +void +procsleep(void) +{ + Proc *p; + Oproc *op; + + p = up; + op = (Oproc*)p->oproc; + WaitForSingleObject(op->sema, INFINITE);} + +void +procwakeup(Proc *p) +{ + Oproc *op; + + op = (Oproc*)p->oproc; + ReleaseSemaphore(op->sema, 1, 0); +} + +void +randominit(void) +{ + srand(seconds()); +} + +ulong +randomread(void *v, ulong n) +{ + int m, i, *r; + + m = (n / sizeof(int)) * sizeof(int); + for (i = 0, r = (int*)v; i < m; i += sizeof(int)) { + *r = rand(); + r += sizeof(int); + } + + return m; +} + +long +seconds(void) +{ + return time(0); +} + +int +ticks(void) +{ + return GetTickCount(); +} + +extern int main(int, char*[]); +static int args(char *argv[], int n, char *p); + +int PASCAL +WinMain(HANDLE hInst, HANDLE hPrev, LPSTR arg, int nshow) +{ + main(__argc, __argv); + ExitThread(0); + return 0; +} diff --git a/kern/parse.c b/kern/parse.c new file mode 100644 index 0000000..8c991f8 --- /dev/null +++ b/kern/parse.c @@ -0,0 +1,113 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +/* + * Generous estimate of number of fields, including terminal nil pointer + */ +static int +ncmdfield(char *p, int n) +{ + int white, nwhite; + char *ep; + int nf; + + if(p == nil) + return 1; + + nf = 0; + ep = p+n; + white = 1; /* first text will start field */ + while(p < ep){ + nwhite = (strchr(" \t\r\n", *p++ & 0xFF) != 0); /* UTF is irrelevant */ + if(white && !nwhite) /* beginning of field */ + nf++; + white = nwhite; + } + return nf+1; /* +1 for nil */ +} + +/* + * parse a command written to a device + */ +Cmdbuf* +parsecmd(char *p, int n) +{ + Cmdbuf *volatile cb; + int nf; + char *sp; + + nf = ncmdfield(p, n); + + /* allocate Cmdbuf plus string pointers plus copy of string including \0 */ + sp = smalloc(sizeof(*cb) + nf * sizeof(char*) + n + 1); + cb = (Cmdbuf*)sp; + cb->f = (char**)(&cb[1]); + cb->buf = (char*)(&cb->f[nf]); + + if(up!=nil && waserror()){ + free(cb); + nexterror(); + } + memmove(cb->buf, p, n); + if(up != nil) + poperror(); + + /* dump new line and null terminate */ + if(n > 0 && cb->buf[n-1] == '\n') + n--; + cb->buf[n] = '\0'; + + cb->nf = tokenize(cb->buf, cb->f, nf-1); + cb->f[cb->nf] = nil; + + return cb; +} + +/* + * Reconstruct original message, for error diagnostic + */ +void +cmderror(Cmdbuf *cb, char *s) +{ + int i; + char *p, *e; + + p = up->genbuf; + e = p+ERRMAX-10; + p = seprint(p, e, "%s \"", s); + for(i=0; inf; i++){ + if(i > 0) + p = seprint(p, e, " "); + p = seprint(p, e, "%q", cb->f[i]); + } + strcpy(p, "\""); + error(up->genbuf); +} + +/* + * Look up entry in table + */ +Cmdtab* +lookupcmd(Cmdbuf *cb, Cmdtab *ctab, int nctab) +{ + int i; + Cmdtab *ct; + + if(cb->nf == 0) + error("empty control message"); + + for(ct = ctab, i=0; icmd, "*") !=0) /* wildcard always matches */ + if(strcmp(ct->cmd, cb->f[0]) != 0) + continue; + if(ct->narg != 0 && ct->narg != cb->nf) + cmderror(cb, Ecmdargs); + return ct; + } + + cmderror(cb, "unknown control message"); + return nil; +} diff --git a/kern/pgrp.c b/kern/pgrp.c new file mode 100644 index 0000000..2271a04 --- /dev/null +++ b/kern/pgrp.c @@ -0,0 +1,272 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +static Ref pgrpid; +static Ref mountid; + +#ifdef NOTDEF +void +pgrpnote(ulong noteid, char *a, long n, int flag) +{ + Proc *p, *ep; + char buf[ERRMAX]; + + if(n >= ERRMAX-1) + error(Etoobig); + + memmove(buf, a, n); + buf[n] = 0; + p = proctab(0); + ep = p+conf.nproc; + for(; p < ep; p++) { + if(p->state == Dead) + continue; + if(up != p && p->noteid == noteid && p->kp == 0) { + qlock(&p->debug); + if(p->pid == 0 || p->noteid != noteid){ + qunlock(&p->debug); + continue; + } + if(!waserror()) { + postnote(p, 0, buf, flag); + poperror(); + } + qunlock(&p->debug); + } + } +} +#endif + +Pgrp* +newpgrp(void) +{ + Pgrp *p; + + p = smalloc(sizeof(Pgrp)); + p->ref.ref = 1; + p->pgrpid = incref(&pgrpid); + return p; +} + +Rgrp* +newrgrp(void) +{ + Rgrp *r; + + r = smalloc(sizeof(Rgrp)); + r->ref.ref = 1; + return r; +} + +void +closergrp(Rgrp *r) +{ + if(decref(&r->ref) == 0) + free(r); +} + +void +closepgrp(Pgrp *p) +{ + Mhead **h, **e, *f, *next; + + if(decref(&p->ref) != 0) + return; + + qlock(&p->debug); + wlock(&p->ns); + p->pgrpid = -1; + + e = &p->mnthash[MNTHASH]; + for(h = p->mnthash; h < e; h++) { + for(f = *h; f; f = next) { + wlock(&f->lock); + cclose(f->from); + mountfree(f->mount); + f->mount = nil; + next = f->hash; + wunlock(&f->lock); + putmhead(f); + } + } + wunlock(&p->ns); + qunlock(&p->debug); + free(p); +} + +void +pgrpinsert(Mount **order, Mount *m) +{ + Mount *f; + + m->order = 0; + if(*order == 0) { + *order = m; + return; + } + for(f = *order; f; f = f->order) { + if(m->mountid < f->mountid) { + m->order = f; + *order = m; + return; + } + order = &f->order; + } + *order = m; +} + +/* + * pgrpcpy MUST preserve the mountid allocation order of the parent group + */ +void +pgrpcpy(Pgrp *to, Pgrp *from) +{ + int i; + Mount *n, *m, **link, *order; + Mhead *f, **tom, **l, *mh; + + wlock(&from->ns); + order = 0; + tom = to->mnthash; + for(i = 0; i < MNTHASH; i++) { + l = tom++; + for(f = from->mnthash[i]; f; f = f->hash) { + rlock(&f->lock); + mh = newmhead(f->from); + *l = mh; + l = &mh->hash; + link = &mh->mount; + for(m = f->mount; m; m = m->next) { + n = newmount(mh, m->to, m->mflag, m->spec); + m->copy = n; + pgrpinsert(&order, m); + *link = n; + link = &n->next; + } + runlock(&f->lock); + } + } + /* + * Allocate mount ids in the same sequence as the parent group + */ + lock(&mountid.lk); + for(m = order; m; m = m->order) + m->copy->mountid = mountid.ref++; + unlock(&mountid.lk); + wunlock(&from->ns); +} + +Fgrp* +dupfgrp(Fgrp *f) +{ + Fgrp *new; + Chan *c; + int i; + + new = smalloc(sizeof(Fgrp)); + if(f == nil){ + new->fd = smalloc(DELTAFD*sizeof(Chan*)); + new->nfd = DELTAFD; + new->ref.ref = 1; + return new; + } + + lock(&f->ref.lk); + /* Make new fd list shorter if possible, preserving quantization */ + new->nfd = f->maxfd+1; + i = new->nfd%DELTAFD; + if(i != 0) + new->nfd += DELTAFD - i; + new->fd = malloc(new->nfd*sizeof(Chan*)); + if(new->fd == 0){ + unlock(&f->ref.lk); + error("no memory for fgrp"); + } + new->ref.ref = 1; + + new->maxfd = f->maxfd; + for(i = 0; i <= f->maxfd; i++) { + if(c = f->fd[i]){ + incref(&c->ref); + new->fd[i] = c; + } + } + unlock(&f->ref.lk); + + return new; +} + +void +closefgrp(Fgrp *f) +{ + int i; + Chan *c; + + if(f == 0) + return; + + if(decref(&f->ref) != 0) + return; + + for(i = 0; i <= f->maxfd; i++) + if(c = f->fd[i]) + cclose(c); + + free(f->fd); + free(f); +} + +Mount* +newmount(Mhead *mh, Chan *to, int flag, char *spec) +{ + Mount *m; + + m = smalloc(sizeof(Mount)); + m->to = to; + m->head = mh; + incref(&to->ref); + m->mountid = incref(&mountid); + m->mflag = flag; + if(spec != 0) + kstrdup(&m->spec, spec); + + return m; +} + +void +mountfree(Mount *m) +{ + Mount *f; + + while(m) { + f = m->next; + cclose(m->to); + m->mountid = 0; + free(m->spec); + free(m); + m = f; + } +} + +#ifdef NOTDEF +void +resrcwait(char *reason) +{ + char *p; + + if(up == 0) + panic("resrcwait"); + + p = up->psstate; + if(reason) { + up->psstate = reason; + print("%s\n", reason); + } + + tsleep(&up->sleep, return0, 0, 300); + up->psstate = p; +} +#endif diff --git a/kern/posix.c b/kern/posix.c new file mode 100644 index 0000000..730b36c --- /dev/null +++ b/kern/posix.c @@ -0,0 +1,190 @@ +/* + * Posix generic OS implementation for drawterm. + */ + +#include +#include +#include +#include +#include +#include + +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" + +typedef struct Oproc Oproc; +struct Oproc +{ + int p[2]; +}; + +static pthread_key_t prdakey; + +Proc* +_getproc(void) +{ + void *v; + + if((v = pthread_getspecific(prdakey)) == nil) + panic("cannot getspecific"); + return v; +} + +void +_setproc(Proc *p) +{ + if(pthread_setspecific(prdakey, p) != 0) + panic("cannot setspecific"); +} + +void +osinit(void) +{ + if(pthread_key_create(&prdakey, 0)) + panic("cannot pthread_key_create"); +} + +#undef pipe +void +osnewproc(Proc *p) +{ + Oproc *op; + + op = (Oproc*)p->oproc; + if(pipe(op->p) < 0) + panic("cannot pipe"); +} + +void +osmsleep(int ms) +{ + struct timeval tv; + + tv.tv_sec = ms / 1000; + tv.tv_usec = (ms % 1000) * 1000; /* micro */ + if(select(0, NULL, NULL, NULL, &tv) < 0) + panic("select"); +} + +void +osyield(void) +{ + sched_yield(); +} + +void +oserrstr(void) +{ + char *p; + char buf[ERRMAX]; + + if((p = strerror(errno)) != nil) + strecpy(up->errstr, up->errstr+ERRMAX, p); + else + snprint(up->errstr, ERRMAX, "unix error %d", errno); +} + +void +oserror(void) +{ + oserrstr(); + nexterror(); +} + +static void* tramp(void*); + +void +osproc(Proc *p) +{ + pthread_t pid; + + if(pthread_create(&pid, nil, tramp, p)){ + oserrstr(); + panic("osproc: %r"); + } + sched_yield(); +} + +static void* +tramp(void *vp) +{ + Proc *p; + + p = vp; + if(pthread_setspecific(prdakey, p)) + panic("cannot setspecific"); + (*p->fn)(p->arg); + /* BUG: leaks Proc */ + pthread_setspecific(prdakey, 0); + pthread_exit(0); +} + +void +procsleep(void) +{ + int c; + Proc *p; + Oproc *op; + + p = up; + op = (Oproc*)p->oproc; + while(read(op->p[0], &c, 1) != 1) + ; +} + +void +procwakeup(Proc *p) +{ + char c; + Oproc *op; + + op = (Oproc*)p->oproc; + c = 'a'; + write(op->p[1], &c, 1); +} + +int randfd; +#undef open +void +randominit(void) +{ + if((randfd = open("/dev/urandom", OREAD)) < 0) + if((randfd = open("/dev/random", OREAD)) < 0) + panic("open /dev/random: %r"); +} + +#undef read +ulong +randomread(void *v, ulong n) +{ + int m; + + if((m = read(randfd, v, n)) != n) + panic("short read from /dev/random: %d but %d", n, m); + return m; +} + +#undef time +long +seconds(void) +{ + return time(0); +} + +ulong +ticks(void) +{ + static long sec0 = 0, usec0; + struct timeval t; + + if(gettimeofday(&t, nil) < 0) + return 0; + if(sec0 == 0){ + sec0 = t.tv_sec; + usec0 = t.tv_usec; + } + return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000; +} + diff --git a/kern/procinit.c b/kern/procinit.c new file mode 100644 index 0000000..a3eadbe --- /dev/null +++ b/kern/procinit.c @@ -0,0 +1,67 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +Rgrp *thergrp; + +void +procinit0(void) +{ + Proc *p; + + p = newproc(); + p->fgrp = dupfgrp(nil); + p->rgrp = newrgrp(); + p->pgrp = newpgrp(); + _setproc(p); + + up->slash = namec("#/", Atodir, 0, 0); + cnameclose(up->slash->name); + up->slash->name = newcname("/"); + up->dot = cclone(up->slash); +} + +Ref pidref; + +Proc* +newproc(void) +{ + Proc *p; + + p = mallocz(sizeof(Proc), 1); + p->pid = incref(&pidref); + strcpy(p->user, eve); + p->syserrstr = p->errbuf0; + p->errstr = p->errbuf1; + strcpy(p->text, "drawterm"); + osnewproc(p); + return p; +} + +int +kproc(char *name, void (*fn)(void*), void *arg) +{ + Proc *p; + + p = newproc(); + p->fn = fn; + p->arg = arg; + p->slash = cclone(up->slash); + p->dot = cclone(up->dot); + p->rgrp = up->rgrp; + if(p->rgrp) + incref(&p->rgrp->ref); + p->pgrp = up->pgrp; + if(up->pgrp) + incref(&up->pgrp->ref); + p->fgrp = up->fgrp; + if(p->fgrp) + incref(&p->fgrp->ref); + strecpy(p->text, sizeof p->text, name); + + osproc(p); + return p->pid; +} + diff --git a/kern/qio.c b/kern/qio.c new file mode 100644 index 0000000..f9c1178 --- /dev/null +++ b/kern/qio.c @@ -0,0 +1,1524 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +static ulong padblockcnt; +static ulong concatblockcnt; +static ulong pullupblockcnt; +static ulong copyblockcnt; +static ulong consumecnt; +static ulong producecnt; +static ulong qcopycnt; + +static int debugging; + +#define QDEBUG if(0) + +/* + * IO queues + */ +struct Queue +{ + Lock lk; + + Block* bfirst; /* buffer */ + Block* blast; + + int len; /* bytes allocated to queue */ + int dlen; /* data bytes in queue */ + int limit; /* max bytes in queue */ + int inilim; /* initial limit */ + int state; + int noblock; /* true if writes return immediately when q full */ + int eof; /* number of eofs read by user */ + + void (*kick)(void*); /* restart output */ + void (*bypass)(void*, Block*); /* bypass queue altogether */ + void* arg; /* argument to kick */ + + QLock rlock; /* mutex for reading processes */ + Rendez rr; /* process waiting to read */ + QLock wlock; /* mutex for writing processes */ + Rendez wr; /* process waiting to write */ + + char err[ERRMAX]; +}; + +enum +{ + Maxatomic = 64*1024, +}; + +uint qiomaxatomic = Maxatomic; + +void +ixsummary(void) +{ + debugging ^= 1; + iallocsummary(); + print("pad %lud, concat %lud, pullup %lud, copy %lud\n", + padblockcnt, concatblockcnt, pullupblockcnt, copyblockcnt); + print("consume %lud, produce %lud, qcopy %lud\n", + consumecnt, producecnt, qcopycnt); +} + +/* + * free a list of blocks + */ +void +freeblist(Block *b) +{ + Block *next; + + for(; b != 0; b = next){ + next = b->next; + b->next = 0; + freeb(b); + } +} + +/* + * pad a block to the front (or the back if size is negative) + */ +Block* +padblock(Block *bp, int size) +{ + int n; + Block *nbp; + + QDEBUG checkb(bp, "padblock 1"); + if(size >= 0){ + if(bp->rp - bp->base >= size){ + bp->rp -= size; + return bp; + } + + if(bp->next) + panic("padblock 0x%luX", getcallerpc(&bp)); + n = BLEN(bp); + padblockcnt++; + nbp = allocb(size+n); + nbp->rp += size; + nbp->wp = nbp->rp; + memmove(nbp->wp, bp->rp, n); + nbp->wp += n; + freeb(bp); + nbp->rp -= size; + } else { + size = -size; + + if(bp->next) + panic("padblock 0x%luX", getcallerpc(&bp)); + + if(bp->lim - bp->wp >= size) + return bp; + + n = BLEN(bp); + padblockcnt++; + nbp = allocb(size+n); + memmove(nbp->wp, bp->rp, n); + nbp->wp += n; + freeb(bp); + } + QDEBUG checkb(nbp, "padblock 1"); + return nbp; +} + +/* + * return count of bytes in a string of blocks + */ +int +blocklen(Block *bp) +{ + int len; + + len = 0; + while(bp) { + len += BLEN(bp); + bp = bp->next; + } + return len; +} + +/* + * return count of space in blocks + */ +int +blockalloclen(Block *bp) +{ + int len; + + len = 0; + while(bp) { + len += BALLOC(bp); + bp = bp->next; + } + return len; +} + +/* + * copy the string of blocks into + * a single block and free the string + */ +Block* +concatblock(Block *bp) +{ + int len; + Block *nb, *f; + + if(bp->next == 0) + return bp; + + nb = allocb(blocklen(bp)); + for(f = bp; f; f = f->next) { + len = BLEN(f); + memmove(nb->wp, f->rp, len); + nb->wp += len; + } + concatblockcnt += BLEN(nb); + freeblist(bp); + QDEBUG checkb(nb, "concatblock 1"); + return nb; +} + +/* + * make sure the first block has at least n bytes + */ +Block* +pullupblock(Block *bp, int n) +{ + int i; + Block *nbp; + + /* + * this should almost always be true, it's + * just to avoid every caller checking. + */ + if(BLEN(bp) >= n) + return bp; + + /* + * if not enough room in the first block, + * add another to the front of the list. + */ + if(bp->lim - bp->rp < n){ + nbp = allocb(n); + nbp->next = bp; + bp = nbp; + } + + /* + * copy bytes from the trailing blocks into the first + */ + n -= BLEN(bp); + while(nbp = bp->next){ + i = BLEN(nbp); + if(i > n) { + memmove(bp->wp, nbp->rp, n); + pullupblockcnt++; + bp->wp += n; + nbp->rp += n; + QDEBUG checkb(bp, "pullupblock 1"); + return bp; + } else { + /* shouldn't happen but why crash if it does */ + if(i < 0){ + print("pullup negative length packet\n"); + i = 0; + } + memmove(bp->wp, nbp->rp, i); + pullupblockcnt++; + bp->wp += i; + bp->next = nbp->next; + nbp->next = 0; + freeb(nbp); + n -= i; + if(n == 0){ + QDEBUG checkb(bp, "pullupblock 2"); + return bp; + } + } + } + freeb(bp); + return 0; +} + +/* + * make sure the first block has at least n bytes + */ +Block* +pullupqueue(Queue *q, int n) +{ + Block *b; + + if(BLEN(q->bfirst) >= n) + return q->bfirst; + q->bfirst = pullupblock(q->bfirst, n); + for(b = q->bfirst; b != nil && b->next != nil; b = b->next) + ; + q->blast = b; + return q->bfirst; +} + +/* + * trim to len bytes starting at offset + */ +Block * +trimblock(Block *bp, int offset, int len) +{ + ulong l; + Block *nb, *startb; + + QDEBUG checkb(bp, "trimblock 1"); + if(blocklen(bp) < offset+len) { + freeblist(bp); + return nil; + } + + while((l = BLEN(bp)) < offset) { + offset -= l; + nb = bp->next; + bp->next = nil; + freeb(bp); + bp = nb; + } + + startb = bp; + bp->rp += offset; + + while((l = BLEN(bp)) < len) { + len -= l; + bp = bp->next; + } + + bp->wp -= (BLEN(bp) - len); + + if(bp->next) { + freeblist(bp->next); + bp->next = nil; + } + + return startb; +} + +/* + * copy 'count' bytes into a new block + */ +Block* +copyblock(Block *bp, int count) +{ + int l; + Block *nbp; + + QDEBUG checkb(bp, "copyblock 0"); + nbp = allocb(count); + for(; count > 0 && bp != 0; bp = bp->next){ + l = BLEN(bp); + if(l > count) + l = count; + memmove(nbp->wp, bp->rp, l); + nbp->wp += l; + count -= l; + } + if(count > 0){ + memset(nbp->wp, 0, count); + nbp->wp += count; + } + copyblockcnt++; + QDEBUG checkb(nbp, "copyblock 1"); + + return nbp; +} + +Block* +adjustblock(Block* bp, int len) +{ + int n; + Block *nbp; + + if(len < 0){ + freeb(bp); + return nil; + } + + if(bp->rp+len > bp->lim){ + nbp = copyblock(bp, len); + freeblist(bp); + QDEBUG checkb(nbp, "adjustblock 1"); + + return nbp; + } + + n = BLEN(bp); + if(len > n) + memset(bp->wp, 0, len-n); + bp->wp = bp->rp+len; + QDEBUG checkb(bp, "adjustblock 2"); + + return bp; +} + + +/* + * throw away up to count bytes from a + * list of blocks. Return count of bytes + * thrown away. + */ +int +pullblock(Block **bph, int count) +{ + Block *bp; + int n, bytes; + + bytes = 0; + if(bph == nil) + return 0; + + while(*bph != nil && count != 0) { + bp = *bph; + n = BLEN(bp); + if(count < n) + n = count; + bytes += n; + count -= n; + bp->rp += n; + QDEBUG checkb(bp, "pullblock "); + if(BLEN(bp) == 0) { + *bph = bp->next; + bp->next = nil; + freeb(bp); + } + } + return bytes; +} + +/* + * get next block from a queue, return null if nothing there + */ +Block* +qget(Queue *q) +{ + int dowakeup; + Block *b; + + /* sync with qwrite */ + ilock(&q->lk); + + b = q->bfirst; + if(b == nil){ + q->state |= Qstarve; + iunlock(&q->lk); + return nil; + } + q->bfirst = b->next; + b->next = 0; + q->len -= BALLOC(b); + q->dlen -= BLEN(b); + QDEBUG checkb(b, "qget"); + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit/2){ + q->state &= ~Qflow; + dowakeup = 1; + } else + dowakeup = 0; + + iunlock(&q->lk); + + if(dowakeup) + wakeup(&q->wr); + + return b; +} + +/* + * throw away the next 'len' bytes in the queue + */ +int +qdiscard(Queue *q, int len) +{ + Block *b; + int dowakeup, n, sofar; + + ilock(&q->lk); + for(sofar = 0; sofar < len; sofar += n){ + b = q->bfirst; + if(b == nil) + break; + QDEBUG checkb(b, "qdiscard"); + n = BLEN(b); + if(n <= len - sofar){ + q->bfirst = b->next; + b->next = 0; + q->len -= BALLOC(b); + q->dlen -= BLEN(b); + freeb(b); + } else { + n = len - sofar; + b->rp += n; + q->dlen -= n; + } + } + + /* + * if writer flow controlled, restart + * + * This used to be + * q->len < q->limit/2 + * but it slows down tcp too much for certain write sizes. + * I really don't understand it completely. It may be + * due to the queue draining so fast that the transmission + * stalls waiting for the app to produce more data. - presotto + */ + if((q->state & Qflow) && q->len < q->limit){ + q->state &= ~Qflow; + dowakeup = 1; + } else + dowakeup = 0; + + iunlock(&q->lk); + + if(dowakeup) + wakeup(&q->wr); + + return sofar; +} + +/* + * Interrupt level copy out of a queue, return # bytes copied. + */ +int +qconsume(Queue *q, void *vp, int len) +{ + Block *b; + int n, dowakeup; + uchar *p = vp; + Block *tofree = nil; + + /* sync with qwrite */ + ilock(&q->lk); + + for(;;) { + b = q->bfirst; + if(b == 0){ + q->state |= Qstarve; + iunlock(&q->lk); + return -1; + } + QDEBUG checkb(b, "qconsume 1"); + + n = BLEN(b); + if(n > 0) + break; + q->bfirst = b->next; + q->len -= BALLOC(b); + + /* remember to free this */ + b->next = tofree; + tofree = b; + }; + + if(n < len) + len = n; + memmove(p, b->rp, len); + consumecnt += n; + b->rp += len; + q->dlen -= len; + + /* discard the block if we're done with it */ + if((q->state & Qmsg) || len == n){ + q->bfirst = b->next; + b->next = 0; + q->len -= BALLOC(b); + q->dlen -= BLEN(b); + + /* remember to free this */ + b->next = tofree; + tofree = b; + } + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit/2){ + q->state &= ~Qflow; + dowakeup = 1; + } else + dowakeup = 0; + + iunlock(&q->lk); + + if(dowakeup) + wakeup(&q->wr); + + if(tofree != nil) + freeblist(tofree); + + return len; +} + +int +qpass(Queue *q, Block *b) +{ + int dlen, len, dowakeup; + + /* sync with qread */ + dowakeup = 0; + ilock(&q->lk); + if(q->len >= q->limit){ + freeblist(b); + iunlock(&q->lk); + return -1; + } + if(q->state & Qclosed){ + freeblist(b); + iunlock(&q->lk); + return BALLOC(b); + } + + /* add buffer to queue */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + len = BALLOC(b); + dlen = BLEN(b); + QDEBUG checkb(b, "qpass"); + while(b->next){ + b = b->next; + QDEBUG checkb(b, "qpass"); + len += BALLOC(b); + dlen += BLEN(b); + } + q->blast = b; + q->len += len; + q->dlen += dlen; + + if(q->len >= q->limit/2) + q->state |= Qflow; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + iunlock(&q->lk); + + if(dowakeup) + wakeup(&q->rr); + + return len; +} + +int +qpassnolim(Queue *q, Block *b) +{ + int dlen, len, dowakeup; + + /* sync with qread */ + dowakeup = 0; + ilock(&q->lk); + + if(q->state & Qclosed){ + freeblist(b); + iunlock(&q->lk); + return BALLOC(b); + } + + /* add buffer to queue */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + len = BALLOC(b); + dlen = BLEN(b); + QDEBUG checkb(b, "qpass"); + while(b->next){ + b = b->next; + QDEBUG checkb(b, "qpass"); + len += BALLOC(b); + dlen += BLEN(b); + } + q->blast = b; + q->len += len; + q->dlen += dlen; + + if(q->len >= q->limit/2) + q->state |= Qflow; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + iunlock(&q->lk); + + if(dowakeup) + wakeup(&q->rr); + + return len; +} + +/* + * if the allocated space is way out of line with the used + * space, reallocate to a smaller block + */ +Block* +packblock(Block *bp) +{ + Block **l, *nbp; + int n; + + for(l = &bp; *l; l = &(*l)->next){ + nbp = *l; + n = BLEN(nbp); + if((n<<2) < BALLOC(nbp)){ + *l = allocb(n); + memmove((*l)->wp, nbp->rp, n); + (*l)->wp += n; + (*l)->next = nbp->next; + freeb(nbp); + } + } + + return bp; +} + +int +qproduce(Queue *q, void *vp, int len) +{ + Block *b; + int dowakeup; + uchar *p = vp; + + /* sync with qread */ + dowakeup = 0; + ilock(&q->lk); + + /* no waiting receivers, room in buffer? */ + if(q->len >= q->limit){ + q->state |= Qflow; + iunlock(&q->lk); + return -1; + } + + /* save in buffer */ + b = iallocb(len); + if(b == 0){ + iunlock(&q->lk); + return 0; + } + memmove(b->wp, p, len); + producecnt += len; + b->wp += len; + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->blast = b; + /* b->next = 0; done by iallocb() */ + q->len += BALLOC(b); + q->dlen += BLEN(b); + QDEBUG checkb(b, "qproduce"); + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + + if(q->len >= q->limit) + q->state |= Qflow; + iunlock(&q->lk); + + if(dowakeup) + wakeup(&q->rr); + + return len; +} + +/* + * copy from offset in the queue + */ +Block* +qcopy(Queue *q, int len, ulong offset) +{ + int sofar; + int n; + Block *b, *nb; + uchar *p; + + nb = allocb(len); + + ilock(&q->lk); + + /* go to offset */ + b = q->bfirst; + for(sofar = 0; ; sofar += n){ + if(b == nil){ + iunlock(&q->lk); + return nb; + } + n = BLEN(b); + if(sofar + n > offset){ + p = b->rp + offset - sofar; + n -= offset - sofar; + break; + } + QDEBUG checkb(b, "qcopy"); + b = b->next; + } + + /* copy bytes from there */ + for(sofar = 0; sofar < len;){ + if(n > len - sofar) + n = len - sofar; + memmove(nb->wp, p, n); + qcopycnt += n; + sofar += n; + nb->wp += n; + b = b->next; + if(b == nil) + break; + n = BLEN(b); + p = b->rp; + } + iunlock(&q->lk); + + return nb; +} + +/* + * called by non-interrupt code + */ +Queue* +qopen(int limit, int msg, void (*kick)(void*), void *arg) +{ + Queue *q; + + q = malloc(sizeof(Queue)); + if(q == 0) + return 0; + + q->limit = q->inilim = limit; + q->kick = kick; + q->arg = arg; + q->state = msg; + + q->state |= Qstarve; + q->eof = 0; + q->noblock = 0; + + return q; +} + +/* open a queue to be bypassed */ +Queue* +qbypass(void (*bypass)(void*, Block*), void *arg) +{ + Queue *q; + + q = malloc(sizeof(Queue)); + if(q == 0) + return 0; + + q->limit = 0; + q->arg = arg; + q->bypass = bypass; + q->state = 0; + + return q; +} + +static int +notempty(void *a) +{ + Queue *q = a; + + return (q->state & Qclosed) || q->bfirst != 0; +} + +/* + * wait for the queue to be non-empty or closed. + * called with q ilocked. + */ +static int +qwait(Queue *q) +{ + /* wait for data */ + for(;;){ + if(q->bfirst != nil) + break; + + if(q->state & Qclosed){ + if(++q->eof > 3) + return -1; + if(*q->err && strcmp(q->err, Ehungup) != 0) + return -1; + return 0; + } + + q->state |= Qstarve; /* flag requesting producer to wake me */ + iunlock(&q->lk); + sleep(&q->rr, notempty, q); + ilock(&q->lk); + } + return 1; +} + +/* + * add a block list to a queue + */ +void +qaddlist(Queue *q, Block *b) +{ + /* queue the block */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->len += blockalloclen(b); + q->dlen += blocklen(b); + while(b->next) + b = b->next; + q->blast = b; +} + +/* + * called with q ilocked + */ +Block* +qremove(Queue *q) +{ + Block *b; + + b = q->bfirst; + if(b == nil) + return nil; + q->bfirst = b->next; + b->next = nil; + q->dlen -= BLEN(b); + q->len -= BALLOC(b); + QDEBUG checkb(b, "qremove"); + return b; +} + +/* + * copy the contents of a string of blocks into + * memory. emptied blocks are freed. return + * pointer to first unconsumed block. + */ +Block* +bl2mem(uchar *p, Block *b, int n) +{ + int i; + Block *next; + + for(; b != nil; b = next){ + i = BLEN(b); + if(i > n){ + memmove(p, b->rp, n); + b->rp += n; + return b; + } + memmove(p, b->rp, i); + n -= i; + p += i; + b->rp += i; + next = b->next; + freeb(b); + } + return nil; +} + +/* + * copy the contents of memory into a string of blocks. + * return nil on error. + */ +Block* +mem2bl(uchar *p, int len) +{ + int n; + Block *b, *first, **l; + + first = nil; + l = &first; + if(waserror()){ + freeblist(first); + nexterror(); + } + do { + n = len; + if(n > Maxatomic) + n = Maxatomic; + + *l = b = allocb(n); + /* setmalloctag(b, (up->text[0]<<24)|(up->text[1]<<16)|(up->text[2]<<8)|up->text[3]); */ + memmove(b->wp, p, n); + b->wp += n; + p += n; + len -= n; + l = &b->next; + } while(len > 0); + poperror(); + + return first; +} + +/* + * put a block back to the front of the queue + * called with q ilocked + */ +void +qputback(Queue *q, Block *b) +{ + b->next = q->bfirst; + if(q->bfirst == nil) + q->blast = b; + q->bfirst = b; + q->len += BALLOC(b); + q->dlen += BLEN(b); +} + +/* + * flow control, get producer going again + * called with q ilocked + */ +static void +qwakeup_iunlock(Queue *q) +{ + int dowakeup = 0; + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit/2){ + q->state &= ~Qflow; + dowakeup = 1; + } + + iunlock(&q->lk); + + /* wakeup flow controlled writers */ + if(dowakeup){ + if(q->kick) + q->kick(q->arg); + wakeup(&q->wr); + } +} + +/* + * get next block from a queue (up to a limit) + */ +Block* +qbread(Queue *q, int len) +{ + Block *b, *nb; + int n; + + qlock(&q->rlock); + if(waserror()){ + qunlock(&q->rlock); + nexterror(); + } + + ilock(&q->lk); + switch(qwait(q)){ + case 0: + /* queue closed */ + iunlock(&q->lk); + qunlock(&q->rlock); + poperror(); + return nil; + case -1: + /* multiple reads on a closed queue */ + iunlock(&q->lk); + error(q->err); + } + + /* if we get here, there's at least one block in the queue */ + b = qremove(q); + n = BLEN(b); + + /* split block if it's too big and this is not a message queue */ + nb = b; + if(n > len){ + if((q->state&Qmsg) == 0){ + n -= len; + b = allocb(n); + memmove(b->wp, nb->rp+len, n); + b->wp += n; + qputback(q, b); + } + nb->wp = nb->rp + len; + } + + /* restart producer */ + qwakeup_iunlock(q); + + poperror(); + qunlock(&q->rlock); + return nb; +} + +/* + * read a queue. if no data is queued, post a Block + * and wait on its Rendez. + */ +long +qread(Queue *q, void *vp, int len) +{ + Block *b, *first, **l; + int m, n; + + qlock(&q->rlock); + if(waserror()){ + qunlock(&q->rlock); + nexterror(); + } + + ilock(&q->lk); +again: + switch(qwait(q)){ + case 0: + /* queue closed */ + iunlock(&q->lk); + qunlock(&q->rlock); + poperror(); + return 0; + case -1: + /* multiple reads on a closed queue */ + iunlock(&q->lk); + error(q->err); + } + + /* if we get here, there's at least one block in the queue */ + if(q->state & Qcoalesce){ + /* when coalescing, 0 length blocks just go away */ + b = q->bfirst; + if(BLEN(b) <= 0){ + freeb(qremove(q)); + goto again; + } + + /* grab the first block plus as many + * following blocks as will completely + * fit in the read. + */ + n = 0; + l = &first; + m = BLEN(b); + for(;;) { + *l = qremove(q); + l = &b->next; + n += m; + + b = q->bfirst; + if(b == nil) + break; + m = BLEN(b); + if(n+m > len) + break; + } + } else { + first = qremove(q); + n = BLEN(first); + } + + /* copy to user space outside of the ilock */ + iunlock(&q->lk); + b = bl2mem(vp, first, len); + ilock(&q->lk); + + /* take care of any left over partial block */ + if(b != nil){ + n -= BLEN(b); + if(q->state & Qmsg) + freeb(b); + else + qputback(q, b); + } + + /* restart producer */ + qwakeup_iunlock(q); + + poperror(); + qunlock(&q->rlock); + return n; +} + +static int +qnotfull(void *a) +{ + Queue *q = a; + + return q->len < q->limit || (q->state & Qclosed); +} + +ulong noblockcnt; + +/* + * add a block to a queue obeying flow control + */ +long +qbwrite(Queue *q, Block *b) +{ + int n, dowakeup; + Proc *p; + + n = BLEN(b); + + if(q->bypass){ + (*q->bypass)(q->arg, b); + return n; + } + + dowakeup = 0; + qlock(&q->wlock); + if(waserror()){ + if(b != nil) + freeb(b); + qunlock(&q->wlock); + nexterror(); + } + + ilock(&q->lk); + + /* give up if the queue is closed */ + if(q->state & Qclosed){ + iunlock(&q->lk); + error(q->err); + } + + /* if nonblocking, don't queue over the limit */ + if(q->len >= q->limit){ + if(q->noblock){ + iunlock(&q->lk); + freeb(b); + noblockcnt += n; + qunlock(&q->wlock); + poperror(); + return n; + } + } + + /* queue the block */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->blast = b; + b->next = 0; + q->len += BALLOC(b); + q->dlen += n; + QDEBUG checkb(b, "qbwrite"); + b = nil; + + /* make sure other end gets awakened */ + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + iunlock(&q->lk); + + /* get output going again */ + if(q->kick && (dowakeup || (q->state&Qkick))) + q->kick(q->arg); + + /* wakeup anyone consuming at the other end */ + if(dowakeup){ + p = wakeup(&q->rr); + + /* if we just wokeup a higher priority process, let it run */ + /* + if(p != nil && p->priority > up->priority) + sched(); + */ + } + + /* + * flow control, wait for queue to get below the limit + * before allowing the process to continue and queue + * more. We do this here so that postnote can only + * interrupt us after the data has been queued. This + * means that things like 9p flushes and ssl messages + * will not be disrupted by software interrupts. + * + * Note - this is moderately dangerous since a process + * that keeps getting interrupted and rewriting will + * queue infinite crud. + */ + for(;;){ + if(q->noblock || qnotfull(q)) + break; + + ilock(&q->lk); + q->state |= Qflow; + iunlock(&q->lk); + sleep(&q->wr, qnotfull, q); + } + USED(b); + + qunlock(&q->wlock); + poperror(); + return n; +} + +/* + * write to a queue. only Maxatomic bytes at a time is atomic. + */ +int +qwrite(Queue *q, void *vp, int len) +{ + int n, sofar; + Block *b; + uchar *p = vp; + + QDEBUG if(!islo()) + print("qwrite hi %lux\n", getcallerpc(&q)); + + sofar = 0; + do { + n = len-sofar; + if(n > Maxatomic) + n = Maxatomic; + + b = allocb(n); + /* setmalloctag(b, (up->text[0]<<24)|(up->text[1]<<16)|(up->text[2]<<8)|up->text[3]); */ + if(waserror()){ + freeb(b); + nexterror(); + } + memmove(b->wp, p+sofar, n); + poperror(); + b->wp += n; + + qbwrite(q, b); + + sofar += n; + } while(sofar < len && (q->state & Qmsg) == 0); + + return len; +} + +/* + * used by print() to write to a queue. Since we may be splhi or not in + * a process, don't qlock. + */ +int +qiwrite(Queue *q, void *vp, int len) +{ + int n, sofar, dowakeup; + Block *b; + uchar *p = vp; + + dowakeup = 0; + + sofar = 0; + do { + n = len-sofar; + if(n > Maxatomic) + n = Maxatomic; + + b = iallocb(n); + if(b == nil) + break; + memmove(b->wp, p+sofar, n); + b->wp += n; + + ilock(&q->lk); + + QDEBUG checkb(b, "qiwrite"); + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->blast = b; + q->len += BALLOC(b); + q->dlen += n; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + + iunlock(&q->lk); + + if(dowakeup){ + if(q->kick) + q->kick(q->arg); + wakeup(&q->rr); + } + + sofar += n; + } while(sofar < len && (q->state & Qmsg) == 0); + + return sofar; +} + +/* + * be extremely careful when calling this, + * as there is no reference accounting + */ +void +qfree(Queue *q) +{ + qclose(q); + free(q); +} + +/* + * Mark a queue as closed. No further IO is permitted. + * All blocks are released. + */ +void +qclose(Queue *q) +{ + Block *bfirst; + + if(q == nil) + return; + + /* mark it */ + ilock(&q->lk); + q->state |= Qclosed; + q->state &= ~(Qflow|Qstarve); + strcpy(q->err, Ehungup); + bfirst = q->bfirst; + q->bfirst = 0; + q->len = 0; + q->dlen = 0; + q->noblock = 0; + iunlock(&q->lk); + + /* free queued blocks */ + freeblist(bfirst); + + /* wake up readers/writers */ + wakeup(&q->rr); + wakeup(&q->wr); +} + +/* + * Mark a queue as closed. Wakeup any readers. Don't remove queued + * blocks. + */ +void +qhangup(Queue *q, char *msg) +{ + /* mark it */ + ilock(&q->lk); + q->state |= Qclosed; + if(msg == 0 || *msg == 0) + strcpy(q->err, Ehungup); + else + strncpy(q->err, msg, ERRMAX-1); + iunlock(&q->lk); + + /* wake up readers/writers */ + wakeup(&q->rr); + wakeup(&q->wr); +} + +/* + * return non-zero if the q is hungup + */ +int +qisclosed(Queue *q) +{ + return q->state & Qclosed; +} + +/* + * mark a queue as no longer hung up + */ +void +qreopen(Queue *q) +{ + ilock(&q->lk); + q->state &= ~Qclosed; + q->state |= Qstarve; + q->eof = 0; + q->limit = q->inilim; + iunlock(&q->lk); +} + +/* + * return bytes queued + */ +int +qlen(Queue *q) +{ + return q->dlen; +} + +/* + * return space remaining before flow control + */ +int +qwindow(Queue *q) +{ + int l; + + l = q->limit - q->len; + if(l < 0) + l = 0; + return l; +} + +/* + * return true if we can read without blocking + */ +int +qcanread(Queue *q) +{ + return q->bfirst!=0; +} + +/* + * change queue limit + */ +void +qsetlimit(Queue *q, int limit) +{ + q->limit = limit; +} + +/* + * set blocking/nonblocking + */ +void +qnoblock(Queue *q, int onoff) +{ + q->noblock = onoff; +} + +/* + * flush the output queue + */ +void +qflush(Queue *q) +{ + Block *bfirst; + + /* mark it */ + ilock(&q->lk); + bfirst = q->bfirst; + q->bfirst = 0; + q->len = 0; + q->dlen = 0; + iunlock(&q->lk); + + /* free queued blocks */ + freeblist(bfirst); + + /* wake up readers/writers */ + wakeup(&q->wr); +} + +int +qfull(Queue *q) +{ + return q->state & Qflow; +} + +int +qstate(Queue *q) +{ + return q->state; +} diff --git a/kern/qlock.c b/kern/qlock.c new file mode 100644 index 0000000..fdbeaea --- /dev/null +++ b/kern/qlock.c @@ -0,0 +1,94 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" + +static void +queue(Proc **first, Proc **last) +{ + Proc *t; + + t = *last; + if(t == 0) + *first = up; + else + t->qnext = up; + *last = up; + up->qnext = 0; +} + +static Proc* +dequeue(Proc **first, Proc **last) +{ + Proc *t; + + t = *first; + if(t == 0) + return 0; + *first = t->qnext; + if(*first == 0) + *last = 0; + return t; +} + +void +qlock(QLock *q) +{ + lock(&q->lk); + + if(q->hold == 0) { + q->hold = up; + unlock(&q->lk); + return; + } + + /* + * Can't assert this because of RWLock + assert(q->hold != up); + */ + + queue((Proc**)&q->first, (Proc**)&q->last); + unlock(&q->lk); + procsleep(); +} + +int +canqlock(QLock *q) +{ + lock(&q->lk); + if(q->hold == 0) { + q->hold = up; + unlock(&q->lk); + return 1; + } + unlock(&q->lk); + return 0; +} + +void +qunlock(QLock *q) +{ + Proc *p; + + lock(&q->lk); + /* + * Can't assert this because of RWlock + assert(q->hold == CT); + */ + p = dequeue((Proc**)&q->first, (Proc**)&q->last); + if(p) { + q->hold = p; + unlock(&q->lk); + procwakeup(p); + } else { + q->hold = 0; + unlock(&q->lk); + } +} + +int +holdqlock(QLock *q) +{ + return q->hold == up; +} + diff --git a/kern/rendez.c b/kern/rendez.c new file mode 100644 index 0000000..6a9ad17 --- /dev/null +++ b/kern/rendez.c @@ -0,0 +1,90 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +void +sleep(Rendez *r, int (*f)(void*), void *arg) +{ + int s; + + s = splhi(); + + lock(&r->lk); + lock(&up->rlock); + if(r->p){ + print("double sleep %lud %lud\n", r->p->pid, up->pid); + dumpstack(); + } + + /* + * Wakeup only knows there may be something to do by testing + * r->p in order to get something to lock on. + * Flush that information out to memory in case the sleep is + * committed. + */ + r->p = up; + + if((*f)(arg) || up->notepending){ + /* + * if condition happened or a note is pending + * never mind + */ + r->p = nil; + unlock(&up->rlock); + unlock(&r->lk); + } else { + /* + * now we are committed to + * change state and call scheduler + */ + up->state = Wakeme; + up->r = r; + + /* statistics */ + /* m->cs++; */ + + unlock(&up->rlock); + unlock(&r->lk); + + procsleep(); + } + + if(up->notepending) { + up->notepending = 0; + splx(s); + error(Eintr); + } + + splx(s); +} + +Proc* +wakeup(Rendez *r) +{ + Proc *p; + int s; + + s = splhi(); + + lock(&r->lk); + p = r->p; + + if(p != nil){ + lock(&p->rlock); + if(p->state != Wakeme || p->r != r) + panic("wakeup: state"); + r->p = nil; + p->r = nil; + p->state = Running; + procwakeup(p); + unlock(&p->rlock); + } + unlock(&r->lk); + + splx(s); + + return p; +} + diff --git a/kern/rwlock.c b/kern/rwlock.c new file mode 100644 index 0000000..3381957 --- /dev/null +++ b/kern/rwlock.c @@ -0,0 +1,39 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +void +rlock(RWlock *l) +{ + qlock(&l->x); /* wait here for writers and exclusion */ + lock(&l->lk); + l->readers++; + canqlock(&l->k); /* block writers if we are the first reader */ + unlock(&l->lk); + qunlock(&l->x); +} + +void +runlock(RWlock *l) +{ + lock(&l->lk); + if(--l->readers == 0) /* last reader out allows writers */ + qunlock(&l->k); + unlock(&l->lk); +} + +void +wlock(RWlock *l) +{ + qlock(&l->x); /* wait here for writers and exclusion */ + qlock(&l->k); /* wait here for last reader */ +} + +void +wunlock(RWlock *l) +{ + qunlock(&l->k); + qunlock(&l->x); +} diff --git a/kern/screen.h b/kern/screen.h new file mode 100644 index 0000000..6e86539 --- /dev/null +++ b/kern/screen.h @@ -0,0 +1,59 @@ +typedef struct Mouseinfo Mouseinfo; +typedef struct Mousestate Mousestate; +typedef struct Cursorinfo Cursorinfo; +typedef struct Screeninfo Screeninfo; + +#define Mousequeue 16 /* queue can only have Mousequeue-1 elements */ +#define Mousewindow 500 /* mouse event window in millisec */ + +struct Mousestate { + int buttons; + Point xy; + ulong msec; +}; + +struct Mouseinfo { + Lock lk; + Mousestate queue[Mousequeue]; + int ri, wi; + int lastb; + int trans; + int open; + Rendez r; +}; + +struct Cursorinfo { + Lock lk; + Point offset; + uchar clr[2*16]; + uchar set[2*16]; +}; + +struct Screeninfo { + Lock lk; + Memimage *newsoft; + int reshaped; + int depth; + int dibtype; +}; + +extern Memimage *gscreen; +extern Mouseinfo mouse; +extern Cursorinfo cursor; +extern Screeninfo screen; + +void screeninit(void); +void screenload(Rectangle, int, uchar *, Point, int); + +void getcolor(ulong, ulong*, ulong*, ulong*); +void setcolor(ulong, ulong, ulong, ulong); + +void refreshrect(Rectangle); + +void cursorarrow(void); +void setcursor(void); +void mouseset(Point); +void drawflushr(Rectangle); +void flushmemscreen(Rectangle); +uchar *attachscreen(Rectangle*, ulong*, int*, int*, int*, void**); + diff --git a/kern/sleep.c b/kern/sleep.c new file mode 100644 index 0000000..6a9ad17 --- /dev/null +++ b/kern/sleep.c @@ -0,0 +1,90 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +void +sleep(Rendez *r, int (*f)(void*), void *arg) +{ + int s; + + s = splhi(); + + lock(&r->lk); + lock(&up->rlock); + if(r->p){ + print("double sleep %lud %lud\n", r->p->pid, up->pid); + dumpstack(); + } + + /* + * Wakeup only knows there may be something to do by testing + * r->p in order to get something to lock on. + * Flush that information out to memory in case the sleep is + * committed. + */ + r->p = up; + + if((*f)(arg) || up->notepending){ + /* + * if condition happened or a note is pending + * never mind + */ + r->p = nil; + unlock(&up->rlock); + unlock(&r->lk); + } else { + /* + * now we are committed to + * change state and call scheduler + */ + up->state = Wakeme; + up->r = r; + + /* statistics */ + /* m->cs++; */ + + unlock(&up->rlock); + unlock(&r->lk); + + procsleep(); + } + + if(up->notepending) { + up->notepending = 0; + splx(s); + error(Eintr); + } + + splx(s); +} + +Proc* +wakeup(Rendez *r) +{ + Proc *p; + int s; + + s = splhi(); + + lock(&r->lk); + p = r->p; + + if(p != nil){ + lock(&p->rlock); + if(p->state != Wakeme || p->r != r) + panic("wakeup: state"); + r->p = nil; + p->r = nil; + p->state = Running; + procwakeup(p); + unlock(&p->rlock); + } + unlock(&r->lk); + + splx(s); + + return p; +} + diff --git a/kern/smalloc.c b/kern/smalloc.c new file mode 100644 index 0000000..314f3c8 --- /dev/null +++ b/kern/smalloc.c @@ -0,0 +1,18 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +void* +smalloc(ulong n) +{ + return mallocz(n, 1); +} + +void* +malloc(ulong n) +{ + return mallocz(n, 1); +} + diff --git a/kern/stub.c b/kern/stub.c new file mode 100644 index 0000000..ae148d5 --- /dev/null +++ b/kern/stub.c @@ -0,0 +1,170 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +void +mallocsummary(void) +{ +} + +void +pagersummary(void) +{ +} + +int +iseve(void) +{ + return 1; +} + +void +setswapchan(Chan *c) +{ + USED(c); +} + +void +splx(int x) +{ + USED(x); +} + +int +splhi(void) +{ + return 0; +} + +int +spllo(void) +{ + return 0; +} + +void +procdump(void) +{ +} + +void +scheddump(void) +{ +} + +void +killbig(void) +{ +} + +void +dumpstack(void) +{ +} + +void +xsummary(void) +{ +} + +void +rebootcmd(int argc, char **argv) +{ + USED(argc); + USED(argv); +} + +void +kickpager(void) +{ +} + +int +userwrite(char *a, int n) +{ + error(Eperm); + return 0; +} + +vlong +todget(vlong *p) +{ + if(p) + *p = 0; + return 0; +} + +void +todset(vlong a, vlong b, int c) +{ + USED(a); + USED(b); + USED(c); +} + +void +todsetfreq(vlong a) +{ + USED(a); +} + +long +hostdomainwrite(char *a, int n) +{ + USED(a); + USED(n); + error(Eperm); + return 0; +} + +long +hostownerwrite(char *a, int n) +{ + USED(a); + USED(n); + error(Eperm); + return 0; +} + +void +todinit(void) +{ +} + +void +rdb(void) +{ +} + +void +setmalloctag(void *v, ulong tag) +{ + USED(v); + USED(tag); +} + +int +postnote(Proc *p, int x, char *msg, int flag) +{ + USED(p); + USED(x); + USED(msg); + USED(flag); +} + +void +exhausted(char *s) +{ + panic("out of %s", s); +} + +uvlong +fastticks(uvlong *v) +{ + if(v) + *v = 1; + return 0; +} + diff --git a/kern/syscall.c b/kern/syscall.c new file mode 100644 index 0000000..1ae9a7c --- /dev/null +++ b/kern/syscall.c @@ -0,0 +1,837 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +Chan* +fdtochan(int fd, int mode, int chkmnt, int iref) +{ + Fgrp *f; + Chan *c; + + c = 0; + f = up->fgrp; + + lock(&f->ref.lk); + if(fd<0 || NFD<=fd || (c = f->fd[fd])==0) { + unlock(&f->ref.lk); + error(Ebadfd); + } + if(iref) + refinc(&c->ref); + unlock(&f->ref.lk); + + if(chkmnt && (c->flag&CMSG)) + goto bad; + if(mode<0 || c->mode==ORDWR) + return c; + if((mode&OTRUNC) && c->mode==OREAD) + goto bad; + if((mode&~OTRUNC) != c->mode) + goto bad; + return c; +bad: + if(iref) + cclose(c); + error(Ebadusefd); + return nil; /* shut up compiler */ +} + +static void +fdclose(int fd, int flag) +{ + int i; + Chan *c; + Fgrp *f; + + f = up->fgrp; + + lock(&f->ref.lk); + c = f->fd[fd]; + if(c == 0) { + unlock(&f->ref.lk); + return; + } + if(flag) { + if(c==0 || !(c->flag&flag)) { + unlock(&f->ref.lk); + return; + } + } + f->fd[fd] = 0; + if(fd == f->maxfd) + for(i=fd; --i>=0 && f->fd[i]==0; ) + f->maxfd = i; + + unlock(&f->ref.lk); + cclose(c); +} + +static int +newfd(Chan *c) +{ + int i; + Fgrp *f; + + f = up->fgrp; + lock(&f->ref.lk); + for(i=0; ifd[i] == 0){ + if(i > f->maxfd) + f->maxfd = i; + f->fd[i] = c; + unlock(&f->ref.lk); + return i; + } + unlock(&f->ref.lk); + error("no file descriptors"); + return 0; +} + +int +sysclose(int fd) +{ + if(waserror()) + return -1; + + fdtochan(fd, -1, 0, 0); + fdclose(fd, 0); + poperror(); + return 0; +} + +int +syscreate(char *path, int mode, ulong perm) +{ + int fd; + Chan *c = 0; + + if(waserror()) { + cclose(c); + return -1; + } + + openmode(mode); /* error check only */ + c = namec(path, Acreate, mode, perm); + fd = newfd((Chan*)c); + poperror(); + return fd; +} + +int +sysdup(int old, int new) +{ + Chan *oc; + Fgrp *f = up->fgrp; + Chan *c = 0; + + if(waserror()) + return -1; + + c = fdtochan(old, -1, 0, 1); + if(new != -1) { + if(new < 0 || NFD <= new) { + cclose(c); + error(Ebadfd); + } + lock(&f->ref.lk); + if(new > f->maxfd) + f->maxfd = new; + oc = f->fd[new]; + f->fd[new] = (Chan*)c; + unlock(&f->ref.lk); + if(oc != 0) + cclose(oc); + } + else { + if(waserror()) { + cclose(c); + nexterror(); + } + new = newfd((Chan*)c); + poperror(); + } + poperror(); + return new; +} + +int +sysfstat(int fd, char *buf) +{ + Chan *c = 0; + + if(waserror()) { + cclose(c); + return -1; + } + c = fdtochan(fd, -1, 0, 1); + devtab[c->type]->stat((Chan*)c, buf); + poperror(); + cclose(c); + return 0; +} + +int +sysfwstat(int fd, char *buf) +{ + Chan *c = 0; + + if(waserror()) { + cclose(c); + return -1; + } + nameok(buf); + c = fdtochan(fd, -1, 1, 1); + devtab[c->type]->wstat((Chan*)c, buf); + poperror(); + cclose(c); + return 0; +} + +int +syschdir(char *dir) +{ + return 0; +} + +long +bindmount(Chan *c0, char *old, int flag, char *spec) +{ + int ret; + Chan *c1 = 0; + + if(flag>MMASK || (flag&MORDER) == (MBEFORE|MAFTER)) + error(Ebadarg); + + c1 = namec(old, Amount, 0, 0); + if(waserror()){ + cclose(c1); + nexterror(); + } + + ret = cmount(c0, c1, flag, spec); + + poperror(); + cclose(c1); + return ret; +} + +int +sysbind(char *new, char *old, int flags) +{ + long r; + Chan *c0 = 0; + + if(waserror()) { + cclose(c0); + return -1; + } + c0 = namec(new, Aaccess, 0, 0); + r = bindmount(c0, old, flags, ""); + poperror(); + cclose(c0); + return 0; +} + +int +sysmount(int fd, char *old, int flags, char *spec) +{ + long r; + Chan *c0 = 0, *bc = 0; + struct { + Chan* chan; + char* spec; + int flags; + } mntparam; + + if(waserror()) { + cclose(bc); + cclose(c0); + return -1; + } + bc = fdtochan(fd, ORDWR, 0, 1); + mntparam.chan = (Chan*)bc; + mntparam.spec = spec; + mntparam.flags = flags; + c0 = (*devtab[devno('M', 0)].attach)(&mntparam); + cclose(bc); + r = bindmount(c0, old, flags, spec); + poperror(); + cclose(c0); + + return r; +} + +int +sysunmount(char *old, char *new) +{ + Chan *cmount = 0, *cmounted = 0; + + if(waserror()) { + cclose(cmount); + cclose(cmounted); + return -1; + } + + cmount = namec(new, Amount, OREAD, 0); + if(old != 0) + cmounted = namec(old, Aopen, OREAD, 0); + + cunmount(cmount, cmounted); + poperror(); + cclose(cmount); + cclose(cmounted); + return 0; +} + +int +sysopen(char *path, int mode) +{ + int fd; + Chan *c = 0; + + if(waserror()){ + cclose(c); + return -1; + } + openmode(mode); /* error check only */ + c = namec(path, Aopen, mode, 0); + fd = newfd((Chan*)c); + poperror(); + return fd; +} + +long +unionread(Chan *c, void *va, long n) +{ + long nr; + Chan *nc = 0; + Pgrp *pg = 0; + + pg = up->pgrp; + rlock(&pg->ns); + + for(;;) { + if(waserror()) { + runlock(&pg->ns); + nexterror(); + } + nc = clone(c->mnt->to, 0); + poperror(); + + if(c->mountid != c->mnt->mountid) { + runlock(&pg->ns); + cclose(nc); + return 0; + } + + /* Error causes component of union to be skipped */ + if(waserror()) { + cclose(nc); + goto next; + } + + nc = (*devtab[nc->type].open)((Chan*)nc, OREAD); + nc->offset = c->offset; + nr = (*devtab[nc->type].read)((Chan*)nc, va, n, nc->offset); + /* devdirread e.g. changes it */ + c->offset = nc->offset; + poperror(); + + cclose(nc); + if(nr > 0) { + runlock(&pg->ns); + return nr; + } + /* Advance to next element */ + next: + c->mnt = c->mnt->next; + if(c->mnt == 0) + break; + c->mountid = c->mnt->mountid; + c->offset = 0; + } + runlock(&pg->ns); + return 0; +} + +long +sysread(int fd, void *va, long n) +{ + int dir; + Lock *cl; + Chan *c = 0; + + if(waserror()) { + cclose(c); + return -1; + } + c = fdtochan(fd, OREAD, 1, 1); + + dir = c->qid.path&CHDIR; + if(dir) { + n -= n%DIRLEN; + if(c->offset%DIRLEN || n==0) + error(Etoosmall); + } + + if(dir && c->mnt) + n = unionread((Chan*)c, va, n); + else + n = (*devtab[c->type].read)((Chan*)c, va, n, c->offset); + + cl = (Lock*)&c->r.l; + lock(cl); + c->offset += n; + unlock(cl); + + poperror(); + cclose(c); + + return n; +} + +int +sysremove(char *path) +{ + Chan *c = 0; + + if(waserror()) { + if(c != 0) + c->type = 0; /* see below */ + cclose(c); + return -1; + } + c = namec(path, Aaccess, 0, 0); + (*devtab[c->type].remove)((Chan*)c); + /* + * Remove clunks the fid, but we need to recover the Chan + * so fake it up. rootclose() is known to be a nop. + */ + c->type = 0; + poperror(); + cclose(c); + return 0; +} + +long +sysseek(int fd, long off, int whence) +{ + Dir dir; + Chan *c; + char buf[DIRLEN]; + + if(waserror()) + return -1; + + c = fdtochan(fd, -1, 1, 0); + if(c->qid.path & CHDIR) + error(Eisdir); + + switch(whence) { + case 0: + c->offset = off; + break; + + case 1: + lock(&c->r.l); /* lock for read/write update */ + c->offset += off; + off = c->offset; + unlock(&c->r.l); + break; + + case 2: + (*devtab[c->type].stat)(c, buf); + convM2D(buf, &dir); + c->offset = dir.length + off; + off = c->offset; + break; + } + poperror(); + return off; +} + +int +sysstat(char *path, char *buf) +{ + Chan *c = 0; + + if(waserror()){ + cclose(c); + return -1; + } + c = namec(path, Aaccess, 0, 0); + (*devtab[c->type].stat)((Chan*)c, buf); + poperror(); + cclose(c); + return 0; +} + +long +syswrite(int fd, void *va, long n) +{ + Lock *cl; + Chan *c = 0; + + if(waserror()) { + cclose(c); + return -1; + } + c = fdtochan(fd, OWRITE, 1, 1); + if(c->qid.path & CHDIR) + error(Eisdir); + + n = (*devtab[c->type].write)((Chan*)c, va, n, c->offset); + + cl = (Lock*)&c->r.l; + lock(cl); + c->offset += n; + unlock(cl); + + poperror(); + cclose(c); + + return n; +} + +int +syswstat(char *path, char *buf) +{ + Chan *c = 0; + + if(waserror()) { + cclose(c); + return -1; + } + + nameok(buf); + c = namec(path, Aaccess, 0, 0); + (*devtab[c->type].wstat)((Chan*)c, buf); + poperror(); + cclose(c); + return 0; +} + +int +sysdirstat(char *name, Dir *dir) +{ + char buf[DIRLEN]; + + if(sysstat(name, buf) == -1) + return -1; + convM2D(buf, dir); + return 0; +} + +int +sysdirfstat(int fd, Dir *dir) +{ + char buf[DIRLEN]; + + if(sysfstat(fd, buf) == -1) + return -1; + + convM2D(buf, dir); + return 0; +} + +int +sysdirwstat(char *name, Dir *dir) +{ + char buf[DIRLEN]; + + convD2M(dir, buf); + return syswstat(name, buf); +} + +int +sysdirfwstat(int fd, Dir *dir) +{ + char buf[DIRLEN]; + + convD2M(dir, buf); + return sysfwstat(fd, buf); +} + +long +sysdirread(int fd, Dir *dbuf, long count) +{ + int c, n, i, r; + char buf[DIRLEN*50]; + + n = 0; + count = (count/sizeof(Dir)) * DIRLEN; + while(n < count) { + c = count - n; + if(c > sizeof(buf)) + c = sizeof(buf); + r = sysread(fd, buf, c); + if(r == 0) + break; + if(r < 0 || r % DIRLEN) + return -1; + for(i=0; i 0){ + net[n] = 0; + p = strchr(net, ' '); + if(p == 0) + continue; + *p++ = 0; + rv = call(net, p, cfdp, dir, local); + if(rv >= 0) + break; + } + sysclose(fd); + return rv; +} + +static int +identtrans(char *addr, char *naddr, int na, char *file, int nf) +{ + char *p; + char reply[4*NAMELEN]; + + /* parse the network */ + strncpy(reply, addr, sizeof(reply)); + reply[sizeof(reply)-1] = 0; + p = strchr(reply, '!'); + if(p) + *p++ = 0; + + sprint(file, "/net/%.*s/clone", na - sizeof("/net//clone"), reply); + strncpy(naddr, p, na); + naddr[na-1] = 0; + return 1; +} + +static int +nettrans(char *addr, char *naddr, int na, char *file, int nf) +{ + long n; + int fd; + char *cp; + char reply[4*NAMELEN]; + + /* + * ask the connection server + */ + fd = sysopen("/net/cs", ORDWR); + if(fd < 0) + return identtrans(addr, naddr, na, file, nf); + if(syswrite(fd, addr, strlen(addr)) < 0){ + sysclose(fd); + return -1; + } + sysseek(fd, 0, 0); + n = sysread(fd, reply, sizeof(reply)-1); + sysclose(fd); + if(n <= 0) + return -1; + reply[n] = '\0'; + + /* + * parse the reply + */ + cp = strchr(reply, ' '); + if(cp == 0) + return -1; + *cp++ = 0; + strncpy(naddr, cp, na); + naddr[na-1] = 0; + strncpy(file, reply, nf); + file[nf-1] = 0; + return 0; +} + +int +sysannounce(char *addr, char *dir) +{ + char *cp; + int ctl, n, m; + char buf[3*NAMELEN]; + char buf2[3*NAMELEN]; + char netdir[2*NAMELEN]; + char naddr[3*NAMELEN]; + + /* + * translate the address + */ + if(nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0){ + werrstr("can't translate address"); + return -1; + } + + /* + * get a control channel + */ + ctl = sysopen(netdir, ORDWR); + if(ctl<0){ + werrstr("can't open control channel"); + return -1; + } + cp = strrchr(netdir, '/'); + *cp = 0; + + /* + * find out which line we have + */ + n = sprint(buf, "%.*s/", 2*NAMELEN+1, netdir); + m = sysread(ctl, &buf[n], sizeof(buf)-n-1); + if(m <= 0) { + sysclose(ctl); + werrstr("can't read control file"); + return -1; + } + buf[n+m] = 0; + + /* + * make the call + */ + n = sprint(buf2, "announce %.*s", 2*NAMELEN, naddr); + if(syswrite(ctl, buf2, n) != n) { + sysclose(ctl); + werrstr("announcement fails"); + return -1; + } + + strcpy(dir, buf); + + return ctl; +} + +int +syslisten(char *dir, char *newdir) +{ + char *cp; + int ctl, n, m; + char buf[3*NAMELEN]; + + /* + * open listen, wait for a call + */ + sprint(buf, "%.*s/listen", 2*NAMELEN+1, dir); + ctl = sysopen(buf, ORDWR); + if(ctl < 0) + return -1; + + /* + * find out which line we have + */ + strcpy(buf, dir); + cp = strrchr(buf, '/'); + *++cp = 0; + n = cp-buf; + m = sysread(ctl, cp, sizeof(buf) - n - 1); + if(n<=0){ + sysclose(ctl); + return -1; + } + buf[n+m] = 0; + + strcpy(newdir, buf); + return ctl; +} diff --git a/kern/sysfile.c b/kern/sysfile.c new file mode 100644 index 0000000..526c15f --- /dev/null +++ b/kern/sysfile.c @@ -0,0 +1,1242 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "user.h" +#undef open +#undef mount +#undef read +#undef write +#undef seek +#undef stat +#undef wstat +#undef remove +#undef close +#undef fstat +#undef fwstat + +/* + * The sys*() routines needn't poperror() as they return directly to syscall(). + */ + +static void +unlockfgrp(Fgrp *f) +{ + int ex; + + ex = f->exceed; + f->exceed = 0; + unlock(&f->ref.lk); + if(ex) + pprint("warning: process exceeds %d file descriptors\n", ex); +} + +int +growfd(Fgrp *f, int fd) /* fd is always >= 0 */ +{ + Chan **newfd, **oldfd; + + if(fd < f->nfd) + return 0; + if(fd >= f->nfd+DELTAFD) + return -1; /* out of range */ + /* + * Unbounded allocation is unwise; besides, there are only 16 bits + * of fid in 9P + */ + if(f->nfd >= 5000){ + Exhausted: + print("no free file descriptors\n"); + return -1; + } + newfd = malloc((f->nfd+DELTAFD)*sizeof(Chan*)); + if(newfd == 0) + goto Exhausted; + oldfd = f->fd; + memmove(newfd, oldfd, f->nfd*sizeof(Chan*)); + f->fd = newfd; + free(oldfd); + f->nfd += DELTAFD; + if(fd > f->maxfd){ + if(fd/100 > f->maxfd/100) + f->exceed = (fd/100)*100; + f->maxfd = fd; + } + return 1; +} + +/* + * this assumes that the fgrp is locked + */ +int +findfreefd(Fgrp *f, int start) +{ + int fd; + + for(fd=start; fdnfd; fd++) + if(f->fd[fd] == 0) + break; + if(fd >= f->nfd && growfd(f, fd) < 0) + return -1; + return fd; +} + +int +newfd(Chan *c) +{ + int fd; + Fgrp *f; + + f = up->fgrp; + lock(&f->ref.lk); + fd = findfreefd(f, 0); + if(fd < 0){ + unlockfgrp(f); + return -1; + } + if(fd > f->maxfd) + f->maxfd = fd; + f->fd[fd] = c; + unlockfgrp(f); + return fd; +} + +int +newfd2(int fd[2], Chan *c[2]) +{ + Fgrp *f; + + f = up->fgrp; + lock(&f->ref.lk); + fd[0] = findfreefd(f, 0); + if(fd[0] < 0){ + unlockfgrp(f); + return -1; + } + fd[1] = findfreefd(f, fd[0]+1); + if(fd[1] < 0){ + unlockfgrp(f); + return -1; + } + if(fd[1] > f->maxfd) + f->maxfd = fd[1]; + f->fd[fd[0]] = c[0]; + f->fd[fd[1]] = c[1]; + unlockfgrp(f); + + return 0; +} + +Chan* +fdtochan(int fd, int mode, int chkmnt, int iref) +{ + Chan *c; + Fgrp *f; + + c = 0; + f = up->fgrp; + + lock(&f->ref.lk); + if(fd<0 || f->nfd<=fd || (c = f->fd[fd])==0) { + unlock(&f->ref.lk); + error(Ebadfd); + } + if(iref) + incref(&c->ref); + unlock(&f->ref.lk); + + if(chkmnt && (c->flag&CMSG)) { + if(iref) + cclose(c); + error(Ebadusefd); + } + + if(mode<0 || c->mode==ORDWR) + return c; + + if((mode&OTRUNC) && c->mode==OREAD) { + if(iref) + cclose(c); + error(Ebadusefd); + } + + if((mode&~OTRUNC) != c->mode) { + if(iref) + cclose(c); + error(Ebadusefd); + } + + return c; +} + +int +openmode(ulong o) +{ + o &= ~(OTRUNC|OCEXEC|ORCLOSE); + if(o > OEXEC) + error(Ebadarg); + if(o == OEXEC) + return OREAD; + return o; +} + +long +_sysfd2path(int fd, char *buf, uint nbuf) +{ + Chan *c; + + c = fdtochan(fd, -1, 0, 1); + + if(c->name == nil) + snprint(buf, nbuf, ""); + else + snprint(buf, nbuf, "%s", c->name->s); + cclose(c); + return 0; +} + +long +_syspipe(int fd[2]) +{ + Chan *c[2]; + Dev *d; + static char *datastr[] = {"data", "data1"}; + + d = devtab[devno('|', 0)]; + c[0] = namec("#|", Atodir, 0, 0); + c[1] = 0; + fd[0] = -1; + fd[1] = -1; + + if(waserror()){ + cclose(c[0]); + if(c[1]) + cclose(c[1]); + nexterror(); + } + c[1] = cclone(c[0]); + if(walk(&c[0], datastr+0, 1, 1, nil) < 0) + error(Egreg); + if(walk(&c[1], datastr+1, 1, 1, nil) < 0) + error(Egreg); + c[0] = d->open(c[0], ORDWR); + c[1] = d->open(c[1], ORDWR); + if(newfd2(fd, c) < 0) + error(Enofd); + poperror(); + + return 0; +} + +long +_sysdup(int fd0, int fd1) +{ + int fd; + Chan *c, *oc; + Fgrp *f = up->fgrp; + + /* + * Close after dup'ing, so date > #d/1 works + */ + c = fdtochan(fd0, -1, 0, 1); + fd = fd1; + if(fd != -1){ + lock(&f->ref.lk); + if(fd<0 || growfd(f, fd)<0) { + unlockfgrp(f); + cclose(c); + error(Ebadfd); + } + if(fd > f->maxfd) + f->maxfd = fd; + + oc = f->fd[fd]; + f->fd[fd] = c; + unlockfgrp(f); + if(oc) + cclose(oc); + }else{ + if(waserror()) { + cclose(c); + nexterror(); + } + fd = newfd(c); + if(fd < 0) + error(Enofd); + poperror(); + } + + return fd; +} + +long +_sysopen(char *name, int mode) +{ + int fd; + Chan *c = 0; + + openmode(mode); /* error check only */ + if(waserror()){ + if(c) + cclose(c); + nexterror(); + } + c = namec(name, Aopen, mode, 0); + fd = newfd(c); + if(fd < 0) + error(Enofd); + poperror(); + return fd; +} + +void +fdclose(int fd, int flag) +{ + int i; + Chan *c; + Fgrp *f = up->fgrp; + + lock(&f->ref.lk); + c = f->fd[fd]; + if(c == 0){ + /* can happen for users with shared fd tables */ + unlock(&f->ref.lk); + return; + } + if(flag){ + if(c==0 || !(c->flag&flag)){ + unlock(&f->ref.lk); + return; + } + } + f->fd[fd] = 0; + if(fd == f->maxfd) + for(i=fd; --i>=0 && f->fd[i]==0; ) + f->maxfd = i; + + unlock(&f->ref.lk); + cclose(c); +} + +long +_sysclose(int fd) +{ + fdtochan(fd, -1, 0, 0); + fdclose(fd, 0); + + return 0; +} + +long +unionread(Chan *c, void *va, long n) +{ + int i; + long nr; + Mhead *m; + Mount *mount; + + qlock(&c->umqlock); + m = c->umh; + rlock(&m->lock); + mount = m->mount; + /* bring mount in sync with c->uri and c->umc */ + for(i = 0; mount != nil && i < c->uri; i++) + mount = mount->next; + + nr = 0; + while(mount != nil) { + /* Error causes component of union to be skipped */ + if(mount->to && !waserror()) { + if(c->umc == nil){ + c->umc = cclone(mount->to); + c->umc = devtab[c->umc->type]->open(c->umc, OREAD); + } + + nr = devtab[c->umc->type]->read(c->umc, va, n, c->umc->offset); + c->umc->offset += nr; + poperror(); + } + if(nr > 0) + break; + + /* Advance to next element */ + c->uri++; + if(c->umc) { + cclose(c->umc); + c->umc = nil; + } + mount = mount->next; + } + runlock(&m->lock); + qunlock(&c->umqlock); + return nr; +} + +static long +kread(int fd, void *buf, long n, vlong *offp) +{ + int dir; + Chan *c; + vlong off; + + c = fdtochan(fd, OREAD, 1, 1); + + if(waserror()) { + cclose(c); + nexterror(); + } + + dir = c->qid.type&QTDIR; + /* + * The offset is passed through on directories, normally. sysseek complains but + * pread is used by servers and e.g. exportfs that shouldn't need to worry about this issue. + */ + + if(offp == nil) /* use and maintain channel's offset */ + off = c->offset; + else + off = *offp; + + if(off < 0) + error(Enegoff); + + if(dir && c->umh) + n = unionread(c, buf, n); + else + n = devtab[c->type]->read(c, buf, n, off); + + if(offp == nil){ + lock(&c->ref.lk); + c->offset += n; + unlock(&c->ref.lk); + } + + poperror(); + cclose(c); + + return n; +} + +long +_sys_read(int fd, void *buf, long n) +{ + return kread(fd, buf, n, nil); +} + +long +_syspread(int fd, void *buf, long n, vlong off) +{ + if(off == ((uvlong) ~0)) + return kread(fd, buf, n, nil); + return kread(fd, buf, n, &off); +} + +static long +kwrite(int fd, void *buf, long nn, vlong *offp) +{ + Chan *c; + long m, n; + vlong off; + + n = 0; + c = fdtochan(fd, OWRITE, 1, 1); + if(waserror()) { + if(offp == nil){ + lock(&c->ref.lk); + c->offset -= n; + unlock(&c->ref.lk); + } + cclose(c); + nexterror(); + } + + if(c->qid.type & QTDIR) + error(Eisdir); + + n = nn; + + if(offp == nil){ /* use and maintain channel's offset */ + lock(&c->ref.lk); + off = c->offset; + c->offset += n; + unlock(&c->ref.lk); + }else + off = *offp; + + if(off < 0) + error(Enegoff); + + m = devtab[c->type]->write(c, buf, n, off); + + if(offp == nil && m < n){ + lock(&c->ref.lk); + c->offset -= n - m; + unlock(&c->ref.lk); + } + + poperror(); + cclose(c); + + return m; +} + +long +sys_write(int fd, void *buf, long n) +{ + return kwrite(fd, buf, n, nil); +} + +long +_syspwrite(int fd, void *buf, long n, vlong off) +{ + if(off == ((uvlong) ~0)) + return kwrite(fd, buf, n, nil); + return kwrite(fd, buf, n, &off); +} + +static vlong +_sysseek(int fd, vlong off, int whence) +{ + Chan *c; + uchar buf[sizeof(Dir)+100]; + Dir dir; + int n; + + c = fdtochan(fd, -1, 1, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + if(devtab[c->type]->dc == '|') + error(Eisstream); + + switch(whence){ + case 0: + if((c->qid.type & QTDIR) && off != 0) + error(Eisdir); + if(off < 0) + error(Enegoff); + c->offset = off; + break; + + case 1: + if(c->qid.type & QTDIR) + error(Eisdir); + lock(&c->ref.lk); /* lock for read/write update */ + off = off + c->offset; + if(off < 0) + error(Enegoff); + c->offset = off; + unlock(&c->ref.lk); + break; + + case 2: + if(c->qid.type & QTDIR) + error(Eisdir); + n = devtab[c->type]->stat(c, buf, sizeof buf); + if(convM2D(buf, n, &dir, nil) == 0) + error("internal error: stat error in seek"); + off = dir.length + off; + if(off < 0) + error(Enegoff); + c->offset = off; + break; + + default: + error(Ebadarg); + } + c->uri = 0; + c->dri = 0; + cclose(c); + poperror(); + return off; +} + +void +validstat(uchar *s, int n) +{ + int m; + char buf[64]; + + if(statcheck(s, n) < 0) + error(Ebadstat); + /* verify that name entry is acceptable */ + s += STATFIXLEN - 4*BIT16SZ; /* location of first string */ + /* + * s now points at count for first string. + * if it's too long, let the server decide; this is + * only for his protection anyway. otherwise + * we'd have to allocate and waserror. + */ + m = GBIT16(s); + s += BIT16SZ; + if(m+1 > sizeof buf) + return; + memmove(buf, s, m); + buf[m] = '\0'; + /* name could be '/' */ + if(strcmp(buf, "/") != 0) + validname(buf, 0); +} + +long +_sysfstat(int fd, void *buf, long n) +{ + Chan *c; + uint l; + + l = n; + validaddr(buf, l, 1); + c = fdtochan(fd, -1, 0, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + l = devtab[c->type]->stat(c, buf, l); + poperror(); + cclose(c); + return l; +} + +long +_sysstat(char *name, void *buf, long n) +{ + Chan *c; + uint l; + + l = n; + validaddr(buf, l, 1); + validaddr(name, 1, 0); + c = namec(name, Aaccess, 0, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + l = devtab[c->type]->stat(c, buf, l); + poperror(); + cclose(c); + return l; +} + +long +_syschdir(char *name) +{ + Chan *c; + + validaddr(name, 1, 0); + + c = namec(name, Atodir, 0, 0); + cclose(up->dot); + up->dot = c; + return 0; +} + +long +bindmount(int ismount, int fd, int afd, char* arg0, char* arg1, ulong flag, char* spec) +{ + int ret; + Chan *c0, *c1, *ac, *bc; + struct{ + Chan *chan; + Chan *authchan; + char *spec; + int flags; + }bogus; + + if((flag&~MMASK) || (flag&MORDER)==(MBEFORE|MAFTER)) + error(Ebadarg); + + bogus.flags = flag & MCACHE; + + if(ismount){ + if(up->pgrp->noattach) + error(Enoattach); + + ac = nil; + bc = fdtochan(fd, ORDWR, 0, 1); + if(waserror()) { + if(ac) + cclose(ac); + cclose(bc); + nexterror(); + } + + if(afd >= 0) + ac = fdtochan(afd, ORDWR, 0, 1); + + bogus.chan = bc; + bogus.authchan = ac; + + validaddr((ulong)spec, 1, 0); + bogus.spec = spec; + if(waserror()) + error(Ebadspec); + validname(spec, 1); + poperror(); + + ret = devno('M', 0); + c0 = devtab[ret]->attach((char*)&bogus); + + poperror(); + if(ac) + cclose(ac); + cclose(bc); + }else{ + bogus.spec = 0; + validaddr((ulong)arg0, 1, 0); + c0 = namec(arg0, Abind, 0, 0); + } + + if(waserror()){ + cclose(c0); + nexterror(); + } + + validaddr((ulong)arg1, 1, 0); + c1 = namec(arg1, Amount, 0, 0); + if(waserror()){ + cclose(c1); + nexterror(); + } + + ret = cmount(&c0, c1, flag, bogus.spec); + + poperror(); + cclose(c1); + poperror(); + cclose(c0); + if(ismount) + fdclose(fd, 0); + + return ret; +} + +long +_sysbind(char *old, char *new, int flag) +{ + return bindmount(0, -1, -1, old, new, flag, nil); +} + +long +_sysmount(int fd, int afd, char *new, int flag, char *spec) +{ + return bindmount(1, fd, afd, nil, new, flag, spec); +} + +long +_sysunmount(char *old, char *new) +{ + Chan *cmount, *cmounted; + + cmounted = 0; + + cmount = namec(new, Amount, 0, 0); + + if(old) { + if(waserror()) { + cclose(cmount); + nexterror(); + } + validaddr(old, 1, 0); + /* + * This has to be namec(..., Aopen, ...) because + * if arg[0] is something like /srv/cs or /fd/0, + * opening it is the only way to get at the real + * Chan underneath. + */ + cmounted = namec(old, Aopen, OREAD, 0); + poperror(); + } + + if(waserror()) { + cclose(cmount); + if(cmounted) + cclose(cmounted); + nexterror(); + } + + cunmount(cmount, cmounted); + cclose(cmount); + if(cmounted) + cclose(cmounted); + poperror(); + return 0; +} + +long +_syscreate(char *name, int mode, ulong perm) +{ + int fd; + Chan *c = 0; + + openmode(mode&~OEXCL); /* error check only; OEXCL okay here */ + if(waserror()) { + if(c) + cclose(c); + nexterror(); + } + validaddr(name, 1, 0); + c = namec(name, Acreate, mode, perm); + fd = newfd(c); + if(fd < 0) + error(Enofd); + poperror(); + return fd; +} + +long +_sysremove(char *name) +{ + Chan *c; + + c = namec(name, Aremove, 0, 0); + if(waserror()){ + c->type = 0; /* see below */ + cclose(c); + nexterror(); + } + devtab[c->type]->remove(c); + /* + * Remove clunks the fid, but we need to recover the Chan + * so fake it up. rootclose() is known to be a nop. + */ + c->type = 0; + poperror(); + cclose(c); + return 0; +} + +long +_syswstat(char *name, void *buf, long n) +{ + Chan *c; + uint l; + + l = n; + validstat(buf, l); + validaddr(name, 1, 0); + c = namec(name, Aaccess, 0, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + l = devtab[c->type]->wstat(c, buf, l); + poperror(); + cclose(c); + return l; +} + +long +_sysfwstat(int fd, void *buf, long n) +{ + Chan *c; + uint l; + + l = n; + validaddr(buf, l, 0); + validstat(buf, l); + c = fdtochan(fd, -1, 1, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + l = devtab[c->type]->wstat(c, buf, l); + poperror(); + cclose(c); + return l; +} + + +static void +starterror(void) +{ + assert(up->nerrlab == 0); +} + +static void +_syserror(void) +{ + char *p; + + p = up->syserrstr; + up->syserrstr = up->errstr; + up->errstr = p; +} + +static void +enderror(void) +{ + assert(up->nerrlab == 1); + poperror(); +} + +int +sysbind(char *old, char *new, int flag) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysbind(old, new, flag); + enderror(); + return n; +} + +int +syschdir(char *path) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _syschdir(path); + enderror(); + return n; +} + +int +sysclose(int fd) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysclose(fd); + enderror(); + return n; +} + +int +syscreate(char *name, int mode, ulong perm) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _syscreate(name, mode, perm); + enderror(); + return n; +} + +int +sysdup(int fd0, int fd1) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysdup(fd0, fd1); + enderror(); + return n; +} + +int +sysfstat(int fd, uchar *buf, int n) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysfstat(fd, buf, n); + enderror(); + return n; +} + +int +sysfwstat(int fd, uchar *buf, int n) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysfwstat(fd, buf, n); + enderror(); + return n; +} + +int +sysmount(int fd, int afd, char *new, int flag, char *spec) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysmount(fd, afd, new, flag, spec); + enderror(); + return n; +} + +int +sysunmount(char *old, char *new) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysunmount(old, new); + enderror(); + return n; +} + +int +sysopen(char *name, int mode) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysopen(name, mode); + enderror(); + return n; +} + +int +syspipe(int *fd) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _syspipe(fd); + enderror(); + return n; +} + +long +syspread(int fd, void *buf, long n, vlong off) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _syspread(fd, buf, n, off); + enderror(); + return n; +} + +long +syspwrite(int fd, void *buf, long n, vlong off) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _syspwrite(fd, buf, n, off); + enderror(); + return n; +} + +long +sysread(int fd, void *buf, long n) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _syspread(fd, buf, n, (uvlong) ~0); + enderror(); + return n; +} + +int +sysremove(char *path) +{ + int n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysremove(path); + enderror(); + return n; +} + +vlong +sysseek(int fd, vlong off, int whence) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + off = _sysseek(fd, off, whence); + enderror(); + return off; +} + +int +sysstat(char *name, uchar *buf, int n) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysstat(name, buf, n); + enderror(); + return n; +} + +long +syswrite(int fd, void *buf, long n) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _syspwrite(fd, buf, n, (uvlong) ~0); + enderror(); + return n; +} + +int +syswstat(char *name, uchar *buf, int n) +{ + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _syswstat(name, buf, n); + enderror(); + return n; +} + +void +werrstr(char *f, ...) +{ + char buf[ERRMAX]; + va_list arg; + + va_start(arg, f); + vsnprint(buf, sizeof buf, f, arg); + va_end(arg); + + if(up->nerrlab) + strecpy(up->errstr, up->errstr+ERRMAX, buf); + else + strecpy(up->syserrstr, up->syserrstr+ERRMAX, buf); +} + +int +errfmt(Fmt *fmt) +{ + if(up->nerrlab) + return fmtstrcpy(fmt, up->errstr); + else + return fmtstrcpy(fmt, up->syserrstr); +} + +int +errstr(char *buf, uint n) +{ + char tmp[ERRMAX]; + char *p; + + p = up->nerrlab ? up->errstr : up->syserrstr; + memmove(tmp, p, ERRMAX); + utfecpy(p, p+ERRMAX, buf); + utfecpy(buf, buf+n, tmp); + return strlen(buf); +} + +int +rerrstr(char *buf, uint n) +{ + char *p; + + p = up->nerrlab ? up->errstr : up->syserrstr; + utfecpy(buf, buf+n, p); + return strlen(buf); +} + +ulong +_sysrendezvous(ulong arg0, ulong arg1) +{ + ulong tag, val; + Proc *p, **l; + + tag = arg0; + l = &REND(up->rgrp, tag); + up->rendval = ~0UL; + + lock(&up->rgrp->ref.lk); + for(p = *l; p; p = p->rendhash) { + if(p->rendtag == tag) { + *l = p->rendhash; + val = p->rendval; + p->rendval = arg1; + + while(p->mach != 0) + ; + procwakeup(p); + unlock(&up->rgrp->ref.lk); + return val; + } + l = &p->rendhash; + } + + /* Going to sleep here */ + up->rendtag = tag; + up->rendval = arg1; + up->rendhash = *l; + *l = up; + up->state = Rendezvous; + unlock(&up->rgrp->ref.lk); + + procsleep(); + + return up->rendval; +} + +ulong +sysrendezvous(ulong tag, ulong val) +{ + ulong n; + + starterror(); + if(waserror()){ + _syserror(); + return -1; + } + n = _sysrendezvous(tag, val); + enderror(); + return n; +} diff --git a/kern/sysproc.c b/kern/sysproc.c new file mode 100644 index 0000000..11caeb7 --- /dev/null +++ b/kern/sysproc.c @@ -0,0 +1,32 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +long +sysexits(ulong *arg) +{ + char *status; + char *inval = "invalid exit string"; + char buf[ERRMAX]; + + status = (char*)arg[0]; + if(status){ + if(waserror()) + status = inval; + else{ + validaddr((ulong)status, 1, 0); + if(vmemchr(status, 0, ERRMAX) == 0){ + memmove(buf, status, ERRMAX); + buf[ERRMAX-1] = 0; + status = buf; + } + } + poperror(); + + } + pexit(status, 1); + return 0; /* not reached */ +} + diff --git a/kern/term.c b/kern/term.c new file mode 100644 index 0000000..f1e93ea --- /dev/null +++ b/kern/term.c @@ -0,0 +1,204 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include +#include +#include "screen.h" + +#define MINX 8 +#define Backgnd 0xFF /* white */ + + Memsubfont *memdefont; + +struct{ + Point pos; + int bwid; +}out; + +Lock screenlock; + +Memimage *conscol; +Memimage *back; +extern Memimage *gscreen; + +static Rectangle flushr; +static Rectangle window; +static Point curpos; +static int h, w; +static void termscreenputs(char*, int); + +Point ZP; + +static void +screenflush(void) +{ + drawflushr(flushr); + flushr = Rect(10000, 10000, -10000, -10000); +} + +static void +addflush(Rectangle r) +{ + if(flushr.min.x >= flushr.max.x) + flushr = r; + else + combinerect(&flushr, r); +} + +static void +screenwin(void) +{ + Point p, q; + char *greet; + Memimage *grey; + + back = memwhite; + conscol = memblack; + memfillcolor(gscreen, 0x444488FF); + + w = memdefont->info[' '].width; + h = memdefont->height; + + window.min = addpt(gscreen->r.min, Pt(20,20)); + window.max.x = window.min.x + Dx(gscreen->r)*3/4-40; + window.max.y = window.min.y + Dy(gscreen->r)*3/4-100; + + memimagedraw(gscreen, window, memblack, ZP, memopaque, ZP, S); + window = insetrect(window, 4); + memimagedraw(gscreen, window, memwhite, ZP, memopaque, ZP, S); + + /* a lot of work to get a grey color */ + grey = allocmemimage(Rect(0,0,1,1), CMAP8); + grey->flags |= Frepl; + grey->clipr = gscreen->r; + memfillcolor(grey, 0xAAAAAAFF); + memimagedraw(gscreen, Rect(window.min.x, window.min.y, + window.max.x, window.min.y+h+5+6), grey, ZP, nil, ZP, S); + freememimage(grey); + window = insetrect(window, 5); + + greet = " Plan 9 Console "; + p = addpt(window.min, Pt(10, 0)); + q = memsubfontwidth(memdefont, greet); + memimagestring(gscreen, p, conscol, ZP, memdefont, greet); + window.min.y += h+6; + curpos = window.min; + window.max.y = window.min.y+((window.max.y-window.min.y)/h)*h; + flushmemscreen(gscreen->r); +} + +void +terminit(void) +{ + memdefont = getmemdefont(); + out.pos.x = MINX; + out.pos.y = 0; + out.bwid = memdefont->info[' '].width; + screenwin(); + screenputs = termscreenputs; +} + +static void +scroll(void) +{ + int o; + Point p; + Rectangle r; + + o = 8*h; + r = Rpt(window.min, Pt(window.max.x, window.max.y-o)); + p = Pt(window.min.x, window.min.y+o); + memimagedraw(gscreen, r, gscreen, p, nil, p, S); + r = Rpt(Pt(window.min.x, window.max.y-o), window.max); + memimagedraw(gscreen, r, back, ZP, nil, ZP, S); + flushmemscreen(gscreen->r); + + curpos.y -= o; +} + +static void +screenputc(char *buf) +{ + Point p; + int w, pos; + Rectangle r; + static int *xp; + static int xbuf[256]; + + if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)]) + xp = xbuf; + + switch(buf[0]) { + case '\n': + if(curpos.y+h >= window.max.y) + scroll(); + curpos.y += h; + screenputc("\r"); + break; + case '\r': + xp = xbuf; + curpos.x = window.min.x; + break; + case '\t': + p = memsubfontwidth(memdefont, " "); + w = p.x; + *xp++ = curpos.x; + pos = (curpos.x-window.min.x)/w; + pos = 8-(pos%8); + r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y + h); + memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S); + addflush(r); + curpos.x += pos*w; + break; + case '\b': + if(xp <= xbuf) + break; + xp--; + r = Rect(*xp, curpos.y, curpos.x, curpos.y + h); + memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S); + addflush(r); + curpos.x = *xp; + break; + default: + p = memsubfontwidth(memdefont, buf); + w = p.x; + + if(curpos.x >= window.max.x-w) + screenputc("\n"); + + *xp++ = curpos.x; + r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y + h); + memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S); + memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf); + addflush(r); + curpos.x += w; + } +} + +static void +termscreenputs(char *s, int n) +{ + int i; + Rune r; + char buf[4]; + + lock(&screenlock); + while(n > 0){ + i = chartorune(&r, s); + if(i == 0){ + s++; + --n; + continue; + } + memmove(buf, s, i); + buf[i] = 0; + n -= i; + s += i; + screenputc(buf); + } + screenflush(); + unlock(&screenlock); +} diff --git a/kern/todo.c b/kern/todo.c new file mode 100644 index 0000000..e69de29 diff --git a/kern/uart.c b/kern/uart.c new file mode 100644 index 0000000..79c9420 --- /dev/null +++ b/kern/uart.c @@ -0,0 +1,14 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +#undef write +void +uartputs(char *s, int n) +{ + write(1, s, n); +} + + diff --git a/kern/waserror.c b/kern/waserror.c new file mode 100644 index 0000000..3a07bc9 --- /dev/null +++ b/kern/waserror.c @@ -0,0 +1,27 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +Label* +pwaserror(void) +{ + if(up->nerrlab == NERR) + panic("error stack overflow"); + return &up->errlab[up->nerrlab++]; +} + +void +nexterror(void) +{ + longjmp(up->errlab[--up->nerrlab].buf, 1); +} + +void +error(char *e) +{ + kstrcpy(up->errstr, e, ERRMAX); + setjmp(up->errlab[NERR-1].buf); + nexterror(); +} diff --git a/kern/winduhz.h b/kern/winduhz.h new file mode 100644 index 0000000..82fa75a --- /dev/null +++ b/kern/winduhz.h @@ -0,0 +1,18 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* disable various silly warnings */ +#pragma warning( disable : 4245 4305 4244 4102 4761 4090 4028 4024) + +typedef __int64 p9_vlong; +typedef unsigned __int64 p9_uvlong; diff --git a/kern/x b/kern/x new file mode 100644 index 0000000..ea5c3e4 --- /dev/null +++ b/kern/x @@ -0,0 +1,601 @@ +#include +#include +#include +#include +#include +#include /* for remove, rename */ +#include + +#ifndef NAME_MAX +# define NAME_MAX 256 +#endif +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + + +typedef struct Ufsinfo Ufsinfo; + +enum +{ + NUID = 256, + NGID = 256, + MAXPATH = 1024, + MAXCOMP = 128 +}; + +struct Ufsinfo +{ + int mode; + int fd; + DIR* dir; + ulong offset; + QLock oq; + char nextname[NAME_MAX]; +}; + +char *base = "/"; + +static Qid fsqid(char*, struct stat *); +static void fspath(Chan*, char*, char*); +static ulong fsdirread(Chan*, uchar*, int, ulong); +static int fsomode(int); + +static char* +lastelem(Chan *c) +{ + char *s, *t; + + s = c2name(c); + if((t = strrchr(s, '/')) == nil) + return s; + if(t[1] == 0) + return t; + return t+1; +} + +static Chan* +fsattach(char *spec) +{ + Chan *c; + struct stat stbuf; + static int devno; + Ufsinfo *uif; + + if(stat(base, &stbuf) < 0) + error(strerror(errno)); + + c = devattach('U', spec); + + uif = mallocz(sizeof(Ufsinfo), 1); + uif->mode = stbuf.st_mode; + + c->aux = uif; + c->dev = devno++; + + return c; +} + +static Chan* +fsclone(Chan *c, Chan *nc) +{ + Ufsinfo *uif; + + uif = mallocz(sizeof(Ufsinfo), 1); + *uif = *(Ufsinfo*)c->aux; + nc->aux = uif; + + return nc; +} + +static int +fswalk1(Chan *c, char *name) +{ + struct stat stbuf; + char path[MAXPATH]; + Ufsinfo *uif; + + fspath(c, name, path); + +/* print("** fs walk '%s' -> %s\n", path, name); */ + + if(stat(path, &stbuf) < 0) + return 0; + + uif = c->aux; + + uif->mode = stbuf.st_mode; + + c->qid = fsqid(path, &stbuf); + + return 1; +} + +static Walkqid* +fswalk(Chan *c, Chan *nc, char **name, int nname) +{ + int i; + Walkqid *wq; + + if(nc != nil) + panic("fswalk: nc != nil"); + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + nc = devclone(c); + fsclone(c, nc); + wq->clone = nc; + for(i=0; iqid[i] = nc->qid; + } + if(i != nname){ + cclose(nc); + wq->clone = nil; + } + wq->nqid = i; + return wq; +} + +static int +fsstat(Chan *c, uchar *buf, int n) +{ + Dir d; + struct stat stbuf; + char path[MAXPATH]; + + if(n < BIT16SZ) + error(Eshortstat); + + fspath(c, 0, path); + if(stat(path, &stbuf) < 0) + error(strerror(errno)); + + d.name = lastelem(c); + d.uid = "unknown"; + d.gid = "unknown"; + d.qid = c->qid; + d.mode = (c->qid.type<<24)|(stbuf.st_mode&0777); + d.atime = stbuf.st_atime; + d.mtime = stbuf.st_mtime; + d.length = stbuf.st_size; + d.type = 'U'; + d.dev = c->dev; + return convD2M(&d, buf, n); +} + +static Chan* +fsopen(Chan *c, int mode) +{ + char path[MAXPATH]; + int m, isdir; + Ufsinfo *uif; + + m = mode & (OTRUNC|3); + switch(m) { + case 0: + break; + case 1: + case 1|16: + break; + case 2: + case 0|16: + case 2|16: + break; + case 3: + break; + default: + error(Ebadarg); + } + + isdir = c->qid.type & QTDIR; + + if(isdir && mode != OREAD) + error(Eperm); + + m = fsomode(m & 3); + c->mode = openmode(mode); + + uif = c->aux; + + fspath(c, 0, path); + if(isdir) { + uif->dir = opendir(path); + if(uif->dir == 0) + error(strerror(errno)); + } + else { + if(mode & OTRUNC) + m |= O_TRUNC; + uif->fd = open(path, m, 0666); + + if(uif->fd < 0) + error(strerror(errno)); + } + uif->offset = 0; + + c->offset = 0; + c->flag |= COPEN; + return c; +} + +static void +fscreate(Chan *c, char *name, int mode, ulong perm) +{ + int fd, m; + char path[MAXPATH]; + struct stat stbuf; + Ufsinfo *uif; + + m = fsomode(mode&3); + + fspath(c, name, path); + + uif = c->aux; + + if(perm & DMDIR) { + if(m) + error(Eperm); + + if(mkdir(path, perm & 0777) < 0) + error(strerror(errno)); + + fd = open(path, 0); + if(fd >= 0) { + chmod(path, perm & 0777); + chown(path, uif->uid, uif->uid); + } + close(fd); + + uif->dir = opendir(path); + if(uif->dir == 0) + error(strerror(errno)); + } + else { + fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666); + if(fd >= 0) { + if(m != 1) { + close(fd); + fd = open(path, m); + } + chmod(path, perm & 0777); + chown(path, uif->uid, uif->gid); + } + if(fd < 0) + error(strerror(errno)); + uif->fd = fd; + } + + if(stat(path, &stbuf) < 0) + error(strerror(errno)); + c->qid = fsqid(path, &stbuf); + c->offset = 0; + c->flag |= COPEN; + c->mode = openmode(mode); +} + +static void +fsclose(Chan *c) +{ + Ufsinfo *uif; + + uif = c->aux; + + if(c->flag & COPEN) { + if(c->qid.type & QTDIR) + closedir(uif->dir); + else + close(uif->fd); + } + + free(uif); +} + +static long +fsread(Chan *c, void *va, long n, vlong offset) +{ + int fd, r; + Ufsinfo *uif; + + if(c->qid.type & QTDIR) + return fsdirread(c, va, n, offset); + + uif = c->aux; + qlock(&uif->oq); + if(waserror()) { + qunlock(&uif->oq); + nexterror(); + } + fd = uif->fd; + if(uif->offset != offset) { + r = lseek(fd, offset, 0); + if(r < 0) + error(strerror(errno)); + uif->offset = offset; + } + + n = read(fd, va, n); + if(n < 0) + error(strerror(errno)); + + uif->offset += n; + qunlock(&uif->oq); + poperror(); + + return n; +} + +static long +fswrite(Chan *c, void *va, long n, vlong offset) +{ + int fd, r; + Ufsinfo *uif; + + uif = c->aux; + + qlock(&uif->oq); + if(waserror()) { + qunlock(&uif->oq); + nexterror(); + } + fd = uif->fd; + if(uif->offset != offset) { + r = lseek(fd, offset, 0); + if(r < 0) + error(strerror(errno)); + uif->offset = offset; + } + + n = write(fd, va, n); + if(n < 0) + error(strerror(errno)); + + uif->offset += n; + qunlock(&uif->oq); + poperror(); + + return n; +} + +static void +fsremove(Chan *c) +{ + int n; + char path[MAXPATH]; + + fspath(c, 0, path); + if(c->qid.type & QTDIR) + n = rmdir(path); + else + n = remove(path); + if(n < 0) + error(strerror(errno)); +} + +void +fswstat(Chan *c, uchar *buf, int n) +{ + Dir d; + struct stat stbuf; + char old[MAXPATH], new[MAXPATH], dir[MAXPATH]; + char strs[MAXPATH*3]; + Ufsinfo *uif; + + if(convM2D(buf, n, &d, strs) != n) + error(Ebadstat); + + fspath(c, 0, old); + if(stat(old, &stbuf) < 0) + error(strerror(errno)); + + uif = c->aux; + + if(d.name[0] && strcmp(d.name, lastelem(c)) != 0) { + fspath(c->parent, 0, dir); + fspath(c, 0, old); + strcpy(new, old); + p = strrchr(new, '/'); + strcpy(p+1, d.name); + if(rename(old, new) < 0) + error(strerror(errno)); + } + + fspath(c, 0, old); + if(~d.mode != 0 && (int)(d.mode&0777) != (int)(stbuf.st_mode&0777)) { + if(chmod(old, d.mode&0777) < 0) + error(strerror(errno)); + uif->mode &= ~0777; + uif->mode |= d.mode&0777; + } +/* + p = name2pass(gid, d.gid); + if(p == 0) + error(Eunknown); + + if(p->id != stbuf.st_gid) { + if(chown(old, stbuf.st_uid, p->id) < 0) + error(strerror(errno)); + + uif->gid = p->id; + } +*/ +} + +static Qid +fsqid(char *p, struct stat *st) +{ + Qid q; + int dev; + ulong h; + static int nqdev; + static uchar *qdev; + + if(qdev == 0) + qdev = mallocz(65536U, 1); + + q.type = 0; + if((st->st_mode&S_IFMT) == S_IFDIR) + q.type = QTDIR; + + dev = st->st_dev & 0xFFFFUL; + if(qdev[dev] == 0) + qdev[dev] = ++nqdev; + + h = 0; + while(*p != '\0') + h += *p++ * 13; + + q.path = (vlong)qdev[dev]<<32; + q.path |= h; + q.vers = st->st_mtime; + + return q; +} + +static void +fspath(Chan *c, char *ext, char *path) +{ + int i, n; + char *comp[MAXCOMP]; + + strcpy(path, base); + strcat(path, "/"); + strcat(path, c2name(c)); + if(ext){ + strcat(path, "/"); + strcat(path, ext); + } + cleanname(path); +} + +static int +isdots(char *name) +{ + if(name[0] != '.') + return 0; + if(name[1] == '\0') + return 1; + if(name[1] != '.') + return 0; + if(name[2] == '\0') + return 1; + return 0; +} + +static int +p9readdir(char *name, Ufsinfo *uif) +{ + struct dirent *de; + + if(uif->nextname[0]){ + strcpy(name, uif->nextname); + uif->nextname[0] = 0; + return 1; + } + + de = readdir(uif->dir); + if(de == NULL) + return 0; + + strcpy(name, de->d_name); + return 1; +} + +static ulong +fsdirread(Chan *c, uchar *va, int count, ulong offset) +{ + int i; + Dir d; + long n; + char de[NAME_MAX]; + struct stat stbuf; + char path[MAXPATH], dirpath[MAXPATH]; + Ufsinfo *uif; + int n; + + i = 0; + uif = c->aux; + + errno = 0; + if(uif->offset != offset) { + if(offset != 0) + error("bad offset in fsdirread"); + uif->offset = offset; /* sync offset */ + uif->nextname[0] = 0; + rewinddir(uif->dir); + } + + fspath(c, 0, dirpath); + + while(i+BIT16SZ < count) { + if(!p9readdir(de, uif)) + break; + + if(de[0]==0 || isdots(de)) + continue; + + d.name = de; + sprint(path, "%s/%s", dirpath, de); + memset(&stbuf, 0, sizeof stbuf); + + if(stat(path, &stbuf) < 0) { + fprint(2, "dir: bad path %s\n", path); + /* but continue... probably a bad symlink */ + } + + d.uid = "unknown"; + d.gid = "unknown"; + d.qid = fsqid(path, &stbuf); + d.mode = (d.qid.type<<24)|(stbuf.st_mode&0777); + d.atime = stbuf.st_atime; + d.mtime = stbuf.st_mtime; + d.length = stbuf.st_size; + d.type = 'U'; + d.dev = c->dev; + n = convD2M(&d, (char*)va+i, count-i); + if(n == BIT16SZ){ + strcpy(uif->nextname, de); + break; + } + i += n; + } + return i; +} + +static int +fsomode(int m) +{ + switch(m) { + case 0: /* OREAD */ + case 3: /* OEXEC */ + return 0; + case 1: /* OWRITE */ + return 1; + case 2: /* ORDWR */ + return 2; + } + error(Ebadarg); + return 0; +} + +Dev fsdevtab = { + 'U', + "fs", + + devreset, + devinit, + devshutdown, + fsattach, + fswalk, + fsstat, + fsopen, + fscreate, + fsclose, + fsread, + devbread, + fswrite, + devbwrite, + fsremove, + fswstat, +}; diff --git a/latin1.c b/latin1.c new file mode 100644 index 0000000..eede94b --- /dev/null +++ b/latin1.c @@ -0,0 +1,177 @@ +#include "u.h" +#include "libc.h" + +/* + * The code makes two assumptions: strlen(ld) is 1 or 2; latintab[i].ld can be a + * prefix of latintab[j].ld only when j", { 0x21D2, 0x2255, 0x2261, 0x229C, 0x22DC, 0x22DD, }, + "V", "=", { 0x21D0, }, + "7", "8", { 0x215E, }, + "5", "68", { 0x215A, 0x215D, }, + "4", "5", { 0x2158, }, + "R", "R", { 0x211D, }, + "Q", "Q", { 0x211A, }, + "P", "P", { 0x2119, }, + "C", "CAU", { 0x2102, 0x22C2, 0x22C3, }, + "e", "nmsl", { 0x2013, 0x2014, 0x2205, 0x22EF, }, + "b", "u0123456789+-=()kqrbnp", { 0x2022, 0x2080, 0x2081, 0x2082, 0x2083, 0x2084, 0x2085, 0x2086, 0x2087, 0x2088, 0x2089, 0x208A, 0x208B, 0x208C, 0x208D, 0x208E, 0x265A, 0x265B, 0x265C, 0x265D, 0x265E, 0x265F, }, + "@e", "h", { 0x44D, }, + "@\'", "\'", { 0x44A, }, + "@s", "hc", { 0x448, 0x449, }, + "@c", "h", { 0x447, }, + "@t", "s", { 0x446, }, + "@k", "h", { 0x445, }, + "@z", "h", { 0x436, }, + "@y", "euao", { 0x435, 0x44E, 0x44F, 0x451, }, + "@E", "Hh", { 0x42D, 0x42D, }, + "@S", "HhCc", { 0x428, 0x428, 0x429, 0x429, }, + "@C", "Hh", { 0x427, 0x427, }, + "@T", "Ss", { 0x426, 0x426, }, + "@K", "Hh", { 0x425, 0x425, }, + "@Z", "Hh", { 0x416, 0x416, }, + "@@", "EZKSTYezksty\'", { 0x415, 0x417, 0x41A, 0x421, 0x422, 0x42B, 0x435, 0x437, 0x43A, 0x441, 0x442, 0x44B, 0x44C, }, + "@Y", "OoEeUuAa", { 0x401, 0x401, 0x415, 0x415, 0x42E, 0x42E, 0x42F, 0x42F, }, + "@", "ABVGDIJLMNOPRUFXabvgdijlmnoprufx", { 0x410, 0x411, 0x412, 0x413, 0x414, 0x418, 0x419, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x420, 0x423, 0x424, 0x425, 0x430, 0x431, 0x432, 0x433, 0x434, 0x438, 0x439, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x440, 0x443, 0x444, 0x445, }, + "*", "ABGDEZYHIKLMNCOPRSTUFXQWabgdezyhiklmncoprstufxqw*", { 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F, 0x3A0, 0x3A1, 0x3A3, 0x3A4, 0x3A5, 0x3A6, 0x3A7, 0x3A8, 0x3A9, 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7, 0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE, 0x3BF, 0x3C0, 0x3C1, 0x3C3, 0x3C4, 0x3C5, 0x3C6, 0x3C7, 0x3C8, 0x3C9, 0x2217, }, + "G", "-", { 0x1E4, }, + "N", "JjN", { 0x1CA, 0x1CB, 0x2115, }, + "2", "-35", { 0x1BB, 0x2154, 0x2156, }, + "z", "-", { 0x1B6, }, + "Z", "-Z", { 0x1B5, 0x2124, }, + "Y", "R", { 0x1A6, }, + "h", "v-", { 0x195, 0x210F, }, + "$*", "hfk", { 0x3D1, 0x3D5, 0x3F0, }, + "$", "fVavgHILlpRBeEFMo", { 0x192, 0x1B2, 0x251, 0x28B, 0x210A, 0x210B, 0x2110, 0x2112, 0x2113, 0x2118, 0x211B, 0x212C, 0x212F, 0x2130, 0x2131, 0x2133, 0x2134, }, + "t", "-smefu", { 0x167, 0x3C2, 0x2122, 0x2203, 0x2234, 0x22A2, }, + "T", "-u", { 0x166, 0x22A8, }, + "L", "-Jj&|", { 0x141, 0x1C7, 0x1C8, 0x22C0, 0x22C1, }, + "i", "j-fsbp", { 0x133, 0x268, 0x221E, 0x222B, 0x2286, 0x2287, }, + "I", "J-", { 0x132, 0x197, }, + "H", "-H", { 0x126, 0x210D, }, + "v\"", "Uu", { 0x1D9, 0x1DA, }, + "v", "CcDdEeLlNnRrSsTtZzAaIiOoUuGgKkj", { 0x10C, 0x10D, 0x10E, 0x10F, 0x11A, 0x11B, 0x13D, 0x13E, 0x147, 0x148, 0x158, 0x159, 0x160, 0x161, 0x164, 0x165, 0x17D, 0x17E, 0x1CD, 0x1CE, 0x1CF, 0x1D0, 0x1D1, 0x1D2, 0x1D3, 0x1D4, 0x1E6, 0x1E7, 0x1E8, 0x1E9, 0x1F0, }, + "u", "AEeGgIiOoUu-a", { 0x102, 0x114, 0x115, 0x11E, 0x11F, 0x12C, 0x12D, 0x14E, 0x14F, 0x16C, 0x16D, 0x289, 0x2191, }, + ":", "-=()", { 0xF7, 0x2254, 0x2639, 0x263A, }, + "a", "ebn", { 0xE6, 0x2194, 0x2220, }, + "/", "Oo", { 0xD8, 0xF8, }, + "Dv", "Zz", { 0x1C4, 0x1C5, }, + "D", "-e", { 0xD0, 0x2206, }, + "A", "E", { 0xC6, }, + "o", "AaeUuiO", { 0xC5, 0xE5, 0x153, 0x16E, 0x16F, 0x1A3, 0x229A, }, + "~!", "=", { 0x2246, }, + "~", "ANOanoIiUu-=~", { 0xC3, 0xD1, 0xD5, 0xE3, 0xF1, 0xF5, 0x128, 0x129, 0x168, 0x169, 0x2243, 0x2245, 0x2248, }, + "^", "AEIOUaeiouCcGgHhJjSsWwYy", { 0xC2, 0xCA, 0xCE, 0xD4, 0xDB, 0xE2, 0xEA, 0xEE, 0xF4, 0xFB, 0x108, 0x109, 0x11C, 0x11D, 0x124, 0x125, 0x134, 0x135, 0x15C, 0x15D, 0x174, 0x175, 0x176, 0x177, }, + "`\"", "Uu", { 0x1DB, 0x1DC, }, + "`", "AEIOUaeiou", { 0xC0, 0xC8, 0xCC, 0xD2, 0xD9, 0xE0, 0xE8, 0xEC, 0xF2, 0xF9, }, + "?", "?!", { 0xBF, 0x203D, }, + "3", "458", { 0xBE, 0x2157, 0x215C, }, + "1", "423568", { 0xBC, 0xBD, 0x2153, 0x2155, 0x2159, 0x215B, }, + ">!", "=~", { 0x2269, 0x22E7, }, + ">", ">=~<", { 0xBB, 0x2265, 0x2273, 0x2277, }, + ",", ",CcAaEeGgIiKkLlNnRrSsTtUuOo", { 0xB8, 0xC7, 0xE7, 0x104, 0x105, 0x118, 0x119, 0x122, 0x123, 0x12E, 0x12F, 0x136, 0x137, 0x13B, 0x13C, 0x145, 0x146, 0x156, 0x157, 0x15E, 0x15F, 0x162, 0x163, 0x172, 0x173, 0x1EA, 0x1EB, }, + ".", ".CcEeGgILlZzO", { 0xB7, 0x10A, 0x10B, 0x116, 0x117, 0x120, 0x121, 0x130, 0x13F, 0x140, 0x17B, 0x17C, 0x2299, }, + "p", "gOdrt", { 0xB6, 0x2117, 0x2202, 0x220F, 0x221D, }, + "m", "iuo", { 0xB5, 0xD7, 0x2208, }, + "\'\"", "Uu", { 0x1D7, 0x1D8, }, + "\'", "\'AEIOUYaeiouyCcgLlNnRrSsZz", { 0xB4, 0xC1, 0xC9, 0xCD, 0xD3, 0xDA, 0xDD, 0xE1, 0xE9, 0xED, 0xF3, 0xFA, 0xFD, 0x106, 0x107, 0x123, 0x139, 0x13A, 0x143, 0x144, 0x154, 0x155, 0x15A, 0x15B, 0x179, 0x17A, }, + "+", "-O", { 0xB1, 0x2295, }, + "dv", "z", { 0x1C6, }, + "d", "e-zgda", { 0xB0, 0xF0, 0x2A3, 0x2020, 0x2021, 0x2193, }, + "_,", "Oo", { 0x1EC, 0x1ED, }, + "_.", "Aa", { 0x1E0, 0x1E1, }, + "_\"", "UuAa", { 0x1D5, 0x1D6, 0x1DE, 0x1DF, }, + "_", "_AaEeIiOoUu", { 0xAF, 0x100, 0x101, 0x112, 0x113, 0x12A, 0x12B, 0x14C, 0x14D, 0x16A, 0x16B, }, + "r", "O\'\"", { 0xAE, 0x2019, 0x201D, }, + "-*", "l", { 0x19B, }, + "-", "-Dd:HLlTtbIZz2Ggiuh>+~O", { 0xAD, 0xD0, 0xF0, 0xF7, 0x126, 0x141, 0x142, 0x166, 0x167, 0x180, 0x197, 0x1B5, 0x1B6, 0x1BB, 0x1E4, 0x1E5, 0x268, 0x289, 0x210F, 0x2192, 0x2213, 0x2242, 0x2296, }, + "n", "oj", { 0xAC, 0x1CC, }, + "", { 0xAB, 0x2190, 0x2264, 0x2272, 0x2276, }, + "s", "a231os0456789+-=()nturbp", { 0xAA, 0xB2, 0xB3, 0xB9, 0xBA, 0xDF, 0x2070, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, 0x207A, 0x207B, 0x207C, 0x207D, 0x207E, 0x207F, 0x220D, 0x2211, 0x221A, 0x2282, 0x2283, }, + "O", "crEIp+-x/.o*=", { 0xA9, 0xAE, 0x152, 0x1A2, 0x2117, 0x2295, 0x2296, 0x2297, 0x2298, 0x2299, 0x229A, 0x229B, 0x229C, }, + "\"*", "IUiu", { 0x3AA, 0x3AB, 0x3CA, 0x3CB, }, + "\"", "\"AEIOUaeiouyY", { 0xA8, 0xC4, 0xCB, 0xCF, 0xD6, 0xDC, 0xE4, 0xEB, 0xEF, 0xF6, 0xFC, 0xFF, 0x178, }, + "S", "S", { 0xA7, }, + "|", "|Pp", { 0xA6, 0xDE, 0xFE, }, + "y", "$", { 0xA5, }, + "g", "$-r", { 0xA4, 0x1E5, 0x2207, }, + "l", "$-j\'\"&|z", { 0xA3, 0x142, 0x1C9, 0x2018, 0x201C, 0x2227, 0x2228, 0x22C4, }, + "c", "$Oaug", { 0xA2, 0xA9, 0x2229, 0x222A, 0x2245, }, + "!~", "-=~", { 0x2244, 0x2247, 0x2249, }, + "!", "!?m=<>bp", { 0xA1, 0x203D, 0x2209, 0x2260, 0x226E, 0x226F, 0x2284, 0x2285, }, + 0, 0, { 0, } +}; + +/* + * Given 5 characters k[0]..k[4], find the rune or return -1 for failure. + */ +long +unicode(Rune *k) +{ + long i, c; + + k++; /* skip 'X' */ + c = 0; + for(i=0; i<4; i++,k++){ + c <<= 4; + if('0'<=*k && *k<='9') + c += *k-'0'; + else if('a'<=*k && *k<='f') + c += 10 + *k-'a'; + else if('A'<=*k && *k<='F') + c += 10 + *k-'A'; + else + return -1; + } + return c; +} + +/* + * Given n characters k[0]..k[n-1], find the corresponding rune or return -1 for + * failure, or something < -1 if n is too small. In the latter case, the result + * is minus the required n. + */ +long +latin1(Rune *k, int n) +{ + struct cvlist *l; + int c; + char* p; + + if(k[0] == 'X'){ + if(n>=5) + return unicode(k); + else + return -5; + } + + for(l=latintab; l->ld!=0; l++) + if(k[0] == l->ld[0]){ + if(n == 1) + return -2; + if(l->ld[1] == 0) + c = k[1]; + else if(l->ld[1] != k[1]) + continue; + else if(n == 2) + return -3; + else + c = k[2]; + for(p=l->si; *p!=0; p++) + if(*p == c) + return l->so[p - l->si]; + return -1; + } + return -1; +} diff --git a/libauthsrv/Makefile b/libauthsrv/Makefile new file mode 100644 index 0000000..0f2b0b5 --- /dev/null +++ b/libauthsrv/Makefile @@ -0,0 +1,28 @@ +LIB=libauthsrv.a +CC=gcc +CFLAGS=-I../include -I. -c -ggdb -D_THREAD_SAFE -pthread +O=o + +OFILES=\ + _asgetticket.$O\ + _asrdresp.$O\ + convA2M.$O\ + convM2A.$O\ + convM2PR.$O\ + convM2T.$O\ + convM2TR.$O\ + convPR2M.$O\ + convT2M.$O\ + convTR2M.$O\ + nvcsum.$O\ + opasstokey.$O\ + passtokey.$O\ + readnvram.$O\ + +$(LIB): $(OFILES) + ar r $(LIB) $(OFILES) + ranlib $(LIB) + +%.$O: %.c + $(CC) $(CFLAGS) $*.c + diff --git a/libauthsrv/authdial.c b/libauthsrv/authdial.c new file mode 100644 index 0000000..c2463a1 --- /dev/null +++ b/libauthsrv/authdial.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include + +int +authdial(char *netroot, char *dom) +{ + char server[Ndbvlen]; + Ndbtuple *nt; + + + if(dom != nil){ + /* look up an auth server in an authentication domain */ + nt = csgetval(netroot, "authdom", dom, "auth", server); + + /* if that didn't work, just try the IP domain */ + if(nt == nil) + nt = csgetval(netroot, "dom", dom, "auth", server); + if(nt == nil){ + werrstr("no auth server found for %s", dom); + return -1; + } + ndbfree(nt); + return dial(netmkaddr(server, netroot, "ticket"), 0, 0, 0); + } else { + /* look for one relative to my machine */ + return dial(netmkaddr("$auth", netroot, "ticket"), 0, 0, 0); + } +} diff --git a/libauthsrv/convA2M.c b/libauthsrv/convA2M.c new file mode 100644 index 0000000..2799cbf --- /dev/null +++ b/libauthsrv/convA2M.c @@ -0,0 +1,25 @@ +#include +#include +#include + +#define CHAR(x) *p++ = f->x +#define SHORT(x) p[0] = f->x; p[1] = f->x>>8; p += 2 +#define VLONG(q) p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4 +#define LONG(x) VLONG(f->x) +#define STRING(x,n) memmove(p, f->x, n); p += n + +int +convA2M(Authenticator *f, char *ap, char *key) +{ + int n; + uchar *p; + + p = (uchar*)ap; + CHAR(num); + STRING(chal, CHALLEN); + LONG(id); + n = p - (uchar*)ap; + if(key) + encrypt(key, ap, n); + return n; +} diff --git a/libauthsrv/convM2A.c b/libauthsrv/convM2A.c new file mode 100644 index 0000000..3d58f9b --- /dev/null +++ b/libauthsrv/convM2A.c @@ -0,0 +1,23 @@ +#include +#include +#include + +#define CHAR(x) f->x = *p++ +#define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2 +#define VLONG(q) q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4 +#define LONG(x) VLONG(f->x) +#define STRING(x,n) memmove(f->x, p, n); p += n + +void +convM2A(char *ap, Authenticator *f, char *key) +{ + uchar *p; + + if(key) + decrypt(key, ap, AUTHENTLEN); + p = (uchar*)ap; + CHAR(num); + STRING(chal, CHALLEN); + LONG(id); + USED(p); +} diff --git a/libauthsrv/convM2PR.c b/libauthsrv/convM2PR.c new file mode 100644 index 0000000..21df5b5 --- /dev/null +++ b/libauthsrv/convM2PR.c @@ -0,0 +1,28 @@ +#include +#include +#include + +#define CHAR(x) f->x = *p++ +#define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2 +#define VLONG(q) q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4 +#define LONG(x) VLONG(f->x) +#define STRING(x,n) memmove(f->x, p, n); p += n + +void +convM2PR(char *ap, Passwordreq *f, char *key) +{ + uchar *p; + + p = (uchar*)ap; + if(key) + decrypt(key, ap, PASSREQLEN); + CHAR(num); + STRING(old, ANAMELEN); + f->old[ANAMELEN-1] = 0; + STRING(new, ANAMELEN); + f->new[ANAMELEN-1] = 0; + CHAR(changesecret); + STRING(secret, SECRETLEN); + f->secret[SECRETLEN-1] = 0; + USED(p); +} diff --git a/libauthsrv/convM2T.c b/libauthsrv/convM2T.c new file mode 100644 index 0000000..372825a --- /dev/null +++ b/libauthsrv/convM2T.c @@ -0,0 +1,28 @@ +#include +#include +#include + +#define CHAR(x) f->x = *p++ +#define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2 +#define VLONG(q) q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4 +#define LONG(x) VLONG(f->x) +#define STRING(x,n) memmove(f->x, p, n); p += n + +void +convM2T(char *ap, Ticket *f, char *key) +{ + uchar *p; + + if(key) + decrypt(key, ap, TICKETLEN); + p = (uchar*)ap; + CHAR(num); + STRING(chal, CHALLEN); + STRING(cuid, ANAMELEN); + f->cuid[ANAMELEN-1] = 0; + STRING(suid, ANAMELEN); + f->suid[ANAMELEN-1] = 0; + STRING(key, DESKEYLEN); + USED(p); +} + diff --git a/libauthsrv/convM2TR.c b/libauthsrv/convM2TR.c new file mode 100644 index 0000000..ffad75c --- /dev/null +++ b/libauthsrv/convM2TR.c @@ -0,0 +1,28 @@ +#include +#include +#include + +#define CHAR(x) f->x = *p++ +#define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2 +#define VLONG(q) q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4 +#define LONG(x) VLONG(f->x) +#define STRING(x,n) memmove(f->x, p, n); p += n + +void +convM2TR(char *ap, Ticketreq *f) +{ + uchar *p; + + p = (uchar*)ap; + CHAR(type); + STRING(authid, ANAMELEN); + f->authid[ANAMELEN-1] = 0; + STRING(authdom, DOMLEN); + f->authdom[DOMLEN-1] = 0; + STRING(chal, CHALLEN); + STRING(hostid, ANAMELEN); + f->hostid[ANAMELEN-1] = 0; + STRING(uid, ANAMELEN); + f->uid[ANAMELEN-1] = 0; + USED(p); +} diff --git a/libauthsrv/convPR2M.c b/libauthsrv/convPR2M.c new file mode 100644 index 0000000..8b2422f --- /dev/null +++ b/libauthsrv/convPR2M.c @@ -0,0 +1,28 @@ +#include +#include +#include + +#define CHAR(x) *p++ = f->x +#define SHORT(x) p[0] = f->x; p[1] = f->x>>8; p += 2 +#define VLONG(q) p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4 +#define LONG(x) VLONG(f->x) +#define STRING(x,n) memmove(p, f->x, n); p += n + +int +convPR2M(Passwordreq *f, char *ap, char *key) +{ + int n; + uchar *p; + + p = (uchar*)ap; + CHAR(num); + STRING(old, ANAMELEN); + STRING(new, ANAMELEN); + CHAR(changesecret); + STRING(secret, SECRETLEN); + n = p - (uchar*)ap; + if(key) + encrypt(key, ap, n); + return n; +} + diff --git a/libauthsrv/convT2M.c b/libauthsrv/convT2M.c new file mode 100644 index 0000000..810ba5c --- /dev/null +++ b/libauthsrv/convT2M.c @@ -0,0 +1,27 @@ +#include +#include +#include + +#define CHAR(x) *p++ = f->x +#define SHORT(x) p[0] = f->x; p[1] = f->x>>8; p += 2 +#define VLONG(q) p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4 +#define LONG(x) VLONG(f->x) +#define STRING(x,n) memmove(p, f->x, n); p += n + +int +convT2M(Ticket *f, char *ap, char *key) +{ + int n; + uchar *p; + + p = (uchar*)ap; + CHAR(num); + STRING(chal, CHALLEN); + STRING(cuid, ANAMELEN); + STRING(suid, ANAMELEN); + STRING(key, DESKEYLEN); + n = p - (uchar*)ap; + if(key) + encrypt(key, ap, n); + return n; +} diff --git a/libauthsrv/convTR2M.c b/libauthsrv/convTR2M.c new file mode 100644 index 0000000..3a7610a --- /dev/null +++ b/libauthsrv/convTR2M.c @@ -0,0 +1,27 @@ +#include +#include +#include + +#define CHAR(x) *p++ = f->x +#define SHORT(x) p[0] = f->x; p[1] = f->x>>8; p += 2 +#define VLONG(q) p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4 +#define LONG(x) VLONG(f->x) +#define STRING(x,n) memmove(p, f->x, n); p += n + +int +convTR2M(Ticketreq *f, char *ap) +{ + int n; + uchar *p; + + p = (uchar*)ap; + CHAR(type); + STRING(authid, 28); /* BUG */ + STRING(authdom, DOMLEN); + STRING(chal, CHALLEN); + STRING(hostid, 28); /* BUG */ + STRING(uid, 28); /* BUG */ + n = p - (uchar*)ap; + return n; +} + diff --git a/libauthsrv/mkfile b/libauthsrv/mkfile new file mode 100644 index 0000000..dbbf583 --- /dev/null +++ b/libauthsrv/mkfile @@ -0,0 +1,25 @@ +<$DSRC/mkfile-$CONF +TARG=libauthsrv.$L + +OFILES=\ + _asgetticket.$O\ + _asrdresp.$O\ + convA2M.$O\ + convM2A.$O\ + convM2PR.$O\ + convM2T.$O\ + convM2TR.$O\ + convPR2M.$O\ + convT2M.$O\ + convTR2M.$O\ + nvcsum.$O\ + opasstokey.$O\ + passtokey.$O\ + readnvram.$O\ + +HFILE=\ + + +TARGOBJ=${OFILES:%=$TARG(%)} + +<$DSRC/mklib-$CONF diff --git a/libauthsrv/nvcsum.c b/libauthsrv/nvcsum.c new file mode 100644 index 0000000..306d7f7 --- /dev/null +++ b/libauthsrv/nvcsum.c @@ -0,0 +1,16 @@ +#include +#include +#include + +uchar +nvcsum(void *vmem, int n) +{ + uchar *mem, sum; + int i; + + sum = 9; + mem = vmem; + for(i = 0; i < n; i++) + sum += mem[i]; + return sum; +} diff --git a/libauthsrv/opasstokey.c b/libauthsrv/opasstokey.c new file mode 100644 index 0000000..d263d4b --- /dev/null +++ b/libauthsrv/opasstokey.c @@ -0,0 +1,29 @@ +#include +#include +#include + +int +opasstokey(char *key, char *p) +{ + uchar t[10]; + int c, n; + + n = strlen(p); + memset(t, ' ', sizeof t); + if(n < 5) + return 0; + if(n > 10) + n = 10; + strncpy((char*)t, p, n); + if(n >= 9){ + c = p[8] & 0xf; + if(n == 10) + c += p[9] << 4; + for(n = 0; n < 8; n++) + if(c & (1 << n)) + t[n] -= ' '; + } + for(n = 0; n < 7; n++) + key[n] = (t[n] >> n) + (t[n+1] << (8 - (n+1))); + return 1; +} diff --git a/libauthsrv/passtokey.c b/libauthsrv/passtokey.c new file mode 100644 index 0000000..8e267ce --- /dev/null +++ b/libauthsrv/passtokey.c @@ -0,0 +1,33 @@ +#include +#include +#include + +int +passtokey(char *key, char *p) +{ + uchar buf[ANAMELEN], *t; + int i, n; + + n = strlen(p); + if(n >= ANAMELEN) + n = ANAMELEN-1; + memset(buf, ' ', 8); + t = buf; + strncpy((char*)t, p, n); + t[n] = 0; + memset(key, 0, DESKEYLEN); + for(;;){ + for(i = 0; i < DESKEYLEN; i++) + key[i] = (t[i] >> i) + (t[i+1] << (8 - (i+1))); + if(n <= 8) + return 1; + n -= 8; + t += 8; + if(n < 8){ + t -= 8 - n; + n = 8; + } + encrypt(key, t, 8); + } + return 1; /* not reached */ +} diff --git a/libauthsrv/readnvram.c b/libauthsrv/readnvram.c new file mode 100644 index 0000000..4c92efc --- /dev/null +++ b/libauthsrv/readnvram.c @@ -0,0 +1,368 @@ +#include +#include +#include + +static long finddosfile(int, char*); + +static int +check(void *x, int len, uchar sum, char *msg) +{ + if(nvcsum(x, len) == sum) + return 0; + memset(x, 0, len); + fprint(2, "%s\n", msg); + return 1; +} + +/* + * get key info out of nvram. since there isn't room in the PC's nvram use + * a disk partition there. + */ +static struct { + char *cputype; + char *file; + int off; + int len; +} nvtab[] = { + "sparc", "#r/nvram", 1024+850, sizeof(Nvrsafe), + "pc", "#S/sdC0/nvram", 0, sizeof(Nvrsafe), + "pc", "#S/sdC0/9fat", -1, sizeof(Nvrsafe), + "pc", "#S/sd00/nvram", 0, sizeof(Nvrsafe), + "pc", "#S/sd00/9fat", -1, sizeof(Nvrsafe), + "pc", "#S/sd01/nvram", 0, sizeof(Nvrsafe), + "pc", "#S/sd01/9fat", -1, sizeof(Nvrsafe), + "pc", "#f/fd0disk", -1, 512, /* 512: #f requires whole sector reads */ + "pc", "#f/fd1disk", -1, 512, + "mips", "#r/nvram", 1024+900, sizeof(Nvrsafe), + "power", "#F/flash/flash0", 0x300000, sizeof(Nvrsafe), + "power", "#r/nvram", 4352, sizeof(Nvrsafe), /* OK for MTX-604e */ + "debug", "/tmp/nvram", 0, sizeof(Nvrsafe), +}; + +static char* +readcons(char *prompt, char *def, int raw, char *buf, int nbuf) +{ + int fdin, fdout, ctl, n, m; + char line[10]; + + fdin = open("/dev/cons", OREAD); + if(fdin < 0) + fdin = 0; + fdout = open("/dev/cons", OWRITE); + if(fdout < 0) + fdout = 1; + if(def != nil) + fprint(fdout, "%s[%s]: ", prompt, def); + else + fprint(fdout, "%s: ", prompt); + if(raw){ + ctl = open("/dev/consctl", OWRITE); + if(ctl >= 0) + write(ctl, "rawon", 5); + } else + ctl = -1; + + m = 0; + for(;;){ + n = read(fdin, line, 1); + if(n == 0){ + close(ctl); + werrstr("readcons: EOF"); + return nil; + } + if(n < 0){ + close(ctl); + werrstr("can't read cons"); + return nil; + } + if(line[0] == 0x7f) + exits(0); + if(n == 0 || line[0] == '\n' || line[0] == '\r'){ + if(raw){ + write(ctl, "rawoff", 6); + write(fdout, "\n", 1); + close(ctl); + } + buf[m] = '\0'; + if(buf[0]=='\0' && def) + strcpy(buf, def); + return buf; + } + if(line[0] == '\b'){ + if(m > 0) + m--; + }else if(line[0] == 0x15){ /* ^U: line kill */ + m = 0; + if(def != nil) + fprint(fdout, "%s[%s]: ", prompt, def); + else + fprint(fdout, "%s: ", prompt); + }else{ + if(m >= nbuf-1){ + fprint(fdout, "line too long\n"); + m = 0; + if(def != nil) + fprint(fdout, "%s[%s]: ", prompt, def); + else + fprint(fdout, "%s: ", prompt); + }else + buf[m++] = line[0]; + } + } + return buf; /* how does this happen */ +} + + +/* + * get key info out of nvram. since there isn't room in the PC's nvram use + * a disk partition there. + */ +int +readnvram(Nvrsafe *safep, int flag) +{ + char buf[1024], in[128], *cputype, *nvrfile, *nvrlen, *nvroff, *v[2]; + int fd, err, i, safeoff, safelen; + Nvrsafe *safe; + + err = 0; + memset(safep, 0, sizeof(*safep)); + + nvrfile = getenv("nvram"); + cputype = getenv("cputype"); + if(cputype == nil) + cputype = "mips"; + if(strcmp(cputype, "386")==0 || strcmp(cputype, "alpha")==0) + cputype = "pc"; + + safe = (Nvrsafe*)buf; + + fd = -1; + safeoff = -1; + safelen = -1; + if(nvrfile != nil){ + /* accept device and device!file */ + i = gettokens(nvrfile, v, nelem(v), "!"); + fd = open(v[0], ORDWR); + safelen = sizeof(Nvrsafe); + if(strstr(v[0], "/9fat") == nil) + safeoff = 0; + nvrlen = getenv("nvrlen"); + if(nvrlen != nil) + safelen = atoi(nvrlen); + nvroff = getenv("nvroff"); + if(nvroff != nil){ + if(strcmp(nvroff, "dos") == 0) + safeoff = -1; + else + safeoff = atoi(nvroff); + } + if(safeoff < 0 && fd >= 0){ + safelen = 512; + safeoff = finddosfile(fd, i == 2 ? v[1] : "plan9.nvr"); + if(safeoff < 0){ + close(fd); + fd = -1; + } + } + free(nvrfile); + if(nvrlen != nil) + free(nvrlen); + if(nvroff != nil) + free(nvroff); + }else{ + for(i=0; imachkey, DESKEYLEN, safe->machsum, "bad nvram key"); +// err |= check(safe->config, CONFIGLEN, safe->configsum, "bad secstore key"); + err |= check(safe->authid, ANAMELEN, safe->authidsum, "bad authentication id"); + err |= check(safe->authdom, DOMLEN, safe->authdomsum, "bad authentication domain"); + } + + if((flag&NVwrite) || (err && (flag&NVwriteonerr))){ + readcons("authid", nil, 0, safe->authid, sizeof(safe->authid)); + readcons("authdom", nil, 0, safe->authdom, sizeof(safe->authdom)); + readcons("secstore key", nil, 1, safe->config, sizeof(safe->config)); + for(;;){ + if(readcons("password", nil, 1, in, sizeof in) == nil) + goto Out; + if(passtokey(safe->machkey, in)) + break; + } + safe->machsum = nvcsum(safe->machkey, DESKEYLEN); + safe->configsum = nvcsum(safe->config, CONFIGLEN); + safe->authidsum = nvcsum(safe->authid, sizeof(safe->authid)); + safe->authdomsum = nvcsum(safe->authdom, sizeof(safe->authdom)); + *(Nvrsafe*)buf = *safe; + if(seek(fd, safeoff, 0) < 0 + || write(fd, buf, safelen) != safelen){ + fprint(2, "can't write key to nvram: %r\n"); + err = 1; + }else + err = 0; + } +Out: + close(fd); + return err ? -1 : 0; +} + +typedef struct Dosboot Dosboot; +struct Dosboot{ + uchar magic[3]; /* really an xx86 JMP instruction */ + uchar version[8]; + uchar sectsize[2]; + uchar clustsize; + uchar nresrv[2]; + uchar nfats; + uchar rootsize[2]; + uchar volsize[2]; + uchar mediadesc; + uchar fatsize[2]; + uchar trksize[2]; + uchar nheads[2]; + uchar nhidden[4]; + uchar bigvolsize[4]; + uchar driveno; + uchar reserved0; + uchar bootsig; + uchar volid[4]; + uchar label[11]; + uchar type[8]; +}; +#define GETSHORT(p) (((p)[1]<<8) | (p)[0]) +#define GETLONG(p) ((GETSHORT((p)+2) << 16) | GETSHORT((p))) + +typedef struct Dosdir Dosdir; +struct Dosdir +{ + char name[8]; + char ext[3]; + uchar attr; + uchar reserved[10]; + uchar time[2]; + uchar date[2]; + uchar start[2]; + uchar length[4]; +}; + +static char* +dosparse(char *from, char *to, int len) +{ + char c; + + memset(to, ' ', len); + if(from == 0) + return 0; + while(len-- > 0){ + c = *from++; + if(c == '.') + return from; + if(c == 0) + break; + if(c >= 'a' && c <= 'z') + *to++ = c + 'A' - 'a'; + else + *to++ = c; + } + return 0; +} + +/* + * return offset of first file block + * + * This is a very simplistic dos file system. It only + * works on floppies, only looks in the root, and only + * returns a pointer to the first block of a file. + * + * This exists for cpu servers that have no hard disk + * or nvram to store the key on. + * + * Please don't make this any smarter: it stays resident + * and I'ld prefer not to waste the space on something that + * runs only at boottime -- presotto. + */ +static long +finddosfile(int fd, char *file) +{ + uchar secbuf[512]; + char name[8]; + char ext[3]; + Dosboot *b; + Dosdir *root, *dp; + int nroot, sectsize, rootoff, rootsects, n; + + /* dos'ize file name */ + file = dosparse(file, name, 8); + dosparse(file, ext, 3); + + /* read boot block, check for sanity */ + b = (Dosboot*)secbuf; + if(read(fd, secbuf, sizeof(secbuf)) != sizeof(secbuf)) + return -1; + if(b->magic[0] != 0xEB || b->magic[1] != 0x3C || b->magic[2] != 0x90) + return -1; + sectsize = GETSHORT(b->sectsize); + if(sectsize != 512) + return -1; + rootoff = (GETSHORT(b->nresrv) + b->nfats*GETSHORT(b->fatsize)) * sectsize; + if(seek(fd, rootoff, 0) < 0) + return -1; + nroot = GETSHORT(b->rootsize); + rootsects = (nroot*sizeof(Dosdir)+sectsize-1)/sectsize; + if(rootsects <= 0 || rootsects > 64) + return -1; + + /* + * read root. it is contiguous to make stuff like + * this easier + */ + root = malloc(rootsects*sectsize); + if(read(fd, root, rootsects*sectsize) != rootsects*sectsize) + return -1; + n = -1; + for(dp = root; dp < &root[nroot]; dp++) + if(memcmp(name, dp->name, 8) == 0 && memcmp(ext, dp->ext, 3) == 0){ + n = GETSHORT(dp->start); + break; + } + free(root); + + if(n < 0) + return -1; + + /* + * dp->start is in cluster units, not sectors. The first + * cluster is cluster 2 which starts immediately after the + * root directory + */ + return rootoff + rootsects*sectsize + (n-2)*sectsize*b->clustsize; +} + diff --git a/libc/Makefile b/libc/Makefile new file mode 100644 index 0000000..03d2585 --- /dev/null +++ b/libc/Makefile @@ -0,0 +1,89 @@ +LIB=libc.a +CC=gcc +CFLAGS=-I../include -I. -c -ggdb -D_THREAD_SAFE -pthread +O=o + +OFILES=\ + charstod.$O\ + cleanname.$O\ + convD2M.$O\ + convM2D.$O\ + convM2S.$O\ + convS2M.$O\ + crypt.$O\ + dial.$O\ + dirfstat.$O\ + dirfwstat.$O\ + dirmodefmt.$O\ + dirstat.$O\ + dirwstat.$O\ + dofmt.$O\ + dorfmt.$O\ + fcallfmt.$O\ + fltfmt.$O\ + fmt.$O\ + fmtfd.$O\ + fmtlock.$O\ + fmtprint.$O\ + fmtquote.$O\ + fmtrune.$O\ + fmtstr.$O\ + fmtvprint.$O\ + fprint.$O\ + frand.$O\ + getfields.$O\ + getpid.$O\ + lnrand.$O\ + lock.$O\ + lrand.$O\ + mallocz.$O\ + nan64.$O\ + netmkaddr.$O\ + nrand.$O\ + nsec.$O\ + pow10.$O\ + pushssl.$O\ + read9pmsg.$O\ + readn.$O\ + rune.$O\ + runefmtstr.$O\ + runeseprint.$O\ + runesmprint.$O\ + runesnprint.$O\ + runesprint.$O\ + runetype.$O\ + runevseprint.$O\ + runevsmprint.$O\ + runevsnprint.$O\ + seprint.$O\ + smprint.$O\ + snprint.$O\ + sprint.$O\ + strecpy.$O\ + strtod.$O\ + strtoll.$O\ + sysfatal.$O\ + time.$O\ + tokenize.$O\ + truerand.$O\ + u16.$O\ + u32.$O\ + u64.$O\ + utfecpy.$O\ + utflen.$O\ + utfnlen.$O\ + utfrrune.$O\ + utfrune.$O\ + utfutf.$O\ + vfprint.$O\ + vseprint.$O\ + vsmprint.$O\ + vsnprint.$O + +$(LIB): $(OFILES) + ar r $(LIB) $(OFILES) + ranlib $(LIB) + +%.$O: %.c + $(CC) $(CFLAGS) $*.c + diff --git a/libc/charstod.c b/libc/charstod.c new file mode 100644 index 0000000..0df7754 --- /dev/null +++ b/libc/charstod.c @@ -0,0 +1,81 @@ +#include +#include + +/* + * 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. + */ + +#define ADVANCE *s++ = c; if(s>=e) return NaN(); c = (*f)(vp) + +double +charstod(int(*f)(void*), void *vp) +{ + char str[400], *s, *e, *start; + int c; + + s = str; + e = str + sizeof str - 1; + c = (*f)(vp); + while(c == ' ' || c == '\t') + c = (*f)(vp); + if(c == '-' || c == '+'){ + ADVANCE; + } + start = s; + while(c >= '0' && c <= '9'){ + ADVANCE; + } + if(c == '.'){ + ADVANCE; + while(c >= '0' && c <= '9'){ + ADVANCE; + } + } + if(s > start && (c == 'e' || c == 'E')){ + ADVANCE; + if(c == '-' || c == '+'){ + ADVANCE; + } + while(c >= '0' && c <= '9'){ + ADVANCE; + } + }else if(s == start && (c == 'i' || c == 'I')){ + ADVANCE; + if(c != 'n' && c != 'N') + return NaN(); + ADVANCE; + if(c != 'f' && c != 'F') + return NaN(); + ADVANCE; + if(c != 'i' && c != 'I') + return NaN(); + ADVANCE; + if(c != 'n' && c != 'N') + return NaN(); + ADVANCE; + if(c != 'i' && c != 'I') + return NaN(); + ADVANCE; + if(c != 't' && c != 'T') + return NaN(); + ADVANCE; + if(c != 'y' && c != 'Y') + return NaN(); + ADVANCE; /* so caller can back up uniformly */ + USED(c); + }else if(s == str && (c == 'n' || c == 'N')){ + ADVANCE; + if(c != 'a' && c != 'A') + return NaN(); + ADVANCE; + if(c != 'n' && c != 'N') + return NaN(); + ADVANCE; /* so caller can back up uniformly */ + USED(c); + } + *s = 0; + return strtod(str, &s); +} diff --git a/libc/cleanname.c b/libc/cleanname.c new file mode 100644 index 0000000..cfcb482 --- /dev/null +++ b/libc/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/libc/convD2M.c b/libc/convD2M.c new file mode 100644 index 0000000..5acee7e --- /dev/null +++ b/libc/convD2M.c @@ -0,0 +1,95 @@ +#include +#include +#include + +uint +sizeD2M(Dir *d) +{ + char *sv[4]; + int i, ns; + + sv[0] = d->name; + sv[1] = d->uid; + sv[2] = d->gid; + sv[3] = d->muid; + + ns = 0; + for(i = 0; i < 4; i++) + if(sv[i]) + ns += strlen(sv[i]); + + return STATFIXLEN + ns; +} + +uint +convD2M(Dir *d, uchar *buf, uint nbuf) +{ + uchar *p, *ebuf; + char *sv[4]; + int i, ns, nsv[4], ss; + + if(nbuf < BIT16SZ) + return 0; + + p = buf; + ebuf = buf + nbuf; + + sv[0] = d->name; + sv[1] = d->uid; + sv[2] = d->gid; + sv[3] = d->muid; + + ns = 0; + for(i = 0; i < 4; i++){ + if(sv[i]) + nsv[i] = strlen(sv[i]); + else + nsv[i] = 0; + ns += nsv[i]; + } + + ss = STATFIXLEN + ns; + + /* set size befor erroring, so user can know how much is needed */ + /* note that length excludes count field itself */ + PBIT16(p, ss-BIT16SZ); + p += BIT16SZ; + + if(ss > nbuf) + return BIT16SZ; + + PBIT16(p, d->type); + p += BIT16SZ; + PBIT32(p, d->dev); + p += BIT32SZ; + PBIT8(p, d->qid.type); + p += BIT8SZ; + PBIT32(p, d->qid.vers); + p += BIT32SZ; + PBIT64(p, d->qid.path); + p += BIT64SZ; + PBIT32(p, d->mode); + p += BIT32SZ; + PBIT32(p, d->atime); + p += BIT32SZ; + PBIT32(p, d->mtime); + p += BIT32SZ; + PBIT64(p, d->length); + p += BIT64SZ; + + for(i = 0; i < 4; i++){ + ns = nsv[i]; + if(p + ns + BIT16SZ > ebuf) + return 0; + PBIT16(p, ns); + p += BIT16SZ; + if(ns) + memmove(p, sv[i], ns); + p += ns; + } + + if(ss != p - buf) + return 0; + + return p - buf; +} diff --git a/libc/convM2D.c b/libc/convM2D.c new file mode 100644 index 0000000..6f4b4bd --- /dev/null +++ b/libc/convM2D.c @@ -0,0 +1,94 @@ +#include +#include +#include + +int +statcheck(uchar *buf, uint nbuf) +{ + uchar *ebuf; + int i; + + ebuf = buf + nbuf; + + if(nbuf < STATFIXLEN || nbuf != BIT16SZ + GBIT16(buf)) + return -1; + + buf += STATFIXLEN - 4 * BIT16SZ; + + for(i = 0; i < 4; i++){ + if(buf + BIT16SZ > ebuf) + return -1; + buf += BIT16SZ + GBIT16(buf); + } + + if(buf != ebuf) + return -1; + + return 0; +} + +static char nullstring[] = ""; + +uint +convM2D(uchar *buf, uint nbuf, Dir *d, char *strs) +{ + uchar *p, *ebuf; + char *sv[4]; + int i, ns; + + if(nbuf < STATFIXLEN) + return 0; + + p = buf; + ebuf = buf + nbuf; + + p += BIT16SZ; /* ignore size */ + d->type = GBIT16(p); + p += BIT16SZ; + d->dev = GBIT32(p); + p += BIT32SZ; + d->qid.type = GBIT8(p); + p += BIT8SZ; + d->qid.vers = GBIT32(p); + p += BIT32SZ; + d->qid.path = GBIT64(p); + p += BIT64SZ; + d->mode = GBIT32(p); + p += BIT32SZ; + d->atime = GBIT32(p); + p += BIT32SZ; + d->mtime = GBIT32(p); + p += BIT32SZ; + d->length = GBIT64(p); + p += BIT64SZ; + + for(i = 0; i < 4; i++){ + if(p + BIT16SZ > ebuf) + return 0; + ns = GBIT16(p); + p += BIT16SZ; + if(p + ns > ebuf) + return 0; + if(strs){ + sv[i] = strs; + memmove(strs, p, ns); + strs += ns; + *strs++ = '\0'; + } + p += ns; + } + + if(strs){ + d->name = sv[0]; + d->uid = sv[1]; + d->gid = sv[2]; + d->muid = sv[3]; + }else{ + d->name = nullstring; + d->uid = nullstring; + d->gid = nullstring; + d->muid = nullstring; + } + + return p - buf; +} diff --git a/libc/convM2S.c b/libc/convM2S.c new file mode 100644 index 0000000..fcdcd42 --- /dev/null +++ b/libc/convM2S.c @@ -0,0 +1,315 @@ +#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; + break; + + case Tattach: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + if(p+BIT32SZ > ep) + return 0; + f->afid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->uname); + if(p == nil) + break; + p = gstring(p, ep, &f->aname); + if(p == nil) + break; + break; + + case Twalk: + if(p+BIT32SZ+BIT32SZ+BIT16SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->newfid = GBIT32(p); + p += BIT32SZ; + f->nwname = GBIT16(p); + p += BIT16SZ; + if(f->nwname > MAXWELEM) + return 0; + for(i=0; inwname; i++){ + p = gstring(p, ep, &f->wname[i]); + if(p == nil) + break; + } + break; + + case Topen: + if(p+BIT32SZ+BIT8SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + + case Tcreate: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->name); + if(p == nil) + break; + if(p+BIT32SZ+BIT8SZ > ep) + return 0; + f->perm = GBIT32(p); + p += BIT32SZ; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + + case Tread: + if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + break; + + case Twrite: + if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + if(p+f->count > ep) + return 0; + f->data = (char*)p; + p += f->count; + break; + + case Tclunk: + case Tremove: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + + case Tstat: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + + case Twstat: + if(p+BIT32SZ+BIT16SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->nstat = GBIT16(p); + p += BIT16SZ; + if(p+f->nstat > ep) + return 0; + f->stat = p; + p += f->nstat; + break; + +/* + */ + case Rversion: + if(p+BIT32SZ > ep) + return 0; + f->msize = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->version); + break; + + case Rerror: + p = gstring(p, ep, &f->ename); + break; + + case Rflush: + break; + + case Rauth: + p = gqid(p, ep, &f->aqid); + if(p == nil) + break; + break; + + case Rattach: + p = gqid(p, ep, &f->qid); + if(p == nil) + break; + break; + + case Rwalk: + if(p+BIT16SZ > ep) + return 0; + f->nwqid = GBIT16(p); + p += BIT16SZ; + if(f->nwqid > MAXWELEM) + return 0; + for(i=0; inwqid; i++){ + p = gqid(p, ep, &f->wqid[i]); + if(p == nil) + break; + } + break; + + case Ropen: + case Rcreate: + p = gqid(p, ep, &f->qid); + if(p == nil) + break; + if(p+BIT32SZ > ep) + return 0; + f->iounit = 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/libc/convS2M.c b/libc/convS2M.c new file mode 100644 index 0000000..9acdcfa --- /dev/null +++ b/libc/convS2M.c @@ -0,0 +1,386 @@ +#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: + 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 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: + 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: + p = pqid(p, &f->qid); + PBIT32(p, f->iounit); + 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/libc/crypt.c b/libc/crypt.c new file mode 100644 index 0000000..0524c94 --- /dev/null +++ b/libc/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/libc/dial.c b/libc/dial.c new file mode 100644 index 0000000..55954d2 --- /dev/null +++ b/libc/dial.c @@ -0,0 +1,209 @@ +#include +#include + +typedef struct DS DS; + +static int call(char*, char*, DS*); +static int csdial(DS*); +static void _dial_string_parse(char*, DS*); + +enum +{ + Maxstring = 128, + Maxpath = 256, +}; + +struct DS { + /* dist string */ + char buf[Maxstring]; + char *netdir; + char *proto; + char *rem; + + /* other args */ + char *local; + char *dir; + int *cfdp; +}; + + +/* + * the dialstring is of the form '[/net/]proto!dest' + */ +int +dial(char *dest, char *local, char *dir, int *cfdp) +{ + DS ds; + int rv; + char err[ERRMAX], alterr[ERRMAX]; + + ds.local = local; + ds.dir = dir; + ds.cfdp = cfdp; + + _dial_string_parse(dest, &ds); + if(ds.netdir) + return csdial(&ds); + + ds.netdir = "/net"; + rv = csdial(&ds); + if(rv >= 0) + return rv; + err[0] = '\0'; + errstr(err, sizeof err); + if(strstr(err, "refused") != 0){ + werrstr("%s", err); + return rv; + } + ds.netdir = "/net.alt"; + rv = csdial(&ds); + if(rv >= 0) + return rv; + + alterr[0] = 0; + errstr(alterr, sizeof alterr); + if(strstr(alterr, "translate") || strstr(alterr, "does not exist")) + werrstr("%s", err); + else + werrstr("%s", alterr); + return rv; +} + +static int +csdial(DS *ds) +{ + int n, fd, rv; + char *p, buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX]; + + /* + * open connection server + */ + snprint(buf, sizeof(buf), "%s/cs", ds->netdir); + fd = open(buf, ORDWR); + if(fd < 0){ + /* no connection server, don't translate */ + snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto); + return call(clone, ds->rem, ds); + } + + /* + * ask connection server to translate + */ + snprint(buf, sizeof(buf), "%s!%s", ds->proto, ds->rem); + if(write(fd, buf, strlen(buf)) < 0){ + close(fd); + return -1; + } + + /* + * loop through each address from the connection server till + * we get one that works. + */ + *besterr = 0; + rv = -1; + seek(fd, 0, 0); + strcpy(err, "cs gave empty translation list"); + while((n = read(fd, buf, sizeof(buf) - 1)) > 0){ + buf[n] = 0; + p = strchr(buf, ' '); + if(p == 0) + continue; + *p++ = 0; + rv = call(buf, p, ds); + if(rv >= 0) + break; + err[0] = '\0'; + errstr(err, sizeof err); + if(strstr(err, "does not exist") == 0) + strcpy(besterr, err); + } + close(fd); + + if(rv < 0 && *besterr) + werrstr("%s", besterr); + else + werrstr("%s", err); + return rv; +} + +static int +call(char *clone, char *dest, DS *ds) +{ + int fd, cfd, n; + char name[Maxpath], data[Maxpath], *p; + + cfd = open(clone, ORDWR); + if(cfd < 0) + return -1; + + /* get directory name */ + n = read(cfd, name, sizeof(name)-1); + if(n < 0){ + close(cfd); + return -1; + } + name[n] = 0; + for(p = name; *p == ' '; p++) + ; + snprint(name, sizeof(name), "%ld", strtoul(p, 0, 0)); + p = strrchr(clone, '/'); + *p = 0; + if(ds->dir) + snprint(ds->dir, NETPATHLEN, "%s/%s", clone, name); + snprint(data, sizeof(data), "%s/%s/data", clone, name); + + /* connect */ + if(ds->local) + snprint(name, sizeof(name), "connect %s %s", dest, ds->local); + else + snprint(name, sizeof(name), "connect %s", dest); + if(write(cfd, name, strlen(name)) < 0){ + close(cfd); + return -1; + } + + /* open data connection */ + fd = open(data, ORDWR); + if(fd < 0){ +print("open %s: %r\n", data); + close(cfd); + return -1; + } + if(ds->cfdp) + *ds->cfdp = cfd; + else + close(cfd); + return fd; +} + +/* + * parse a dial string + */ +static void +_dial_string_parse(char *str, DS *ds) +{ + char *p, *p2; + + strncpy(ds->buf, str, Maxstring); + ds->buf[Maxstring-1] = 0; + + p = strchr(ds->buf, '!'); + if(p == 0) { + ds->netdir = 0; + ds->proto = "net"; + ds->rem = ds->buf; + } else { + if(*ds->buf != '/' && *ds->buf != '#'){ + ds->netdir = 0; + ds->proto = ds->buf; + } else { + for(p2 = p; *p2 != '/'; p2--) + ; + *p2++ = 0; + ds->netdir = ds->buf; + ds->proto = p2; + } + *p = 0; + ds->rem = p + 1; + } +} diff --git a/libc/dirfstat.c b/libc/dirfstat.c new file mode 100644 index 0000000..0534a22 --- /dev/null +++ b/libc/dirfstat.c @@ -0,0 +1,37 @@ +#include +#include +#include + +enum +{ + DIRSIZE = STATFIXLEN + 16 * 4 /* enough for encoded stat buf + some reasonable strings */ +}; + +Dir* +dirfstat(int fd) +{ + Dir *d; + uchar *buf; + int n, nd, i; + + nd = DIRSIZE; + for(i=0; i<2; i++){ /* should work by the second try */ + d = malloc(sizeof(Dir) + BIT16SZ + nd); + if(d == nil) + return nil; + buf = (uchar*)&d[1]; + n = fstat(fd, buf, BIT16SZ+nd); + if(n < BIT16SZ){ + free(d); + return nil; + } + nd = GBIT16(buf); /* upper bound on size of Dir + strings */ + if(nd <= n){ + convM2D(buf, n, d, (char*)&d[1]); + return d; + } + /* else sizeof(Dir)+BIT16SZ+nd is plenty */ + free(d); + } + return nil; +} diff --git a/libc/dirfwstat.c b/libc/dirfwstat.c new file mode 100644 index 0000000..85803ff --- /dev/null +++ b/libc/dirfwstat.c @@ -0,0 +1,19 @@ +#include +#include +#include + +int +dirfwstat(int fd, Dir *d) +{ + uchar *buf; + int r; + + r = sizeD2M(d); + buf = malloc(r); + if(buf == nil) + return -1; + convD2M(d, buf, r); + r = fwstat(fd, buf, r); + free(buf); + return r; +} diff --git a/libc/dirmodefmt.c b/libc/dirmodefmt.c new file mode 100644 index 0000000..82eb5a3 --- /dev/null +++ b/libc/dirmodefmt.c @@ -0,0 +1,48 @@ +#include +#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 + buf[0]='-'; + if(m & DMEXCL) + 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/libc/dirstat.c b/libc/dirstat.c new file mode 100644 index 0000000..eb5171d --- /dev/null +++ b/libc/dirstat.c @@ -0,0 +1,37 @@ +#include +#include +#include + +enum +{ + DIRSIZE = STATFIXLEN + 16 * 4 /* enough for encoded stat buf + some reasonable strings */ +}; + +Dir* +dirstat(char *name) +{ + Dir *d; + uchar *buf; + int n, nd, i; + + nd = DIRSIZE; + for(i=0; i<2; i++){ /* should work by the second try */ + d = malloc(sizeof(Dir) + BIT16SZ + nd); + if(d == nil) + return nil; + buf = (uchar*)&d[1]; + n = stat(name, buf, BIT16SZ+nd); + if(n < BIT16SZ){ + free(d); + return nil; + } + nd = GBIT16((uchar*)buf); /* upper bound on size of Dir + strings */ + if(nd <= n){ + convM2D(buf, n, d, (char*)&d[1]); + return d; + } + /* else sizeof(Dir)+BIT16SZ+nd is plenty */ + free(d); + } + return nil; +} diff --git a/libc/dirwstat.c b/libc/dirwstat.c new file mode 100644 index 0000000..cb62e3f --- /dev/null +++ b/libc/dirwstat.c @@ -0,0 +1,19 @@ +#include +#include +#include + +int +dirwstat(char *name, Dir *d) +{ + uchar *buf; + int r; + + r = sizeD2M(d); + buf = malloc(r); + if(buf == nil) + return -1; + convD2M(d, buf, r); + r = wstat(name, buf, r); + free(buf); + return r; +} diff --git a/libc/dofmt.c b/libc/dofmt.c new file mode 100644 index 0000000..cdf40a3 --- /dev/null +++ b/libc/dofmt.c @@ -0,0 +1,520 @@ +#include +#include +#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 = f->to; + rs = 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 = f->to; + s = 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 = _fmtflush(f, t, n); + if(t != nil) + s = 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 = _fmtdispatch(f, fmt, 0); + if(fmt == nil) + return -1; + } + return 0; /* not reached */ +} + +void * +_fmtflush(Fmt *f, void *t, int len) +{ + if(f->runes) + f->nfmt += (Rune*)t - (Rune*)f->to; + else + f->nfmt += (char*)t - (char *)f->to; + f->to = t; + if(f->flush == 0 || (*f->flush)(f) == 0 || (char*)f->to + len > (char*)f->stop){ + f->stop = f->to; + return nil; + } + return f->to; +} + +/* + * put a formatted block of memory sz bytes long of n runes into the output buffer, + * left/right justified in a field of at least f->width charactes + */ +int +_fmtpad(Fmt *f, int n) +{ + char *t, *s; + int i; + + t = f->to; + s = 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 = f->to; + s = 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, void *vm, int n, int sz) +{ + Rune *rt, *rs, r; + char *t, *s, *m, *me; + ulong fl; + int nc, w; + + m = vm; + me = m + sz; + w = f->width; + fl = f->flags; + if((fl & FmtPrec) && n > f->prec) + n = f->prec; + if(f->runes){ + if(!(fl & FmtLeft) && _rfmtpad(f, w - n) < 0) + return -1; + rt = f->to; + rs = 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(m < me) + return -1; + if(fl & FmtLeft && _rfmtpad(f, w - n) < 0) + return -1; + }else{ + if(!(fl & FmtLeft) && _fmtpad(f, w - n) < 0) + return -1; + t = f->to; + s = 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, void *vm, int n) +{ + Rune r, *m, *me, *rt, *rs; + char *t, *s; + ulong fl; + int w; + + m = vm; + w = f->width; + fl = f->flags; + if((fl & FmtPrec) && n > f->prec) + n = f->prec; + if(f->runes){ + if(!(fl & FmtLeft) && _rfmtpad(f, w - n) < 0) + return -1; + rt = f->to; + rs = 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 = f->to; + s = 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, x, 1, 1); +} + +/* fmt out one rune */ +int +_runefmt(Fmt *f) +{ + Rune x[1]; + + x[0] = va_arg(f->args, int); + return _fmtrcpy(f, x, 1); +} + +/* public helper routine: fmt out a null terminated string already in hand */ +int +fmtstrcpy(Fmt *f, char *s) +{ + int p, i; + 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(i = 0; i < p; i++) + if(s[i] == 0) + break; + return _fmtcpy(f, s, utfnlen(s, i), i); /* BUG?: won't print a partial rune at end */ + } + + 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, x, 1); +} + +/* fmt an integer */ +int +_ifmt(Fmt *f) +{ + char buf[70], *p, *conv; + uvlong vu; + ulong u; + int neg, base, i, n, fl, w, isv; + + neg = 0; + fl = f->flags; + isv = 0; + vu = 0; + u = 0; + if(f->r == 'p'){ + u = (ulong)va_arg(f->args, void*); + f->r = 'x'; + fl |= FmtUnsigned; + }else if(fl & FmtVLong){ + isv = 1; + if(fl & FmtUnsigned) + vu = va_arg(f->args, uvlong); + else + vu = va_arg(f->args, vlong); + }else if(fl & FmtLong){ + if(fl & FmtUnsigned) + u = va_arg(f->args, ulong); + else + u = va_arg(f->args, long); + }else if(fl & FmtByte){ + if(fl & FmtUnsigned) + u = (uchar)va_arg(f->args, int); + else + u = (char)va_arg(f->args, int); + }else if(fl & FmtShort){ + if(fl & FmtUnsigned) + u = (ushort)va_arg(f->args, int); + else + u = (short)va_arg(f->args, int); + }else{ + if(fl & FmtUnsigned) + u = va_arg(f->args, uint); + else + u = va_arg(f->args, int); + } + conv = "0123456789abcdef"; + switch(f->r){ + case 'd': + base = 10; + break; + case 'x': + base = 16; + break; + case 'X': + base = 16; + conv = "0123456789ABCDEF"; + break; + case 'b': + base = 2; + break; + case 'o': + base = 8; + break; + default: + return -1; + } + if(!(fl & FmtUnsigned)){ + if(isv && (vlong)vu < 0){ + vu = -(vlong)vu; + neg = 1; + }else if(!isv && (long)u < 0){ + u = -(long)u; + neg = 1; + } + } + p = buf + sizeof buf - 1; + n = 0; + if(isv){ + while(vu){ + i = vu % base; + vu /= base; + if((fl & FmtComma) && n % 4 == 3){ + *p-- = ','; + n++; + } + *p-- = conv[i]; + n++; + } + }else{ + while(u){ + i = u % base; + u /= base; + if((fl & FmtComma) && n % 4 == 3){ + *p-- = ','; + n++; + } + *p-- = conv[i]; + n++; + } + } + if(n == 0){ + *p-- = '0'; + n = 1; + } + for(w = f->prec; n < w && p > buf+3; n++) + *p-- = '0'; + if(neg || (fl & (FmtSign|FmtSpace))) + n++; + if(fl & FmtSharp){ + if(base == 16) + n += 2; + else if(base == 8){ + if(p[1] == '0') + fl &= ~FmtSharp; + else + n++; + } + } + if((fl & FmtZero) && !(fl & FmtLeft)){ + for(w = f->width; n < w && p > buf+3; n++) + *p-- = '0'; + f->width = 0; + } + if(fl & FmtSharp){ + if(base == 16) + *p-- = f->r; + if(base == 16 || base == 8) + *p-- = '0'; + } + if(neg) + *p-- = '-'; + else if(fl & FmtSign) + *p-- = '+'; + else if(fl & FmtSpace) + *p-- = ' '; + f->flags &= ~FmtPrec; + return _fmtcpy(f, p + 1, n, n); +} + +int +_countfmt(Fmt *f) +{ + void *p; + ulong fl; + + fl = f->flags; + p = va_arg(f->args, void*); + if(fl & FmtVLong){ + *(vlong*)p = f->nfmt; + }else if(fl & FmtLong){ + *(long*)p = f->nfmt; + }else if(fl & FmtByte){ + *(char*)p = f->nfmt; + }else if(fl & FmtShort){ + *(short*)p = f->nfmt; + }else{ + *(int*)p = f->nfmt; + } + return 0; +} + +int +_flagfmt(Fmt *f) +{ + switch(f->r){ + case ',': + f->flags |= FmtComma; + break; + case '-': + f->flags |= FmtLeft; + break; + case '+': + f->flags |= FmtSign; + break; + case '#': + f->flags |= FmtSharp; + break; + case ' ': + f->flags |= FmtSpace; + break; + case 'u': + f->flags |= FmtUnsigned; + break; + case 'h': + if(f->flags & FmtShort) + f->flags |= FmtByte; + f->flags |= FmtShort; + break; + case 'l': + if(f->flags & FmtLong) + f->flags |= FmtVLong; + f->flags |= FmtLong; + break; + } + return 1; +} + +/* default error format */ +int +_badfmt(Fmt *f) +{ + char x[3]; + + x[0] = '%'; + x[1] = f->r; + x[2] = '%'; + f->prec = 3; + _fmtcpy(f, x, 3, 3); + return 0; +} diff --git a/libc/dorfmt.c b/libc/dorfmt.c new file mode 100644 index 0000000..582c4ac --- /dev/null +++ b/libc/dorfmt.c @@ -0,0 +1,46 @@ +#include +#include +#include "fmtdef.h" + +/* format the output into f->to and return the number of characters fmted */ + +int +dorfmt(Fmt *f, Rune *fmt) +{ + Rune *rt, *rs; + int r; + char *t, *s; + int nfmt; + + nfmt = f->nfmt; + for(;;){ + if(f->runes){ + rt = f->to; + rs = f->stop; + while((r = *fmt++) && r != '%'){ + FMTRCHAR(f, rt, rs, r); + } + f->nfmt += rt - (Rune *)f->to; + f->to = rt; + if(!r) + return f->nfmt - nfmt; + f->stop = rs; + }else{ + t = f->to; + s = f->stop; + while((r = *fmt++) && r != '%'){ + FMTRUNE(f, t, f->stop, r); + } + f->nfmt += t - (char *)f->to; + f->to = t; + if(!r) + return f->nfmt - nfmt; + f->stop = s; + } + + fmt = _fmtdispatch(f, fmt, 1); + if(fmt == nil) + return -1; + } + return 0; /* not reached */ +} diff --git a/libc/errfmt.c b/libc/errfmt.c new file mode 100644 index 0000000..5b29b16 --- /dev/null +++ b/libc/errfmt.c @@ -0,0 +1,12 @@ +#include +#include +#include "fmtdef.h" + +int +errfmt(Fmt *f) +{ + char buf[ERRMAX]; + + rerrstr(buf, sizeof buf); + return _fmtcpy(f, buf, utflen(buf), strlen(buf)); +} diff --git a/libc/fcallfmt.c b/libc/fcallfmt.c new file mode 100644 index 0000000..41b0317 --- /dev/null +++ b/libc/fcallfmt.c @@ -0,0 +1,234 @@ +#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 Tcreate: /* 114 */ + seprint(buf, e, "Tcreate tag %ud fid %ud name %s perm %M mode %d", tag, fid, f->name, (ulong)f->perm, f->mode); + break; + case Rcreate: + seprint(buf, e, "Rcreate tag %ud qid " QIDFMT " iounit %ud ", tag, + f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit); + break; + case Tread: /* 116 */ + seprint(buf, e, "Tread tag %ud fid %d offset %lld count %ud", + tag, fid, f->offset, f->count); + break; + case Rread: + p = seprint(buf, e, "Rread tag %ud count %ud ", tag, f->count); + dumpsome(p, e, f->data, f->count); + break; + case Twrite: /* 118 */ + p = seprint(buf, e, "Twrite tag %ud fid %d offset %lld count %ud ", + tag, fid, f->offset, f->count); + dumpsome(p, e, f->data, f->count); + break; + case Rwrite: + seprint(buf, e, "Rwrite tag %ud count %ud", tag, f->count); + break; + case Tclunk: /* 120 */ + seprint(buf, e, "Tclunk tag %ud fid %ud", tag, fid); + break; + case Rclunk: + seprint(buf, e, "Rclunk tag %ud", tag); + break; + case Tremove: /* 122 */ + seprint(buf, e, "Tremove tag %ud fid %ud", tag, fid); + break; + case Rremove: + seprint(buf, e, "Rremove tag %ud", tag); + break; + case Tstat: /* 124 */ + seprint(buf, e, "Tstat tag %ud fid %ud", tag, fid); + break; + case Rstat: + p = seprint(buf, e, "Rstat tag %ud ", tag); + if(f->nstat > sizeof tmp) + seprint(p, e, " stat(%d bytes)", f->nstat); + else{ + d = (Dir*)tmp; + convM2D(f->stat, f->nstat, d, (char*)(d+1)); + seprint(p, e, " stat "); + fdirconv(p+6, e, d); + } + break; + case Twstat: /* 126 */ + p = seprint(buf, e, "Twstat tag %ud fid %ud", tag, fid); + if(f->nstat > sizeof tmp) + seprint(p, e, " stat(%d bytes)", f->nstat); + else{ + d = (Dir*)tmp; + convM2D(f->stat, f->nstat, d, (char*)(d+1)); + seprint(p, e, " stat "); + fdirconv(p+6, e, d); + } + break; + case Rwstat: + seprint(buf, e, "Rwstat tag %ud", tag); + break; + default: + seprint(buf, e, "unknown type %d", type); + } + return fmtstrcpy(fmt, buf); +} + +static char* +qidtype(char *s, uchar t) +{ + char *p; + + p = s; + if(t & QTDIR) + *p++ = 'd'; + if(t & QTAPPEND) + *p++ = 'a'; + if(t & QTEXCL) + *p++ = 'l'; + if(t & QTAUTH) + *p++ = 'A'; + *p = '\0'; + return s; +} + +int +dirfmt(Fmt *fmt) +{ + char buf[160]; + + fdirconv(buf, buf+sizeof buf, va_arg(fmt->args, Dir*)); + return fmtstrcpy(fmt, buf); +} + +static void +fdirconv(char *buf, char *e, Dir *d) +{ + char tmp[16]; + + seprint(buf, e, "'%s' '%s' '%s' '%s' " + "q " QIDFMT " m %#luo " + "at %ld mt %ld l %lld " + "t %d d %d", + d->name, d->uid, d->gid, d->muid, + d->qid.path, d->qid.vers, qidtype(tmp, d->qid.type), d->mode, + d->atime, d->mtime, d->length, + d->type, d->dev); +} + +/* + * dump out count (or DUMPL, if count is bigger) bytes from + * buf to ans, as a string if they are all printable, + * else as a series of hex bytes + */ +#define DUMPL 64 + +static uint +dumpsome(char *ans, char *e, char *buf, long count) +{ + int i, printable; + char *p; + + if(buf == nil){ + seprint(ans, e, ""); + return strlen(ans); + } + printable = 1; + if(count > DUMPL) + count = DUMPL; + for(i=0; i127) + printable = 0; + p = ans; + *p++ = '\''; + if(printable){ + if(count > e-p-2) + count = e-p-2; + memmove(p, buf, count); + p += count; + }else{ + if(2*count > e-p-2) + count = (e-p-2)/2; + for(i=0; i0 && i%4==0) + *p++ = ' '; + sprint(p, "%2.2ux", buf[i]); + p += 2; + } + } + *p++ = '\''; + *p = 0; + return p - ans; +} diff --git a/libc/fltfmt.c b/libc/fltfmt.c new file mode 100644 index 0000000..80ab472 --- /dev/null +++ b/libc/fltfmt.c @@ -0,0 +1,312 @@ +#include +#include +#include +#include "fmtdef.h" + +enum +{ + FDIGIT = 30, + FDEFLT = 6, + NSIGNIF = 17, +}; + +static int +xadd(char *a, int n, int v) +{ + char *b; + int c; + + if(n < 0 || n >= NSIGNIF) + return 0; + for(b = a+n; b >= a; b--) { + c = *b + v; + if(c <= '9') { + *b = c; + return 0; + } + *b = '0'; + v = 1; + } + *a = '1'; // overflow adding + return 1; +} + +static int +xsub(char *a, int n, int v) +{ + char *b; + int c; + + for(b = a+n; b >= a; b--) { + c = *b - v; + if(c >= '0') { + *b = c; + return 0; + } + *b = '9'; + v = 1; + } + *a = '9'; // underflow subtracting + return 1; +} + +static void +xdtoa(Fmt *fmt, char *s2, double f) +{ + char s1[NSIGNIF+10]; + double g, h; + int e, d, i, n; + int c1, c2, c3, c4, ucase, sign, chr, prec; + + prec = FDEFLT; + if(fmt->flags & FmtPrec) + prec = fmt->prec; + if(prec > FDIGIT) + prec = FDIGIT; + if(__isNaN(f)) { + strcpy(s2, "NaN"); + return; + } + if(__isInf(f, 1)) { + strcpy(s2, "+Inf"); + return; + } + if(__isInf(f, -1)) { + strcpy(s2, "-Inf"); + return; + } + sign = 0; + if(f < 0) { + f = -f; + sign++; + } + ucase = 0; + chr = fmt->r; + if(isupper(chr)) { + ucase = 1; + chr = tolower(chr); + } + + e = 0; + g = f; + if(g != 0) { + frexp(f, &e); + e = e * .301029995664; + if(e >= -150 && e <= +150) { + d = 0; + h = f; + } else { + d = e/2; + h = f * pow10(-d); + } + g = h * pow10(d-e); + while(g < 1) { + e--; + g = h * pow10(d-e); + } + while(g >= 10) { + e++; + g = h * pow10(d-e); + } + } + + /* + * convert NSIGNIF digits and convert + * back to get accuracy. + */ + for(i=0; i= NSIGNIF-2) { + strcpy(s2, s1); + d = e; + s1[NSIGNIF-2] = '0'; + s1[NSIGNIF-1] = '0'; + sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1); + g = strtod(s1, nil); + if(g == f) + goto found; + if(xadd(s1, NSIGNIF-3, 1)) { + e++; + sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1); + } + g = strtod(s1, nil); + if(g == f) + goto found; + strcpy(s1, s2); + e = d; + } + + /* + * convert back so s1 gets exact answer + */ + for(;;) { + sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1); + g = strtod(s1, nil); + if(f > g) { + if(xadd(s1, NSIGNIF-1, 1)) + e--; + continue; + } + if(f < g) { + if(xsub(s1, NSIGNIF-1, 1)) + e++; + continue; + } + break; + } + +found: + /* + * sign + */ + d = 0; + i = 0; + if(sign) + s2[d++] = '-'; + else if(fmt->flags & FmtSign) + s2[d++] = '+'; + else if(fmt->flags & FmtSpace) + s2[d++] = ' '; + + /* + * copy into final place + * c1 digits of leading '0' + * c2 digits from conversion + * c3 digits of trailing '0' + * c4 digits after '.' + */ + c1 = 0; + c2 = prec + 1; + c3 = 0; + c4 = prec; + switch(chr) { + default: + if(xadd(s1, c2, 5)) + e++; + break; + case 'g': + /* + * decide on 'e' of 'f' style convers + */ + if(xadd(s1, c2, 5)) + e++; + if(e >= -5 && e <= prec) { + c1 = -e - 1; + c4 = prec - e; + chr = 'h'; // flag for 'f' style + } + break; + case 'f': + if(xadd(s1, c2+e, 5)) + e++; + c1 = -e; + if(c1 > prec) + c1 = c2; + c2 += e; + break; + } + + /* + * clean up c1 c2 and c3 + */ + if(c1 < 0) + c1 = 0; + if(c2 < 0) + c2 = 0; + if(c2 > NSIGNIF) { + c3 = c2-NSIGNIF; + c2 = NSIGNIF; + } + + /* + * copy digits + */ + while(c1 > 0) { + if(c1+c2+c3 == c4) + s2[d++] = '.'; + s2[d++] = '0'; + c1--; + } + while(c2 > 0) { + if(c2+c3 == c4) + s2[d++] = '.'; + s2[d++] = s1[i++]; + c2--; + } + while(c3 > 0) { + if(c3 == c4) + s2[d++] = '.'; + s2[d++] = '0'; + c3--; + } + + /* + * strip trailing '0' on g conv + */ + if(fmt->flags & FmtSharp) { + if(0 == c4) + s2[d++] = '.'; + } else + if(chr == 'g' || chr == 'h') { + for(n=d-1; n>=0; n--) + if(s2[n] != '0') + break; + for(i=n; i>=0; i--) + if(s2[i] == '.') { + d = n; + if(i != n) + d++; + break; + } + } + if(chr == 'e' || chr == 'g') { + if(ucase) + s2[d++] = 'E'; + else + s2[d++] = 'e'; + c1 = e; + if(c1 < 0) { + s2[d++] = '-'; + c1 = -c1; + } else + s2[d++] = '+'; + if(c1 >= 100) { + s2[d++] = c1/100 + '0'; + c1 = c1%100; + } + s2[d++] = c1/10 + '0'; + s2[d++] = c1%10 + '0'; + } + s2[d] = 0; +} + +int +_floatfmt(Fmt *fmt, double f) +{ + char s[FDIGIT+10]; + + xdtoa(fmt, s, f); + fmt->flags &= FmtWidth|FmtLeft; + _fmtcpy(fmt, s, strlen(s), strlen(s)); + return 0; +} + +int +_efgfmt(Fmt *f) +{ + double d; + + d = va_arg(f->args, double); + return _floatfmt(f, d); +} diff --git a/libc/fmt.c b/libc/fmt.c new file mode 100644 index 0000000..5d36dac --- /dev/null +++ b/libc/fmt.c @@ -0,0 +1,193 @@ +#include +#include +#include "fmtdef.h" + +enum +{ + Maxfmt = 64 +}; + +typedef struct Convfmt Convfmt; +struct Convfmt +{ + int c; + volatile Fmts fmt; /* for spin lock in fmtfmt; avoids race due to write order */ +}; + +struct +{ + /* lock by calling _fmtlock, _fmtunlock */ + int nfmt; + Convfmt fmt[Maxfmt]; +} fmtalloc; + +static Convfmt knownfmt[] = { + ' ', _flagfmt, + '#', _flagfmt, + '%', _percentfmt, + '+', _flagfmt, + ',', _flagfmt, + '-', _flagfmt, + 'C', _runefmt, + 'E', _efgfmt, + 'G', _efgfmt, + 'S', _runesfmt, + 'X', _ifmt, + 'b', _ifmt, + 'c', _charfmt, + 'd', _ifmt, + 'e', _efgfmt, + 'f', _efgfmt, + 'g', _efgfmt, + 'h', _flagfmt, + 'l', _flagfmt, + 'n', _countfmt, + 'o', _ifmt, + 'p', _ifmt, +/* 'r', errfmt, */ + 's', _strfmt, + 'u', _flagfmt, + 'x', _ifmt, + 0, nil, +}; + +int (*doquote)(int); + +/* + * _fmtlock() must be set + */ +static int +_fmtinstall(int c, Fmts f) +{ + Convfmt *p, *ep; + + if(c<=0 || c>=65536) + return -1; + if(!f) + f = _badfmt; + + ep = &fmtalloc.fmt[fmtalloc.nfmt]; + for(p=fmtalloc.fmt; pc == c) + break; + + if(p == &fmtalloc.fmt[Maxfmt]) + return -1; + + p->fmt = f; + if(p == ep){ /* installing a new format character */ + fmtalloc.nfmt++; + p->c = c; + } + + return 0; +} + +int +fmtinstall(int c, Fmts f) +{ + int ret; + + _fmtlock(); + ret = _fmtinstall(c, f); + _fmtunlock(); + return ret; +} + +static Fmts +fmtfmt(int c) +{ + Convfmt *p, *ep; + + ep = &fmtalloc.fmt[fmtalloc.nfmt]; + for(p=fmtalloc.fmt; pc == c){ + while(p->fmt == nil) /* loop until value is updated */ + ; + return p->fmt; + } + + /* is this a predefined format char? */ + _fmtlock(); + for(p=knownfmt; p->c; p++) + if(p->c == c){ + _fmtinstall(p->c, p->fmt); + _fmtunlock(); + return p->fmt; + } + _fmtunlock(); + + return _badfmt; +} + +void* +_fmtdispatch(Fmt *f, void *fmt, int isrunes) +{ + Rune rune, r; + int i, n; + + f->flags = 0; + f->width = f->prec = 0; + + for(;;){ + if(isrunes){ + r = *(Rune*)fmt; + fmt = (Rune*)fmt + 1; + }else{ + fmt = (char*)fmt + chartorune(&rune, 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){ + i = -i; + f->flags |= FmtLeft; + } + goto numflag; + } + n = (*fmtfmt(r))(f); + if(n < 0) + return nil; + if(n == 0) + return fmt; + } +} diff --git a/libc/fmt.h b/libc/fmt.h new file mode 100644 index 0000000..e3c9a98 --- /dev/null +++ b/libc/fmt.h @@ -0,0 +1,99 @@ + +/* + * 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. + */ + +#ifndef _FMTH_ +#define _FMTH_ 1 + +#include + +#ifndef _UTFH_ +#include +#endif + +typedef struct Fmt Fmt; +struct Fmt{ + unsigned char runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + unsigned long flags; +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + FmtLDouble = FmtByte << 1, + + FmtFlag = FmtLDouble << 1 +}; + +extern int 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 quotestrfmt(Fmt *f); +extern void quotefmtinstall(void); +extern int (*fmtdoquote)(int); + + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); + +extern double fmtstrtod(const char *, char **); +extern double fmtcharstod(int(*)(void*), void*); + +#endif diff --git a/libc/fmtdef.h b/libc/fmtdef.h new file mode 100644 index 0000000..d8f3680 --- /dev/null +++ b/libc/fmtdef.h @@ -0,0 +1,85 @@ +/* + * 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 */ +}; + +void *_fmtflush(Fmt*, void*, int); +void *_fmtdispatch(Fmt*, void*, int); +int _floatfmt(Fmt*, double); +int _fmtpad(Fmt*, int); +int _rfmtpad(Fmt*, int); +int _fmtFdFlush(Fmt*); + +int _efgfmt(Fmt*); +int _charfmt(Fmt*); +int _countfmt(Fmt*); +int _flagfmt(Fmt*); +int _percentfmt(Fmt*); +int _ifmt(Fmt*); +int _runefmt(Fmt*); +int _runesfmt(Fmt*); +int _strfmt(Fmt*); +int _badfmt(Fmt*); +int _fmtcpy(Fmt*, void*, int, int); +int _fmtrcpy(Fmt*, void*, int n); + +void _fmtlock(void); +void _fmtunlock(void); + +#define FMTCHAR(f, t, s, c)\ + do{\ + if(t + 1 > (char*)s){\ + t = _fmtflush(f, t, 1);\ + if(t != nil)\ + s = f->stop;\ + else\ + return -1;\ + }\ + *t++ = c;\ + }while(0) + +#define FMTRCHAR(f, t, s, c)\ + do{\ + if(t + 1 > (Rune*)s){\ + t = _fmtflush(f, t, sizeof(Rune));\ + if(t != nil)\ + s = f->stop;\ + else\ + return -1;\ + }\ + *t++ = c;\ + }while(0) + +#define FMTRUNE(f, t, s, r)\ + do{\ + Rune _rune;\ + int _runelen;\ + if(t + UTFmax > (char*)s && t + (_runelen = runelen(r)) > (char*)s){\ + t = _fmtflush(f, t, _runelen);\ + if(t != nil)\ + s = f->stop;\ + else\ + return -1;\ + }\ + if(r < Runeself)\ + *t++ = r;\ + else{\ + _rune = r;\ + t += runetochar(t, &_rune);\ + }\ + }while(0) diff --git a/libc/fmtfd.c b/libc/fmtfd.c new file mode 100644 index 0000000..db42eba --- /dev/null +++ b/libc/fmtfd.c @@ -0,0 +1,31 @@ +#include +#include +#include "fmtdef.h" + +/* + * public routine for final flush of a formatting buffer + * to a file descriptor; returns total char count. + */ +int +fmtfdflush(Fmt *f) +{ + if(_fmtFdFlush(f) <= 0) + return -1; + return f->nfmt; +} + +/* + * initialize an output buffer for buffered printing + */ +int +fmtfdinit(Fmt *f, int fd, char *buf, int size) +{ + f->runes = 0; + f->start = buf; + f->to = buf; + f->stop = buf + size; + f->flush = _fmtFdFlush; + f->farg = (void*)fd; + f->nfmt = 0; + return 0; +} diff --git a/libc/fmtlock.c b/libc/fmtlock.c new file mode 100644 index 0000000..02ae963 --- /dev/null +++ b/libc/fmtlock.c @@ -0,0 +1,16 @@ +#include +#include + +static Lock fmtl; + +void +_fmtlock(void) +{ + lock(&fmtl); +} + +void +_fmtunlock(void) +{ + unlock(&fmtl); +} diff --git a/libc/fmtprint.c b/libc/fmtprint.c new file mode 100644 index 0000000..4c858b9 --- /dev/null +++ b/libc/fmtprint.c @@ -0,0 +1,32 @@ +#include +#include +#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 = f->args; + va_start(f->args, fmt); + n = dofmt(f, fmt); + va_end(f->args); + f->flags = 0; + f->width = 0; + f->prec = 0; + f->args = va; + if(n >= 0) + return 0; + return n; +} + diff --git a/libc/fmtquote.c b/libc/fmtquote.c new file mode 100644 index 0000000..68e3b1f --- /dev/null +++ b/libc/fmtquote.c @@ -0,0 +1,247 @@ +#include +#include +#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-=w){ + 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'\'') || (doquote!=nil && doquote(c))){ + if(!q->quoted){ + if(runesout){ + if(1+q->nrunesout+1+1 > nout) /* no room for quotes */ + break; + }else{ + if(1+q->nbytesout+w+1 > nout) /* no room for quotes */ + break; + } + q->nrunesout += 2; /* include quotes */ + q->nbytesout += 2; /* include quotes */ + q->quoted = 1; + } + if(c == '\'') { + if(runesout){ + if(1+q->nrunesout+1 > nout) /* no room for quotes */ + break; + }else{ + if(1+q->nbytesout+w > nout) /* no room for quotes */ + break; + } + q->nbytesout++; + q->nrunesout++; /* quotes reproduce as two characters */ + } + } + + /* advance input */ + if(s) + s += w; + else + r++; + q->nbytesin += w; + q->nrunesin++; + + /* advance output */ + q->nbytesout += w; + q->nrunesout++; + } +} + +static int +qstrfmt(char *sin, Rune *rin, Quoteinfo *q, Fmt *f) +{ + Rune r, *rm, *rme; + char *t, *s, *m, *me; + Rune *rt, *rs; + ulong fl; + int nc, w; + + m = sin; + me = m + q->nbytesin; + rm = rin; + rme = rm + q->nrunesin; + + w = f->width; + fl = f->flags; + if(f->runes){ + if(!(fl & FmtLeft) && _rfmtpad(f, w - q->nrunesout) < 0) + return -1; + }else{ + if(!(fl & FmtLeft) && _fmtpad(f, w - q->nbytesout) < 0) + return -1; + } + t = f->to; + s = f->stop; + rt = f->to; + rs = 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 outlen; + Rune *r; + char *s; + Quoteinfo q; + + f->flags &= ~FmtPrec; /* ignored for %q %Q, so disable for %s %S in easy case */ + 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, "", 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, -1, 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/libc/fmtrune.c b/libc/fmtrune.c new file mode 100644 index 0000000..e2db137 --- /dev/null +++ b/libc/fmtrune.c @@ -0,0 +1,25 @@ +#include +#include +#include "fmtdef.h" + +int +fmtrune(Fmt *f, int r) +{ + Rune *rt; + char *t; + int n; + + if(f->runes){ + rt = f->to; + FMTRCHAR(f, rt, f->stop, r); + f->to = rt; + n = 1; + }else{ + t = f->to; + FMTRUNE(f, t, f->stop, r); + n = t - (char*)f->to; + f->to = t; + } + f->nfmt += n; + return 0; +} diff --git a/libc/fmtstr.c b/libc/fmtstr.c new file mode 100644 index 0000000..6f58213 --- /dev/null +++ b/libc/fmtstr.c @@ -0,0 +1,11 @@ +#include +#include + +char* +fmtstrflush(Fmt *f) +{ + if(f->start == nil) + return nil; + *(char*)f->to = '\0'; + return f->start; +} diff --git a/libc/fmtvprint.c b/libc/fmtvprint.c new file mode 100644 index 0000000..1d6493e --- /dev/null +++ b/libc/fmtvprint.c @@ -0,0 +1,31 @@ +#include +#include +#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 = f->args; + f->args = args; + n = dofmt(f, fmt); + f->flags = 0; + f->width = 0; + f->prec = 0; + f->args = va; + if(n >= 0) + return 0; + return n; +} + diff --git a/libc/fprint.c b/libc/fprint.c new file mode 100644 index 0000000..b1376a0 --- /dev/null +++ b/libc/fprint.c @@ -0,0 +1,14 @@ +#include +#include + +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/libc/frand.c b/libc/frand.c new file mode 100644 index 0000000..e13e1d8 --- /dev/null +++ b/libc/frand.c @@ -0,0 +1,17 @@ +#include +#include + +#define MASK 0x7fffffffL +#define NORM (1.0/(1.0+MASK)) + +double +frand(void) +{ + double x; + + do { + x = lrand() * NORM; + x = (x + lrand()) * NORM; + } while(x >= 1); + return x; +} diff --git a/libc/getfields.c b/libc/getfields.c new file mode 100644 index 0000000..b12c0cc --- /dev/null +++ b/libc/getfields.c @@ -0,0 +1,37 @@ +#include +#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/libc/getpid.c b/libc/getpid.c new file mode 100644 index 0000000..9a9c86c --- /dev/null +++ b/libc/getpid.c @@ -0,0 +1,17 @@ +#include +#include + +int +getpid(void) +{ + char b[20]; + int f; + + memset(b, 0, sizeof(b)); + f = open("#c/pid", 0); + if(f >= 0) { + read(f, b, sizeof(b)); + close(f); + } + return atol(b); +} diff --git a/libc/lnrand.c b/libc/lnrand.c new file mode 100644 index 0000000..5b648d0 --- /dev/null +++ b/libc/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/libc/lock.c b/libc/lock.c new file mode 100644 index 0000000..b17f8a8 --- /dev/null +++ b/libc/lock.c @@ -0,0 +1,64 @@ +#include +#include + +int +canlock(Lock *lk) +{ + return !tas(&lk->key); +} + +void +lock(Lock *lk) +{ + int i; + + /* easy case */ + if(canlock(lk)) + return; + + /* for multi processor machines */ + for(i=0; i<100; i++) + if(canlock(lk)) + return; + + for(i=0; i<100; i++) { + osyield(0); + if(canlock(lk)) + return; + } + + /* looking bad - make sure it is not a priority problem */ + for(i=0; i<12; i++) { + osmsleep(1<key, lk, getcallerpc(&lk)); + osmsleep(1000); + } +} + +void +unlock(Lock *lk) +{ + assert(lk->key); + lk->key = 0; +} + +void +ilock(Lock *lk) +{ + lock(lk); +} + +void +iunlock(Lock *lk) +{ + unlock(lk); +} + diff --git a/libc/lrand.c b/libc/lrand.c new file mode 100644 index 0000000..2ebb396 --- /dev/null +++ b/libc/lrand.c @@ -0,0 +1,83 @@ +#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 +srand(long seed) +{ + lock(&lk); + isrand(seed); + unlock(&lk); +} + +long +lrand(void) +{ + ulong x; + + lock(&lk); + + rng_tap--; + if(rng_tap < rng_vec) { + if(rng_feed == 0) { + isrand(1); + rng_tap--; + } + rng_tap += LEN; + } + rng_feed--; + if(rng_feed < rng_vec) + rng_feed += LEN; + x = (*rng_feed + *rng_tap) & MASK; + *rng_feed = x; + + unlock(&lk); + + return x; +} diff --git a/libc/mallocz.c b/libc/mallocz.c new file mode 100644 index 0000000..07247df --- /dev/null +++ b/libc/mallocz.c @@ -0,0 +1,13 @@ +#include +#include + +void* +mallocz(ulong n, int clr) +{ + void *v; + + v = malloc(n); + if(v && clr) + memset(v, 0, n); + return v; +} diff --git a/libc/mkfile b/libc/mkfile new file mode 100644 index 0000000..7a3681a --- /dev/null +++ b/libc/mkfile @@ -0,0 +1,90 @@ +<$DSRC/mkfile-$CONF +TARG=libc.$L + +OFILES=\ + charstod.$O\ + cleanname.$O\ + convD2M.$O\ + convM2D.$O\ + convM2S.$O\ + convS2M.$O\ + crypt.$O\ + dial.$O\ + dirfstat.$O\ + dirfwstat.$O\ + dirmodefmt.$O\ + dirstat.$O\ + dirwstat.$O\ + dofmt.$O\ + dorfmt.$O\ + fcallfmt.$O\ + fltfmt.$O\ + fmt.$O\ + fmtfd.$O\ + fmtlock.$O\ + fmtprint.$O\ + fmtquote.$O\ + fmtrune.$O\ + fmtstr.$O\ + fmtvprint.$O\ + fprint.$O\ + frand.$O\ + getfields.$O\ + getpid.$O\ + lnrand.$O\ + lock.$O\ + lrand.$O\ + mallocz.$O\ + nan64.$O\ + netmkaddr.$O\ + nrand.$O\ + nsec.$O\ + pow10.$O\ + pushssl.$O\ + read9pmsg.$O\ + readn.$O\ + rune.$O\ + runefmtstr.$O\ + runeseprint.$O\ + runesmprint.$O\ + runesnprint.$O\ + runesprint.$O\ + runetype.$O\ + runevseprint.$O\ + runevsmprint.$O\ + runevsnprint.$O\ + seprint.$O\ + smprint.$O\ + snprint.$O\ + sprint.$O\ + strecpy.$O\ + strtod.$O\ + strtoll.$O\ + sysfatal.$O\ + time.$O\ + tokenize.$O\ + truerand.$O\ + u16.$O\ + u32.$O\ + u64.$O\ + utfecpy.$O\ + utflen.$O\ + utfnlen.$O\ + utfrrune.$O\ + utfrune.$O\ + utfutf.$O\ + vfprint.$O\ + vseprint.$O\ + vsmprint.$O\ + vsnprint.$O + +HFILES=../include/libc.h\ + fmt.h\ + fmtdef.h\ + nan.h\ + strtod.h\ + utf.h\ + utfdef.h + +<$DSRC/mklib-$CONF + diff --git a/libc/nan.h b/libc/nan.h new file mode 100644 index 0000000..be78e14 --- /dev/null +++ b/libc/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/libc/nan64.c b/libc/nan64.c new file mode 100644 index 0000000..76bfab3 --- /dev/null +++ b/libc/nan64.c @@ -0,0 +1,55 @@ +/* + * 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 +#include +#include "nan.h" + +// typedef unsigned long long uvlong; +// typedef unsigned long ulong; + +static uvlong uvnan = 0x7FF0000000000001; +static uvlong uvinf = 0x7FF0000000000000; +static uvlong uvneginf = 0xFFF0000000000000; + +double +__NaN(void) +{ + return *(double*)&uvnan; +} + +int +__isNaN(double d) +{ + uvlong x = *(uvlong*)&d; + return (ulong)(x>>32)==0x7FF00000 && !__isInf(d, 0); +} + +double +__Inf(int sign) +{ + if(sign < 0) + return *(double*)&uvinf; + else + return *(double*)&uvneginf; +} + +int +__isInf(double d, int sign) +{ + uvlong x; + + x = *(uvlong*)&d; + if(sign == 0) + return x==uvinf || x==uvneginf; + else if(sign > 0) + return x==uvinf; + else + return x==uvneginf; +} + + diff --git a/libc/netmkaddr.c b/libc/netmkaddr.c new file mode 100644 index 0000000..fd53f46 --- /dev/null +++ b/libc/netmkaddr.c @@ -0,0 +1,52 @@ +#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){ + if(defsrv) + snprint(addr, sizeof(addr), "net!%s!%s", + linear, defsrv); + else + snprint(addr, sizeof(addr), "net!%s", linear); + } + else { + if(defsrv) + snprint(addr, sizeof(addr), "%s!%s!%s", defnet, + linear, defsrv); + else + snprint(addr, sizeof(addr), "%s!%s", defnet, + linear); + } + return addr; + } + + /* + * if there is already a service, use it + */ + cp = strchr(cp+1, '!'); + if(cp) + return linear; + + /* + * add default service + */ + if(defsrv == 0) + return linear; + snprint(addr, sizeof(addr), "%s!%s", linear, defsrv); + + return addr; +} diff --git a/libc/nrand.c b/libc/nrand.c new file mode 100644 index 0000000..9888fb5 --- /dev/null +++ b/libc/nrand.c @@ -0,0 +1,18 @@ +#include +#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/libc/nsec.c b/libc/nsec.c new file mode 100644 index 0000000..0328a86 --- /dev/null +++ b/libc/nsec.c @@ -0,0 +1,66 @@ +#include +#include + + +static uvlong order = (uvlong) 0x0001020304050607; + +static void +be2vlong(vlong *to, uchar *f) +{ + uchar *t, *o; + int i; + + t = (uchar*)to; + o = (uchar*)ℴ + for(i = 0; i < 8; i++) + t[o[i]] = f[i]; +} + +/* + * After a fork with fd's copied, both fd's are pointing to + * the same Chan structure. Since the offset is kept in the Chan + * structure, the seek's and read's in the two processes can + * compete at moving the offset around. Hence the retry loop. + * + * Since the bintime version doesn't need a seek, it doesn't + * have the loop. + */ +vlong +nsec(void) +{ + char b[12+1]; + static int f = -1; + static int usebintime; + int retries; + vlong t; + + if(f < 0){ + usebintime = 1; + f = open("/dev/bintime", OREAD|OCEXEC); + if(f < 0){ + usebintime = 0; + f = open("/dev/nsec", OREAD|OCEXEC); + if(f < 0) + return 0; + } + } + + if(usebintime){ + if(read(f, b, sizeof(uvlong)) < 0) + goto error; + be2vlong(&t, (uchar*)b); + return t; + } else { + for(retries = 0; retries < 100; retries++){ + if(seek(f, 0, 0) >= 0 && read(f, b, sizeof(b)-1) >= 0){ + b[sizeof(b)-1] = 0; + return strtoll(b, 0, 0); + } + } + } + +error: + close(f); + f = -1; + return 0; +} diff --git a/libc/pow10.c b/libc/pow10.c new file mode 100644 index 0000000..7e09f3c --- /dev/null +++ b/libc/pow10.c @@ -0,0 +1,49 @@ +#include +#include + +/* + * 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, + 1.0e70, 1.0e71, 1.0e72, 1.0e73, 1.0e74, 1.0e75, 1.0e76, 1.0e77, 1.0e78, 1.0e79, + 1.0e80, 1.0e81, 1.0e82, 1.0e83, 1.0e84, 1.0e85, 1.0e86, 1.0e87, 1.0e88, 1.0e89, + 1.0e90, 1.0e91, 1.0e92, 1.0e93, 1.0e94, 1.0e95, 1.0e96, 1.0e97, 1.0e98, 1.0e99, + 1.0e100,1.0e101,1.0e102,1.0e103,1.0e104,1.0e105,1.0e106,1.0e107,1.0e108,1.0e109, + 1.0e110,1.0e111,1.0e112,1.0e113,1.0e114,1.0e115,1.0e116,1.0e117,1.0e118,1.0e119, + 1.0e120,1.0e121,1.0e122,1.0e123,1.0e124,1.0e125,1.0e126,1.0e127,1.0e128,1.0e129, + 1.0e130,1.0e131,1.0e132,1.0e133,1.0e134,1.0e135,1.0e136,1.0e137,1.0e138,1.0e139, + 1.0e140,1.0e141,1.0e142,1.0e143,1.0e144,1.0e145,1.0e146,1.0e147,1.0e148,1.0e149, + 1.0e150,1.0e151,1.0e152,1.0e153,1.0e154,1.0e155,1.0e156,1.0e157,1.0e158,1.0e159, +}; + +double +pow10(int n) +{ + int m; + + if(n < 0) { + n = -n; + if(n < sizeof(tab)/sizeof(tab[0])) + return 1/tab[n]; + m = n/2; + return 1/(pow10(m) * pow10(n-m)); + } + if(n < sizeof(tab)/sizeof(tab[0])) + return tab[n]; + m = n/2; + return pow10(m) * pow10(n-m); +} diff --git a/libc/print.c b/libc/print.c new file mode 100644 index 0000000..fdbb522 --- /dev/null +++ b/libc/print.c @@ -0,0 +1,14 @@ +#include +#include + +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/libc/pushssl.c b/libc/pushssl.c new file mode 100644 index 0000000..8817dd1 --- /dev/null +++ b/libc/pushssl.c @@ -0,0 +1,44 @@ +#include +#include + +/* + * Since the SSL device uses decimal file descriptors to name channels, + * it is impossible for a user-level file server to stand in for the kernel device. + * Thus we hard-code #D rather than use /net/ssl. + */ + +int +pushssl(int fd, char *alg, char *secin, char *secout, int *cfd) +{ + char buf[8]; + char dname[64]; + int n, data, ctl; + + ctl = open("#D/ssl/clone", ORDWR); + if(ctl < 0) + return -1; + n = read(ctl, buf, sizeof(buf)-1); + if(n < 0) + goto error; + buf[n] = 0; + sprint(dname, "#D/ssl/%s/data", buf); + data = open(dname, ORDWR); + if(data < 0) + goto error; + if(fprint(ctl, "fd %d", fd) < 0 || + fprint(ctl, "secretin %s", secin) < 0 || + fprint(ctl, "secretout %s", secout) < 0 || + fprint(ctl, "alg %s", alg) < 0){ + close(data); + goto error; + } + close(fd); + if(cfd != 0) + *cfd = ctl; + else + close(ctl); + return data; +error: + close(ctl); + return -1; +} diff --git a/libc/rand.c b/libc/rand.c new file mode 100644 index 0000000..366e9e9 --- /dev/null +++ b/libc/rand.c @@ -0,0 +1,8 @@ +#include +#include + +int +rand(void) +{ + return lrand() & 0x7fff; +} diff --git a/libc/read9pmsg.c b/libc/read9pmsg.c new file mode 100644 index 0000000..9e90ec5 --- /dev/null +++ b/libc/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/libc/readn.c b/libc/readn.c new file mode 100644 index 0000000..629dfd9 --- /dev/null +++ b/libc/readn.c @@ -0,0 +1,22 @@ +#include +#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/libc/rune.c b/libc/rune.c new file mode 100644 index 0000000..b62da9e --- /dev/null +++ b/libc/rune.c @@ -0,0 +1,162 @@ +#include +#include + +enum +{ + Bit1 = 7, + Bitx = 6, + Bit2 = 5, + Bit3 = 4, + Bit4 = 3, + + T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */ + Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */ + T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */ + T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */ + T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */ + + Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */ + Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */ + Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */ + + Maskx = (1< T1 + */ + c = *(uchar*)str; + if(c < Tx) { + *rune = c; + return 1; + } + + /* + * two character sequence + * 0080-07FF => T2 Tx + */ + c1 = *(uchar*)(str+1) ^ Tx; + if(c1 & Testx) + goto bad; + if(c < T3) { + if(c < T2) + goto bad; + l = ((c << Bitx) | c1) & Rune2; + if(l <= Rune1) + goto bad; + *rune = l; + return 2; + } + + /* + * three character sequence + * 0800-FFFF => T3 Tx Tx + */ + c2 = *(uchar*)(str+2) ^ Tx; + if(c2 & Testx) + goto bad; + if(c < T4) { + l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3; + if(l <= Rune2) + goto bad; + *rune = l; + return 3; + } + + /* + * bad decoding + */ +bad: + *rune = Bad; + return 1; +} + +int +runetochar(char *str, Rune *rune) +{ + long c; + + /* + * one character sequence + * 00000-0007F => 00-7F + */ + c = *rune; + if(c <= Rune1) { + str[0] = c; + return 1; + } + + /* + * two character sequence + * 0080-07FF => T2 Tx + */ + if(c <= Rune2) { + str[0] = T2 | (c >> 1*Bitx); + str[1] = Tx | (c & Maskx); + return 2; + } + + /* + * three character sequence + * 0800-FFFF => T3 Tx Tx + */ + str[0] = T3 | (c >> 2*Bitx); + str[1] = Tx | ((c >> 1*Bitx) & Maskx); + str[2] = Tx | (c & Maskx); + return 3; +} + +int +runelen(long c) +{ + Rune rune; + char str[10]; + + rune = c; + return runetochar(str, &rune); +} + +int +runenlen(Rune *r, int nrune) +{ + int nb, c; + + nb = 0; + while(nrune--) { + c = *r++; + if(c <= Rune1) + nb++; + else + if(c <= Rune2) + nb += 2; + else + nb += 3; + } + return nb; +} + +int +fullrune(char *str, int n) +{ + int c; + + if(n > 0) { + c = *(uchar*)str; + if(c < Tx) + return 1; + if(n > 1) + if(c < T3 || n > 2) + return 1; + } + return 0; +} diff --git a/libc/runefmtstr.c b/libc/runefmtstr.c new file mode 100644 index 0000000..9ce9c13 --- /dev/null +++ b/libc/runefmtstr.c @@ -0,0 +1,11 @@ +#include +#include + +Rune* +runefmtstrflush(Fmt *f) +{ + if(f->start == nil) + return nil; + *(Rune*)f->to = '\0'; + return f->start; +} diff --git a/libc/runeseprint.c b/libc/runeseprint.c new file mode 100644 index 0000000..77a761e --- /dev/null +++ b/libc/runeseprint.c @@ -0,0 +1,14 @@ +#include +#include + +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/libc/runesmprint.c b/libc/runesmprint.c new file mode 100644 index 0000000..1f8b220 --- /dev/null +++ b/libc/runesmprint.c @@ -0,0 +1,14 @@ +#include +#include + +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/libc/runesnprint.c b/libc/runesnprint.c new file mode 100644 index 0000000..ac15ec0 --- /dev/null +++ b/libc/runesnprint.c @@ -0,0 +1,15 @@ +#include +#include + +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/libc/runesprint.c b/libc/runesprint.c new file mode 100644 index 0000000..5eca8cd --- /dev/null +++ b/libc/runesprint.c @@ -0,0 +1,14 @@ +#include +#include + +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/libc/runestrcat.c b/libc/runestrcat.c new file mode 100644 index 0000000..4c83630 --- /dev/null +++ b/libc/runestrcat.c @@ -0,0 +1,10 @@ +#include +#include + +Rune* +runestrcat(Rune *s1, Rune *s2) +{ + + runestrcpy(runestrchr(s1, 0), s2); + return s1; +} diff --git a/libc/runestrchr.c b/libc/runestrchr.c new file mode 100644 index 0000000..af7fc4e --- /dev/null +++ b/libc/runestrchr.c @@ -0,0 +1,20 @@ +#include +#include + +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/libc/runestrcmp.c b/libc/runestrcmp.c new file mode 100644 index 0000000..e3bda37 --- /dev/null +++ b/libc/runestrcmp.c @@ -0,0 +1,20 @@ +#include +#include + +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/libc/runestrcpy.c b/libc/runestrcpy.c new file mode 100644 index 0000000..efddc07 --- /dev/null +++ b/libc/runestrcpy.c @@ -0,0 +1,13 @@ +#include +#include + +Rune* +runestrcpy(Rune *s1, Rune *s2) +{ + Rune *os1; + + os1 = s1; + while(*s1++ = *s2++) + ; + return os1; +} diff --git a/libc/runestrdup.c b/libc/runestrdup.c new file mode 100644 index 0000000..f66b430 --- /dev/null +++ b/libc/runestrdup.c @@ -0,0 +1,14 @@ +#include +#include + +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/libc/runestrecpy.c b/libc/runestrecpy.c new file mode 100644 index 0000000..d3ca810 --- /dev/null +++ b/libc/runestrecpy.c @@ -0,0 +1,17 @@ +#include +#include + +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/libc/runestrlen.c b/libc/runestrlen.c new file mode 100644 index 0000000..2bf4a4e --- /dev/null +++ b/libc/runestrlen.c @@ -0,0 +1,9 @@ +#include +#include + +long +runestrlen(Rune *s) +{ + + return runestrchr(s, 0) - s; +} diff --git a/libc/runestrncat.c b/libc/runestrncat.c new file mode 100644 index 0000000..9d6ee46 --- /dev/null +++ b/libc/runestrncat.c @@ -0,0 +1,17 @@ +#include +#include + +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/libc/runestrncmp.c b/libc/runestrncmp.c new file mode 100644 index 0000000..5e566fa --- /dev/null +++ b/libc/runestrncmp.c @@ -0,0 +1,22 @@ +#include +#include + +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/libc/runestrncpy.c b/libc/runestrncpy.c new file mode 100644 index 0000000..7e84e33 --- /dev/null +++ b/libc/runestrncpy.c @@ -0,0 +1,18 @@ +#include +#include + +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/libc/runestrrchr.c b/libc/runestrrchr.c new file mode 100644 index 0000000..4c8074b --- /dev/null +++ b/libc/runestrrchr.c @@ -0,0 +1,15 @@ +#include +#include + +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/libc/runestrstr.c b/libc/runestrstr.c new file mode 100644 index 0000000..b7f6964 --- /dev/null +++ b/libc/runestrstr.c @@ -0,0 +1,29 @@ +#include +#include + +/* + * 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/libc/runetype.c b/libc/runetype.c new file mode 100644 index 0000000..57a2c6a --- /dev/null +++ b/libc/runetype.c @@ -0,0 +1,1138 @@ +#include +#include + +/* + * 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 */ +}; + +#define bsearch xbsearch +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/libc/runevseprint.c b/libc/runevseprint.c new file mode 100644 index 0000000..43c601e --- /dev/null +++ b/libc/runevseprint.c @@ -0,0 +1,23 @@ +#include +#include + +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; + f.args = args; + dofmt(&f, fmt); + *(Rune*)f.to = '\0'; + return f.to; +} + diff --git a/libc/runevsmprint.c b/libc/runevsmprint.c new file mode 100644 index 0000000..41326dd --- /dev/null +++ b/libc/runevsmprint.c @@ -0,0 +1,70 @@ +#include +#include +#include "fmtdef.h" + +static int +runeFmtStrFlush(Fmt *f) +{ + Rune *s; + int n; + + if(f->start == nil) + return 0; + n = (int)f->farg; + n *= 2; + s = f->start; + f->start = realloc(s, sizeof(Rune)*n); + if(f->start == nil){ + f->farg = nil; + f->to = nil; + f->stop = nil; + free(s); + return 0; + } + f->farg = (void*)n; + f->to = (Rune*)f->start + ((Rune*)f->to - s); + f->stop = (Rune*)f->start + n - 1; + return 1; +} + +int +runefmtstrinit(Fmt *f) +{ + int n; + + memset(f, 0, sizeof *f); + f->runes = 1; + n = 32; + f->start = malloc(sizeof(Rune)*n); + if(f->start == nil) + return -1; + f->to = f->start; + f->stop = (Rune*)f->start + n - 1; + f->flush = runeFmtStrFlush; + f->farg = (void*)n; + f->nfmt = 0; + return 0; +} + +/* + * print into an allocated string buffer + */ +Rune* +runevsmprint(char *fmt, va_list args) +{ + Fmt f; + int n; + + if(runefmtstrinit(&f) < 0) + return nil; + f.args = args; + n = dofmt(&f, fmt); + if(f.start == nil) + return nil; + if(n < 0){ + free(f.start); + return nil; + } + *(Rune*)f.to = '\0'; + return f.start; +} diff --git a/libc/runevsnprint.c b/libc/runevsnprint.c new file mode 100644 index 0000000..61fe4af --- /dev/null +++ b/libc/runevsnprint.c @@ -0,0 +1,22 @@ +#include +#include + +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; + f.args = args; + dofmt(&f, fmt); + *(Rune*)f.to = '\0'; + return (Rune*)f.to - buf; +} diff --git a/libc/seprint.c b/libc/seprint.c new file mode 100644 index 0000000..519c1aa --- /dev/null +++ b/libc/seprint.c @@ -0,0 +1,14 @@ +#include +#include + +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/libc/smprint.c b/libc/smprint.c new file mode 100644 index 0000000..bc1b545 --- /dev/null +++ b/libc/smprint.c @@ -0,0 +1,14 @@ +#include +#include + +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/libc/snprint.c b/libc/snprint.c new file mode 100644 index 0000000..8a46813 --- /dev/null +++ b/libc/snprint.c @@ -0,0 +1,15 @@ +#include +#include + +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/libc/sprint.c b/libc/sprint.c new file mode 100644 index 0000000..2da6d92 --- /dev/null +++ b/libc/sprint.c @@ -0,0 +1,14 @@ +#include +#include + +int +sprint(char *buf, char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = vsnprint(buf, 65536, fmt, args); /* big number, but sprint is deprecated anyway */ + va_end(args); + return n; +} diff --git a/libc/strecpy.c b/libc/strecpy.c new file mode 100644 index 0000000..a44dffd --- /dev/null +++ b/libc/strecpy.c @@ -0,0 +1,17 @@ +#include +#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/libc/strtod.c b/libc/strtod.c new file mode 100644 index 0000000..e4f8b7d --- /dev/null +++ b/libc/strtod.c @@ -0,0 +1,532 @@ +#include +#include +#include + +/* + * 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 + Bias = 1022, + 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 ulong +umuldiv(ulong a, ulong b, ulong c) +{ + double d; + + d = ((double)a * (double)b) / (double)c; + if(d >= 4294967295.) + d = 4294967295.; + return d; +} + +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*); +static void divby(char*, int*, int); + +typedef struct Tab Tab; +struct Tab +{ + int bp; + int siz; + char* cmp; +}; + +double +strtod(char *as, char **aas) +{ + int na, ona, ex, dp, bp, c, i, flag, state; + ulong low[Prec], hig[Prec], mid[Prec], num, den; + 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=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 = 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-Nmant/3) /* actually -Nmant*log(2)/log(10), but Nmant/3 close enough */ + 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); + a[na] = 0; + + /* + * very small numbers are represented using + * bp = -Bias+1. adjust accordingly. + */ + if(bp < -Bias+1){ + ona = na; + divby(a, &na, -bp-Bias+1); + if(na < ona){ + memmove(a+ona-na, a, na); + memset(a, '0', ona-na); + na = ona; + } + a[na] = 0; + bp = -Bias+1; + } + + /* close approx by naive conversion */ + num = 0; + den = 1; + for(i=0; i<9 && (c=a[i]); i++) { + num = num*10 + (c-'0'); + den *= 10; + } + low[0] = umuldiv(num, One, den); + hig[0] = umuldiv(num+1, One, den); + 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); + } + 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:; + } + return 0; +} + +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< 9){ + _divby(a, na, 9); + a[*na] = 0; + b -= 9; + } + if(b > 0) + _divby(a, na, b); +} + +static Tab tab1[] = +{ + 1, 0, "", + 3, 1, "7", + 6, 2, "63", + 9, 3, "511", + 13, 4, "8191", + 16, 5, "65535", + 19, 6, "524287", + 23, 7, "8388607", + 26, 8, "67108863", + 27, 9, "134217727", +}; + +static void +divascii(char *a, int *na, int *dp, int *bp) +{ + int b, d; + Tab *t; + + d = *dp; + if(d >= nelem(tab1)) + d = 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<= nelem(tab2)) + d = nelem(tab2)-1; + t = tab2 + d; + b = t->bp; + if(memcmp(a, t->cmp, t->siz) < 0) + d--; + p = a + *na; + *bp -= b; + *dp += d; + *na += d; + mulby(a, p+d, p, b); +} + +static int +xcmp(char *a, char *b) +{ + int c1, c2; + + while(c1 = *b++) { + c2 = *a++; + if(isupper(c2)) + c2 = tolower(c2); + if(c1 != c2) + return 1; + } + return 0; +} diff --git a/libc/strtod.h b/libc/strtod.h new file mode 100644 index 0000000..82c3d46 --- /dev/null +++ b/libc/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/libc/strtoll.c b/libc/strtoll.c new file mode 100644 index 0000000..098cfc1 --- /dev/null +++ b/libc/strtoll.c @@ -0,0 +1,93 @@ +#include +#include +#define VLONG_MAX ~(((vlong) 1)<<63) +#define VLONG_MIN (((vlong) 1)<<63) +vlong +strtoll(const char *nptr, char **endptr, int base) +{ + char *p; + vlong n, nn, m; + int c, ovfl, v, neg, ndig; + p = (char*)nptr; + neg = 0; + n = 0; + ndig = 0; + ovfl = 0; + /* + * White space + */ + for(;; p++) { + switch(*p) { + case ' ': + case '\t': + case '\n': + case '\f': + case '\r': + case '\v': + continue; + } + break; + } + /* + * Sign + */ + if(*p=='-' || *p=='+') + if(*p++ == '-') + neg = 1; + /* + * Base + */ + if(base==0){ + base = 10; + if(*p == '0') { + base = 8; + if(p[1]=='x' || p[1]=='X') { + p += 2; + base = 16; + } + } + } else + if(base==16 && *p=='0') { + if(p[1]=='x' || p[1]=='X') + p += 2; + } else + if(base<0 || 36= base) + break; + if(n > m) + ovfl = 1; + nn = n*base + v; + if(nn < n) + ovfl = 1; + n = nn; + } +Return: + if(ndig == 0) + p = (char*)nptr; + if(endptr) + *endptr = p; + if(ovfl){ + if(neg) + return VLONG_MIN; + return VLONG_MAX; + } + if(neg) + return -n; + return n; +} diff --git a/libc/sysfatal.c b/libc/sysfatal.c new file mode 100644 index 0000000..f5caee9 --- /dev/null +++ b/libc/sysfatal.c @@ -0,0 +1,30 @@ +#include +#include + +static void +_sysfatalimpl(char *fmt, va_list arg) +{ + char buf[1024]; + + vseprint(buf, buf+sizeof(buf), fmt, arg); + if(argv0) + fprint(2, "%s: %s\n", argv0, buf); + else + fprint(2, "%s\n", buf); +#undef write +write(2, buf, strlen(buf)); +write(2, "\n", 1); + panic("sysfatal"); +} + +void (*_sysfatal)(char *fmt, va_list arg) = _sysfatalimpl; + +void +sysfatal(char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + (*_sysfatal)(fmt, arg); + va_end(arg); +} diff --git a/libc/time.c b/libc/time.c new file mode 100644 index 0000000..5b584e2 --- /dev/null +++ b/libc/time.c @@ -0,0 +1,51 @@ +#include +#include + + +/* + * After a fork with fd's copied, both fd's are pointing to + * the same Chan structure. Since the offset is kept in the Chan + * structure, the seek's and read's in the two processes can + * compete at moving the offset around. Hence the unusual loop + * in the middle of this routine. + */ +static long +oldtime(long *tp) +{ + char b[20]; + static int f = -1; + int i, retries; + long t; + + memset(b, 0, sizeof(b)); + for(retries = 0; retries < 100; retries++){ + if(f < 0) + f = open("/dev/time", OREAD|OCEXEC); + if(f < 0) + break; + if(seek(f, 0, 0) < 0 || (i = read(f, b, sizeof(b))) < 0){ + close(f); + f = -1; + } else { + if(i != 0) + break; + } + } + t = atol(b); + if(tp) + *tp = t; + return t; +} + +long +time(long *tp) +{ + vlong t; + + t = nsec()/((vlong)1000000000); + if(t == 0) + t = oldtime(0); + if(tp != nil) + *tp = t; + return t; +} diff --git a/libc/tokenize.c b/libc/tokenize.c new file mode 100644 index 0000000..de234d4 --- /dev/null +++ b/libc/tokenize.c @@ -0,0 +1,107 @@ +#include +#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) +{ + ulong x; + static int randfd = -1; + + if(randfd < 0) + randfd = open("/dev/random", OREAD|OCEXEC); + if(randfd < 0) + sysfatal("can't open /dev/random"); + if(read(randfd, &x, sizeof(x)) != sizeof(x)) + sysfatal("can't read /dev/random"); + return x; +} diff --git a/libc/u16.c b/libc/u16.c new file mode 100644 index 0000000..4e16371 --- /dev/null +++ b/libc/u16.c @@ -0,0 +1,53 @@ +#include +#include +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/libc/u32.c b/libc/u32.c new file mode 100644 index 0000000..7423984 --- /dev/null +++ b/libc/u32.c @@ -0,0 +1,110 @@ +#include +#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/libc/u64.c b/libc/u64.c new file mode 100644 index 0000000..bf86c63 --- /dev/null +++ b/libc/u64.c @@ -0,0 +1,127 @@ +#include +#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/libc/utf.h b/libc/utf.h new file mode 100644 index 0000000..623bfda --- /dev/null +++ b/libc/utf.h @@ -0,0 +1,51 @@ +#ifndef _UTFH_ +#define _UTFH_ 1 + +typedef unsigned short Rune; /* 16 bits */ + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80, /* decoding error in UTF */ +}; + +/* + * rune routines + */ +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); + +#endif diff --git a/libc/utfdef.h b/libc/utfdef.h new file mode 100644 index 0000000..4b58ae8 --- /dev/null +++ b/libc/utfdef.h @@ -0,0 +1,14 @@ +#define uchar _utfuchar +#define ushort _utfushort +#define uint _utfuint +#define ulong _utfulong +#define vlong _utfvlong +#define uvlong _utfuvlong + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#define nil ((void*)0) diff --git a/libc/utfecpy.c b/libc/utfecpy.c new file mode 100644 index 0000000..bc451c0 --- /dev/null +++ b/libc/utfecpy.c @@ -0,0 +1,21 @@ +#include +#include + +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/libc/utflen.c b/libc/utflen.c new file mode 100644 index 0000000..7273a69 --- /dev/null +++ b/libc/utflen.c @@ -0,0 +1,23 @@ +#include +#include + +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++; + } + return 0; +} diff --git a/libc/utfnlen.c b/libc/utfnlen.c new file mode 100644 index 0000000..43e9c4a --- /dev/null +++ b/libc/utfnlen.c @@ -0,0 +1,26 @@ +#include +#include + +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/libc/utfrrune.c b/libc/utfrrune.c new file mode 100644 index 0000000..900e5bc --- /dev/null +++ b/libc/utfrrune.c @@ -0,0 +1,31 @@ +#include +#include + +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; + } + return 0; +} diff --git a/libc/utfrune.c b/libc/utfrune.c new file mode 100644 index 0000000..2373db2 --- /dev/null +++ b/libc/utfrune.c @@ -0,0 +1,30 @@ +#include +#include + +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; + } + return 0; +} diff --git a/libc/utfutf.c b/libc/utfutf.c new file mode 100644 index 0000000..48d1ee8 --- /dev/null +++ b/libc/utfutf.c @@ -0,0 +1,26 @@ +#include +#include + + +/* + * 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/libc/vfprint.c b/libc/vfprint.c new file mode 100644 index 0000000..9312915 --- /dev/null +++ b/libc/vfprint.c @@ -0,0 +1,34 @@ +#include +#include +#include "fmtdef.h" + +/* + * generic routine for flushing a formatting buffer + * to a file descriptor + */ +int +_fmtFdFlush(Fmt *f) +{ + int n; + + n = (char*)f->to - (char*)f->start; + if(n && write((int)f->farg, f->start, n) != n) + return 0; + f->to = f->start; + return 1; +} + +int +vfprint(int fd, char *fmt, va_list args) +{ + Fmt f; + char buf[256]; + int n; + + fmtfdinit(&f, fd, buf, sizeof(buf)); + f.args = args; + n = dofmt(&f, fmt); + if(n > 0 && _fmtFdFlush(&f) == 0) + return -1; + return n; +} diff --git a/libc/vseprint.c b/libc/vseprint.c new file mode 100644 index 0000000..72d02f7 --- /dev/null +++ b/libc/vseprint.c @@ -0,0 +1,23 @@ +#include +#include + +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 = nil; + f.farg = nil; + f.nfmt = 0; + f.args = args; + dofmt(&f, fmt); + *(char*)f.to = '\0'; + return f.to; +} + diff --git a/libc/vsmprint.c b/libc/vsmprint.c new file mode 100644 index 0000000..865c2dd --- /dev/null +++ b/libc/vsmprint.c @@ -0,0 +1,70 @@ +#include +#include +#include "fmtdef.h" + +static int +fmtStrFlush(Fmt *f) +{ + char *s; + int n; + + if(f->start == nil) + return 0; + n = (int)f->farg; + n *= 2; + s = f->start; + f->start = realloc(s, n); + if(f->start == nil){ + f->farg = nil; + f->to = nil; + f->stop = nil; + free(s); + return 0; + } + f->farg = (void*)n; + f->to = (char*)f->start + ((char*)f->to - s); + f->stop = (char*)f->start + n - 1; + return 1; +} + +int +fmtstrinit(Fmt *f) +{ + int n; + + memset(f, 0, sizeof *f); + f->runes = 0; + n = 32; + f->start = malloc(n); + if(f->start == nil) + return -1; + f->to = f->start; + f->stop = (char*)f->start + n - 1; + f->flush = fmtStrFlush; + f->farg = (void*)n; + f->nfmt = 0; + return 0; +} + +/* + * print into an allocated string buffer + */ +char* +vsmprint(char *fmt, va_list args) +{ + Fmt f; + int n; + + if(fmtstrinit(&f) < 0) + return nil; + f.args = args; + n = dofmt(&f, fmt); + if(f.start == nil) + return nil; + if(n < 0){ + free(f.start); + return nil; + } + *(char*)f.to = '\0'; + return f.start; +} diff --git a/libc/vsnprint.c b/libc/vsnprint.c new file mode 100644 index 0000000..bd20496 --- /dev/null +++ b/libc/vsnprint.c @@ -0,0 +1,22 @@ +#include +#include + +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 = nil; + f.farg = nil; + f.nfmt = 0; + f.args = args; + dofmt(&f, fmt); + *(char*)f.to = '\0'; + return (char*)f.to - buf; +} diff --git a/libdraw/Makefile b/libdraw/Makefile new file mode 100644 index 0000000..965e193 --- /dev/null +++ b/libdraw/Makefile @@ -0,0 +1,24 @@ +LIB=libdraw.a +CC=gcc +CFLAGS=-I../include -I. -c -ggdb -D_THREAD_SAFE -pthread +O=o + +OFILES=\ + alloc.$O\ + arith.$O\ + bytesperline.$O\ + chan.$O\ + defont.$O\ + drawrepl.$O\ + icossin.$O\ + icossin2.$O\ + rectclip.$O\ + rgb.$O + +$(LIB): $(OFILES) + ar r $(LIB) $(OFILES) + ranlib $(LIB) + +%.$O: %.c + $(CC) $(CFLAGS) $*.c + diff --git a/libdraw/alloc.c b/libdraw/alloc.c new file mode 100644 index 0000000..bc7a7d7 --- /dev/null +++ b/libdraw/alloc.c @@ -0,0 +1,237 @@ +#include +#include +#include + +Image* +allocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val) +{ + return _allocimage(nil, d, r, chan, repl, val, 0, 0); +} + +Image* +_allocimage(Image *ai, Display *d, Rectangle r, ulong chan, int repl, ulong val, int screenid, int refresh) +{ + uchar *a; + char *err; + Image *i; + Rectangle clipr; + int id; + int depth; + + err = 0; + i = 0; + + if(chan == 0){ + werrstr("bad channel descriptor"); + return nil; + } + + depth = chantodepth(chan); + if(depth == 0){ + err = "bad channel descriptor"; + Error: + if(err) + werrstr("allocimage: %s", err); + else + werrstr("allocimage: %r"); + free(i); + return 0; + } + + /* flush pending data so we don't get error allocating the image */ + flushimage(d, 0); + a = bufimage(d, 1+4+4+1+4+1+4*4+4*4+4); + if(a == 0) + goto Error; + d->imageid++; + id = d->imageid; + a[0] = 'b'; + BPLONG(a+1, id); + BPLONG(a+5, screenid); + a[9] = refresh; + BPLONG(a+10, chan); + a[14] = repl; + BPLONG(a+15, r.min.x); + BPLONG(a+19, r.min.y); + BPLONG(a+23, r.max.x); + BPLONG(a+27, r.max.y); + if(repl) + /* huge but not infinite, so various offsets will leave it huge, not overflow */ + clipr = Rect(-0x3FFFFFFF, -0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF); + else + clipr = r; + BPLONG(a+31, clipr.min.x); + BPLONG(a+35, clipr.min.y); + BPLONG(a+39, clipr.max.x); + BPLONG(a+43, clipr.max.y); + BPLONG(a+47, val); + if(flushimage(d, 0) < 0) + goto Error; + + if(ai) + i = ai; + else{ + i = malloc(sizeof(Image)); + if(i == nil){ + a = bufimage(d, 1+4); + if(a){ + a[0] = 'f'; + BPLONG(a+1, id); + flushimage(d, 0); + } + goto Error; + } + } + i->display = d; + i->id = id; + i->depth = depth; + i->chan = chan; + i->r = r; + i->clipr = clipr; + i->repl = repl; + i->screen = 0; + i->next = 0; + return i; +} + +Image* +namedimage(Display *d, char *name) +{ + uchar *a; + char *err, buf[12*12+1]; + Image *i; + int id, n; + ulong chan; + + err = 0; + i = 0; + + n = strlen(name); + if(n >= 256){ + err = "name too long"; + Error: + if(err) + werrstr("namedimage: %s", err); + else + werrstr("namedimage: %r"); + if(i) + free(i); + return 0; + } + /* flush pending data so we don't get error allocating the image */ + flushimage(d, 0); + a = bufimage(d, 1+4+1+n); + if(a == 0) + goto Error; + d->imageid++; + id = d->imageid; + a[0] = 'n'; + BPLONG(a+1, id); + a[5] = n; + memmove(a+6, name, n); + if(flushimage(d, 0) < 0) + goto Error; + + if(pread(d->ctlfd, buf, sizeof buf, 0) < 12*12) + goto Error; + buf[12*12] = '\0'; + + i = malloc(sizeof(Image)); + if(i == nil){ + Error1: + a = bufimage(d, 1+4); + if(a){ + a[0] = 'f'; + BPLONG(a+1, id); + flushimage(d, 0); + } + goto Error; + } + i->display = d; + i->id = id; + if((chan=strtochan(buf+2*12))==0){ + werrstr("bad channel '%.12s' from devdraw", buf+2*12); + goto Error1; + } + i->chan = chan; + i->depth = chantodepth(chan); + i->repl = atoi(buf+3*12); + i->r.min.x = atoi(buf+4*12); + i->r.min.y = atoi(buf+5*12); + i->r.max.x = atoi(buf+6*12); + i->r.max.y = atoi(buf+7*12); + i->clipr.min.x = atoi(buf+8*12); + i->clipr.min.y = atoi(buf+9*12); + i->clipr.max.x = atoi(buf+10*12); + i->clipr.max.y = atoi(buf+11*12); + i->screen = 0; + i->next = 0; + return i; +} + +int +nameimage(Image *i, char *name, int in) +{ + uchar *a; + int n; + + n = strlen(name); + a = bufimage(i->display, 1+4+1+1+n); + if(a == 0) + return 0; + a[0] = 'N'; + BPLONG(a+1, i->id); + a[5] = in; + a[6] = n; + memmove(a+7, name, n); + if(flushimage(i->display, 0) < 0) + return 0; + return 1; +} + +int +_freeimage1(Image *i) +{ + uchar *a; + Display *d; + Image *w; + + if(i == 0) + return 0; + /* make sure no refresh events occur on this if we block in the write */ + d = i->display; + /* flush pending data so we don't get error deleting the image */ + flushimage(d, 0); + a = bufimage(d, 1+4); + if(a == 0) + return -1; + a[0] = 'f'; + BPLONG(a+1, i->id); + if(i->screen){ + w = d->windows; + if(w == i) + d->windows = i->next; + else + while(w){ + if(w->next == i){ + w->next = i->next; + break; + } + w = w->next; + } + } + if(flushimage(d, i->screen!=0) < 0) + return -1; + + return 0; +} + +int +freeimage(Image *i) +{ + int ret; + + ret = _freeimage1(i); + free(i); + return ret; +} diff --git a/libdraw/arith.c b/libdraw/arith.c new file mode 100644 index 0000000..885b3c7 --- /dev/null +++ b/libdraw/arith.c @@ -0,0 +1,206 @@ +#include +#include +#include + +Point +Pt(int x, int y) +{ + Point p; + + p.x = x; + p.y = y; + return p; +} + +Rectangle +Rect(int x, int y, int bx, int by) +{ + Rectangle r; + + r.min.x = x; + r.min.y = y; + r.max.x = bx; + r.max.y = by; + return r; +} + +Rectangle +Rpt(Point min, Point max) +{ + Rectangle r; + + r.min = min; + r.max = max; + return r; +} + +Point +addpt(Point a, Point b) +{ + a.x += b.x; + a.y += b.y; + return a; +} + +Point +subpt(Point a, Point b) +{ + a.x -= b.x; + a.y -= b.y; + return a; +} + +Rectangle +insetrect(Rectangle r, int n) +{ + r.min.x += n; + r.min.y += n; + r.max.x -= n; + r.max.y -= n; + return r; +} + +Point +divpt(Point a, int b) +{ + a.x /= b; + a.y /= b; + return a; +} + +Point +mulpt(Point a, int b) +{ + a.x *= b; + a.y *= b; + return a; +} + +Rectangle +rectsubpt(Rectangle r, Point p) +{ + r.min.x -= p.x; + r.min.y -= p.y; + r.max.x -= p.x; + r.max.y -= p.y; + return r; +} + +Rectangle +rectaddpt(Rectangle r, Point p) +{ + r.min.x += p.x; + r.min.y += p.y; + r.max.x += p.x; + r.max.y += p.y; + return r; +} + +int +eqpt(Point p, Point q) +{ + return p.x==q.x && p.y==q.y; +} + +int +eqrect(Rectangle r, Rectangle s) +{ + return r.min.x==s.min.x && r.max.x==s.max.x && + r.min.y==s.min.y && r.max.y==s.max.y; +} + +int +rectXrect(Rectangle r, Rectangle s) +{ + return r.min.x=r.min.x && p.x=r.min.y && p.ymin.x > r2.min.x) + r1->min.x = r2.min.x; + if(r1->min.y > r2.min.y) + r1->min.y = r2.min.y; + if(r1->max.x < r2.max.x) + r1->max.x = r2.max.x; + if(r1->max.y < r2.max.y) + r1->max.y = r2.max.y; +} + +ulong +drawld2chan[] = { + GREY1, + GREY2, + GREY4, + CMAP8, +}; + +int log2[] = { -1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, 4 /* BUG */, -1, -1, -1, -1, -1, -1, -1, 5 }; + +ulong +setalpha(ulong color, uchar alpha) +{ + int red, green, blue; + + red = (color >> 3*8) & 0xFF; + green = (color >> 2*8) & 0xFF; + blue = (color >> 1*8) & 0xFF; + /* ignore incoming alpha */ + red = (red * alpha)/255; + green = (green * alpha)/255; + blue = (blue * alpha)/255; + return (red<<3*8) | (green<<2*8) | (blue<<1*8) | (alpha<<0*8); +} + +Point ZP; +Rectangle ZR; +int +Rfmt(Fmt *f) +{ + Rectangle r; + + r = va_arg(f->args, Rectangle); + return fmtprint(f, "%P %P", r.min, r.max); +} + +int +Pfmt(Fmt *f) +{ + Point p; + + p = va_arg(f->args, Point); + return fmtprint(f, "[%d %d]", p.x, p.y); +} + diff --git a/libdraw/bytesperline.c b/libdraw/bytesperline.c new file mode 100644 index 0000000..08ff7d7 --- /dev/null +++ b/libdraw/bytesperline.c @@ -0,0 +1,34 @@ +#include +#include +#include + +static +int +unitsperline(Rectangle r, int d, int bitsperunit) +{ + ulong l, t; + + if(d <= 0 || d > 32) /* being called wrong. d is image depth. */ + abort(); + + if(r.min.x >= 0){ + l = (r.max.x*d+bitsperunit-1)/bitsperunit; + l -= (r.min.x*d)/bitsperunit; + }else{ /* make positive before divide */ + t = (-r.min.x*d+bitsperunit-1)/bitsperunit; + l = t+(r.max.x*d+bitsperunit-1)/bitsperunit; + } + return l; +} + +int +wordsperline(Rectangle r, int d) +{ + return unitsperline(r, d, 8*sizeof(ulong)); +} + +int +bytesperline(Rectangle r, int d) +{ + return unitsperline(r, d, 8); +} diff --git a/libdraw/chan.c b/libdraw/chan.c new file mode 100644 index 0000000..5145b42 --- /dev/null +++ b/libdraw/chan.c @@ -0,0 +1,77 @@ +#include +#include +#include + +static char channames[] = "rgbkamx"; +char* +chantostr(char *buf, ulong cc) +{ + ulong c, rc; + char *p; + + if(chantodepth(cc) == 0) + return nil; + + /* reverse the channel descriptor so we can easily generate the string in the right order */ + rc = 0; + for(c=cc; c; c>>=8){ + rc <<= 8; + rc |= c&0xFF; + } + + p = buf; + for(c=rc; c; c>>=8) { + *p++ = channames[TYPE(c)]; + *p++ = '0'+NBITS(c); + } + *p = 0; + + return buf; +} + +/* avoid pulling in ctype when using with drawterm etc. */ +static int +isspace(char c) +{ + return c==' ' || c== '\t' || c=='\r' || c=='\n'; +} + +ulong +strtochan(char *s) +{ + char *p, *q; + ulong c; + int t, n; + + c = 0; + p=s; + while(*p && isspace(*p)) + p++; + + while(*p && !isspace(*p)){ + if((q = strchr(channames, p[0])) == nil) + return 0; + t = q-channames; + if(p[1] < '0' || p[1] > '9') + return 0; + n = p[1]-'0'; + c = (c<<8) | __DC(t, n); + p += 2; + } + return c; +} + +int +chantodepth(ulong c) +{ + int n; + + for(n=0; c; c>>=8){ + if(TYPE(c) >= NChan || NBITS(c) > 8 || NBITS(c) <= 0) + return 0; + n += NBITS(c); + } + if(n==0 || (n>8 && n%8) || (n<8 && 8%n)) + return 0; + return n; +} diff --git a/libdraw/defont.c b/libdraw/defont.c new file mode 100644 index 0000000..b0c85e1 --- /dev/null +++ b/libdraw/defont.c @@ -0,0 +1,402 @@ +#include +#include +#include + +/* + * lucm/latin1.9, in uncompressed form + */ +uchar +defontdata[] = +{ +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x32,0x33,0x30,0x34,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x31,0x35,0x20,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x30,0x06,0x06,0x03,0x42,0x40,0x00,0x00,0x00,0x18,0x03,0x03, +0x02,0x43,0x00,0x60,0x60,0x48,0x00,0x0d,0x0c,0x01,0x81,0x80,0xd0,0x90,0x00,0x00, +0x18,0x01,0x81,0x81,0x40,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x7f,0x9c,0x1c, +0x0e,0x07,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x70, +0x38,0x1c,0x0e,0x04,0x81,0xc1,0xc0,0x70,0x00,0x1c,0x1c,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xaa,0x80,0xc0,0x63,0xe3, +0xf1,0xf8,0xfe,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x7f,0xff,0xff,0x1f,0x8f, +0xc7,0xe3,0xf1,0xfb,0x7e,0x3e,0x3f,0x8f,0xff,0xe3,0xe3,0xff,0xff,0xff,0xff,0xff, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x0c,0x18,0x09,0x05,0x82,0x40,0xc0,0x00,0x00,0x06,0x0c,0x04, +0x82,0x40,0xc1,0x80,0x90,0x48,0x00,0x16,0x03,0x06,0x02,0x41,0x60,0x90,0x00,0x00, +0x06,0x06,0x02,0x41,0x41,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x7f,0xa0,0x10, +0x08,0x04,0x02,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x48, +0x24,0x12,0x09,0x06,0x82,0x01,0x00,0x90,0x00,0x20,0x10,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x04,0x80,0x00,0x40,0x00,0x00,0x38,0x06,0x18,0x00,0x00,0x00,0x00,0x00, +0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x07,0xc6,0x01,0xf0,0x00,0x00,0x0c,0x00,0x18,0x00,0x00,0x30,0x00,0x3c, +0x00,0x60,0x06,0x01,0x8c,0x07,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xe0,0xc3,0xc0,0x01,0x54,0x9c,0xc0,0x5f,0xef, +0xf7,0xfb,0xfd,0xbf,0xff,0xff,0xff,0xff,0xff,0xff,0xfb,0x7f,0xff,0xff,0x6f,0xb7, +0xdb,0xed,0xf6,0xf9,0x7d,0xfe,0xff,0x6f,0xff,0xdf,0xef,0xff,0xff,0xff,0xff,0xff, +0xff,0x00,0x01,0x00,0x00,0x00,0x00,0x30,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x30,0x06,0x06,0x06,0x82,0x80,0xc0,0x00, +0x00,0x18,0x03,0x03,0x02,0x41,0x80,0x30,0x30,0x24,0x76,0x0d,0x0c,0x00,0xc0,0xc0, +0xd0,0x50,0x00,0x00,0x18,0x01,0x81,0x81,0x40,0x30,0x00,0x28,0x0f,0x7f,0xbc,0x1c, +0x0e,0x07,0x03,0xc0,0x10,0x70,0x24,0x10,0x09,0x07,0x03,0x80,0xe0,0x70,0x90,0x48, +0x24,0x12,0x09,0x05,0x81,0x81,0xc0,0x80,0x70,0x18,0x1c,0x07,0x01,0xc1,0xc0,0x90, +0x00,0x0c,0x04,0x84,0x83,0xe1,0xc0,0xe0,0x38,0x0c,0x0c,0x02,0x00,0x00,0x00,0x00, +0x00,0x06,0x1c,0x06,0x0f,0x87,0xc0,0x63,0xf8,0x78,0xfe,0x3e,0x0e,0x00,0x00,0x00, +0x00,0x00,0x00,0x7c,0x1c,0x0c,0x1f,0x03,0xc7,0xc3,0xf1,0xf8,0x3c,0x63,0x3f,0x0f, +0x8c,0x66,0x06,0x19,0x84,0x78,0x7e,0x1e,0x1f,0x07,0xcf,0xf3,0x1b,0x0d,0x86,0x63, +0x61,0x9f,0xc6,0x06,0x00,0x30,0x00,0x00,0x10,0x00,0x18,0x00,0x00,0x30,0x00,0x60, +0x00,0x60,0x06,0x01,0x8c,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80, +0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0xc0,0x60,0x00,0xaa,0xb6,0xc0,0x43,0xe3, +0xf1,0xf8,0xfc,0x3f,0xef,0x8f,0xdb,0xef,0xf6,0xf8,0xfb,0xff,0x1f,0x8f,0x6f,0xb7, +0xdb,0xed,0xf6,0xfa,0x7e,0x7e,0x3f,0x7f,0x8f,0xe7,0xe3,0xf8,0xfe,0x3e,0x3f,0x6f, +0x00,0x00,0x01,0x01,0xc8,0x0b,0x0c,0x30,0x7c,0x14,0x0f,0x0f,0x00,0x00,0x00,0x00, +0x78,0x00,0x1c,0x00,0x0f,0x07,0x81,0x80,0x00,0x7c,0x00,0x00,0x1c,0x0f,0x80,0x04, +0x42,0x23,0x90,0x00,0x18,0x0c,0x06,0x03,0x01,0x80,0xc0,0x3c,0x3c,0x3f,0x1f,0x8f, +0xc7,0xe7,0xe3,0xf1,0xf8,0xfc,0x7c,0x30,0x8f,0x07,0x83,0xc1,0xe0,0xf0,0x00,0x3d, +0x31,0x98,0xcc,0x66,0x36,0x19,0x80,0xcc,0x0c,0x18,0x09,0x0b,0x02,0x81,0x20,0x00, +0x00,0x06,0x0c,0x04,0x82,0x40,0x60,0xc0,0x48,0x24,0x18,0x16,0x03,0x03,0x01,0x21, +0x60,0x50,0x00,0x00,0x06,0x06,0x02,0x41,0x40,0xc1,0x80,0x28,0x87,0x7f,0x84,0x10, +0x08,0x04,0x02,0x40,0x38,0x48,0x24,0x10,0x09,0x04,0x04,0x81,0x00,0x80,0x90,0x48, +0x24,0x12,0x09,0x04,0x80,0x41,0x00,0x80,0x40,0x04,0x10,0x04,0x02,0x01,0x20,0x90, +0x00,0x0c,0x04,0x84,0x86,0x53,0x65,0xb0,0x08,0x18,0x06,0x0a,0x80,0x00,0x00,0x00, +0x00,0x0c,0x36,0x0e,0x19,0xcc,0xe0,0xe3,0xf8,0xcc,0xfe,0x63,0x1b,0x00,0x00,0x00, +0x00,0x00,0x00,0xc6,0x62,0x0c,0x19,0x86,0x66,0x63,0x01,0x80,0x66,0x63,0x0c,0x01, +0x8c,0xc6,0x06,0x19,0xc4,0xcc,0x63,0x33,0x19,0x8c,0x61,0x83,0x1b,0x0d,0x86,0x63, +0x61,0x80,0xc6,0x03,0x00,0x30,0x30,0x00,0x1c,0x00,0x18,0x00,0x00,0x30,0x00,0x60, +0x00,0x60,0x00,0x00,0x0c,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80, +0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0xc0,0x60,0x01,0x54,0x86,0xc0,0x7b,0xef, +0xf7,0xfb,0xfd,0xbf,0xc7,0xb7,0xdb,0xef,0xf6,0xfb,0xfb,0x7e,0xff,0x7f,0x6f,0xb7, +0xdb,0xed,0xf6,0xfb,0x7f,0xbe,0xff,0x7f,0xbf,0xfb,0xef,0xfb,0xfd,0xfe,0xdf,0x6f, +0xff,0x00,0x07,0x83,0x24,0x13,0x0c,0x30,0xc6,0x00,0x10,0x81,0x80,0x00,0x00,0x00, +0x84,0x00,0x22,0x00,0x01,0x80,0xc0,0x00,0x00,0xf4,0x00,0x00,0x2c,0x18,0xc0,0x0c, +0x46,0x20,0x90,0x00,0x18,0x0c,0x06,0x03,0x01,0x80,0xc0,0x70,0x66,0x30,0x18,0x0c, +0x06,0x01,0x80,0xc0,0x60,0x30,0x66,0x38,0x99,0x8c,0xc6,0x63,0x31,0x98,0x00,0x66, +0x31,0x98,0xcc,0x66,0x36,0x19,0x80,0xcc,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6c,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x00,0xff,0x7f,0xb8,0x1c, +0x0e,0x07,0x02,0x40,0x7c,0x70,0x3c,0x10,0x09,0x07,0x04,0x00,0xc0,0x60,0xe0,0x70, +0x38,0x1c,0x0e,0x04,0x83,0x81,0xc0,0x70,0x70,0x38,0x1c,0x07,0x02,0xc1,0xc0,0x90, +0x00,0x0c,0x00,0x04,0x86,0x43,0x69,0xb0,0x30,0x18,0x06,0x07,0x01,0x00,0x00,0x00, +0x00,0x0c,0x63,0x16,0x00,0xc0,0x61,0x62,0x01,0x80,0x06,0x63,0x31,0x80,0x00,0x00, +0x60,0x00,0xc0,0x06,0x43,0x16,0x19,0x8c,0x06,0x33,0x01,0x80,0xc0,0x63,0x0c,0x01, +0x8c,0x86,0x07,0x39,0xc5,0x86,0x63,0x61,0x99,0x8c,0x01,0x83,0x1b,0x0d,0xb6,0x63, +0x31,0x01,0x86,0x03,0x00,0x30,0x30,0x00,0x1c,0x3e,0x1b,0x03,0xc1,0xf0,0xf0,0x60, +0x3e,0x6e,0x3e,0x0f,0x8c,0x60,0xc5,0xb1,0xb8,0x38,0x6c,0x0f,0x8c,0xc7,0xc1,0x83, +0x19,0x8d,0x82,0x63,0x31,0x9f,0xc1,0x80,0xc0,0xc0,0x00,0xaa,0x86,0xc0,0x47,0xe3, +0xf1,0xf8,0xfd,0xbf,0x83,0x8f,0xc3,0xef,0xf6,0xf8,0xfc,0xff,0x3f,0x9f,0x1f,0x8f, +0xc7,0xe3,0xf1,0xfb,0x7c,0x7e,0x3f,0x8f,0x8f,0xc7,0xe3,0xf8,0xfd,0x3e,0x3f,0x6f, +0x00,0x0c,0x0d,0x43,0x03,0xe1,0x88,0x30,0xc0,0x00,0x27,0x41,0x80,0x00,0x00,0x01, +0x72,0x00,0x22,0x04,0x01,0x80,0xc0,0x03,0x18,0xf4,0x00,0x00,0x0c,0x18,0xc0,0x04, +0x82,0x43,0x20,0x18,0x2c,0x16,0x0b,0x05,0x82,0xc1,0x60,0xb0,0xc0,0x30,0x18,0x0c, +0x06,0x01,0x80,0xc0,0x60,0x30,0x63,0x38,0xb0,0xd8,0x6c,0x36,0x1b,0x0c,0x00,0xc7, +0x31,0x98,0xcc,0x66,0x33,0x11,0xf8,0xc8,0x7c,0x3e,0x1f,0x0f,0x87,0xc3,0xe1,0xd8, +0x3c,0x1e,0x0f,0x07,0x83,0xc7,0xc3,0xe1,0xf0,0xf8,0x06,0x37,0x07,0x03,0x81,0xc0, +0xe0,0x70,0x10,0x1d,0x31,0x98,0xcc,0x66,0x33,0x19,0xb0,0xc6,0x8f,0x7f,0x87,0x03, +0x81,0x80,0x90,0x30,0x6c,0x48,0x24,0x10,0x06,0x04,0x04,0x80,0x20,0x10,0x10,0x0e, +0x07,0x03,0x81,0xc0,0x60,0x88,0x38,0x0c,0x40,0x09,0x03,0x84,0x02,0x41,0x40,0x90, +0x00,0x0c,0x00,0x1f,0xe7,0x41,0xd1,0xa0,0x00,0x30,0x03,0x0a,0x81,0x00,0x00,0x00, +0x00,0x18,0x63,0x06,0x00,0xc0,0xc2,0x62,0x01,0xb0,0x0c,0x72,0x31,0x86,0x03,0x00, +0xc0,0x00,0x60,0x06,0x8f,0x16,0x19,0x0c,0x06,0x33,0x01,0x80,0xc0,0x63,0x0c,0x01, +0x8d,0x06,0x07,0x39,0x65,0x86,0x63,0x61,0x99,0x0e,0x01,0x83,0x19,0x89,0xb6,0x32, +0x33,0x03,0x06,0x01,0x80,0x30,0x78,0x00,0x00,0x03,0x1d,0x86,0x23,0x31,0x99,0xfc, +0x66,0x77,0x06,0x01,0x8c,0x40,0xc6,0xd9,0xdc,0x6c,0x76,0x19,0x8d,0xcc,0x27,0xf3, +0x19,0x8d,0x82,0x63,0x31,0x80,0xc0,0x80,0xc0,0x80,0x01,0x54,0x8c,0xc0,0x78,0xfc, +0x7e,0x7f,0x6f,0xcf,0x93,0xb7,0xdb,0xef,0xf9,0xfb,0xff,0xff,0xdf,0xef,0xef,0xf1, +0xf8,0xfc,0x7e,0x3f,0x9f,0x77,0xc7,0xf3,0xbf,0xf6,0xfc,0x7b,0xfd,0xbe,0xbf,0x6f, +0xff,0x0c,0x19,0x03,0x03,0x61,0x98,0x30,0x78,0x00,0x28,0x4f,0x83,0x30,0x00,0x01, +0x4a,0x00,0x1c,0x04,0x03,0x03,0x80,0x03,0x18,0xf4,0x00,0x00,0x0c,0x18,0xd9,0x84, +0x82,0x40,0xa0,0x18,0x2c,0x16,0x0b,0x05,0x82,0xc1,0x60,0xb0,0xc0,0x30,0x18,0x0c, +0x06,0x01,0x80,0xc0,0x60,0x30,0x63,0x2c,0xb0,0xd8,0x6c,0x36,0x1b,0x0c,0x64,0xcb, +0x31,0x98,0xcc,0x66,0x33,0x31,0x8c,0xd8,0x06,0x03,0x01,0x80,0xc0,0x60,0x30,0x6c, +0x62,0x33,0x19,0x8c,0xc6,0x60,0xc0,0x60,0x30,0x18,0x1e,0x3b,0x8d,0x86,0xc3,0x61, +0xb0,0xd8,0x10,0x36,0x31,0x98,0xcc,0x66,0x33,0x19,0xd8,0xc6,0x0f,0x7f,0x82,0x01, +0x02,0x40,0xd0,0x40,0x6c,0x70,0x24,0x1c,0x06,0x04,0x03,0x01,0xc0,0xe0,0x10,0x12, +0x09,0x04,0x82,0x40,0x90,0x50,0x10,0x12,0x70,0x09,0x04,0x04,0x01,0xc1,0x20,0x60, +0x00,0x0c,0x00,0x04,0x83,0xc0,0x20,0xcc,0x00,0x30,0x03,0x02,0x01,0x00,0x00,0x00, +0x00,0x18,0x63,0x06,0x01,0x83,0x84,0x63,0xf1,0xd8,0x18,0x3c,0x31,0x86,0x03,0x01, +0x83,0xf8,0x30,0x1c,0x9b,0x33,0x1e,0x0c,0x06,0x33,0xe1,0x80,0xc0,0x7f,0x0c,0x01, +0x8f,0x06,0x07,0x79,0x65,0x86,0x66,0x61,0x9e,0x07,0x81,0x83,0x19,0x89,0xb6,0x1c, +0x1a,0x03,0x06,0x01,0x80,0x30,0x48,0x00,0x00,0x03,0x18,0xcc,0x06,0x33,0x18,0x60, +0xc6,0x63,0x06,0x01,0x8c,0x80,0xc6,0xd9,0x8c,0xc6,0x63,0x31,0x8e,0x4c,0x01,0x83, +0x19,0x8d,0x92,0x32,0x31,0x81,0x87,0x00,0xc0,0x70,0xe4,0xaa,0x98,0xc0,0x7d,0xfe, +0xfd,0xbf,0x2f,0xbf,0x93,0x8f,0xdb,0xe3,0xf9,0xfb,0xff,0x1e,0x3f,0x1f,0xef,0xed, +0xf6,0xfb,0x7d,0xbf,0x6f,0xaf,0xef,0xed,0x8f,0xf6,0xfb,0xfb,0xfe,0x3e,0xdf,0x9f, +0x00,0x00,0x19,0x0f,0xc6,0x30,0xd0,0x00,0xcc,0x00,0x28,0x59,0x86,0x67,0xf0,0x01, +0x72,0x00,0x00,0x3f,0x86,0x00,0xc0,0x03,0x18,0xf4,0x00,0x00,0x0c,0x18,0xcc,0xc5, +0x32,0x83,0x4c,0x00,0x66,0x33,0x19,0x8c,0xc6,0x63,0x31,0xbc,0xc0,0x3e,0x1f,0x0f, +0x87,0xc1,0x80,0xc0,0x60,0x30,0xfb,0x2c,0xb0,0xd8,0x6c,0x36,0x1b,0x0c,0x38,0xcb, +0x31,0x98,0xcc,0x66,0x31,0xa1,0x8c,0xcc,0x06,0x03,0x01,0x80,0xc0,0x60,0x30,0x6c, +0xc0,0x63,0x31,0x98,0xcc,0x60,0xc0,0x60,0x30,0x18,0x37,0x31,0x98,0xcc,0x66,0x33, +0x19,0x8c,0x00,0x67,0x31,0x98,0xcc,0x66,0x33,0x19,0x8c,0xc6,0x1f,0x7f,0x82,0x01, +0x02,0x40,0xb0,0x40,0x6c,0x07,0x03,0x83,0x80,0xe0,0xe0,0x00,0x18,0x0e,0x10,0x10, +0x08,0x04,0x02,0x00,0xf0,0x20,0x10,0x1e,0x08,0x89,0x03,0x00,0xe0,0x38,0x1c,0x0e, +0x00,0x0c,0x00,0x04,0x81,0xe0,0x41,0x6c,0x00,0x30,0x03,0x00,0x0f,0xe0,0x03,0xf8, +0x00,0x30,0x63,0x06,0x03,0x00,0xc7,0xf0,0x39,0x8c,0x30,0x3e,0x1b,0x80,0x00,0x03, +0x00,0x00,0x18,0x30,0x9b,0x23,0x19,0x0c,0x06,0x33,0x01,0xf8,0xc6,0x63,0x0c,0x01, +0x8d,0x86,0x05,0xd9,0x35,0x86,0x7c,0x61,0x9b,0x01,0xc1,0x83,0x19,0x99,0xb4,0x1c, +0x0c,0x06,0x06,0x00,0xc0,0x30,0xcc,0x00,0x00,0x3f,0x18,0xcc,0x06,0x33,0xf8,0x60, +0xc6,0x63,0x06,0x01,0x8f,0x00,0xc6,0xd9,0x8c,0xc6,0x63,0x31,0x8c,0x0f,0x81,0x83, +0x18,0xd9,0xba,0x1c,0x1b,0x03,0x00,0x80,0xc0,0x81,0x75,0x54,0x98,0xc0,0x7d,0xfe, +0xfd,0xbf,0x4f,0xbf,0x93,0xf8,0xfc,0x7c,0x7f,0x1f,0x1f,0x6f,0xe7,0xf1,0xef,0xef, +0xf7,0xfb,0xfd,0xff,0x0f,0xdf,0xef,0xe1,0xf7,0x76,0xfc,0xff,0x1f,0xc7,0xe3,0xf1, +0xff,0x08,0x19,0x03,0x06,0x31,0xf8,0x00,0xc6,0x00,0x28,0x5b,0x8c,0xc0,0x11,0xf1, +0x4a,0x00,0x00,0x04,0x0c,0x00,0xc0,0x03,0x18,0x74,0x38,0x00,0x0c,0x18,0xc6,0x65, +0x52,0xb8,0x54,0x18,0x46,0x23,0x11,0x88,0xc4,0x62,0x31,0x30,0xc0,0x30,0x18,0x0c, +0x06,0x01,0x80,0xc0,0x60,0x30,0x63,0x26,0xb0,0xd8,0x6c,0x36,0x1b,0x0c,0x10,0xd3, +0x31,0x98,0xcc,0x66,0x30,0xc1,0x8c,0xc6,0x7e,0x3f,0x1f,0x8f,0xc7,0xe3,0xf1,0xfc, +0xc0,0x7f,0x3f,0x9f,0xcf,0xe0,0xc0,0x60,0x30,0x18,0x63,0x31,0x98,0xcc,0x66,0x33, +0x19,0x8c,0xfe,0x6b,0x31,0x98,0xcc,0x66,0x31,0xb1,0x8c,0x6c,0x0e,0x7f,0x82,0x01, +0x01,0x80,0x90,0x30,0xc6,0x08,0x01,0x02,0x00,0x40,0x80,0xe0,0x24,0x04,0x1c,0x10, +0x08,0x04,0x02,0x00,0x90,0x20,0x10,0x12,0x0d,0x86,0x00,0x81,0x00,0x40,0x20,0x10, +0x00,0x04,0x00,0x1f,0xe1,0x70,0xbb,0x28,0x00,0x30,0x03,0x00,0x01,0x00,0x00,0x00, +0x00,0x30,0x63,0x06,0x06,0x00,0x67,0xf0,0x19,0x8c,0x30,0x67,0x0d,0x80,0x00,0x01, +0x83,0xf8,0x30,0x30,0x9b,0x7f,0x19,0x8c,0x06,0x33,0x01,0x80,0xc6,0x63,0x0c,0x01, +0x8c,0xc6,0x05,0xd9,0x35,0x86,0x60,0x61,0x99,0x80,0xe1,0x83,0x18,0xd0,0xdc,0x26, +0x0c,0x0c,0x06,0x00,0xc0,0x30,0x84,0x00,0x00,0x63,0x18,0xcc,0x06,0x33,0x00,0x60, +0xc6,0x63,0x06,0x01,0x8d,0x80,0xc6,0xd9,0x8c,0xc6,0x63,0x31,0x8c,0x03,0xe1,0x83, +0x18,0xd9,0xba,0x1c,0x1b,0x06,0x01,0x80,0xc0,0xc1,0x38,0xaa,0x80,0xc0,0x7d,0xfe, +0xfe,0x7f,0x6f,0xcf,0x39,0xf7,0xfe,0xfd,0xff,0xbf,0x7f,0x0f,0xdb,0xfb,0xe3,0xef, +0xf7,0xfb,0xfd,0xff,0x6f,0xdf,0xef,0xed,0xf2,0x79,0xff,0x7e,0xff,0xbf,0xdf,0xef, +0x00,0x0c,0x19,0x03,0x03,0x60,0x60,0x30,0x66,0x00,0x28,0x4d,0xc6,0x60,0x10,0x00, +0x84,0x00,0x00,0x04,0x0f,0x87,0x80,0x03,0x18,0x14,0x38,0x00,0x3f,0x0f,0x8c,0xc2, +0x90,0x84,0xa4,0x18,0xfe,0x7f,0x3f,0x9f,0xcf,0xe7,0xf1,0xf0,0xc0,0x30,0x18,0x0c, +0x06,0x01,0x80,0xc0,0x60,0x30,0x63,0x26,0xb0,0xd8,0x6c,0x36,0x1b,0x0c,0x38,0xd3, +0x31,0x98,0xcc,0x66,0x30,0xc1,0x98,0xc6,0xc6,0x63,0x31,0x98,0xcc,0x66,0x33,0x60, +0xc0,0x60,0x30,0x18,0x0c,0x00,0xc0,0x60,0x30,0x18,0x63,0x31,0x98,0xcc,0x66,0x33, +0x19,0x8c,0x00,0x6b,0x31,0x98,0xcc,0x66,0x31,0xb1,0x8c,0x6c,0x1c,0x7f,0x81,0x20, +0x90,0x38,0x18,0x0b,0x83,0x06,0x01,0x03,0x80,0x40,0xe0,0x90,0x24,0x04,0x03,0x8e, +0x86,0xc3,0x61,0x90,0x24,0x12,0x0e,0x04,0x8a,0x81,0xc7,0x70,0xc0,0x30,0x18,0x0c, +0x00,0x00,0x00,0x04,0x81,0x31,0x6f,0x30,0x00,0x18,0x06,0x00,0x01,0x00,0x00,0x00, +0x00,0x60,0x63,0x06,0x0c,0x00,0x60,0x60,0x19,0x8c,0x60,0x63,0x01,0x80,0x00,0x00, +0xc0,0x00,0x60,0x00,0x4d,0xe1,0x99,0x8c,0x06,0x33,0x01,0x80,0xc6,0x63,0x0c,0x01, +0x8c,0xc6,0x04,0x99,0x1d,0x86,0x60,0x61,0x99,0x80,0x61,0x83,0x18,0xd0,0xdc,0x63, +0x0c,0x0c,0x06,0x00,0x60,0x30,0x84,0x00,0x00,0x63,0x18,0xcc,0x06,0x33,0x00,0x60, +0x6e,0x63,0x06,0x01,0x8c,0xc0,0xc6,0xd9,0x8c,0xc6,0x63,0x31,0x8c,0x00,0x61,0x83, +0x18,0xd0,0xcc,0x26,0x0e,0x0c,0x03,0x00,0xc0,0x60,0x01,0x54,0x98,0xc0,0x7e,0xdf, +0x6f,0xc7,0xe7,0xf4,0x7c,0xf9,0xfe,0xfc,0x7f,0xbf,0x1f,0x5f,0xdb,0xfb,0xfc,0x71, +0x79,0x3c,0x9e,0x6f,0xdb,0xed,0xf1,0xfb,0x75,0x7e,0x38,0x8f,0x3f,0xcf,0xe7,0xf3, +0xff,0x0c,0x0d,0x03,0x03,0xe1,0xf8,0x30,0x3c,0x00,0x27,0x40,0x03,0x30,0x00,0x00, +0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x18,0x14,0x00,0x00,0x00,0x00,0x19,0x82, +0xf8,0x98,0xbe,0x70,0xc3,0x61,0xb0,0xd8,0x6c,0x36,0x1b,0x30,0xc0,0x30,0x18,0x0c, +0x06,0x01,0x80,0xc0,0x60,0x30,0x63,0x23,0xb0,0xd8,0x6c,0x36,0x1b,0x0c,0x4c,0xe3, +0x31,0x98,0xcc,0x66,0x30,0xc1,0xf0,0xc6,0xc6,0x63,0x31,0x98,0xcc,0x66,0x33,0x60, +0xc0,0x60,0x30,0x18,0x0c,0x00,0xc0,0x60,0x30,0x18,0x63,0x31,0x98,0xcc,0x66,0x33, +0x19,0x8c,0x10,0x73,0x31,0x98,0xcc,0x66,0x30,0xe1,0x8c,0x38,0x1c,0x7f,0x80,0xa0, +0x50,0x10,0x24,0x0d,0xff,0x01,0x01,0x02,0x00,0x40,0x80,0xf0,0x24,0x04,0x02,0x01, +0x81,0x20,0x10,0x30,0x28,0x1a,0x09,0x06,0x8a,0x81,0x20,0x90,0x20,0x08,0x04,0x02, +0x00,0x0c,0x00,0x04,0x85,0x32,0x6f,0xb8,0x00,0x18,0x06,0x00,0x01,0x01,0xc0,0x00, +0x70,0x60,0x36,0x06,0x1f,0xcc,0xe0,0x63,0x30,0xd8,0x60,0x63,0x33,0x06,0x03,0x00, +0x60,0x00,0xc0,0x30,0x60,0x61,0x99,0x86,0x66,0x63,0x01,0x80,0x66,0x63,0x0c,0x03, +0x0c,0x66,0x04,0x19,0x1c,0xcc,0x60,0x33,0x18,0xcc,0x61,0x81,0xb0,0x60,0xcc,0x63, +0x0c,0x18,0x06,0x00,0x60,0x30,0x00,0x00,0x00,0x67,0x19,0x86,0x23,0x71,0x88,0x60, +0x36,0x63,0x06,0x01,0x8c,0x60,0xc6,0xd9,0x8c,0x6c,0x66,0x1b,0x8c,0x08,0x61,0x83, +0xb8,0x70,0xcc,0x63,0x0c,0x18,0x03,0x00,0xc0,0x60,0x00,0xaa,0x98,0xc0,0x7f,0x5f, +0xaf,0xef,0xdb,0xf2,0x00,0xfe,0xfe,0xfd,0xff,0xbf,0x7f,0x6f,0xdb,0xfb,0xfd,0xfe, +0x7e,0xdf,0xef,0xcf,0xd7,0xe5,0xf6,0xf9,0x75,0x7e,0xdf,0x6f,0xdf,0xf7,0xfb,0xfd, +0x00,0x0c,0x07,0xc6,0x04,0x10,0x60,0x30,0x06,0x00,0x10,0x80,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x3f,0x80,0x00,0x00,0x03,0xb8,0x14,0x00,0x00,0x00,0x00,0x00,0x04, +0x11,0x21,0x04,0xc0,0xc3,0x61,0xb0,0xd8,0x6c,0x36,0x1b,0x30,0x66,0x30,0x18,0x0c, +0x06,0x01,0x80,0xc0,0x60,0x30,0x66,0x23,0x99,0x8c,0xc6,0x63,0x31,0x98,0x00,0x66, +0x1b,0x0d,0x86,0xc3,0x60,0xc1,0x80,0xc6,0xce,0x67,0x33,0x99,0xcc,0xe6,0x73,0x74, +0x62,0x31,0x18,0x8c,0x46,0x20,0xc0,0x60,0x30,0x18,0x36,0x31,0x8d,0x86,0xc3,0x61, +0xb0,0xd8,0x10,0x36,0x3b,0x9d,0xce,0xe7,0x70,0xc1,0x98,0x30,0x00,0x7f,0x80,0xc0, +0x60,0x10,0x24,0x0c,0x38,0x0e,0x01,0x02,0x00,0x40,0x80,0xa0,0x18,0x0e,0x03,0x00, +0x80,0x40,0x60,0x50,0x30,0x16,0x0e,0x05,0x88,0x81,0xc0,0x81,0xc0,0x70,0x38,0x1c, +0x00,0x0c,0x00,0x04,0x83,0xe0,0x39,0xcc,0x00,0x0c,0x0c,0x00,0x00,0x01,0xc0,0x00, +0x70,0xc0,0x1c,0x06,0x1f,0xc7,0xc0,0x61,0xe0,0x70,0x60,0x3e,0x1e,0x06,0x03,0x00, +0x00,0x00,0x00,0x30,0x1e,0x61,0x9f,0x03,0xc7,0xc3,0xf1,0x80,0x3e,0x63,0x3f,0x1e, +0x0c,0x67,0xe4,0x19,0x0c,0x78,0x60,0x1e,0x18,0xc7,0xc1,0x80,0xe0,0x60,0xcc,0x63, +0x0c,0x1f,0xc6,0x00,0x30,0x30,0x00,0x00,0x00,0x3b,0x9f,0x03,0xc1,0xb0,0xf0,0x60, +0x06,0x63,0x06,0x01,0x8c,0x70,0xc6,0xd9,0x8c,0x38,0x7c,0x0d,0x8c,0x07,0xc0,0xf1, +0xd8,0x60,0xcc,0x63,0x0c,0x1f,0xc3,0x00,0xc0,0x60,0x01,0x54,0x80,0xc0,0x7f,0x3f, +0x9f,0xef,0xdb,0xf3,0xc7,0xf1,0xfe,0xfd,0xff,0xbf,0x7f,0xff,0xe7,0xf1,0xfc,0xff, +0x7f,0xbf,0x9f,0xaf,0xcf,0xe9,0xf1,0xfa,0x77,0x7e,0x3f,0x7e,0x3f,0x8f,0xc7,0xe3, +0xff,0x0c,0x01,0x0f,0xe8,0x08,0x60,0x30,0xc6,0x00,0x0f,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xd8,0x14,0x00,0x00,0x00,0x00,0x00,0x04, +0x11,0x3d,0x04,0xc0,0xc3,0x61,0xb0,0xd8,0x6c,0x36,0x1b,0x3c,0x3c,0x3f,0x1f,0x8f, +0xc7,0xe7,0xe3,0xf1,0xf8,0xfc,0x7c,0x21,0x8f,0x07,0x83,0xc1,0xe0,0xf0,0x00,0xbc, +0x0e,0x07,0x03,0x81,0xc0,0xc1,0x80,0xcc,0x77,0x3b,0x9d,0xce,0xe7,0x73,0xb9,0x98, +0x3c,0x1e,0x0f,0x07,0x83,0xc0,0xc0,0x60,0x30,0x18,0x1c,0x31,0x87,0x03,0x81,0xc0, +0xe0,0x70,0x00,0x5c,0x1d,0x8e,0xc7,0x63,0xb0,0xc1,0xf0,0x30,0x00,0x7f,0x81,0x40, +0xa0,0x10,0x28,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x00,0x00,0x02,0x00, +0x80,0x80,0x10,0xf8,0x28,0x12,0x09,0x04,0x80,0x01,0x20,0x80,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x06,0x18,0x00,0x00,0x00,0x40,0x00, +0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x07,0xc0,0x31,0xf0,0x01,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0xcc,0x00,0x00,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x60,0x01,0x80,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x18,0x00,0x01,0xe0,0xc3,0xc0,0x00,0x00,0xff,0xc0,0x7e,0xbf, +0x5f,0xef,0xd7,0xf5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfd,0xff, +0x7f,0x7f,0xef,0x07,0xd7,0xed,0xf6,0xfb,0x7f,0xfe,0xdf,0x7f,0xff,0xff,0xff,0xff, +0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x30,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x14,0x00,0x08,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0xc6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x81,0x80,0x60,0x00,0x7f,0x81,0x20, +0x90,0x10,0x1c,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x80, +0x81,0xe0,0x60,0x10,0x24,0x12,0x0e,0x04,0x80,0x01,0xc0,0x70,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x78,0x00,0x00,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x01,0x80,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0xfe,0xdf, +0x6f,0xef,0xe3,0xf5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x7f, +0x7e,0x1f,0x9f,0xef,0xdb,0xed,0xf1,0xfb,0x7f,0xfe,0x3f,0x8f,0xff,0xff,0xff,0xff, +0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x81,0x80,0x60,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x32,0x35,0x36,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x31,0x35,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x31,0x33,0x20, +0x00,0x00,0x01,0x0c,0x00,0x09,0x09,0x00,0x01,0x0f,0x00,0x09,0x12,0x00,0x01,0x0f, +0x00,0x09,0x1b,0x00,0x01,0x0f,0x00,0x09,0x24,0x00,0x01,0x0f,0x00,0x09,0x2d,0x00, +0x01,0x0f,0x00,0x09,0x36,0x00,0x01,0x0f,0x00,0x09,0x3f,0x00,0x03,0x0d,0x00,0x09, +0x48,0x00,0x03,0x0d,0x00,0x09,0x51,0x00,0x03,0x0d,0x00,0x09,0x5a,0x00,0x03,0x0d, +0x00,0x09,0x63,0x00,0x03,0x0d,0x00,0x09,0x6c,0x00,0x03,0x0d,0x00,0x09,0x75,0x00, +0x03,0x0e,0x00,0x09,0x7e,0x00,0x03,0x0d,0x00,0x09,0x87,0x00,0x03,0x0d,0x00,0x09, +0x90,0x00,0x01,0x0f,0x00,0x09,0x99,0x00,0x01,0x0f,0x00,0x09,0xa2,0x00,0x01,0x0f, +0x00,0x09,0xab,0x00,0x01,0x0f,0x00,0x09,0xb4,0x00,0x01,0x0f,0x00,0x09,0xbd,0x00, +0x01,0x0f,0x00,0x09,0xc6,0x00,0x01,0x0f,0x00,0x09,0xcf,0x00,0x01,0x0f,0x00,0x09, +0xd8,0x00,0x01,0x0f,0x00,0x09,0xe1,0x00,0x03,0x0d,0x00,0x09,0xea,0x00,0x01,0x0f, +0x00,0x09,0xf3,0x00,0x01,0x0f,0x00,0x09,0xfc,0x00,0x03,0x0d,0x00,0x09,0x05,0x01, +0x03,0x0d,0x00,0x09,0x0e,0x01,0x03,0x0d,0x00,0x09,0x17,0x01,0x03,0x0d,0x00,0x09, +0x20,0x01,0x00,0x00,0x00,0x09,0x29,0x01,0x03,0x0d,0x00,0x09,0x32,0x01,0x02,0x05, +0x00,0x09,0x3b,0x01,0x03,0x0d,0x00,0x09,0x44,0x01,0x02,0x0e,0x00,0x09,0x4d,0x01, +0x03,0x0d,0x00,0x09,0x56,0x01,0x03,0x0d,0x00,0x09,0x5f,0x01,0x02,0x06,0x00,0x09, +0x68,0x01,0x02,0x0e,0x00,0x09,0x71,0x01,0x02,0x0e,0x00,0x09,0x7a,0x01,0x03,0x08, +0x00,0x09,0x83,0x01,0x05,0x0c,0x00,0x09,0x8c,0x01,0x0b,0x0f,0x00,0x09,0x95,0x01, +0x08,0x09,0x00,0x09,0x9e,0x01,0x0b,0x0d,0x00,0x09,0xa7,0x01,0x02,0x0e,0x00,0x09, +0xb0,0x01,0x03,0x0d,0x00,0x09,0xb9,0x01,0x03,0x0d,0x00,0x09,0xc2,0x01,0x03,0x0d, +0x00,0x09,0xcb,0x01,0x03,0x0d,0x00,0x09,0xd4,0x01,0x03,0x0d,0x00,0x09,0xdd,0x01, +0x03,0x0d,0x00,0x09,0xe6,0x01,0x03,0x0d,0x00,0x09,0xef,0x01,0x03,0x0d,0x00,0x09, +0xf8,0x01,0x03,0x0d,0x00,0x09,0x01,0x02,0x03,0x0d,0x00,0x09,0x0a,0x02,0x06,0x0d, +0x00,0x09,0x13,0x02,0x06,0x0f,0x00,0x09,0x1c,0x02,0x05,0x0c,0x00,0x09,0x25,0x02, +0x07,0x0a,0x00,0x09,0x2e,0x02,0x05,0x0c,0x00,0x09,0x37,0x02,0x03,0x0d,0x00,0x09, +0x40,0x02,0x03,0x0d,0x00,0x09,0x49,0x02,0x03,0x0d,0x00,0x09,0x52,0x02,0x03,0x0d, +0x00,0x09,0x5b,0x02,0x03,0x0d,0x00,0x09,0x64,0x02,0x03,0x0d,0x00,0x09,0x6d,0x02, +0x03,0x0d,0x00,0x09,0x76,0x02,0x03,0x0d,0x00,0x09,0x7f,0x02,0x03,0x0d,0x00,0x09, +0x88,0x02,0x03,0x0d,0x00,0x09,0x91,0x02,0x03,0x0d,0x00,0x09,0x9a,0x02,0x03,0x0d, +0x00,0x09,0xa3,0x02,0x03,0x0d,0x00,0x09,0xac,0x02,0x03,0x0d,0x00,0x09,0xb5,0x02, +0x03,0x0d,0x00,0x09,0xbe,0x02,0x03,0x0d,0x00,0x09,0xc7,0x02,0x03,0x0d,0x00,0x09, +0xd0,0x02,0x03,0x0d,0x00,0x09,0xd9,0x02,0x03,0x0f,0x00,0x09,0xe2,0x02,0x03,0x0d, +0x00,0x09,0xeb,0x02,0x03,0x0d,0x00,0x09,0xf4,0x02,0x03,0x0d,0x00,0x09,0xfd,0x02, +0x03,0x0d,0x00,0x09,0x06,0x03,0x03,0x0d,0x00,0x09,0x0f,0x03,0x03,0x0d,0x00,0x09, +0x18,0x03,0x03,0x0d,0x00,0x09,0x21,0x03,0x03,0x0d,0x00,0x09,0x2a,0x03,0x03,0x0d, +0x00,0x09,0x33,0x03,0x02,0x0e,0x00,0x09,0x3c,0x03,0x02,0x0e,0x00,0x09,0x45,0x03, +0x02,0x0e,0x00,0x09,0x4e,0x03,0x04,0x0b,0x00,0x09,0x57,0x03,0x0d,0x0e,0x00,0x09, +0x60,0x03,0x02,0x06,0x00,0x09,0x69,0x03,0x05,0x0d,0x00,0x09,0x72,0x03,0x02,0x0d, +0x00,0x09,0x7b,0x03,0x05,0x0d,0x00,0x09,0x84,0x03,0x02,0x0d,0x00,0x09,0x8d,0x03, +0x05,0x0d,0x00,0x09,0x96,0x03,0x02,0x0d,0x00,0x09,0x9f,0x03,0x05,0x0f,0x00,0x09, +0xa8,0x03,0x02,0x0d,0x00,0x09,0xb1,0x03,0x02,0x0d,0x00,0x09,0xba,0x03,0x02,0x0f, +0x00,0x09,0xc3,0x03,0x02,0x0d,0x00,0x09,0xcc,0x03,0x02,0x0d,0x00,0x09,0xd5,0x03, +0x05,0x0d,0x00,0x09,0xde,0x03,0x05,0x0d,0x00,0x09,0xe7,0x03,0x05,0x0d,0x00,0x09, +0xf0,0x03,0x05,0x0f,0x00,0x09,0xf9,0x03,0x05,0x0f,0x00,0x09,0x02,0x04,0x05,0x0d, +0x00,0x09,0x0b,0x04,0x05,0x0d,0x00,0x09,0x14,0x04,0x03,0x0d,0x00,0x09,0x1d,0x04, +0x05,0x0d,0x00,0x09,0x26,0x04,0x05,0x0d,0x00,0x09,0x2f,0x04,0x05,0x0d,0x00,0x09, +0x38,0x04,0x05,0x0d,0x00,0x09,0x41,0x04,0x05,0x0f,0x00,0x09,0x4a,0x04,0x05,0x0d, +0x00,0x09,0x53,0x04,0x02,0x0e,0x00,0x09,0x5c,0x04,0x02,0x0e,0x00,0x09,0x65,0x04, +0x02,0x0e,0x00,0x09,0x6e,0x04,0x07,0x0a,0x00,0x09,0x77,0x04,0x01,0x0d,0x00,0x09, +0x80,0x04,0x00,0x0e,0x00,0x09,0x89,0x04,0x00,0x0f,0x00,0x09,0x92,0x04,0x00,0x0f, +0x00,0x09,0x9b,0x04,0x00,0x0f,0x00,0x09,0xa4,0x04,0x00,0x0f,0x00,0x09,0xad,0x04, +0x00,0x0f,0x00,0x09,0xb6,0x04,0x00,0x0f,0x00,0x09,0xbf,0x04,0x00,0x0f,0x00,0x09, +0xc8,0x04,0x00,0x0f,0x00,0x09,0xd1,0x04,0x00,0x0f,0x00,0x09,0xda,0x04,0x00,0x0f, +0x00,0x09,0xe3,0x04,0x00,0x0f,0x00,0x09,0xec,0x04,0x00,0x0f,0x00,0x09,0xf5,0x04, +0x00,0x0f,0x00,0x09,0xfe,0x04,0x00,0x0f,0x00,0x09,0x07,0x05,0x00,0x0f,0x00,0x09, +0x10,0x05,0x00,0x0f,0x00,0x09,0x19,0x05,0x00,0x0f,0x00,0x09,0x22,0x05,0x00,0x0f, +0x00,0x09,0x2b,0x05,0x00,0x0f,0x00,0x09,0x34,0x05,0x00,0x0f,0x00,0x09,0x3d,0x05, +0x00,0x0f,0x00,0x09,0x46,0x05,0x00,0x0f,0x00,0x09,0x4f,0x05,0x00,0x0f,0x00,0x09, +0x58,0x05,0x00,0x0f,0x00,0x09,0x61,0x05,0x00,0x0f,0x00,0x09,0x6a,0x05,0x00,0x0f, +0x00,0x09,0x73,0x05,0x00,0x0f,0x00,0x09,0x7c,0x05,0x00,0x0f,0x00,0x09,0x85,0x05, +0x00,0x0f,0x00,0x09,0x8e,0x05,0x00,0x0f,0x00,0x09,0x97,0x05,0x00,0x0f,0x00,0x09, +0xa0,0x05,0x00,0x0d,0x00,0x09,0xa9,0x05,0x05,0x0f,0x00,0x09,0xb2,0x05,0x02,0x0e, +0x00,0x09,0xbb,0x05,0x03,0x0d,0x00,0x09,0xc4,0x05,0x03,0x0d,0x00,0x09,0xcd,0x05, +0x03,0x0d,0x00,0x09,0xd6,0x05,0x02,0x0e,0x00,0x09,0xdf,0x05,0x03,0x0e,0x00,0x09, +0xe8,0x05,0x02,0x04,0x00,0x09,0xf1,0x05,0x03,0x0d,0x00,0x09,0xfa,0x05,0x03,0x0a, +0x00,0x09,0x03,0x06,0x06,0x0b,0x00,0x09,0x0c,0x06,0x07,0x0a,0x00,0x09,0x15,0x06, +0x08,0x09,0x00,0x09,0x1e,0x06,0x03,0x0b,0x00,0x09,0x27,0x06,0x02,0x03,0x00,0x09, +0x30,0x06,0x03,0x07,0x00,0x09,0x39,0x06,0x05,0x0c,0x00,0x09,0x42,0x06,0x03,0x0a, +0x00,0x09,0x4b,0x06,0x03,0x0a,0x00,0x09,0x54,0x06,0x02,0x04,0x00,0x09,0x5d,0x06, +0x05,0x0f,0x00,0x09,0x66,0x06,0x03,0x0e,0x00,0x09,0x6f,0x06,0x08,0x0a,0x00,0x09, +0x78,0x06,0x0d,0x0f,0x00,0x09,0x81,0x06,0x03,0x0a,0x00,0x09,0x8a,0x06,0x03,0x0a, +0x00,0x09,0x93,0x06,0x06,0x0b,0x00,0x09,0x9c,0x06,0x03,0x0d,0x00,0x09,0xa5,0x06, +0x03,0x0d,0x00,0x09,0xae,0x06,0x03,0x0d,0x00,0x09,0xb7,0x06,0x05,0x0f,0x00,0x09, +0xc0,0x06,0x00,0x0d,0x00,0x09,0xc9,0x06,0x00,0x0d,0x00,0x09,0xd2,0x06,0x00,0x0d, +0x00,0x09,0xdb,0x06,0x00,0x0d,0x00,0x09,0xe4,0x06,0x00,0x0d,0x00,0x09,0xed,0x06, +0x01,0x0d,0x00,0x09,0xf6,0x06,0x03,0x0d,0x00,0x09,0xff,0x06,0x03,0x0f,0x00,0x09, +0x08,0x07,0x00,0x0d,0x00,0x09,0x11,0x07,0x00,0x0d,0x00,0x09,0x1a,0x07,0x00,0x0d, +0x00,0x09,0x23,0x07,0x00,0x0d,0x00,0x09,0x2c,0x07,0x00,0x0d,0x00,0x09,0x35,0x07, +0x00,0x0d,0x00,0x09,0x3e,0x07,0x00,0x0d,0x00,0x09,0x47,0x07,0x00,0x0d,0x00,0x09, +0x50,0x07,0x03,0x0d,0x00,0x09,0x59,0x07,0x00,0x0d,0x00,0x09,0x62,0x07,0x00,0x0d, +0x00,0x09,0x6b,0x07,0x00,0x0d,0x00,0x09,0x74,0x07,0x00,0x0d,0x00,0x09,0x7d,0x07, +0x00,0x0d,0x00,0x09,0x86,0x07,0x00,0x0d,0x00,0x09,0x8f,0x07,0x06,0x0b,0x00,0x09, +0x98,0x07,0x03,0x0d,0x00,0x09,0xa1,0x07,0x00,0x0d,0x00,0x09,0xaa,0x07,0x00,0x0d, +0x00,0x09,0xb3,0x07,0x00,0x0d,0x00,0x09,0xbc,0x07,0x00,0x0d,0x00,0x09,0xc5,0x07, +0x00,0x0d,0x00,0x09,0xce,0x07,0x03,0x0d,0x00,0x09,0xd7,0x07,0x02,0x0d,0x00,0x09, +0xe0,0x07,0x02,0x0d,0x00,0x09,0xe9,0x07,0x02,0x0d,0x00,0x09,0xf2,0x07,0x02,0x0d, +0x00,0x09,0xfb,0x07,0x02,0x0d,0x00,0x09,0x04,0x08,0x02,0x0d,0x00,0x09,0x0d,0x08, +0x02,0x0d,0x00,0x09,0x16,0x08,0x05,0x0d,0x00,0x09,0x1f,0x08,0x05,0x0f,0x00,0x09, +0x28,0x08,0x02,0x0d,0x00,0x09,0x31,0x08,0x02,0x0d,0x00,0x09,0x3a,0x08,0x02,0x0d, +0x00,0x09,0x43,0x08,0x02,0x0d,0x00,0x09,0x4c,0x08,0x02,0x0d,0x00,0x09,0x55,0x08, +0x02,0x0d,0x00,0x09,0x5e,0x08,0x02,0x0d,0x00,0x09,0x67,0x08,0x02,0x0d,0x00,0x09, +0x70,0x08,0x02,0x0d,0x00,0x09,0x79,0x08,0x02,0x0d,0x00,0x09,0x82,0x08,0x02,0x0d, +0x00,0x09,0x8b,0x08,0x02,0x0d,0x00,0x09,0x94,0x08,0x02,0x0d,0x00,0x09,0x9d,0x08, +0x02,0x0d,0x00,0x09,0xa6,0x08,0x02,0x0d,0x00,0x09,0xaf,0x08,0x05,0x0c,0x00,0x09, +0xb8,0x08,0x05,0x0d,0x00,0x09,0xc1,0x08,0x02,0x0d,0x00,0x09,0xca,0x08,0x02,0x0d, +0x00,0x09,0xd3,0x08,0x02,0x0d,0x00,0x09,0xdc,0x08,0x02,0x0d,0x00,0x09,0xe5,0x08, +0x02,0x0f,0x00,0x09,0xee,0x08,0x03,0x0f,0x00,0x09,0xf7,0x08,0x02,0x0f,0x00,0x09, +0x00,0x09,0x00,0x00,0x00,0x00, +}; + +int sizeofdefont = sizeof defontdata; + +void +_unpackinfo(Fontchar *fc, uchar *p, int n) +{ + int j; + + for(j=0; j<=n; j++){ + fc->x = p[0]|(p[1]<<8); + fc->top = p[2]; + fc->bottom = p[3]; + fc->left = p[4]; + fc->width = p[5]; + fc++; + p += 6; + } +} diff --git a/libdraw/drawrepl.c b/libdraw/drawrepl.c new file mode 100644 index 0000000..c72fee2 --- /dev/null +++ b/libdraw/drawrepl.c @@ -0,0 +1,23 @@ +#include +#include +#include + +int +drawreplxy(int min, int max, int x) +{ + int sx; + + sx = (x-min)%(max-min); + if(sx < 0) + sx += max-min; + return sx+min; +} + +Point +drawrepl(Rectangle r, Point p) +{ + p.x = drawreplxy(r.min.x, r.max.x, p.x); + p.y = drawreplxy(r.min.y, r.max.y, p.y); + return p; +} + diff --git a/libdraw/icossin.c b/libdraw/icossin.c new file mode 100644 index 0000000..b66417d --- /dev/null +++ b/libdraw/icossin.c @@ -0,0 +1,140 @@ +#include +#include +#include + +/* + * Integer sine and cosine for integral degree argument. + * Tables computed by (sin,cos)(PI*d/180). + */ +static short sinus[91] = { + 0, /* 0 */ + 18, /* 1 */ + 36, /* 2 */ + 54, /* 3 */ + 71, /* 4 */ + 89, /* 5 */ + 107, /* 6 */ + 125, /* 7 */ + 143, /* 8 */ + 160, /* 9 */ + 178, /* 10 */ + 195, /* 11 */ + 213, /* 12 */ + 230, /* 13 */ + 248, /* 14 */ + 265, /* 15 */ + 282, /* 16 */ + 299, /* 17 */ + 316, /* 18 */ + 333, /* 19 */ + 350, /* 20 */ + 367, /* 21 */ + 384, /* 22 */ + 400, /* 23 */ + 416, /* 24 */ + 433, /* 25 */ + 449, /* 26 */ + 465, /* 27 */ + 481, /* 28 */ + 496, /* 29 */ + 512, /* 30 */ + 527, /* 31 */ + 543, /* 32 */ + 558, /* 33 */ + 573, /* 34 */ + 587, /* 35 */ + 602, /* 36 */ + 616, /* 37 */ + 630, /* 38 */ + 644, /* 39 */ + 658, /* 40 */ + 672, /* 41 */ + 685, /* 42 */ + 698, /* 43 */ + 711, /* 44 */ + 724, /* 45 */ + 737, /* 46 */ + 749, /* 47 */ + 761, /* 48 */ + 773, /* 49 */ + 784, /* 50 */ + 796, /* 51 */ + 807, /* 52 */ + 818, /* 53 */ + 828, /* 54 */ + 839, /* 55 */ + 849, /* 56 */ + 859, /* 57 */ + 868, /* 58 */ + 878, /* 59 */ + 887, /* 60 */ + 896, /* 61 */ + 904, /* 62 */ + 912, /* 63 */ + 920, /* 64 */ + 928, /* 65 */ + 935, /* 66 */ + 943, /* 67 */ + 949, /* 68 */ + 956, /* 69 */ + 962, /* 70 */ + 968, /* 71 */ + 974, /* 72 */ + 979, /* 73 */ + 984, /* 74 */ + 989, /* 75 */ + 994, /* 76 */ + 998, /* 77 */ + 1002, /* 78 */ + 1005, /* 79 */ + 1008, /* 80 */ + 1011, /* 81 */ + 1014, /* 82 */ + 1016, /* 83 */ + 1018, /* 84 */ + 1020, /* 85 */ + 1022, /* 86 */ + 1023, /* 87 */ + 1023, /* 88 */ + 1024, /* 89 */ + 1024, /* 90 */ +}; + +void +icossin(int deg, int *cosp, int *sinp) +{ + int sinsign, cossign; + short *stp, *ctp; + + deg %= 360; + if(deg < 0) + deg += 360; + sinsign = 1; + cossign = 1; + stp = 0; + ctp = 0; + switch(deg/90){ + case 2: + sinsign = -1; + cossign = -1; + deg -= 180; + /* fall through */ + case 0: + stp = &sinus[deg]; + ctp = &sinus[90-deg]; + break; + case 3: + sinsign = -1; + cossign = -1; + deg -= 180; + /* fall through */ + case 1: + deg = 180-deg; + cossign = -cossign; + stp = &sinus[deg]; + ctp = &sinus[90-deg]; + break; + } + *sinp = sinsign*stp[0]; + *cosp = cossign*ctp[0]; +} diff --git a/libdraw/icossin2.c b/libdraw/icossin2.c new file mode 100644 index 0000000..aa864e1 --- /dev/null +++ b/libdraw/icossin2.c @@ -0,0 +1,261 @@ +#include +#include +#include + +/* + * Sine and Cosine of arctangents, calculated by + * (sin(atan(index/100.0))*1024.+0.5) + * (cos(atan(index/100.0))*1024.+0.5) + * To use, get rational tangent between 0<=tan<=1, scale by 100, + * and look up sin and cos, and use linear interpolation. divide by 1024. + * Maximum error is 0.0020. Without linear interpolation, it's 0.010. + */ +static +short sinus[] = { + 0, /* 0.00 */ + 10, /* 0.01 */ + 20, /* 0.02 */ + 31, /* 0.03 */ + 41, /* 0.04 */ + 51, /* 0.05 */ + 61, /* 0.06 */ + 72, /* 0.07 */ + 82, /* 0.08 */ + 92, /* 0.09 */ + 102, /* 0.10 */ + 112, /* 0.11 */ + 122, /* 0.12 */ + 132, /* 0.13 */ + 142, /* 0.14 */ + 152, /* 0.15 */ + 162, /* 0.16 */ + 172, /* 0.17 */ + 181, /* 0.18 */ + 191, /* 0.19 */ + 201, /* 0.20 */ + 210, /* 0.21 */ + 220, /* 0.22 */ + 230, /* 0.23 */ + 239, /* 0.24 */ + 248, /* 0.25 */ + 258, /* 0.26 */ + 267, /* 0.27 */ + 276, /* 0.28 */ + 285, /* 0.29 */ + 294, /* 0.30 */ + 303, /* 0.31 */ + 312, /* 0.32 */ + 321, /* 0.33 */ + 330, /* 0.34 */ + 338, /* 0.35 */ + 347, /* 0.36 */ + 355, /* 0.37 */ + 364, /* 0.38 */ + 372, /* 0.39 */ + 380, /* 0.40 */ + 388, /* 0.41 */ + 397, /* 0.42 */ + 405, /* 0.43 */ + 412, /* 0.44 */ + 420, /* 0.45 */ + 428, /* 0.46 */ + 436, /* 0.47 */ + 443, /* 0.48 */ + 451, /* 0.49 */ + 458, /* 0.50 */ + 465, /* 0.51 */ + 472, /* 0.52 */ + 480, /* 0.53 */ + 487, /* 0.54 */ + 493, /* 0.55 */ + 500, /* 0.56 */ + 507, /* 0.57 */ + 514, /* 0.58 */ + 520, /* 0.59 */ + 527, /* 0.60 */ + 533, /* 0.61 */ + 540, /* 0.62 */ + 546, /* 0.63 */ + 552, /* 0.64 */ + 558, /* 0.65 */ + 564, /* 0.66 */ + 570, /* 0.67 */ + 576, /* 0.68 */ + 582, /* 0.69 */ + 587, /* 0.70 */ + 593, /* 0.71 */ + 598, /* 0.72 */ + 604, /* 0.73 */ + 609, /* 0.74 */ + 614, /* 0.75 */ + 620, /* 0.76 */ + 625, /* 0.77 */ + 630, /* 0.78 */ + 635, /* 0.79 */ + 640, /* 0.80 */ + 645, /* 0.81 */ + 649, /* 0.82 */ + 654, /* 0.83 */ + 659, /* 0.84 */ + 663, /* 0.85 */ + 668, /* 0.86 */ + 672, /* 0.87 */ + 676, /* 0.88 */ + 681, /* 0.89 */ + 685, /* 0.90 */ + 689, /* 0.91 */ + 693, /* 0.92 */ + 697, /* 0.93 */ + 701, /* 0.94 */ + 705, /* 0.95 */ + 709, /* 0.96 */ + 713, /* 0.97 */ + 717, /* 0.98 */ + 720, /* 0.99 */ + 724, /* 1.00 */ + 728, /* 1.01 */ +}; + +static +short cosinus[] = { + 1024, /* 0.00 */ + 1024, /* 0.01 */ + 1024, /* 0.02 */ + 1024, /* 0.03 */ + 1023, /* 0.04 */ + 1023, /* 0.05 */ + 1022, /* 0.06 */ + 1022, /* 0.07 */ + 1021, /* 0.08 */ + 1020, /* 0.09 */ + 1019, /* 0.10 */ + 1018, /* 0.11 */ + 1017, /* 0.12 */ + 1015, /* 0.13 */ + 1014, /* 0.14 */ + 1013, /* 0.15 */ + 1011, /* 0.16 */ + 1010, /* 0.17 */ + 1008, /* 0.18 */ + 1006, /* 0.19 */ + 1004, /* 0.20 */ + 1002, /* 0.21 */ + 1000, /* 0.22 */ + 998, /* 0.23 */ + 996, /* 0.24 */ + 993, /* 0.25 */ + 991, /* 0.26 */ + 989, /* 0.27 */ + 986, /* 0.28 */ + 983, /* 0.29 */ + 981, /* 0.30 */ + 978, /* 0.31 */ + 975, /* 0.32 */ + 972, /* 0.33 */ + 969, /* 0.34 */ + 967, /* 0.35 */ + 963, /* 0.36 */ + 960, /* 0.37 */ + 957, /* 0.38 */ + 954, /* 0.39 */ + 951, /* 0.40 */ + 947, /* 0.41 */ + 944, /* 0.42 */ + 941, /* 0.43 */ + 937, /* 0.44 */ + 934, /* 0.45 */ + 930, /* 0.46 */ + 927, /* 0.47 */ + 923, /* 0.48 */ + 920, /* 0.49 */ + 916, /* 0.50 */ + 912, /* 0.51 */ + 909, /* 0.52 */ + 905, /* 0.53 */ + 901, /* 0.54 */ + 897, /* 0.55 */ + 893, /* 0.56 */ + 890, /* 0.57 */ + 886, /* 0.58 */ + 882, /* 0.59 */ + 878, /* 0.60 */ + 874, /* 0.61 */ + 870, /* 0.62 */ + 866, /* 0.63 */ + 862, /* 0.64 */ + 859, /* 0.65 */ + 855, /* 0.66 */ + 851, /* 0.67 */ + 847, /* 0.68 */ + 843, /* 0.69 */ + 839, /* 0.70 */ + 835, /* 0.71 */ + 831, /* 0.72 */ + 827, /* 0.73 */ + 823, /* 0.74 */ + 819, /* 0.75 */ + 815, /* 0.76 */ + 811, /* 0.77 */ + 807, /* 0.78 */ + 804, /* 0.79 */ + 800, /* 0.80 */ + 796, /* 0.81 */ + 792, /* 0.82 */ + 788, /* 0.83 */ + 784, /* 0.84 */ + 780, /* 0.85 */ + 776, /* 0.86 */ + 773, /* 0.87 */ + 769, /* 0.88 */ + 765, /* 0.89 */ + 761, /* 0.90 */ + 757, /* 0.91 */ + 754, /* 0.92 */ + 750, /* 0.93 */ + 746, /* 0.94 */ + 742, /* 0.95 */ + 739, /* 0.96 */ + 735, /* 0.97 */ + 731, /* 0.98 */ + 728, /* 0.99 */ + 724, /* 1.00 */ + 720, /* 1.01 */ +}; + +void +icossin2(int x, int y, int *cosp, int *sinp) +{ + int sinsign, cossign, tan, tan10, rem; + short *stp, *ctp; + + if(x == 0){ + if(y >= 0) + *sinp = ICOSSCALE, *cosp = 0; + else + *sinp = -ICOSSCALE, *cosp = 0; + return; + } + sinsign = cossign = 1; + if(x < 0){ + cossign = -1; + x = -x; + } + if(y < 0){ + sinsign = -1; + y = -y; + } + if(y > x){ + tan = 1000*x/y; + tan10 = tan/10; + stp = &cosinus[tan10]; + ctp = &sinus[tan10]; + }else{ + tan = 1000*y/x; + tan10 = tan/10; + stp = &sinus[tan10]; + ctp = &cosinus[tan10]; + } + rem = tan-(tan10*10); + *sinp = sinsign*(stp[0]+(stp[1]-stp[0])*rem/10); + *cosp = cossign*(ctp[0]+(ctp[1]-ctp[0])*rem/10); +} diff --git a/libdraw/mkfile b/libdraw/mkfile new file mode 100644 index 0000000..95acfb9 --- /dev/null +++ b/libdraw/mkfile @@ -0,0 +1,18 @@ +<$DSRC/mkfile-$CONF +TARG=libdraw.$L + +OFILES=\ + alloc.$O\ + arith.$O\ + bytesperline.$O\ + chan.$O\ + defont.$O\ + drawrepl.$O\ + icossin.$O\ + icossin2.$O\ + rectclip.$O\ + rgb.$O + +HFILES=\ + +<$DSRC/mklib-$CONF diff --git a/libdraw/rectclip.c b/libdraw/rectclip.c new file mode 100644 index 0000000..2228628 --- /dev/null +++ b/libdraw/rectclip.c @@ -0,0 +1,25 @@ +#include +#include +#include + +int +rectclip(Rectangle *rp, Rectangle b) /* first by reference, second by value */ +{ + Rectangle *bp = &b; + /* + * Expand rectXrect() in line for speed + */ + if((rp->min.xmax.x && bp->min.xmax.x && + rp->min.ymax.y && bp->min.ymax.y)==0) + return 0; + /* They must overlap */ + if(rp->min.x < bp->min.x) + rp->min.x = bp->min.x; + if(rp->min.y < bp->min.y) + rp->min.y = bp->min.y; + if(rp->max.x > bp->max.x) + rp->max.x = bp->max.x; + if(rp->max.y > bp->max.y) + rp->max.y = bp->max.y; + return 1; +} diff --git a/libdraw/rgb.c b/libdraw/rgb.c new file mode 100644 index 0000000..eda28b4 --- /dev/null +++ b/libdraw/rgb.c @@ -0,0 +1,99 @@ +#include +#include +#include + +/* + * This original version, although fast and a true inverse of + * cmap2rgb, in the sense that rgb2cmap(cmap2rgb(c)) + * returned the original color, does a terrible job for RGB + * triples that do not appear in the color map, so it has been + * replaced by the much slower version below, that loops + * over the color map looking for the nearest point in RGB + * space. There is no visual psychology reason for that + * criterion, but it's easy to implement and the results are + * far more pleasing. + * +int +rgb2cmap(int cr, int cg, int cb) +{ + int r, g, b, v, cv; + + if(cr < 0) + cr = 0; + else if(cr > 255) + cr = 255; + if(cg < 0) + cg = 0; + else if(cg > 255) + cg = 255; + if(cb < 0) + cb = 0; + else if(cb > 255) + cb = 255; + r = cr>>6; + g = cg>>6; + b = cb>>6; + cv = cr; + if(cg > cv) + cv = cg; + if(cb > cv) + cv = cb; + v = (cv>>4)&3; + return ((((r<<2)+v)<<4)+(((g<<2)+b+v-r)&15)); +} +*/ + +int +rgb2cmap(int cr, int cg, int cb) +{ + int i, r, g, b, sq; + ulong rgb; + int best, bestsq; + + best = 0; + bestsq = 0x7FFFFFFF; + for(i=0; i<256; i++){ + rgb = cmap2rgb(i); + r = (rgb>>16) & 0xFF; + g = (rgb>>8) & 0xFF; + b = (rgb>>0) & 0xFF; + sq = (r-cr)*(r-cr)+(g-cg)*(g-cg)+(b-cb)*(b-cb); + if(sq < bestsq){ + bestsq = sq; + best = i; + } + } + return best; +} + +int +cmap2rgb(int c) +{ + int j, num, den, r, g, b, v, rgb; + + r = c>>6; + v = (c>>4)&3; + j = (c-v+r)&15; + g = j>>2; + b = j&3; + den=r; + if(g>den) + den=g; + if(b>den) + den=b; + if(den==0) { + v *= 17; + rgb = (v<<16)|(v<<8)|v; + } + else{ + num=17*(4*den+v); + rgb = ((r*num/den)<<16)|((g*num/den)<<8)|(b*num/den); + } + return rgb; +} + +int +cmap2rgba(int c) +{ + return (cmap2rgb(c)<<8)|0xFF; +} diff --git a/libmemdraw/Makefile b/libmemdraw/Makefile new file mode 100644 index 0000000..e8a1b39 --- /dev/null +++ b/libmemdraw/Makefile @@ -0,0 +1,33 @@ +LIB=libmemdraw.a +CC=gcc +CFLAGS=-I../include -I. -c -ggdb -D_THREAD_SAFE -pthread +O=o + +OFILES=\ + alloc.$O\ + arc.$O\ + cload.$O\ + cmap.$O\ + cread.$O\ + defont.$O\ + draw.$O\ + ellipse.$O\ + fillpoly.$O\ + hwdraw.$O\ + line.$O\ + load.$O\ + openmemsubfont.$O\ + poly.$O\ + read.$O\ + string.$O\ + subfont.$O\ + unload.$O\ + write.$O + +$(LIB): $(OFILES) + ar r $(LIB) $(OFILES) + ranlib $(LIB) + +%.$O: %.c + $(CC) $(CFLAGS) $*.c + diff --git a/libmemdraw/alloc.c b/libmemdraw/alloc.c new file mode 100644 index 0000000..fdc0f3c --- /dev/null +++ b/libmemdraw/alloc.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include + +#define poolalloc(a, b) malloc(b) +#define poolfree(a, b) free(b) + +void +memimagemove(void *from, void *to) +{ + Memdata *md; + + md = *(Memdata**)to; + if(md->base != from){ + print("compacted data not right: #%p\n", md->base); + abort(); + } + md->base = to; + + /* if allocmemimage changes this must change too */ + md->bdata = (uchar*)&md->base[2]; +} + +Memimage* +allocmemimaged(Rectangle r, ulong chan, Memdata *md, void *X) +{ + int d; + ulong l; + Memimage *i; + + if(Dx(r) <= 0 || Dy(r) <= 0){ + werrstr("bad rectangle %R", r); + return nil; + } + if((d = chantodepth(chan)) == 0) { + werrstr("bad channel descriptor %.8lux", chan); + return nil; + } + + l = wordsperline(r, d); + + i = mallocz(sizeof(Memimage), 1); + if(i == nil) + return nil; + + i->X = X; + i->data = md; + i->zero = sizeof(ulong)*l*r.min.y; + + if(r.min.x >= 0) + i->zero += (r.min.x*d)/8; + else + i->zero -= (-r.min.x*d+7)/8; + i->zero = -i->zero; + i->width = l; + i->r = r; + i->clipr = r; + i->flags = 0; + i->layer = nil; + i->cmap = memdefcmap; + if(memsetchan(i, chan) < 0){ + free(i); + return nil; + } + return i; +} + +Memimage* +_allocmemimage(Rectangle r, ulong chan) +{ + int d; + ulong l, nw; + Memdata *md; + Memimage *i; + + if((d = chantodepth(chan)) == 0) { + werrstr("bad channel descriptor %.8lux", chan); + return nil; + } + + l = wordsperline(r, d); + nw = l*Dy(r); + md = malloc(sizeof(Memdata)); + if(md == nil) + return nil; + + md->ref = 1; + md->base = poolalloc(imagmem, (2+nw)*sizeof(ulong)); + if(md->base == nil){ + free(md); + return nil; + } + + md->base[0] = (ulong)md; + md->base[1] = getcallerpc(&r); + + /* if this changes, memimagemove must change too */ + md->bdata = (uchar*)&md->base[2]; + + md->allocd = 1; + + i = allocmemimaged(r, chan, md, nil); + if(i == nil){ + poolfree(imagmem, md->base); + free(md); + return nil; + } + md->imref = i; + return i; +} + +void +_freememimage(Memimage *i) +{ + if(i == nil) + return; + if(i->data->ref-- == 1 && i->data->allocd){ + if(i->data->base) + poolfree(imagmem, i->data->base); + free(i->data); + } + free(i); +} + +/* + * Wordaddr is deprecated. + */ +ulong* +wordaddr(Memimage *i, Point p) +{ + return (ulong*) ((ulong)byteaddr(i, p) & ~(sizeof(ulong)-1)); +} + +uchar* +byteaddr(Memimage *i, Point p) +{ + uchar *a; + + a = i->data->bdata+i->zero+sizeof(ulong)*p.y*i->width; + + if(i->depth < 8){ + /* + * We need to always round down, + * but C rounds toward zero. + */ + int np; + np = 8/i->depth; + if(p.x < 0) + return a+(p.x-np+1)/np; + else + return a+p.x/np; + } + else + return a+p.x*(i->depth/8); +} + +int +memsetchan(Memimage *i, ulong chan) +{ + int d; + int t, j, k; + ulong cc; + int bytes; + + if((d = chantodepth(chan)) == 0) { + werrstr("bad channel descriptor"); + return -1; + } + + i->depth = d; + i->chan = chan; + i->flags &= ~(Fgrey|Falpha|Fcmap|Fbytes); + bytes = 1; + for(cc=chan, j=0, k=0; cc; j+=NBITS(cc), cc>>=8, k++){ + t=TYPE(cc); + if(t < 0 || t >= NChan){ + werrstr("bad channel string"); + return -1; + } + if(t == CGrey) + i->flags |= Fgrey; + if(t == CAlpha) + i->flags |= Falpha; + if(t == CMap && i->cmap == nil){ + i->cmap = memdefcmap; + i->flags |= Fcmap; + } + + i->shift[t] = j; + i->mask[t] = (1<nbits[t] = NBITS(cc); + if(NBITS(cc) != 8) + bytes = 0; + } + i->nchan = k; + if(bytes) + i->flags |= Fbytes; + return 0; +} diff --git a/libmemdraw/alpha.hoc b/libmemdraw/alpha.hoc new file mode 100644 index 0000000..2f24942 --- /dev/null +++ b/libmemdraw/alpha.hoc @@ -0,0 +1,9 @@ +func f(x) { + return x-x%1 +} + +func pixel(dr, dg, db, da, sr, sg, sb, sa, m) { + M = 255-f((sa*m)/255) + print f((sr*m+dr*M)/255), " ", f((sg*m+dg*M)/255), " ", f((sb*m+db*M)/255), " ", f((sa*m+da*M)/255), "\n" + return 0 +} diff --git a/libmemdraw/arc.c b/libmemdraw/arc.c new file mode 100644 index 0000000..a0854c0 --- /dev/null +++ b/libmemdraw/arc.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include + +/* + * elarc(dst,c,a,b,t,src,sp,alpha,phi) + * draws the part of an ellipse between rays at angles alpha and alpha+phi + * measured counterclockwise from the positive x axis. other + * arguments are as for ellipse(dst,c,a,b,t,src,sp) + */ + +enum +{ + R, T, L, B /* right, top, left, bottom */ +}; + +static +Point corners[] = { + {1,1}, + {-1,1}, + {-1,-1}, + {1,-1} +}; + +static +Point p00; + +/* + * make a "wedge" mask covering the desired angle and contained in + * a surrounding square; draw a full ellipse; intersect that with the + * wedge to make a mask through which to copy src to dst. + */ +void +memarc(Memimage *dst, Point c, int a, int b, int t, Memimage *src, Point sp, int alpha, int phi, int op) +{ + int i, w, beta, tmp, c1, c2, m, m1; + Rectangle rect; + Point p, bnd[8]; + Memimage *wedge, *figure, *mask; + + if(a < 0) + a = -a; + if(b < 0) + b = -b; + w = t; + if(w < 0) + w = 0; + alpha = -alpha; /* compensate for upside-down coords */ + phi = -phi; + beta = alpha + phi; + if(phi < 0){ + tmp = alpha; + alpha = beta; + beta = tmp; + phi = -phi; + } + if(phi >= 360){ + memellipse(dst, c, a, b, t, src, sp, op); + return; + } + while(alpha < 0) + alpha += 360; + while(beta < 0) + beta += 360; + c1 = alpha/90 & 3; /* number of nearest corner */ + c2 = beta/90 & 3; + /* + * icossin returns point at radius ICOSSCALE. + * multiplying by m1 moves it outside the ellipse + */ + rect = Rect(-a-w, -b-w, a+w+1, b+w+1); + m = rect.max.x; /* inradius of bounding square */ + if(m < rect.max.y) + m = rect.max.y; + m1 = (m+ICOSSCALE-1) >> 10; + m = m1 << 10; /* assure m1*cossin is inside */ + i = 0; + bnd[i++] = Pt(0,0); + icossin(alpha, &p.x, &p.y); + bnd[i++] = mulpt(p, m1); + for(;;) { + bnd[i++] = mulpt(corners[c1], m); + if(c1==c2 && phi<180) + break; + c1 = (c1+1) & 3; + phi -= 90; + } + icossin(beta, &p.x, &p.y); + bnd[i++] = mulpt(p, m1); + + figure = nil; + mask = nil; + wedge = allocmemimage(rect, GREY1); + if(wedge == nil) + goto Return; + memfillcolor(wedge, DTransparent); + memfillpoly(wedge, bnd, i, ~0, memopaque, p00, S); + figure = allocmemimage(rect, GREY1); + if(figure == nil) + goto Return; + memfillcolor(figure, DTransparent); + memellipse(figure, p00, a, b, t, memopaque, p00, S); + mask = allocmemimage(rect, GREY1); + if(mask == nil) + goto Return; + memfillcolor(mask, DTransparent); + memimagedraw(mask, rect, figure, rect.min, wedge, rect.min, S); + c = subpt(c, dst->r.min); + memdraw(dst, dst->r, src, subpt(sp, c), mask, subpt(p00, c), op); + + Return: + freememimage(wedge); + freememimage(figure); + freememimage(mask); +} diff --git a/libmemdraw/arctest.c b/libmemdraw/arctest.c new file mode 100644 index 0000000..a9ad1fb --- /dev/null +++ b/libmemdraw/arctest.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include + +extern int drawdebug; +void +main(int argc, char **argv) +{ + char cc; + Memimage *x; + Point c = {208,871}; + int a = 441; + int b = 441; + int thick = 0; + Point sp = {0,0}; + int alpha = 51; + int phi = 3; + vlong t0, t1; + int i, n; + vlong del; + + memimageinit(); + + x = allocmemimage(Rect(0,0,1000,1000), CMAP8); + n = atoi(argv[1]); + + t0 = nsec(); + t0 = nsec(); + t0 = nsec(); + t1 = nsec(); + del = t1-t0; + t0 = nsec(); + for(i=0; i +#include +#include +#include + +int +_cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) +{ + int y, bpl, c, cnt, offs; + uchar mem[NMEM], *memp, *omemp, *emem, *linep, *elinep, *u, *eu; + + if(!rectinrect(r, i->r)) + return -1; + bpl = bytesperline(r, i->depth); + u = data; + eu = data+ndata; + memp = mem; + emem = mem+NMEM; + y = r.min.y; + linep = byteaddr(i, Pt(r.min.x, y)); + elinep = linep+bpl; + for(;;){ + if(linep == elinep){ + if(++y == r.max.y) + break; + linep = byteaddr(i, Pt(r.min.x, y)); + elinep = linep+bpl; + } + if(u == eu){ /* buffer too small */ + return -1; + } + c = *u++; + if(c >= 128){ + for(cnt=c-128+1; cnt!=0 ;--cnt){ + if(u == eu){ /* buffer too small */ + return -1; + } + if(linep == elinep){ /* phase error */ + return -1; + } + *linep++ = *u; + *memp++ = *u++; + if(memp == emem) + memp = mem; + } + } + else{ + if(u == eu) /* short buffer */ + return -1; + offs = *u++ + ((c&3)<<8)+1; + if(memp-mem < offs) + omemp = memp+(NMEM-offs); + else + omemp = memp-offs; + for(cnt=(c>>2)+NMATCH; cnt!=0; --cnt){ + if(linep == elinep) /* phase error */ + return -1; + *linep++ = *omemp; + *memp++ = *omemp++; + if(omemp == emem) + omemp = mem; + if(memp == emem) + memp = mem; + } + } + } + return u-data; +} diff --git a/libmemdraw/cmap.c b/libmemdraw/cmap.c new file mode 100644 index 0000000..2561c3f --- /dev/null +++ b/libmemdraw/cmap.c @@ -0,0 +1,320 @@ +/* + * generated by mkcmap.c + */ +#include +#include +#include +#include + +static Memcmap def = { +/* cmap2rgb */ { + 0x00,0x00,0x00,0x00,0x00,0x44,0x00,0x00,0x88,0x00,0x00,0xcc,0x00,0x44,0x00,0x00, + 0x44,0x44,0x00,0x44,0x88,0x00,0x44,0xcc,0x00,0x88,0x00,0x00,0x88,0x44,0x00,0x88, + 0x88,0x00,0x88,0xcc,0x00,0xcc,0x00,0x00,0xcc,0x44,0x00,0xcc,0x88,0x00,0xcc,0xcc, + 0x00,0xdd,0xdd,0x11,0x11,0x11,0x00,0x00,0x55,0x00,0x00,0x99,0x00,0x00,0xdd,0x00, + 0x55,0x00,0x00,0x55,0x55,0x00,0x4c,0x99,0x00,0x49,0xdd,0x00,0x99,0x00,0x00,0x99, + 0x4c,0x00,0x99,0x99,0x00,0x93,0xdd,0x00,0xdd,0x00,0x00,0xdd,0x49,0x00,0xdd,0x93, + 0x00,0xee,0x9e,0x00,0xee,0xee,0x22,0x22,0x22,0x00,0x00,0x66,0x00,0x00,0xaa,0x00, + 0x00,0xee,0x00,0x66,0x00,0x00,0x66,0x66,0x00,0x55,0xaa,0x00,0x4f,0xee,0x00,0xaa, + 0x00,0x00,0xaa,0x55,0x00,0xaa,0xaa,0x00,0x9e,0xee,0x00,0xee,0x00,0x00,0xee,0x4f, + 0x00,0xff,0x55,0x00,0xff,0xaa,0x00,0xff,0xff,0x33,0x33,0x33,0x00,0x00,0x77,0x00, + 0x00,0xbb,0x00,0x00,0xff,0x00,0x77,0x00,0x00,0x77,0x77,0x00,0x5d,0xbb,0x00,0x55, + 0xff,0x00,0xbb,0x00,0x00,0xbb,0x5d,0x00,0xbb,0xbb,0x00,0xaa,0xff,0x00,0xff,0x00, + 0x44,0x00,0x44,0x44,0x00,0x88,0x44,0x00,0xcc,0x44,0x44,0x00,0x44,0x44,0x44,0x44, + 0x44,0x88,0x44,0x44,0xcc,0x44,0x88,0x00,0x44,0x88,0x44,0x44,0x88,0x88,0x44,0x88, + 0xcc,0x44,0xcc,0x00,0x44,0xcc,0x44,0x44,0xcc,0x88,0x44,0xcc,0xcc,0x44,0x00,0x00, + 0x55,0x00,0x00,0x55,0x00,0x55,0x4c,0x00,0x99,0x49,0x00,0xdd,0x55,0x55,0x00,0x55, + 0x55,0x55,0x4c,0x4c,0x99,0x49,0x49,0xdd,0x4c,0x99,0x00,0x4c,0x99,0x4c,0x4c,0x99, + 0x99,0x49,0x93,0xdd,0x49,0xdd,0x00,0x49,0xdd,0x49,0x49,0xdd,0x93,0x49,0xdd,0xdd, + 0x4f,0xee,0xee,0x66,0x00,0x00,0x66,0x00,0x66,0x55,0x00,0xaa,0x4f,0x00,0xee,0x66, + 0x66,0x00,0x66,0x66,0x66,0x55,0x55,0xaa,0x4f,0x4f,0xee,0x55,0xaa,0x00,0x55,0xaa, + 0x55,0x55,0xaa,0xaa,0x4f,0x9e,0xee,0x4f,0xee,0x00,0x4f,0xee,0x4f,0x4f,0xee,0x9e, + 0x55,0xff,0xaa,0x55,0xff,0xff,0x77,0x00,0x00,0x77,0x00,0x77,0x5d,0x00,0xbb,0x55, + 0x00,0xff,0x77,0x77,0x00,0x77,0x77,0x77,0x5d,0x5d,0xbb,0x55,0x55,0xff,0x5d,0xbb, + 0x00,0x5d,0xbb,0x5d,0x5d,0xbb,0xbb,0x55,0xaa,0xff,0x55,0xff,0x00,0x55,0xff,0x55, + 0x88,0x00,0x88,0x88,0x00,0xcc,0x88,0x44,0x00,0x88,0x44,0x44,0x88,0x44,0x88,0x88, + 0x44,0xcc,0x88,0x88,0x00,0x88,0x88,0x44,0x88,0x88,0x88,0x88,0x88,0xcc,0x88,0xcc, + 0x00,0x88,0xcc,0x44,0x88,0xcc,0x88,0x88,0xcc,0xcc,0x88,0x00,0x00,0x88,0x00,0x44, + 0x99,0x00,0x4c,0x99,0x00,0x99,0x93,0x00,0xdd,0x99,0x4c,0x00,0x99,0x4c,0x4c,0x99, + 0x4c,0x99,0x93,0x49,0xdd,0x99,0x99,0x00,0x99,0x99,0x4c,0x99,0x99,0x99,0x93,0x93, + 0xdd,0x93,0xdd,0x00,0x93,0xdd,0x49,0x93,0xdd,0x93,0x93,0xdd,0xdd,0x99,0x00,0x00, + 0xaa,0x00,0x00,0xaa,0x00,0x55,0xaa,0x00,0xaa,0x9e,0x00,0xee,0xaa,0x55,0x00,0xaa, + 0x55,0x55,0xaa,0x55,0xaa,0x9e,0x4f,0xee,0xaa,0xaa,0x00,0xaa,0xaa,0x55,0xaa,0xaa, + 0xaa,0x9e,0x9e,0xee,0x9e,0xee,0x00,0x9e,0xee,0x4f,0x9e,0xee,0x9e,0x9e,0xee,0xee, + 0xaa,0xff,0xff,0xbb,0x00,0x00,0xbb,0x00,0x5d,0xbb,0x00,0xbb,0xaa,0x00,0xff,0xbb, + 0x5d,0x00,0xbb,0x5d,0x5d,0xbb,0x5d,0xbb,0xaa,0x55,0xff,0xbb,0xbb,0x00,0xbb,0xbb, + 0x5d,0xbb,0xbb,0xbb,0xaa,0xaa,0xff,0xaa,0xff,0x00,0xaa,0xff,0x55,0xaa,0xff,0xaa, + 0xcc,0x00,0xcc,0xcc,0x44,0x00,0xcc,0x44,0x44,0xcc,0x44,0x88,0xcc,0x44,0xcc,0xcc, + 0x88,0x00,0xcc,0x88,0x44,0xcc,0x88,0x88,0xcc,0x88,0xcc,0xcc,0xcc,0x00,0xcc,0xcc, + 0x44,0xcc,0xcc,0x88,0xcc,0xcc,0xcc,0xcc,0x00,0x00,0xcc,0x00,0x44,0xcc,0x00,0x88, + 0xdd,0x00,0x93,0xdd,0x00,0xdd,0xdd,0x49,0x00,0xdd,0x49,0x49,0xdd,0x49,0x93,0xdd, + 0x49,0xdd,0xdd,0x93,0x00,0xdd,0x93,0x49,0xdd,0x93,0x93,0xdd,0x93,0xdd,0xdd,0xdd, + 0x00,0xdd,0xdd,0x49,0xdd,0xdd,0x93,0xdd,0xdd,0xdd,0xdd,0x00,0x00,0xdd,0x00,0x49, + 0xee,0x00,0x4f,0xee,0x00,0x9e,0xee,0x00,0xee,0xee,0x4f,0x00,0xee,0x4f,0x4f,0xee, + 0x4f,0x9e,0xee,0x4f,0xee,0xee,0x9e,0x00,0xee,0x9e,0x4f,0xee,0x9e,0x9e,0xee,0x9e, + 0xee,0xee,0xee,0x00,0xee,0xee,0x4f,0xee,0xee,0x9e,0xee,0xee,0xee,0xee,0x00,0x00, + 0xff,0x00,0x00,0xff,0x00,0x55,0xff,0x00,0xaa,0xff,0x00,0xff,0xff,0x55,0x00,0xff, + 0x55,0x55,0xff,0x55,0xaa,0xff,0x55,0xff,0xff,0xaa,0x00,0xff,0xaa,0x55,0xff,0xaa, + 0xaa,0xff,0xaa,0xff,0xff,0xff,0x00,0xff,0xff,0x55,0xff,0xff,0xaa,0xff,0xff,0xff, +}, +/* rgb2cmap */ { + 0x00,0x00,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x00,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x11,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x04,0x04,0x04,0x05,0x05,0x05,0x05,0x06,0x06,0x06,0x17,0x07,0x07,0x18,0x18,0x29, + 0x04,0x04,0x04,0x05,0x05,0x05,0x16,0x06,0x06,0x17,0x28,0x07,0x07,0x18,0x29,0x3a, + 0x15,0x15,0x15,0x05,0x05,0x16,0x16,0x06,0x06,0x17,0x28,0x39,0x07,0x18,0x29,0x3a, + 0x26,0x26,0x26,0x05,0x16,0x16,0x27,0x27,0x38,0x28,0x28,0x39,0x39,0x29,0x29,0x3a, + 0x37,0x37,0x37,0x09,0x09,0x09,0x27,0x38,0x0a,0x0a,0x39,0x0b,0x0b,0x0b,0x1c,0x3a, + 0x08,0x08,0x08,0x09,0x09,0x09,0x38,0x0a,0x0a,0x0a,0x1b,0x0b,0x0b,0x1c,0x1c,0x2d, + 0x19,0x19,0x19,0x09,0x1a,0x1a,0x2b,0x0a,0x0a,0x1b,0x1b,0x0b,0x0b,0x1c,0x2d,0x3e, + 0x2a,0x2a,0x2a,0x1a,0x2b,0x2b,0x2b,0x3c,0x1b,0x1b,0x2c,0x2c,0x3d,0x2d,0x2d,0x3e, + 0x3b,0x3b,0x3b,0x0d,0x0d,0x3c,0x3c,0x0e,0x0e,0x0e,0x2c,0x3d,0x0f,0x0f,0x3e,0x3e, + 0x0c,0x0c,0x0c,0x0d,0x0d,0x0d,0x3c,0x0e,0x0e,0x0e,0x3d,0x0f,0x0f,0x0f,0x10,0x3e, + 0x1d,0x1d,0x1d,0x1e,0x1e,0x1e,0x2f,0x0e,0x1f,0x1f,0x20,0x0f,0x0f,0x10,0x10,0x21, + 0x2e,0x2e,0x2e,0x1e,0x2f,0x2f,0x2f,0x1f,0x1f,0x20,0x20,0x31,0x10,0x10,0x21,0x21, + 0x3f,0x3f,0x3f,0x2f,0x30,0x30,0x30,0x30,0x20,0x31,0x31,0x31,0x31,0x21,0x21,0x32, + 0x00,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x11,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x11,0x11,0x22,0x22,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x04,0x04,0x22,0x05,0x05,0x05,0x05,0x06,0x06,0x06,0x17,0x07,0x07,0x18,0x18,0x29, + 0x04,0x04,0x04,0x05,0x05,0x05,0x16,0x06,0x06,0x17,0x28,0x07,0x07,0x18,0x29,0x3a, + 0x15,0x15,0x15,0x05,0x05,0x16,0x16,0x06,0x06,0x17,0x28,0x39,0x07,0x18,0x29,0x3a, + 0x26,0x26,0x26,0x05,0x16,0x16,0x27,0x27,0x38,0x28,0x28,0x39,0x39,0x29,0x29,0x3a, + 0x37,0x37,0x37,0x09,0x09,0x09,0x27,0x38,0x0a,0x0a,0x39,0x0b,0x0b,0x0b,0x1c,0x3a, + 0x08,0x08,0x08,0x09,0x09,0x09,0x38,0x0a,0x0a,0x0a,0x1b,0x0b,0x0b,0x1c,0x1c,0x2d, + 0x19,0x19,0x19,0x09,0x1a,0x1a,0x2b,0x0a,0x0a,0x1b,0x1b,0x0b,0x0b,0x1c,0x2d,0x3e, + 0x2a,0x2a,0x2a,0x1a,0x2b,0x2b,0x2b,0x3c,0x1b,0x1b,0x2c,0x2c,0x3d,0x2d,0x2d,0x3e, + 0x3b,0x3b,0x3b,0x0d,0x0d,0x3c,0x3c,0x0e,0x0e,0x0e,0x2c,0x3d,0x0f,0x0f,0x3e,0x3e, + 0x0c,0x0c,0x0c,0x0d,0x0d,0x0d,0x3c,0x0e,0x0e,0x0e,0x3d,0x0f,0x0f,0x0f,0x10,0x3e, + 0x1d,0x1d,0x1d,0x1e,0x1e,0x1e,0x2f,0x0e,0x1f,0x1f,0x20,0x0f,0x0f,0x10,0x10,0x21, + 0x2e,0x2e,0x2e,0x1e,0x2f,0x2f,0x2f,0x1f,0x1f,0x20,0x20,0x31,0x10,0x10,0x21,0x21, + 0x3f,0x3f,0x3f,0x2f,0x30,0x30,0x30,0x30,0x20,0x31,0x31,0x31,0x31,0x21,0x21,0x32, + 0x11,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x11,0x11,0x22,0x22,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x11,0x22,0x22,0x22,0x33,0x33,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x04,0x22,0x22,0x33,0x33,0x33,0x05,0x06,0x06,0x06,0x17,0x07,0x07,0x18,0x18,0x29, + 0x04,0x04,0x33,0x33,0x33,0x05,0x16,0x06,0x06,0x17,0x28,0x07,0x07,0x18,0x29,0x3a, + 0x15,0x15,0x33,0x33,0x05,0x16,0x16,0x06,0x06,0x17,0x28,0x39,0x07,0x18,0x29,0x3a, + 0x26,0x26,0x26,0x05,0x16,0x16,0x27,0x27,0x38,0x28,0x28,0x39,0x39,0x29,0x29,0x3a, + 0x37,0x37,0x37,0x09,0x09,0x09,0x27,0x38,0x0a,0x0a,0x39,0x0b,0x0b,0x0b,0x1c,0x3a, + 0x08,0x08,0x08,0x09,0x09,0x09,0x38,0x0a,0x0a,0x0a,0x1b,0x0b,0x0b,0x1c,0x1c,0x2d, + 0x19,0x19,0x19,0x09,0x1a,0x1a,0x2b,0x0a,0x0a,0x1b,0x1b,0x0b,0x0b,0x1c,0x2d,0x3e, + 0x2a,0x2a,0x2a,0x1a,0x2b,0x2b,0x2b,0x3c,0x1b,0x1b,0x2c,0x2c,0x3d,0x2d,0x2d,0x3e, + 0x3b,0x3b,0x3b,0x0d,0x0d,0x3c,0x3c,0x0e,0x0e,0x0e,0x2c,0x3d,0x0f,0x0f,0x3e,0x3e, + 0x0c,0x0c,0x0c,0x0d,0x0d,0x0d,0x3c,0x0e,0x0e,0x0e,0x3d,0x0f,0x0f,0x0f,0x10,0x3e, + 0x1d,0x1d,0x1d,0x1e,0x1e,0x1e,0x2f,0x0e,0x1f,0x1f,0x20,0x0f,0x0f,0x10,0x10,0x21, + 0x2e,0x2e,0x2e,0x1e,0x2f,0x2f,0x2f,0x1f,0x1f,0x20,0x20,0x31,0x10,0x10,0x21,0x21, + 0x3f,0x3f,0x3f,0x2f,0x30,0x30,0x30,0x30,0x20,0x31,0x31,0x31,0x31,0x21,0x21,0x32, + 0x4f,0x4f,0x22,0x40,0x40,0x40,0x40,0x41,0x41,0x41,0x52,0x42,0x42,0x53,0x53,0x64, + 0x4f,0x22,0x22,0x22,0x40,0x40,0x40,0x41,0x41,0x41,0x52,0x42,0x42,0x53,0x53,0x64, + 0x22,0x22,0x22,0x33,0x33,0x33,0x40,0x41,0x41,0x41,0x52,0x42,0x42,0x53,0x53,0x64, + 0x43,0x22,0x33,0x33,0x33,0x44,0x44,0x45,0x45,0x45,0x56,0x46,0x46,0x46,0x57,0x68, + 0x43,0x43,0x33,0x33,0x44,0x44,0x44,0x45,0x45,0x45,0x56,0x46,0x46,0x57,0x57,0x68, + 0x43,0x43,0x33,0x44,0x44,0x44,0x55,0x45,0x45,0x56,0x56,0x46,0x46,0x57,0x68,0x68, + 0x43,0x43,0x43,0x44,0x44,0x55,0x55,0x45,0x45,0x56,0x67,0x46,0x46,0x57,0x68,0x79, + 0x47,0x47,0x47,0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x49,0x4a,0x4a,0x4a,0x5b,0x79, + 0x47,0x47,0x47,0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x5a,0x4a,0x4a,0x4a,0x5b,0x6c, + 0x47,0x47,0x47,0x48,0x48,0x59,0x59,0x49,0x49,0x5a,0x5a,0x4a,0x4a,0x5b,0x5b,0x6c, + 0x58,0x58,0x58,0x59,0x59,0x59,0x6a,0x49,0x5a,0x5a,0x6b,0x6b,0x5b,0x5b,0x6c,0x7d, + 0x4b,0x4b,0x4b,0x4c,0x4c,0x4c,0x4c,0x4d,0x4d,0x4d,0x6b,0x4e,0x4e,0x4e,0x6c,0x7d, + 0x4b,0x4b,0x4b,0x4c,0x4c,0x4c,0x4c,0x4d,0x4d,0x4d,0x5e,0x4e,0x4e,0x4e,0x5f,0x5f, + 0x5c,0x5c,0x5c,0x4c,0x5d,0x5d,0x5d,0x4d,0x4d,0x5e,0x5e,0x4e,0x4e,0x5f,0x5f,0x60, + 0x5c,0x5c,0x5c,0x5d,0x5d,0x6e,0x6e,0x5e,0x5e,0x5e,0x6f,0x6f,0x5f,0x5f,0x60,0x60, + 0x6d,0x6d,0x6d,0x6e,0x6e,0x6e,0x7f,0x7f,0x6f,0x6f,0x70,0x70,0x5f,0x60,0x60,0x71, + 0x4f,0x4f,0x40,0x40,0x40,0x40,0x51,0x41,0x41,0x52,0x63,0x42,0x42,0x53,0x64,0x75, + 0x4f,0x4f,0x22,0x40,0x40,0x40,0x51,0x41,0x41,0x52,0x63,0x42,0x42,0x53,0x64,0x75, + 0x43,0x22,0x33,0x33,0x33,0x40,0x51,0x41,0x41,0x52,0x63,0x42,0x42,0x53,0x64,0x75, + 0x43,0x43,0x33,0x33,0x44,0x44,0x44,0x45,0x45,0x45,0x56,0x46,0x46,0x57,0x57,0x68, + 0x43,0x43,0x33,0x44,0x44,0x44,0x55,0x45,0x45,0x56,0x56,0x46,0x46,0x57,0x68,0x68, + 0x43,0x43,0x43,0x44,0x44,0x55,0x55,0x45,0x45,0x56,0x67,0x46,0x46,0x57,0x68,0x79, + 0x54,0x54,0x54,0x44,0x55,0x55,0x55,0x45,0x56,0x56,0x67,0x78,0x78,0x57,0x68,0x79, + 0x47,0x47,0x47,0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x49,0x4a,0x4a,0x4a,0x5b,0x79, + 0x47,0x47,0x47,0x48,0x48,0x48,0x59,0x49,0x49,0x49,0x5a,0x4a,0x4a,0x5b,0x5b,0x6c, + 0x58,0x58,0x58,0x48,0x59,0x59,0x59,0x49,0x49,0x5a,0x5a,0x4a,0x4a,0x5b,0x6c,0x6c, + 0x69,0x69,0x69,0x59,0x59,0x6a,0x6a,0x49,0x5a,0x5a,0x6b,0x6b,0x5b,0x5b,0x6c,0x7d, + 0x4b,0x4b,0x4b,0x4c,0x4c,0x4c,0x7b,0x4d,0x4d,0x4d,0x6b,0x4e,0x4e,0x4e,0x7d,0x7d, + 0x4b,0x4b,0x4b,0x4c,0x4c,0x4c,0x7b,0x4d,0x4d,0x4d,0x5e,0x4e,0x4e,0x4e,0x5f,0x7d, + 0x5c,0x5c,0x5c,0x5d,0x5d,0x5d,0x5d,0x4d,0x5e,0x5e,0x5e,0x4e,0x4e,0x5f,0x5f,0x60, + 0x6d,0x6d,0x6d,0x5d,0x6e,0x6e,0x6e,0x5e,0x5e,0x6f,0x6f,0x70,0x5f,0x5f,0x60,0x60, + 0x7e,0x7e,0x7e,0x6e,0x6e,0x7f,0x7f,0x7f,0x6f,0x6f,0x70,0x70,0x70,0x60,0x60,0x71, + 0x50,0x50,0x50,0x40,0x40,0x51,0x51,0x41,0x41,0x52,0x63,0x74,0x42,0x53,0x64,0x75, + 0x50,0x50,0x50,0x40,0x40,0x51,0x51,0x41,0x41,0x52,0x63,0x74,0x42,0x53,0x64,0x75, + 0x50,0x50,0x33,0x33,0x40,0x51,0x51,0x41,0x41,0x52,0x63,0x74,0x42,0x53,0x64,0x75, + 0x43,0x43,0x33,0x44,0x44,0x44,0x55,0x45,0x45,0x56,0x56,0x46,0x46,0x57,0x68,0x68, + 0x43,0x43,0x43,0x44,0x44,0x55,0x55,0x45,0x45,0x56,0x67,0x46,0x46,0x57,0x68,0x79, + 0x54,0x54,0x54,0x44,0x55,0x55,0x55,0x45,0x56,0x56,0x67,0x78,0x78,0x57,0x68,0x79, + 0x54,0x54,0x54,0x55,0x55,0x55,0x66,0x66,0x56,0x67,0x67,0x78,0x78,0x68,0x68,0x79, + 0x47,0x47,0x47,0x48,0x48,0x48,0x66,0x49,0x49,0x49,0x78,0x78,0x4a,0x4a,0x5b,0x79, + 0x47,0x47,0x47,0x48,0x48,0x59,0x59,0x49,0x49,0x5a,0x5a,0x4a,0x4a,0x5b,0x6c,0x6c, + 0x58,0x58,0x58,0x59,0x59,0x59,0x6a,0x49,0x5a,0x5a,0x6b,0x6b,0x5b,0x5b,0x6c,0x7d, + 0x69,0x69,0x69,0x59,0x6a,0x6a,0x6a,0x7b,0x5a,0x6b,0x6b,0x6b,0x7c,0x6c,0x6c,0x7d, + 0x7a,0x7a,0x7a,0x4c,0x4c,0x7b,0x7b,0x7b,0x4d,0x6b,0x6b,0x7c,0x7c,0x4e,0x7d,0x7d, + 0x4b,0x4b,0x4b,0x4c,0x4c,0x7b,0x7b,0x4d,0x4d,0x5e,0x7c,0x7c,0x4e,0x5f,0x5f,0x7d, + 0x5c,0x5c,0x5c,0x5d,0x5d,0x5d,0x6e,0x4d,0x5e,0x5e,0x6f,0x4e,0x5f,0x5f,0x60,0x60, + 0x6d,0x6d,0x6d,0x6e,0x6e,0x6e,0x6e,0x5e,0x6f,0x6f,0x6f,0x70,0x5f,0x60,0x60,0x71, + 0x7e,0x7e,0x7e,0x6e,0x7f,0x7f,0x7f,0x7f,0x6f,0x70,0x70,0x70,0x70,0x60,0x71,0x71, + 0x61,0x61,0x61,0x40,0x51,0x51,0x62,0x62,0x73,0x63,0x63,0x74,0x74,0x64,0x64,0x75, + 0x61,0x61,0x61,0x40,0x51,0x51,0x62,0x62,0x73,0x63,0x63,0x74,0x74,0x64,0x64,0x75, + 0x61,0x61,0x61,0x40,0x51,0x51,0x62,0x62,0x73,0x63,0x63,0x74,0x74,0x64,0x64,0x75, + 0x43,0x43,0x43,0x44,0x44,0x55,0x55,0x45,0x45,0x56,0x67,0x46,0x46,0x57,0x68,0x79, + 0x54,0x54,0x54,0x44,0x55,0x55,0x55,0x45,0x56,0x56,0x67,0x78,0x78,0x57,0x68,0x79, + 0x54,0x54,0x54,0x55,0x55,0x55,0x66,0x66,0x56,0x67,0x67,0x78,0x78,0x68,0x68,0x79, + 0x65,0x65,0x65,0x55,0x55,0x66,0x66,0x66,0x77,0x67,0x78,0x78,0x78,0x78,0x79,0x79, + 0x65,0x65,0x65,0x48,0x48,0x66,0x66,0x77,0x77,0x77,0x78,0x78,0x78,0x5b,0x79,0x79, + 0x76,0x76,0x76,0x48,0x59,0x59,0x77,0x77,0x77,0x5a,0x5a,0x4a,0x4a,0x5b,0x6c,0x6c, + 0x69,0x69,0x69,0x59,0x59,0x6a,0x6a,0x77,0x5a,0x5a,0x6b,0x6b,0x5b,0x6c,0x6c,0x7d, + 0x69,0x69,0x69,0x6a,0x6a,0x6a,0x7b,0x7b,0x5a,0x6b,0x6b,0x7c,0x7c,0x6c,0x7d,0x7d, + 0x7a,0x7a,0x7a,0x4c,0x7b,0x7b,0x7b,0x7b,0x4d,0x6b,0x7c,0x7c,0x7c,0x7c,0x7d,0x7d, + 0x7a,0x7a,0x7a,0x4c,0x7b,0x7b,0x7b,0x7b,0x4d,0x5e,0x7c,0x7c,0x7c,0x5f,0x5f,0x7d, + 0x6d,0x6d,0x6d,0x5d,0x5d,0x6e,0x7b,0x5e,0x5e,0x6f,0x6f,0x7c,0x5f,0x5f,0x60,0x60, + 0x6d,0x6d,0x6d,0x6e,0x6e,0x6e,0x7f,0x7f,0x6f,0x6f,0x70,0x70,0x5f,0x60,0x60,0x71, + 0x7e,0x7e,0x7e,0x7f,0x7f,0x7f,0x7f,0x7f,0x6f,0x70,0x70,0x70,0x70,0x60,0x71,0x71, + 0x72,0x72,0x72,0x8f,0x8f,0x62,0x62,0x73,0x73,0x80,0x74,0x81,0x81,0x81,0x92,0x75, + 0x72,0x72,0x72,0x8f,0x8f,0x62,0x62,0x73,0x73,0x80,0x74,0x81,0x81,0x81,0x92,0x75, + 0x72,0x72,0x72,0x83,0x83,0x62,0x62,0x73,0x73,0x80,0x74,0x81,0x81,0x81,0x92,0x75, + 0x82,0x82,0x82,0x83,0x83,0x83,0x83,0x84,0x84,0x84,0x84,0x85,0x85,0x85,0x96,0x79, + 0x82,0x82,0x82,0x83,0x83,0x83,0x66,0x84,0x84,0x84,0x67,0x85,0x85,0x85,0x96,0x79, + 0x65,0x65,0x65,0x83,0x83,0x66,0x66,0x66,0x84,0x84,0x78,0x78,0x85,0x85,0x96,0x79, + 0x65,0x65,0x65,0x83,0x66,0x66,0x66,0x77,0x77,0x77,0x78,0x78,0x78,0x96,0x79,0x79, + 0x76,0x76,0x76,0x87,0x87,0x66,0x77,0x77,0x77,0x88,0x78,0x89,0x89,0x89,0x89,0x79, + 0x76,0x76,0x76,0x87,0x87,0x87,0x77,0x77,0x88,0x88,0x88,0x89,0x89,0x89,0x9a,0x9a, + 0x86,0x86,0x86,0x87,0x87,0x87,0x77,0x88,0x88,0x88,0x6b,0x89,0x89,0x9a,0x9a,0x7d, + 0x7a,0x7a,0x7a,0x87,0x6a,0x7b,0x7b,0x7b,0x88,0x6b,0x6b,0x7c,0x7c,0x9a,0x7d,0x7d, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x7b,0x7b,0x8c,0x8c,0x8c,0x7c,0x7c,0x8d,0x8d,0x7d,0x7d, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x8b,0x7b,0x8c,0x8c,0x8c,0x7c,0x8d,0x8d,0x8d,0x9e,0x9e, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x8b,0x9c,0x8c,0x8c,0x9d,0x9d,0x8d,0x8d,0x9e,0x9e,0x9e, + 0x9b,0x9b,0x9b,0x9c,0x9c,0x9c,0x7f,0x8c,0x9d,0x9d,0x70,0x70,0x9e,0x9e,0x9e,0x71, + 0x7e,0x7e,0x7e,0x7f,0x7f,0x7f,0x7f,0x7f,0x9d,0x70,0x70,0x70,0x9e,0x9e,0x71,0x71, + 0x8e,0x8e,0x8e,0x8f,0x8f,0x8f,0x73,0x73,0x80,0x80,0x91,0x81,0x81,0x92,0x92,0xa3, + 0x8e,0x8e,0x8e,0x8f,0x8f,0x8f,0x73,0x73,0x80,0x80,0x91,0x81,0x81,0x92,0x92,0xa3, + 0x82,0x82,0x82,0x83,0x83,0x83,0x73,0x73,0x80,0x80,0x91,0x81,0x81,0x92,0x92,0xa3, + 0x82,0x82,0x82,0x83,0x83,0x83,0x83,0x84,0x84,0x84,0x95,0x85,0x85,0x85,0x96,0xa7, + 0x82,0x82,0x82,0x83,0x83,0x83,0x94,0x84,0x84,0x84,0x95,0x85,0x85,0x96,0x96,0xa7, + 0x82,0x82,0x82,0x83,0x83,0x94,0x94,0x84,0x84,0x95,0x95,0x85,0x85,0x96,0xa7,0xa7, + 0x76,0x76,0x76,0x83,0x94,0x94,0x77,0x77,0x77,0x95,0x95,0x85,0x85,0x96,0xa7,0xa7, + 0x76,0x76,0x76,0x87,0x87,0x87,0x77,0x77,0x88,0x88,0x88,0x89,0x89,0x89,0x9a,0x9a, + 0x86,0x86,0x86,0x87,0x87,0x87,0x77,0x88,0x88,0x88,0x99,0x89,0x89,0x9a,0x9a,0xab, + 0x86,0x86,0x86,0x87,0x87,0x98,0x98,0x88,0x88,0x99,0x99,0x89,0x89,0x9a,0x9a,0xab, + 0x97,0x97,0x97,0x98,0x98,0x98,0x98,0x88,0x99,0x99,0x99,0x89,0x9a,0x9a,0xab,0xab, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x8b,0x8b,0x8c,0x8c,0x8c,0x8c,0x8d,0x8d,0x8d,0xab,0xbc, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x8b,0x8b,0x8c,0x8c,0x8c,0x9d,0x8d,0x8d,0x8d,0x9e,0x9e, + 0x9b,0x9b,0x9b,0x8b,0x9c,0x9c,0x9c,0x8c,0x9d,0x9d,0x9d,0x8d,0x8d,0x9e,0x9e,0xaf, + 0x9b,0x9b,0x9b,0x9c,0x9c,0xad,0xad,0x9d,0x9d,0x9d,0xae,0xae,0x9e,0x9e,0xaf,0xaf, + 0xac,0xac,0xac,0xad,0xad,0xad,0xad,0x9d,0xae,0xae,0xae,0xbf,0x9e,0xaf,0xaf,0xaf, + 0x9f,0x9f,0x9f,0x8f,0x90,0x90,0xa1,0x80,0x80,0x91,0x91,0x81,0x81,0x92,0xa3,0xb4, + 0x9f,0x9f,0x9f,0x8f,0x90,0x90,0xa1,0x80,0x80,0x91,0x91,0x81,0x81,0x92,0xa3,0xb4, + 0x9f,0x9f,0x9f,0x83,0x90,0x90,0xa1,0x80,0x80,0x91,0x91,0x81,0x81,0x92,0xa3,0xb4, + 0x82,0x82,0x82,0x83,0x83,0x94,0x94,0x84,0x84,0x95,0x95,0x85,0x85,0x96,0x96,0xa7, + 0x93,0x93,0x93,0x83,0x94,0x94,0x94,0x84,0x84,0x95,0x95,0x85,0x85,0x96,0xa7,0xa7, + 0x93,0x93,0x93,0x94,0x94,0x94,0xa5,0x84,0x95,0x95,0xa6,0xa6,0x96,0x96,0xa7,0xb8, + 0xa4,0xa4,0xa4,0x94,0x94,0xa5,0xa5,0x77,0x95,0x95,0xa6,0xa6,0x96,0xa7,0xa7,0xb8, + 0x86,0x86,0x86,0x87,0x87,0x87,0x77,0x88,0x88,0x88,0x99,0x89,0x89,0x9a,0x9a,0xb8, + 0x86,0x86,0x86,0x87,0x87,0x98,0x98,0x88,0x88,0x99,0x99,0x89,0x89,0x9a,0x9a,0xab, + 0x97,0x97,0x97,0x98,0x98,0x98,0x98,0x88,0x99,0x99,0x99,0x89,0x9a,0x9a,0xab,0xab, + 0x97,0x97,0x97,0x98,0x98,0xa9,0xa9,0x99,0x99,0x99,0xaa,0xaa,0x9a,0xab,0xab,0xbc, + 0x8a,0x8a,0x8a,0x8b,0x8b,0xa9,0xa9,0x8c,0x8c,0x8c,0xaa,0x8d,0x8d,0x8d,0xab,0xbc, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x9c,0x9c,0x8c,0x8c,0x9d,0x9d,0x8d,0x8d,0x9e,0x9e,0xbc, + 0x9b,0x9b,0x9b,0x9c,0x9c,0x9c,0xad,0x9d,0x9d,0x9d,0xae,0x8d,0x9e,0x9e,0xaf,0xaf, + 0xac,0xac,0xac,0x9c,0xad,0xad,0xad,0x9d,0x9d,0xae,0xae,0xae,0x9e,0xaf,0xaf,0xaf, + 0xbd,0xbd,0xbd,0xad,0xad,0xbe,0xbe,0xbe,0xae,0xae,0xbf,0xbf,0xbf,0xaf,0xaf,0xb0, + 0xa0,0xa0,0xa0,0x90,0xa1,0xa1,0xa1,0xb2,0x91,0x91,0xa2,0xa2,0xb3,0xa3,0xa3,0xb4, + 0xa0,0xa0,0xa0,0x90,0xa1,0xa1,0xa1,0xb2,0x91,0x91,0xa2,0xa2,0xb3,0xa3,0xa3,0xb4, + 0xa0,0xa0,0xa0,0x90,0xa1,0xa1,0xa1,0xb2,0x91,0x91,0xa2,0xa2,0xb3,0xa3,0xa3,0xb4, + 0x93,0x93,0x93,0x94,0x94,0x94,0xa5,0x84,0x95,0x95,0xa6,0xa6,0x96,0x96,0xa7,0xb8, + 0xa4,0xa4,0xa4,0x94,0x94,0xa5,0xa5,0x84,0x95,0x95,0xa6,0xa6,0x96,0x96,0xa7,0xb8, + 0xa4,0xa4,0xa4,0x94,0xa5,0xa5,0xa5,0xb6,0x95,0xa6,0xa6,0xa6,0xb7,0xa7,0xa7,0xb8, + 0xa4,0xa4,0xa4,0xa5,0xa5,0xa5,0xb6,0xb6,0x95,0xa6,0xa6,0xb7,0xb7,0xa7,0xb8,0xb8, + 0xb5,0xb5,0xb5,0x87,0x87,0xb6,0xb6,0xb6,0x88,0x99,0xa6,0xb7,0xb7,0x9a,0xb8,0xb8, + 0x97,0x97,0x97,0x98,0x98,0x98,0x98,0x88,0x99,0x99,0x99,0x89,0x9a,0x9a,0xab,0xab, + 0x97,0x97,0x97,0x98,0x98,0xa9,0xa9,0x99,0x99,0x99,0xaa,0xaa,0x9a,0xab,0xab,0xbc, + 0xa8,0xa8,0xa8,0xa9,0xa9,0xa9,0xa9,0xa9,0x99,0xaa,0xaa,0xaa,0xbb,0xab,0xab,0xbc, + 0xa8,0xa8,0xa8,0xa9,0xa9,0xa9,0xba,0xba,0x8c,0xaa,0xaa,0xbb,0xbb,0xab,0xbc,0xbc, + 0xb9,0xb9,0xb9,0x9c,0x9c,0xba,0xba,0xba,0x9d,0x9d,0xbb,0xbb,0xbb,0x9e,0x9e,0xbc, + 0xac,0xac,0xac,0x9c,0x9c,0xad,0xad,0x9d,0x9d,0xae,0xae,0xae,0x9e,0x9e,0xaf,0xaf, + 0xac,0xac,0xac,0xad,0xad,0xad,0xbe,0xbe,0xae,0xae,0xae,0xbf,0x9e,0xaf,0xaf,0xb0, + 0xbd,0xbd,0xbd,0xbe,0xbe,0xbe,0xbe,0xbe,0xae,0xbf,0xbf,0xbf,0xbf,0xaf,0xb0,0xb0, + 0xb1,0xb1,0xb1,0xce,0xce,0xb2,0xb2,0xcf,0xcf,0xa2,0xa2,0xb3,0xb3,0xc0,0xb4,0xb4, + 0xb1,0xb1,0xb1,0xce,0xce,0xb2,0xb2,0xcf,0xcf,0xa2,0xa2,0xb3,0xb3,0xc0,0xb4,0xb4, + 0xb1,0xb1,0xb1,0xc2,0xc2,0xb2,0xb2,0xc3,0xc3,0xa2,0xa2,0xb3,0xb3,0xc0,0xb4,0xb4, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xc2,0xa5,0xc3,0xc3,0xc3,0xa6,0xc4,0xc4,0xc4,0xa7,0xb8, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xa5,0xb6,0xc3,0xc3,0xc3,0xa6,0xc4,0xc4,0xc4,0xb8,0xb8, + 0xb5,0xb5,0xb5,0xc2,0xa5,0xb6,0xb6,0xb6,0xc3,0xa6,0xa6,0xb7,0xb7,0xc4,0xb8,0xb8, + 0xb5,0xb5,0xb5,0xa5,0xb6,0xb6,0xb6,0xb6,0xc3,0xa6,0xb7,0xb7,0xb7,0xb7,0xb8,0xb8, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xb6,0xb6,0xc7,0xc7,0xc7,0xb7,0xb7,0xc8,0xc8,0xb8,0xb8, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xc6,0xc7,0xc7,0xc7,0xaa,0xc8,0xc8,0xc8,0xab,0xbc, + 0xa8,0xa8,0xa8,0xc6,0xc6,0xa9,0xa9,0xc7,0xc7,0xaa,0xaa,0xaa,0xc8,0xc8,0xab,0xbc, + 0xa8,0xa8,0xa8,0xa9,0xa9,0xa9,0xba,0xba,0xaa,0xaa,0xaa,0xbb,0xbb,0xab,0xbc,0xbc, + 0xb9,0xb9,0xb9,0xca,0xca,0xba,0xba,0xba,0xcb,0xaa,0xbb,0xbb,0xbb,0xcc,0xbc,0xbc, + 0xb9,0xb9,0xb9,0xca,0xca,0xba,0xba,0xcb,0xcb,0xcb,0xbb,0xbb,0xcc,0xcc,0xcc,0xbc, + 0xc9,0xc9,0xc9,0xca,0xca,0xca,0xba,0xcb,0xcb,0xcb,0xae,0xcc,0xcc,0xcc,0xaf,0xaf, + 0xbd,0xbd,0xbd,0xad,0xbe,0xbe,0xbe,0xbe,0xae,0xae,0xbf,0xbf,0xcc,0xaf,0xaf,0xb0, + 0xbd,0xbd,0xbd,0xbe,0xbe,0xbe,0xbe,0xbe,0xbf,0xbf,0xbf,0xbf,0xbf,0xaf,0xb0,0xb0, + 0xcd,0xcd,0xcd,0xce,0xce,0xce,0xb2,0xcf,0xcf,0xcf,0xb3,0xb3,0xc0,0xc0,0xd1,0xb4, + 0xcd,0xcd,0xcd,0xce,0xce,0xce,0xb2,0xcf,0xcf,0xcf,0xb3,0xb3,0xc0,0xc0,0xd1,0xb4, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xc2,0xb2,0xc3,0xc3,0xc3,0xb3,0xb3,0xc0,0xc0,0xd1,0xb4, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xc2,0xc2,0xc3,0xc3,0xc3,0xd4,0xc4,0xc4,0xc4,0xd5,0xd5, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xc2,0xb6,0xc3,0xc3,0xc3,0xd4,0xc4,0xc4,0xc4,0xd5,0xb8, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xb6,0xb6,0xc3,0xc3,0xd4,0xb7,0xb7,0xc4,0xd5,0xd5,0xb8, + 0xb5,0xb5,0xb5,0xc2,0xb6,0xb6,0xb6,0xb6,0xc3,0xd4,0xb7,0xb7,0xb7,0xd5,0xd5,0xb8, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xb6,0xc7,0xc7,0xc7,0xb7,0xc8,0xc8,0xc8,0xd9,0xd9, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xc6,0xc7,0xc7,0xc7,0xd8,0xc8,0xc8,0xc8,0xd9,0xd9, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xd7,0xd7,0xc7,0xc7,0xd8,0xd8,0xc8,0xc8,0xd9,0xd9,0xbc, + 0xb9,0xb9,0xb9,0xd7,0xd7,0xba,0xba,0xba,0xd8,0xd8,0xbb,0xbb,0xbb,0xd9,0xd9,0xbc, + 0xb9,0xb9,0xb9,0xca,0xca,0xba,0xba,0xcb,0xcb,0xcb,0xbb,0xbb,0xcc,0xcc,0xcc,0xbc, + 0xc9,0xc9,0xc9,0xca,0xca,0xca,0xba,0xcb,0xcb,0xcb,0xbb,0xcc,0xcc,0xcc,0xdd,0xdd, + 0xc9,0xc9,0xc9,0xca,0xca,0xdb,0xdb,0xcb,0xcb,0xdc,0xdc,0xcc,0xcc,0xdd,0xdd,0xdd, + 0xda,0xda,0xda,0xdb,0xdb,0xdb,0xdb,0xdc,0xdc,0xdc,0xdc,0xcc,0xdd,0xdd,0xdd,0xb0, + 0xbd,0xbd,0xbd,0xdb,0xbe,0xbe,0xbe,0xdc,0xdc,0xbf,0xbf,0xbf,0xdd,0xdd,0xb0,0xb0, + 0xde,0xde,0xde,0xdf,0xdf,0xdf,0xe0,0xcf,0xd0,0xd0,0xe1,0xc0,0xc0,0xd1,0xd1,0xe2, + 0xde,0xde,0xde,0xdf,0xdf,0xdf,0xe0,0xcf,0xd0,0xd0,0xe1,0xc0,0xc0,0xd1,0xd1,0xe2, + 0xde,0xde,0xde,0xdf,0xdf,0xdf,0xe0,0xc3,0xd0,0xd0,0xe1,0xc0,0xc0,0xd1,0xd1,0xe2, + 0xd2,0xd2,0xd2,0xc2,0xd3,0xd3,0xd3,0xc3,0xc3,0xd4,0xd4,0xc4,0xc4,0xd5,0xd5,0xe6, + 0xd2,0xd2,0xd2,0xd3,0xd3,0xd3,0xd3,0xc3,0xd4,0xd4,0xd4,0xc4,0xc4,0xd5,0xd5,0xe6, + 0xd2,0xd2,0xd2,0xd3,0xd3,0xd3,0xe4,0xc3,0xd4,0xd4,0xe5,0xc4,0xd5,0xd5,0xe6,0xe6, + 0xe3,0xe3,0xe3,0xd3,0xd3,0xe4,0xb6,0xd4,0xd4,0xe5,0xe5,0xb7,0xd5,0xd5,0xe6,0xe6, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xd7,0xc7,0xc7,0xd8,0xd8,0xc8,0xc8,0xd9,0xd9,0xd9, + 0xd6,0xd6,0xd6,0xc6,0xd7,0xd7,0xd7,0xc7,0xd8,0xd8,0xd8,0xc8,0xc8,0xd9,0xd9,0xea, + 0xd6,0xd6,0xd6,0xd7,0xd7,0xd7,0xe8,0xd8,0xd8,0xd8,0xe9,0xc8,0xd9,0xd9,0xea,0xea, + 0xe7,0xe7,0xe7,0xd7,0xd7,0xe8,0xe8,0xd8,0xd8,0xe9,0xe9,0xe9,0xd9,0xd9,0xea,0xea, + 0xc9,0xc9,0xc9,0xca,0xca,0xca,0xba,0xcb,0xcb,0xcb,0xe9,0xcc,0xcc,0xcc,0xea,0xea, + 0xc9,0xc9,0xc9,0xca,0xca,0xdb,0xdb,0xcb,0xcb,0xdc,0xdc,0xcc,0xcc,0xdd,0xdd,0xdd, + 0xda,0xda,0xda,0xdb,0xdb,0xdb,0xdb,0xdc,0xdc,0xdc,0xdc,0xcc,0xdd,0xdd,0xdd,0xee, + 0xda,0xda,0xda,0xdb,0xdb,0xec,0xec,0xdc,0xdc,0xed,0xed,0xed,0xdd,0xdd,0xee,0xee, + 0xeb,0xeb,0xeb,0xec,0xec,0xec,0xec,0xdc,0xed,0xed,0xed,0xed,0xdd,0xee,0xee,0xee, + 0xef,0xef,0xef,0xdf,0xe0,0xe0,0xe0,0xd0,0xd0,0xe1,0xe1,0xf2,0xd1,0xd1,0xe2,0xe2, + 0xef,0xef,0xef,0xdf,0xe0,0xe0,0xe0,0xd0,0xd0,0xe1,0xe1,0xf2,0xd1,0xd1,0xe2,0xe2, + 0xef,0xef,0xef,0xdf,0xe0,0xe0,0xe0,0xd0,0xd0,0xe1,0xe1,0xf2,0xd1,0xd1,0xe2,0xe2, + 0xd2,0xd2,0xd2,0xd3,0xd3,0xe4,0xe4,0xd4,0xd4,0xd4,0xe5,0xe5,0xd5,0xd5,0xe6,0xe6, + 0xe3,0xe3,0xe3,0xd3,0xe4,0xe4,0xe4,0xd4,0xd4,0xe5,0xe5,0xf6,0xd5,0xd5,0xe6,0xe6, + 0xe3,0xe3,0xe3,0xe4,0xe4,0xe4,0xe4,0xd4,0xe5,0xe5,0xe5,0xf6,0xd5,0xe6,0xe6,0xf7, + 0xe3,0xe3,0xe3,0xe4,0xe4,0xe4,0xf5,0xf5,0xe5,0xe5,0xf6,0xf6,0xd5,0xe6,0xe6,0xf7, + 0xd6,0xd6,0xd6,0xd7,0xd7,0xd7,0xf5,0xc7,0xd8,0xd8,0xf6,0xc8,0xd9,0xd9,0xd9,0xf7, + 0xd6,0xd6,0xd6,0xd7,0xd7,0xe8,0xe8,0xd8,0xd8,0xd8,0xe9,0xe9,0xd9,0xd9,0xea,0xea, + 0xe7,0xe7,0xe7,0xd7,0xe8,0xe8,0xe8,0xd8,0xd8,0xe9,0xe9,0xe9,0xd9,0xea,0xea,0xea, + 0xe7,0xe7,0xe7,0xe8,0xe8,0xe8,0xf9,0xf9,0xe9,0xe9,0xe9,0xfa,0xd9,0xea,0xea,0xfb, + 0xf8,0xf8,0xf8,0xe8,0xf9,0xf9,0xf9,0xcb,0xe9,0xe9,0xfa,0xfa,0xcc,0xea,0xea,0xfb, + 0xda,0xda,0xda,0xdb,0xdb,0xdb,0xdb,0xdc,0xdc,0xdc,0xdc,0xcc,0xdd,0xdd,0xdd,0xee, + 0xda,0xda,0xda,0xdb,0xdb,0xec,0xec,0xdc,0xdc,0xed,0xed,0xed,0xdd,0xdd,0xee,0xee, + 0xeb,0xeb,0xeb,0xec,0xec,0xec,0xec,0xdc,0xed,0xed,0xed,0xed,0xdd,0xee,0xee,0xee, + 0xeb,0xeb,0xeb,0xec,0xec,0xfd,0xfd,0xfd,0xed,0xed,0xfe,0xfe,0xee,0xee,0xee,0xff, + 0xf0,0xf0,0xf0,0xe0,0xf1,0xf1,0xf1,0xf1,0xe1,0xf2,0xf2,0xf2,0xf2,0xe2,0xe2,0xf3, + 0xf0,0xf0,0xf0,0xe0,0xf1,0xf1,0xf1,0xf1,0xe1,0xf2,0xf2,0xf2,0xf2,0xe2,0xe2,0xf3, + 0xf0,0xf0,0xf0,0xe0,0xf1,0xf1,0xf1,0xf1,0xe1,0xf2,0xf2,0xf2,0xf2,0xe2,0xe2,0xf3, + 0xe3,0xe3,0xe3,0xe4,0xe4,0xe4,0xf5,0xf5,0xe5,0xe5,0xf6,0xf6,0xd5,0xe6,0xe6,0xf7, + 0xf4,0xf4,0xf4,0xe4,0xe4,0xf5,0xf5,0xf5,0xe5,0xe5,0xf6,0xf6,0xf6,0xe6,0xe6,0xf7, + 0xf4,0xf4,0xf4,0xe4,0xf5,0xf5,0xf5,0xf5,0xe5,0xf6,0xf6,0xf6,0xf6,0xe6,0xf7,0xf7, + 0xf4,0xf4,0xf4,0xf5,0xf5,0xf5,0xf5,0xf5,0xe5,0xf6,0xf6,0xf6,0xf6,0xe6,0xf7,0xf7, + 0xf4,0xf4,0xf4,0xf5,0xf5,0xf5,0xf5,0xf5,0xd8,0xf6,0xf6,0xf6,0xd9,0xd9,0xf7,0xf7, + 0xe7,0xe7,0xe7,0xe8,0xe8,0xe8,0xe8,0xd8,0xe9,0xe9,0xe9,0xfa,0xd9,0xea,0xea,0xea, + 0xf8,0xf8,0xf8,0xe8,0xe8,0xf9,0xf9,0xf9,0xe9,0xe9,0xfa,0xfa,0xfa,0xea,0xea,0xfb, + 0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xf9,0xf9,0xe9,0xfa,0xfa,0xfa,0xfa,0xea,0xfb,0xfb, + 0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xf9,0xf9,0xfa,0xfa,0xfa,0xfa,0xfa,0xea,0xfb,0xfb, + 0xf8,0xf8,0xf8,0xdb,0xf9,0xf9,0xf9,0xdc,0xdc,0xfa,0xfa,0xfa,0xdd,0xdd,0xee,0xfb, + 0xeb,0xeb,0xeb,0xec,0xec,0xec,0xec,0xdc,0xed,0xed,0xed,0xed,0xdd,0xee,0xee,0xee, + 0xeb,0xeb,0xeb,0xec,0xec,0xfd,0xfd,0xfd,0xed,0xed,0xfe,0xfe,0xee,0xee,0xee,0xff, + 0xfc,0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfd,0xed,0xfe,0xfe,0xfe,0xfe,0xee,0xff,0xff, +} +}; +Memcmap *memdefcmap = &def; +void _memmkcmap(void){} diff --git a/libmemdraw/cread.c b/libmemdraw/cread.c new file mode 100644 index 0000000..f414cd1 --- /dev/null +++ b/libmemdraw/cread.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include + +Memimage* +creadmemimage(int fd) +{ + char hdr[5*12+1]; + Rectangle r; + int m, nb, miny, maxy, new, ldepth, ncblock; + uchar *buf; + Memimage *i; + ulong chan; + + if(readn(fd, hdr, 5*12) != 5*12){ + werrstr("readmemimage: short header (2)"); + return nil; + } + + /* + * distinguish new channel descriptor from old ldepth. + * channel descriptors have letters as well as numbers, + * while ldepths are a single digit formatted as %-11d. + */ + new = 0; + for(m=0; m<10; m++){ + if(hdr[m] != ' '){ + new = 1; + break; + } + } + if(hdr[11] != ' '){ + werrstr("creadimage: bad format"); + return nil; + } + if(new){ + hdr[11] = '\0'; + if((chan = strtochan(hdr)) == 0){ + werrstr("creadimage: bad channel string %s", hdr); + return nil; + } + }else{ + ldepth = ((int)hdr[10])-'0'; + if(ldepth<0 || ldepth>3){ + werrstr("creadimage: bad ldepth %d", ldepth); + return nil; + } + chan = drawld2chan[ldepth]; + } + r.min.x=atoi(hdr+1*12); + r.min.y=atoi(hdr+2*12); + r.max.x=atoi(hdr+3*12); + r.max.y=atoi(hdr+4*12); + if(r.min.x>r.max.x || r.min.y>r.max.y){ + werrstr("creadimage: bad rectangle"); + return nil; + } + + i = allocmemimage(r, chan); + if(i == nil) + return nil; + ncblock = _compblocksize(r, i->depth); + buf = malloc(ncblock); + if(buf == nil) + goto Errout; + miny = r.min.y; + while(miny != r.max.y){ + if(readn(fd, hdr, 2*12) != 2*12){ + Shortread: + werrstr("readmemimage: short read"); + Errout: + freememimage(i); + free(buf); + return nil; + } + maxy = atoi(hdr+0*12); + nb = atoi(hdr+1*12); + if(maxy<=miny || r.max.y +#include +#include +#include + +Memsubfont* +getmemdefont(void) +{ + char *hdr, *p; + int n; + Fontchar *fc; + Memsubfont *f; + int ld; + Rectangle r; + Memdata *md; + Memimage *i; + + /* + * make sure data is word-aligned. this is true with Plan 9 compilers + * but not in general. the byte order is right because the data is + * declared as char*, not ulong*. + */ + p = (char*)defontdata; + n = (ulong)p & 3; + if(n != 0){ + memmove(p+(4-n), p, sizeofdefont-n); + p += 4-n; + } + ld = atoi(p+0*12); + r.min.x = atoi(p+1*12); + r.min.y = atoi(p+2*12); + r.max.x = atoi(p+3*12); + r.max.y = atoi(p+4*12); + + md = mallocz(sizeof(Memdata), 1); + if(md == nil) + return nil; + + p += 5*12; + + md->base = nil; /* so freememimage doesn't free p */ + md->bdata = (uchar*)p; /* ick */ + md->ref = 1; + md->allocd = 1; /* so freememimage does free md */ + + i = allocmemimaged(r, drawld2chan[ld], md, nil); + if(i == nil){ + free(md); + return nil; + } + + hdr = p+Dy(r)*i->width*sizeof(ulong); + n = atoi(hdr); + p = hdr+3*12; + fc = malloc(sizeof(Fontchar)*(n+1)); + if(fc == 0){ + freememimage(i); + return 0; + } + _unpackinfo(fc, (uchar*)p, n); + f = allocmemsubfont("*default*", n, atoi(hdr+12), atoi(hdr+24), fc, i); + if(f == 0){ + freememimage(i); + free(fc); + return 0; + } + return f; +} diff --git a/libmemdraw/draw.c b/libmemdraw/draw.c new file mode 100644 index 0000000..255c2da --- /dev/null +++ b/libmemdraw/draw.c @@ -0,0 +1,2499 @@ +#include +#include +#include +#include + +int drawdebug; +static int tablesbuilt; + +/* perfect approximation to NTSC = .299r+.587g+.114b when 0 ≤ r,g,b < 256 */ +#define RGB2K(r,g,b) ((156763*(r)+307758*(g)+59769*(b))>>19) + +/* + * for 0 ≤ x ≤ 255*255, (x*0x0101+0x100)>>16 is a perfect approximation. + * for 0 ≤ x < (1<<16), x/255 = ((x+1)*0x0101)>>16 is a perfect approximation. + * the last one is perfect for all up to 1<<16, avoids a multiply, but requires a rathole. + */ +/* #define DIV255(x) (((x)*257+256)>>16) */ +#define DIV255(x) ((((x)+1)*257)>>16) +/* #define DIV255(x) (tmp=(x)+1, (tmp+(tmp>>8))>>8) */ + +#define MUL(x, y, t) (t = (x)*(y)+128, (t+(t>>8))>>8) +#define MASK13 0xFF00FF00 +#define MASK02 0x00FF00FF +#define MUL13(a, x, t) (t = (a)*(((x)&MASK13)>>8)+128, ((t+((t>>8)&MASK02))>>8)&MASK02) +#define MUL02(a, x, t) (t = (a)*(((x)&MASK02)>>0)+128, ((t+((t>>8)&MASK02))>>8)&MASK02) +#define MUL0123(a, x, s, t) ((MUL13(a, x, s)<<8)|MUL02(a, x, t)) + +#define MUL2(u, v, x, y) (t = (u)*(v)+(x)*(y)+256, (t+(t>>8))>>8) + +static void mktables(void); +typedef int Subdraw(Memdrawparam*); +static Subdraw chardraw, alphadraw, memoptdraw; + +static Memimage* memones; +static Memimage* memzeros; +Memimage *memwhite; +Memimage *memblack; +Memimage *memtransparent; +Memimage *memopaque; + +int _ifmt(Fmt*); + +void +_memimageinit(void) +{ + static int didinit = 0; + + if(didinit) + return; + + didinit = 1; + + mktables(); + _memmkcmap(); + + fmtinstall('R', Rfmt); + fmtinstall('P', Pfmt); + fmtinstall('b', _ifmt); + + memones = allocmemimage(Rect(0,0,1,1), GREY1); + memones->flags |= Frepl; + memones->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); + *byteaddr(memones, ZP) = ~0; + + memzeros = allocmemimage(Rect(0,0,1,1), GREY1); + memzeros->flags |= Frepl; + memzeros->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); + *byteaddr(memzeros, ZP) = 0; + + if(memones == nil || memzeros == nil) + assert(0 /*cannot initialize memimage library */); /* RSC BUG */ + + memwhite = memones; + memblack = memzeros; + memopaque = memones; + memtransparent = memzeros; +} + +ulong _imgtorgba(Memimage*, ulong); +ulong _rgbatoimg(Memimage*, ulong); +ulong _pixelbits(Memimage*, Point); + +#define DBG if(0) +static Memdrawparam par; + +Memdrawparam* +_memimagedrawsetup(Memimage *dst, Rectangle r, Memimage *src, Point p0, Memimage *mask, Point p1, int op) +{ + static int n = 0; + + if(mask == nil) + mask = memopaque; + +DBG print("memimagedraw %p/%luX %R @ %p %p/%luX %P %p/%luX %P... ", dst, dst->chan, r, dst->data->bdata, src, src->chan, p0, mask, mask->chan, p1); + + if(drawclip(dst, &r, src, &p0, mask, &p1, &par.sr, &par.mr) == 0){ +// if(drawdebug) +// iprint("empty clipped rectangle\n"); + return nil; + } + + if(op < Clear || op > SoverD){ +// if(drawdebug) +// iprint("op out of range: %d\n", op); + return nil; + } + + par.op = op; + par.dst = dst; + par.r = r; + par.src = src; + /* par.sr set by drawclip */ + par.mask = mask; + /* par.mr set by drawclip */ + + par.state = 0; + if(src->flags&Frepl){ + par.state |= Replsrc; + if(Dx(src->r)==1 && Dy(src->r)==1){ + par.sval = _pixelbits(src, src->r.min); + par.state |= Simplesrc; + par.srgba = _imgtorgba(src, par.sval); + par.sdval = _rgbatoimg(dst, par.srgba); + if((par.srgba&0xFF) == 0 && (op&DoutS)){ +// if (drawdebug) iprint("fill with transparent source\n"); + return nil; /* no-op successfully handled */ + } + } + } + + if(mask->flags & Frepl){ + par.state |= Replmask; + if(Dx(mask->r)==1 && Dy(mask->r)==1){ + par.mval = _pixelbits(mask, mask->r.min); + if(par.mval == 0 && (op&DoutS)){ +// if(drawdebug) iprint("fill with zero mask\n"); + return nil; /* no-op successfully handled */ + } + par.state |= Simplemask; + if(par.mval == ~0) + par.state |= Fullmask; + par.mrgba = _imgtorgba(mask, par.mval); + } + } + +// if(drawdebug) +// iprint("dr %R sr %R mr %R...", r, par.sr, par.mr); +DBG print("draw dr %R sr %R mr %R %lux\n", r, par.sr, par.mr, par.state); + + return ∥ +} + +void +_memimagedraw(Memdrawparam *par) +{ + if (par == nil) + return; + + /* + * Now that we've clipped the parameters down to be consistent, we + * simply try sub-drawing routines in order until we find one that was able + * to handle us. If the sub-drawing routine returns zero, it means it was + * unable to satisfy the request, so we do not return. + */ + + /* + * Hardware support. Each video driver provides this function, + * which checks to see if there is anything it can help with. + * There could be an if around this checking to see if dst is in video memory. + */ +DBG print("test hwdraw\n"); + if(hwdraw(par)){ +//if(drawdebug) iprint("hw handled\n"); +DBG print("hwdraw handled\n"); + return; + } + /* + * Optimizations using memmove and memset. + */ +DBG print("test memoptdraw\n"); + if(memoptdraw(par)){ +//if(drawdebug) iprint("memopt handled\n"); +DBG print("memopt handled\n"); + return; + } + + /* + * Character drawing. + * Solid source color being painted through a boolean mask onto a high res image. + */ +DBG print("test chardraw\n"); + if(chardraw(par)){ +//if(drawdebug) iprint("chardraw handled\n"); +DBG print("chardraw handled\n"); + return; + } + + /* + * General calculation-laden case that does alpha for each pixel. + */ +DBG print("do alphadraw\n"); + alphadraw(par); +//if(drawdebug) iprint("alphadraw handled\n"); +DBG print("alphadraw handled\n"); +} +#undef DBG + +/* + * Clip the destination rectangle further based on the properties of the + * source and mask rectangles. Once the destination rectangle is properly + * clipped, adjust the source and mask rectangles to be the same size. + * Then if source or mask is replicated, move its clipped rectangle + * so that its minimum point falls within the repl rectangle. + * + * Return zero if the final rectangle is null. + */ +int +drawclip(Memimage *dst, Rectangle *r, Memimage *src, Point *p0, Memimage *mask, Point *p1, Rectangle *sr, Rectangle *mr) +{ + Point rmin, delta; + int splitcoords; + Rectangle omr; + + if(r->min.x>=r->max.x || r->min.y>=r->max.y) + return 0; + splitcoords = (p0->x!=p1->x) || (p0->y!=p1->y); + /* clip to destination */ + rmin = r->min; + if(!rectclip(r, dst->r) || !rectclip(r, dst->clipr)) + return 0; + /* move mask point */ + p1->x += r->min.x-rmin.x; + p1->y += r->min.y-rmin.y; + /* move source point */ + p0->x += r->min.x-rmin.x; + p0->y += r->min.y-rmin.y; + /* map destination rectangle into source */ + sr->min = *p0; + sr->max.x = p0->x+Dx(*r); + sr->max.y = p0->y+Dy(*r); + /* sr is r in source coordinates; clip to source */ + if(!(src->flags&Frepl) && !rectclip(sr, src->r)) + return 0; + if(!rectclip(sr, src->clipr)) + return 0; + /* compute and clip rectangle in mask */ + if(splitcoords){ + /* move mask point with source */ + p1->x += sr->min.x-p0->x; + p1->y += sr->min.y-p0->y; + mr->min = *p1; + mr->max.x = p1->x+Dx(*sr); + mr->max.y = p1->y+Dy(*sr); + omr = *mr; + /* mr is now rectangle in mask; clip it */ + if(!(mask->flags&Frepl) && !rectclip(mr, mask->r)) + return 0; + if(!rectclip(mr, mask->clipr)) + return 0; + /* reflect any clips back to source */ + sr->min.x += mr->min.x-omr.min.x; + sr->min.y += mr->min.y-omr.min.y; + sr->max.x += mr->max.x-omr.max.x; + sr->max.y += mr->max.y-omr.max.y; + *p1 = mr->min; + }else{ + if(!(mask->flags&Frepl) && !rectclip(sr, mask->r)) + return 0; + if(!rectclip(sr, mask->clipr)) + return 0; + *p1 = sr->min; + } + + /* move source clipping back to destination */ + delta.x = r->min.x - p0->x; + delta.y = r->min.y - p0->y; + r->min.x = sr->min.x + delta.x; + r->min.y = sr->min.y + delta.y; + r->max.x = sr->max.x + delta.x; + r->max.y = sr->max.y + delta.y; + + /* move source rectangle so sr->min is in src->r */ + if(src->flags&Frepl) { + delta.x = drawreplxy(src->r.min.x, src->r.max.x, sr->min.x) - sr->min.x; + delta.y = drawreplxy(src->r.min.y, src->r.max.y, sr->min.y) - sr->min.y; + sr->min.x += delta.x; + sr->min.y += delta.y; + sr->max.x += delta.x; + sr->max.y += delta.y; + } + *p0 = sr->min; + + /* move mask point so it is in mask->r */ + *p1 = drawrepl(mask->r, *p1); + mr->min = *p1; + mr->max.x = p1->x+Dx(*sr); + mr->max.y = p1->y+Dy(*sr); + + assert(Dx(*sr) == Dx(*mr) && Dx(*mr) == Dx(*r)); + assert(Dy(*sr) == Dy(*mr) && Dy(*mr) == Dy(*r)); + assert(ptinrect(*p0, src->r)); + assert(ptinrect(*p1, mask->r)); + assert(ptinrect(r->min, dst->r)); + + return 1; +} + +/* + * Conversion tables. + */ +static uchar replbit[1+8][256]; /* replbit[x][y] is the replication of the x-bit quantity y to 8-bit depth */ +static uchar conv18[256][8]; /* conv18[x][y] is the yth pixel in the depth-1 pixel x */ +static uchar conv28[256][4]; /* ... */ +static uchar conv48[256][2]; + +/* + * bitmap of how to replicate n bits to fill 8, for 1 ≤ n ≤ 8. + * the X's are where to put the bottom (ones) bit of the n-bit pattern. + * only the top 8 bits of the result are actually used. + * (the lower 8 bits are needed to get bits in the right place + * when n is not a divisor of 8.) + * + * Should check to see if its easier to just refer to replmul than + * use the precomputed values in replbit. On PCs it may well + * be; on machines with slow multiply instructions it probably isn't. + */ +#define a ((((((((((((((((0 +#define X *2+1) +#define _ *2) +static int replmul[1+8] = { + 0, + a X X X X X X X X X X X X X X X X, + a _ X _ X _ X _ X _ X _ X _ X _ X, + a _ _ X _ _ X _ _ X _ _ X _ _ X _, + a _ _ _ X _ _ _ X _ _ _ X _ _ _ X, + a _ _ _ _ X _ _ _ _ X _ _ _ _ X _, + a _ _ _ _ _ X _ _ _ _ _ X _ _ _ _, + a _ _ _ _ _ _ X _ _ _ _ _ _ X _ _, + a _ _ _ _ _ _ _ X _ _ _ _ _ _ _ X, +}; +#undef a +#undef X +#undef _ + +static void +mktables(void) +{ + int i, j, mask, sh, small; + + if(tablesbuilt) + return; + + fmtinstall('R', Rfmt); + fmtinstall('P', Pfmt); + tablesbuilt = 1; + + /* bit replication up to 8 bits */ + for(i=0; i<256; i++){ + for(j=0; j<=8; j++){ /* j <= 8 [sic] */ + small = i & ((1<>8; + } + } + + /* bit unpacking up to 8 bits, only powers of 2 */ + for(i=0; i<256; i++){ + for(j=0, sh=7, mask=1; j<8; j++, sh--) + conv18[i][j] = replbit[1][(i>>sh)&mask]; + + for(j=0, sh=6, mask=3; j<4; j++, sh-=2) + conv28[i][j] = replbit[2][(i>>sh)&mask]; + + for(j=0, sh=4, mask=15; j<2; j++, sh-=4) + conv48[i][j] = replbit[4][(i>>sh)&mask]; + } +} + +static uchar ones = 0xff; + +/* + * General alpha drawing case. Can handle anything. + */ +typedef struct Buffer Buffer; +struct Buffer { + /* used by most routines */ + uchar *red; + uchar *grn; + uchar *blu; + uchar *alpha; + uchar *grey; + ulong *rgba; + int delta; /* number of bytes to add to pointer to get next pixel to the right */ + + /* used by boolcalc* for mask data */ + uchar *m; /* ptr to mask data r.min byte; like p->bytermin */ + int mskip; /* no. of left bits to skip in *m */ + uchar *bm; /* ptr to mask data img->r.min byte; like p->bytey0s */ + int bmskip; /* no. of left bits to skip in *bm */ + uchar *em; /* ptr to mask data img->r.max.x byte; like p->bytey0e */ + int emskip; /* no. of right bits to skip in *em */ +}; + +typedef struct Param Param; +typedef Buffer Readfn(Param*, uchar*, int); +typedef void Writefn(Param*, uchar*, Buffer); +typedef Buffer Calcfn(Buffer, Buffer, Buffer, int, int, int); + +enum { + MAXBCACHE = 16 +}; + +/* giant rathole to customize functions with */ +struct Param { + Readfn *replcall; + Readfn *greymaskcall; + Readfn *convreadcall; + Writefn *convwritecall; + + Memimage *img; + Rectangle r; + int dx; /* of r */ + int needbuf; + int convgrey; + int alphaonly; + + uchar *bytey0s; /* byteaddr(Pt(img->r.min.x, img->r.min.y)) */ + uchar *bytermin; /* byteaddr(Pt(r.min.x, img->r.min.y)) */ + uchar *bytey0e; /* byteaddr(Pt(img->r.max.x, img->r.min.y)) */ + int bwidth; + + int replcache; /* if set, cache buffers */ + Buffer bcache[MAXBCACHE]; + ulong bfilled; + uchar *bufbase; + int bufoff; + int bufdelta; + + int dir; + + int convbufoff; + uchar *convbuf; + Param *convdpar; + int convdx; +}; + +static uchar *drawbuf; +static int ndrawbuf; +static int mdrawbuf; +static Param spar, mpar, dpar; /* easier on the stacks */ +static Readfn greymaskread, replread, readptr; +static Writefn nullwrite; +static Calcfn alphacalc0, alphacalc14, alphacalc2810, alphacalc3679, alphacalc5, alphacalc11, alphacalcS; +static Calcfn boolcalc14, boolcalc236789, boolcalc1011; + +static Readfn* readfn(Memimage*); +static Readfn* readalphafn(Memimage*); +static Writefn* writefn(Memimage*); + +static Calcfn* boolcopyfn(Memimage*, Memimage*); +static Readfn* convfn(Memimage*, Param*, Memimage*, Param*); +static Readfn* ptrfn(Memimage*); + +static Calcfn *alphacalc[Ncomp] = +{ + alphacalc0, /* Clear */ + alphacalc14, /* DoutS */ + alphacalc2810, /* SoutD */ + alphacalc3679, /* DxorS */ + alphacalc14, /* DinS */ + alphacalc5, /* D */ + alphacalc3679, /* DatopS */ + alphacalc3679, /* DoverS */ + alphacalc2810, /* SinD */ + alphacalc3679, /* SatopD */ + alphacalc2810, /* S */ + alphacalc11, /* SoverD */ +}; + +static Calcfn *boolcalc[Ncomp] = +{ + alphacalc0, /* Clear */ + boolcalc14, /* DoutS */ + boolcalc236789, /* SoutD */ + boolcalc236789, /* DxorS */ + boolcalc14, /* DinS */ + alphacalc5, /* D */ + boolcalc236789, /* DatopS */ + boolcalc236789, /* DoverS */ + boolcalc236789, /* SinD */ + boolcalc236789, /* SatopD */ + boolcalc1011, /* S */ + boolcalc1011, /* SoverD */ +}; + +static int +allocdrawbuf(void) +{ + uchar *p; + + if(ndrawbuf > mdrawbuf){ + p = realloc(drawbuf, ndrawbuf); + if(p == nil){ + werrstr("memimagedraw out of memory"); + return -1; + } + drawbuf = p; + mdrawbuf = ndrawbuf; + } + return 0; +} + +static Param +getparam(Memimage *img, Rectangle r, int convgrey, int needbuf) +{ + Param p; + int nbuf; + + memset(&p, 0, sizeof p); + + p.img = img; + p.r = r; + p.dx = Dx(r); + p.needbuf = needbuf; + p.convgrey = convgrey; + + assert(img->r.min.x <= r.min.x && r.min.x < img->r.max.x); + + p.bytey0s = byteaddr(img, Pt(img->r.min.x, img->r.min.y)); + p.bytermin = byteaddr(img, Pt(r.min.x, img->r.min.y)); + p.bytey0e = byteaddr(img, Pt(img->r.max.x, img->r.min.y)); + p.bwidth = sizeof(ulong)*img->width; + + assert(p.bytey0s <= p.bytermin && p.bytermin <= p.bytey0e); + + if(p.r.min.x == p.img->r.min.x) + assert(p.bytermin == p.bytey0s); + + nbuf = 1; + if((img->flags&Frepl) && Dy(img->r) <= MAXBCACHE && Dy(img->r) < Dy(r)){ + p.replcache = 1; + nbuf = Dy(img->r); + } + p.bufdelta = 4*p.dx; + p.bufoff = ndrawbuf; + ndrawbuf += p.bufdelta*nbuf; + + return p; +} + +static void +clipy(Memimage *img, int *y) +{ + int dy; + + dy = Dy(img->r); + if(*y == dy) + *y = 0; + else if(*y == -1) + *y = dy-1; + assert(0 <= *y && *y < dy); +} + +static void +dumpbuf(char *s, Buffer b, int n) +{ + int i; + uchar *p; + + print("%s", s); + for(i=0; ir; + dx = Dx(r); + dy = Dy(r); + + ndrawbuf = 0; + + src = par->src; + mask = par->mask; + dst = par->dst; + sr = par->sr; + mr = par->mr; + op = par->op; + + isgrey = dst->flags&Fgrey; + + /* + * Buffering when src and dst are the same bitmap is sufficient but not + * necessary. There are stronger conditions we could use. We could + * check to see if the rectangles intersect, and if simply moving in the + * correct y direction can avoid the need to buffer. + */ + needbuf = (src->data == dst->data); + + spar = getparam(src, sr, isgrey, needbuf); + dpar = getparam(dst, r, isgrey, needbuf); + mpar = getparam(mask, mr, 0, needbuf); + + dir = (needbuf && byteaddr(dst, r.min) > byteaddr(src, sr.min)) ? -1 : 1; + spar.dir = mpar.dir = dpar.dir = dir; + + /* + * If the mask is purely boolean, we can convert from src to dst format + * when we read src, and then just copy it to dst where the mask tells us to. + * This requires a boolean (1-bit grey) mask and lack of a source alpha channel. + * + * The computation is accomplished by assigning the function pointers as follows: + * rdsrc - read and convert source into dst format in a buffer + * rdmask - convert mask to bytes, set pointer to it + * rddst - fill with pointer to real dst data, but do no reads + * calc - copy src onto dst when mask says to. + * wrdst - do nothing + * This is slightly sleazy, since things aren't doing exactly what their names say, + * but it avoids a fair amount of code duplication to make this a case here + * rather than have a separate booldraw. + */ +//if(drawdebug) iprint("flag %lud mchan %lux=?%x dd %d\n", src->flags&Falpha, mask->chan, GREY1, dst->depth); + if(!(src->flags&Falpha) && mask->chan == GREY1 && dst->depth >= 8 && op == SoverD){ +//if(drawdebug) iprint("boolcopy..."); + rdsrc = convfn(dst, &dpar, src, &spar); + rddst = readptr; + rdmask = readfn(mask); + calc = boolcopyfn(dst, mask); + wrdst = nullwrite; + }else{ + /* usual alphadraw parameter fetching */ + rdsrc = readfn(src); + rddst = readfn(dst); + wrdst = writefn(dst); + calc = alphacalc[op]; + + /* + * If there is no alpha channel, we'll ask for a grey channel + * and pretend it is the alpha. + */ + if(mask->flags&Falpha){ + rdmask = readalphafn(mask); + mpar.alphaonly = 1; + }else{ + mpar.greymaskcall = readfn(mask); + mpar.convgrey = 1; + rdmask = greymaskread; + + /* + * Should really be above, but then boolcopyfns would have + * to deal with bit alignment, and I haven't written that. + * + * This is a common case for things like ellipse drawing. + * When there's no alpha involved and the mask is boolean, + * we can avoid all the division and multiplication. + */ + if(mask->chan == GREY1 && !(src->flags&Falpha)) + calc = boolcalc[op]; + else if(op == SoverD && !(src->flags&Falpha)) + calc = alphacalcS; + } + } + + /* + * If the image has a small enough repl rectangle, + * we can just read each line once and cache them. + */ + if(spar.replcache){ + spar.replcall = rdsrc; + rdsrc = replread; + } + if(mpar.replcache){ + mpar.replcall = rdmask; + rdmask = replread; + } + + if(allocdrawbuf() < 0) + return 0; + + /* + * Before we were saving only offsets from drawbuf in the parameter + * structures; now that drawbuf has been grown to accomodate us, + * we can fill in the pointers. + */ + spar.bufbase = drawbuf+spar.bufoff; + mpar.bufbase = drawbuf+mpar.bufoff; + dpar.bufbase = drawbuf+dpar.bufoff; + spar.convbuf = drawbuf+spar.convbufoff; + + if(dir == 1){ + starty = 0; + endy = dy; + }else{ + starty = dy-1; + endy = -1; + } + + /* + * srcy, masky, and dsty are offsets from the top of their + * respective Rectangles. they need to be contained within + * the rectangles, so clipy can keep them there without division. + */ + srcy = (starty + sr.min.y - src->r.min.y)%Dy(src->r); + masky = (starty + mr.min.y - mask->r.min.y)%Dy(mask->r); + dsty = starty + r.min.y - dst->r.min.y; + + assert(0 <= srcy && srcy < Dy(src->r)); + assert(0 <= masky && masky < Dy(mask->r)); + assert(0 <= dsty && dsty < Dy(dst->r)); + + for(y=starty; y!=endy; y+=dir, srcy+=dir, masky+=dir, dsty+=dir){ + clipy(src, &srcy); + clipy(dst, &dsty); + clipy(mask, &masky); + + bsrc = rdsrc(&spar, spar.bufbase, srcy); +DBG print("["); + bmask = rdmask(&mpar, mpar.bufbase, masky); +DBG print("]\n"); + bdst = rddst(&dpar, dpar.bufbase, dsty); +DBG dumpbuf("src", bsrc, dx); +DBG dumpbuf("mask", bmask, dx); +DBG dumpbuf("dst", bdst, dx); + bdst = calc(bdst, bsrc, bmask, dx, isgrey, op); + wrdst(&dpar, dpar.bytermin+dsty*dpar.bwidth, bdst); + } + + return 1; +} +#undef DBG + +static Buffer +alphacalc0(Buffer bdst, Buffer b1, Buffer b2, int dx, int grey, int op) +{ + USED(grey); + USED(op); + memset(bdst.rgba, 0, dx*bdst.delta); + return bdst; +} + +static Buffer +alphacalc14(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op) +{ + Buffer obdst; + int fd, sadelta; + int i, sa, ma, q; + ulong s, t; + + obdst = bdst; + sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta; + q = bsrc.delta == 4 && bdst.delta == 4; + + for(i=0; ibcache[y]; + if((p->bfilled & (1<bfilled |= 1<replcall(p, p->bufbase+y*p->bufdelta, y); + } + return *b; +} + +/* + * Alpha reading function that simply relabels the grey pointer. + */ +static Buffer +greymaskread(Param *p, uchar *buf, int y) +{ + Buffer b; + + b = p->greymaskcall(p, buf, y); + b.alpha = b.grey; + return b; +} + +#define DBG if(0) +static Buffer +readnbit(Param *p, uchar *buf, int y) +{ + Buffer b; + Memimage *img; + uchar *repl, *r, *w, *ow, bits; + int i, n, sh, depth, x, dx, npack, nbits; + + b.rgba = (ulong*)buf; + b.grey = w = buf; + b.red = b.blu = b.grn = w; + b.alpha = &ones; + b.delta = 1; + + dx = p->dx; + img = p->img; + depth = img->depth; + repl = &replbit[depth][0]; + npack = 8/depth; + sh = 8-depth; + + /* copy from p->r.min.x until end of repl rectangle */ + x = p->r.min.x; + n = dx; + if(n > p->img->r.max.x - x) + n = p->img->r.max.x - x; + + r = p->bytermin + y*p->bwidth; +DBG print("readnbit dx %d %p=%p+%d*%d, *r=%d fetch %d ", dx, r, p->bytermin, y, p->bwidth, *r, n); + bits = *r++; + nbits = 8; + if(i=x&(npack-1)){ +DBG print("throwaway %d...", i); + bits <<= depth*i; + nbits -= depth*i; + } + for(i=0; i>sh]; +DBG print("bit %x...", repl[bits>>sh]); + bits <<= depth; + nbits -= depth; + } + dx -= n; + if(dx == 0) + return b; + + assert(x+i == p->img->r.max.x); + + /* copy from beginning of repl rectangle until where we were before. */ + x = p->img->r.min.x; + n = dx; + if(n > p->r.min.x - x) + n = p->r.min.x - x; + + r = p->bytey0s + y*p->bwidth; +DBG print("x=%d r=%p...", x, r); + bits = *r++; + nbits = 8; + if(i=x&(npack-1)){ + bits <<= depth*i; + nbits -= depth*i; + } +DBG print("nbits=%d...", nbits); + for(i=0; i>sh]; +DBG print("bit %x...", repl[bits>>sh]); + bits <<= depth; + nbits -= depth; +DBG print("bits %x nbits %d...", bits, nbits); + } + dx -= n; + if(dx == 0) + return b; + + assert(dx > 0); + /* now we have exactly one full scan line: just replicate the buffer itself until we are done */ + ow = buf; + while(dx--) + *w++ = *ow++; + + return b; +} +#undef DBG + +#define DBG if(0) +static void +writenbit(Param *p, uchar *w, Buffer src) +{ + uchar *r; + ulong bits; + int i, sh, depth, npack, nbits, x, ex; + + assert(src.grey != nil && src.delta == 1); + + x = p->r.min.x; + ex = x+p->dx; + depth = p->img->depth; + npack = 8/depth; + + i=x&(npack-1); + bits = i ? (*w >> (8-depth*i)) : 0; + nbits = depth*i; + sh = 8-depth; + r = src.grey; + + for(; x> sh); + nbits += depth; + if(nbits == 8){ + *w++ = bits; + nbits = 0; + } + } + + if(nbits){ + sh = 8-nbits; + bits <<= sh; + bits |= *w & ((1<bytey0s + y*p->bwidth; + r = p->bytermin + y*p->bwidth; + end = p->bytey0e + y*p->bwidth; + cmap = p->img->cmap->cmap2rgb; + convgrey = p->convgrey; + copyalpha = (p->img->flags&Falpha) ? 1 : 0; + + w = buf; + dx = p->dx; + if(copyalpha){ + b.alpha = buf++; + a = p->img->shift[CAlpha]/8; + m = p->img->shift[CMap]/8; + for(i=0; iimg->cmap->rgb2cmap; + + delta = src.delta; + red= src.red; + grn = src.grn; + blu = src.blu; + + dx = p->dx; + for(i=0; i>4)*256+(*grn>>4)*16+(*blu>>4)]; +} + +#define DBG if(0) +static Buffer +readbyte(Param *p, uchar *buf, int y) +{ + Buffer b; + Memimage *img; + int dx, isgrey, convgrey, alphaonly, copyalpha, i, nb; + uchar *begin, *end, *r, *w, *rrepl, *grepl, *brepl, *arepl, *krepl; + uchar ured, ugrn, ublu; + ulong u; + + img = p->img; + begin = p->bytey0s + y*p->bwidth; + r = p->bytermin + y*p->bwidth; + end = p->bytey0e + y*p->bwidth; + + w = buf; + dx = p->dx; + nb = img->depth/8; + + convgrey = p->convgrey; /* convert rgb to grey */ + isgrey = img->flags&Fgrey; + alphaonly = p->alphaonly; + copyalpha = (img->flags&Falpha) ? 1 : 0; + +DBG print("copyalpha %d alphaonly %d convgrey %d isgrey %d\n", copyalpha, alphaonly, convgrey, isgrey); + /* if we can, avoid processing everything */ + if(!(img->flags&Frepl) && !convgrey && (img->flags&Fbytes)){ + memset(&b, 0, sizeof b); + if(p->needbuf){ + memmove(buf, r, dx*nb); + r = buf; + } + b.rgba = (ulong*)r; + if(copyalpha) + b.alpha = r+img->shift[CAlpha]/8; + else + b.alpha = &ones; + if(isgrey){ + b.grey = r+img->shift[CGrey]/8; + b.red = b.grn = b.blu = b.grey; + }else{ + b.red = r+img->shift[CRed]/8; + b.grn = r+img->shift[CGreen]/8; + b.blu = r+img->shift[CBlue]/8; + } + b.delta = nb; + return b; + } + +DBG print("2\n"); + rrepl = replbit[img->nbits[CRed]]; + grepl = replbit[img->nbits[CGreen]]; + brepl = replbit[img->nbits[CBlue]]; + arepl = replbit[img->nbits[CAlpha]]; + krepl = replbit[img->nbits[CGrey]]; + + for(i=0; i>img->shift[CAlpha]) & img->mask[CAlpha]]; +DBG print("a %x\n", w[-1]); + } + + if(isgrey) + *w++ = krepl[(u >> img->shift[CGrey]) & img->mask[CGrey]]; + else if(!alphaonly){ + ured = rrepl[(u >> img->shift[CRed]) & img->mask[CRed]]; + ugrn = grepl[(u >> img->shift[CGreen]) & img->mask[CGreen]]; + ublu = brepl[(u >> img->shift[CBlue]) & img->mask[CBlue]]; + if(convgrey){ +DBG print("g %x %x %x\n", ured, ugrn, ublu); + *w++ = RGB2K(ured, ugrn, ublu); +DBG print("%x\n", w[-1]); + }else{ + *w++ = brepl[(u >> img->shift[CBlue]) & img->mask[CBlue]]; + *w++ = grepl[(u >> img->shift[CGreen]) & img->mask[CGreen]]; + *w++ = rrepl[(u >> img->shift[CRed]) & img->mask[CRed]]; + } + } + r += nb; + if(r == end) + r = begin; + } + + b.alpha = copyalpha ? buf : &ones; + b.rgba = (ulong*)buf; + if(alphaonly){ + b.red = b.grn = b.blu = b.grey = nil; + if(!copyalpha) + b.rgba = nil; + b.delta = 1; + }else if(isgrey || convgrey){ + b.grey = buf+copyalpha; + b.red = b.grn = b.blu = buf+copyalpha; + b.delta = copyalpha+1; +DBG print("alpha %x grey %x\n", b.alpha ? *b.alpha : 0xFF, *b.grey); + }else{ + b.blu = buf+copyalpha; + b.grn = buf+copyalpha+1; + b.grey = nil; + b.red = buf+copyalpha+2; + b.delta = copyalpha+3; + } + return b; +} +#undef DBG + +#define DBG if(0) +static void +writebyte(Param *p, uchar *w, Buffer src) +{ + Memimage *img; + int i, isalpha, isgrey, nb, delta, dx, adelta; + uchar ff, *red, *grn, *blu, *grey, *alpha; + ulong u, mask; + + img = p->img; + + red = src.red; + grn = src.grn; + blu = src.blu; + alpha = src.alpha; + delta = src.delta; + grey = src.grey; + dx = p->dx; + + nb = img->depth/8; + mask = (nb==4) ? 0 : ~((1<depth)-1); + + isalpha = img->flags&Falpha; + isgrey = img->flags&Fgrey; + adelta = src.delta; + + if(isalpha && (alpha == nil || alpha == &ones)){ + ff = 0xFF; + alpha = &ff; + adelta = 0; + } + + for(i=0; i> (8-img->nbits[CGrey])) & img->mask[CGrey]) << img->shift[CGrey]; +DBG print("|grey %.8lux...", u); + grey += delta; + }else{ + u |= ((*red >> (8-img->nbits[CRed])) & img->mask[CRed]) << img->shift[CRed]; + u |= ((*grn >> (8-img->nbits[CGreen])) & img->mask[CGreen]) << img->shift[CGreen]; + u |= ((*blu >> (8-img->nbits[CBlue])) & img->mask[CBlue]) << img->shift[CBlue]; + red += delta; + grn += delta; + blu += delta; +DBG print("|rgb %.8lux...", u); + } + + if(isalpha){ + u |= ((*alpha >> (8-img->nbits[CAlpha])) & img->mask[CAlpha]) << img->shift[CAlpha]; + alpha += adelta; +DBG print("|alpha %.8lux...", u); + } + + w[0] = u; + w[1] = u>>8; + w[2] = u>>16; + w[3] = u>>24; + w += nb; + } +} +#undef DBG + +static Readfn* +readfn(Memimage *img) +{ + if(img->depth < 8) + return readnbit; + if(img->nbits[CMap] == 8) + return readcmap; + return readbyte; +} + +static Readfn* +readalphafn(Memimage *m) +{ + USED(m); + return readbyte; +} + +static Writefn* +writefn(Memimage *img) +{ + if(img->depth < 8) + return writenbit; + if(img->chan == CMAP8) + return writecmap; + return writebyte; +} + +static void +nullwrite(Param *p, uchar *s, Buffer b) +{ + USED(p); + USED(s); +} + +static Buffer +readptr(Param *p, uchar *s, int y) +{ + Buffer b; + uchar *q; + + USED(s); + q = p->bytermin + y*p->bwidth; + b.red = q; /* ptr to data */ + b.grn = b.blu = b.grey = b.alpha = nil; + b.rgba = (ulong*)q; + b.delta = p->img->depth/8; + return b; +} + +static Buffer +boolmemmove(Buffer bdst, Buffer bsrc, Buffer b1, int dx, int i, int o) +{ + USED(i); + USED(o); + memmove(bdst.red, bsrc.red, dx*bdst.delta); + return bdst; +} + +static Buffer +boolcopy8(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int i, int o) +{ + uchar *m, *r, *w, *ew; + + USED(i); + USED(o); + m = bmask.grey; + w = bdst.red; + r = bsrc.red; + ew = w+dx; + for(; w < ew; w++,r++) + if(*m++) + *w = *r; + return bdst; /* not used */ +} + +static Buffer +boolcopy16(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int i, int o) +{ + uchar *m; + ushort *r, *w, *ew; + + USED(i); + USED(o); + m = bmask.grey; + w = (ushort*)bdst.red; + r = (ushort*)bsrc.red; + ew = w+dx; + for(; w < ew; w++,r++) + if(*m++) + *w = *r; + return bdst; /* not used */ +} + +static Buffer +boolcopy24(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int i, int o) +{ + uchar *m; + uchar *r, *w, *ew; + + USED(i); + USED(o); + m = bmask.grey; + w = bdst.red; + r = bsrc.red; + ew = w+dx*3; + while(w < ew){ + if(*m++){ + *w++ = *r++; + *w++ = *r++; + *w++ = *r++; + }else{ + w += 3; + r += 3; + } + } + return bdst; /* not used */ +} + +static Buffer +boolcopy32(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int i, int o) +{ + uchar *m; + ulong *r, *w, *ew; + + USED(i); + USED(o); + m = bmask.grey; + w = (ulong*)bdst.red; + r = (ulong*)bsrc.red; + ew = w+dx; + for(; w < ew; w++,r++) + if(*m++) + *w = *r; + return bdst; /* not used */ +} + +static Buffer +genconv(Param *p, uchar *buf, int y) +{ + Buffer b; + int nb; + uchar *r, *w, *ew; + + /* read from source into RGB format in convbuf */ + b = p->convreadcall(p, p->convbuf, y); + + /* write RGB format into dst format in buf */ + p->convwritecall(p->convdpar, buf, b); + + if(p->convdx){ + nb = p->convdpar->img->depth/8; + r = buf; + w = buf+nb*p->dx; + ew = buf+nb*p->convdx; + while(wchan == src->chan && !(src->flags&Frepl)){ +//if(drawdebug) iprint("readptr..."); + return readptr; + } + + if(dst->chan==CMAP8 && (src->chan==GREY1||src->chan==GREY2||src->chan==GREY4)){ + /* cheat because we know the replicated value is exactly the color map entry. */ +//if(drawdebug) iprint("Readnbit..."); + return readnbit; + } + + spar->convreadcall = readfn(src); + spar->convwritecall = writefn(dst); + spar->convdpar = dpar; + + /* allocate a conversion buffer */ + spar->convbufoff = ndrawbuf; + ndrawbuf += spar->dx*4; + + if(spar->dx > Dx(spar->img->r)){ + spar->convdx = spar->dx; + spar->dx = Dx(spar->img->r); + } + +//if(drawdebug) iprint("genconv..."); + return genconv; +} + +ulong +_pixelbits(Memimage *i, Point pt) +{ + uchar *p; + ulong val; + int off, bpp, npack; + + val = 0; + p = byteaddr(i, pt); + switch(bpp=i->depth){ + case 1: + case 2: + case 4: + npack = 8/bpp; + off = pt.x%npack; + val = p[0] >> bpp*(npack-1-off); + val &= (1<flags&Frepl && Dx(mask->r)==1 && Dy(mask->r)==1 && pixelbits(mask, mask->r.min)==~0) + return boolmemmove; + + switch(img->depth){ + case 8: + return boolcopy8; + case 16: + return boolcopy16; + case 24: + return boolcopy24; + case 32: + return boolcopy32; + default: + assert(0 /* boolcopyfn */); + } + return nil; +} + +/* + * Optimized draw for filling and scrolling; uses memset and memmove. + */ +static void +memsetb(void *vp, uchar val, int n) +{ + uchar *p, *ep; + + p = vp; + ep = p+n; + while(p>8; + c = val>>16; + while(pchan; chan; chan>>=8){ + nb = NBITS(chan); + ov = v = val&((1<>= nb; + + while(nb < 8){ + v |= v<>= (nb-8); + + switch(TYPE(chan)){ + case CRed: + r = v; + break; + case CGreen: + g = v; + break; + case CBlue: + b = v; + break; + case CAlpha: + a = v; + break; + case CGrey: + r = g = b = v; + break; + case CMap: + p = img->cmap->cmap2rgb+3*ov; + r = *p++; + g = *p++; + b = *p; + break; + } + } + return (r<<24)|(g<<16)|(b<<8)|a; +} + +ulong +_rgbatoimg(Memimage *img, ulong rgba) +{ + ulong chan; + int d, nb; + ulong v; + uchar *p, r, g, b, a, m; + + v = 0; + r = rgba>>24; + g = rgba>>16; + b = rgba>>8; + a = rgba; + d = 0; + for(chan=img->chan; chan; chan>>=8){ + nb = NBITS(chan); + switch(TYPE(chan)){ + case CRed: + v |= (r>>(8-nb))<>(8-nb))<>(8-nb))<>(8-nb))<cmap->rgb2cmap; + m = p[(r>>4)*256+(g>>4)*16+(b>>4)]; + v |= (m>>(8-nb))<>(8-nb))<r); + dy = Dy(par->r); + src = par->src; + dst = par->dst; + op = par->op; + +DBG print("state %lux mval %lux dd %d\n", par->state, par->mval, dst->depth); + /* + * If we have an opaque mask and source is one opaque pixel we can convert to the + * destination format and just replicate with memset. + */ + m = Simplesrc|Simplemask|Fullmask; + if((par->state&m)==m && (par->srgba&0xFF) == 0xFF && (op ==S || op == SoverD)){ + uchar *dp, p[4]; + int d, dwid, ppb, np, nb; + uchar lm, rm; + +DBG print("memopt, dst %p, dst->data->bdata %p\n", dst, dst->data->bdata); + dwid = dst->width*sizeof(ulong); + dp = byteaddr(dst, par->r.min); + v = par->sdval; +DBG print("sdval %lud, depth %d\n", v, dst->depth); + switch(dst->depth){ + case 1: + case 2: + case 4: + for(d=dst->depth; d<8; d*=2) + v |= (v<depth; /* pixels per byte */ + m = ppb-1; + /* left edge */ + np = par->r.min.x&m; /* no. pixels unused on left side of word */ + dx -= (ppb-np); + nb = 8 - np * dst->depth; /* no. bits used on right side of word */ + lm = (1<r.min.x, nb, lm, ppb, m); + + /* right edge */ + np = par->r.max.x&m; /* no. pixels used on left side of word */ + dx -= np; + nb = 8 - np * dst->depth; /* no. bits unused on right side of word */ + rm = ~((1<r.max.x, nb, rm, ppb, m); + +DBG print("dx %d Dx %d\n", dx, Dx(par->r)); + /* lm, rm are masks that are 1 where we should touch the bits */ + if(dx < 0){ /* just one byte */ + lm &= rm; + for(y=0; y>8; + v = *(ushort*)p; +DBG print("dp=%p; dx=%d; for(y=0; y<%d; y++, dp+=%d)\nmemsets(dp, v, dx);\n", + dp, dx, dy, dwid); + for(y=0; y>8; + p[2] = v>>16; + p[3] = v>>24; + v = *(ulong*)p; + for(y=0; ystate&(m|Replsrc))==m && src->depth >= 8 + && src->chan == dst->chan && !(src->flags&Falpha) && (op == S || op == SoverD)){ + uchar *sp, *dp; + long swid, dwid, nb; + int dir; + + if(src->data == dst->data && byteaddr(dst, par->r.min) > byteaddr(src, par->sr.min)) + dir = -1; + else + dir = 1; + + swid = src->width*sizeof(ulong); + dwid = dst->width*sizeof(ulong); + sp = byteaddr(src, par->sr.min); + dp = byteaddr(dst, par->r.min); + if(dir == -1){ + sp += (dy-1)*swid; + dp += (dy-1)*dwid; + swid = -swid; + dwid = -dwid; + } + nb = (dx*src->depth)/8; + for(y=0; ystate&(Simplemask|Simplesrc|Replmask|Replsrc))==0 + && dst->chan==GREY1 && src->chan==GREY1 && par->mask->chan==GREY1 + && (par->r.min.x&7)==(par->sr.min.x&7) && (par->r.min.x&7)==(par->mr.min.x&7)){ + uchar *sp, *dp, *mp; + uchar lm, rm; + long swid, dwid, mwid; + int i, x, dir; + + sp = byteaddr(src, par->sr.min); + dp = byteaddr(dst, par->r.min); + mp = byteaddr(par->mask, par->mr.min); + swid = src->width*sizeof(ulong); + dwid = dst->width*sizeof(ulong); + mwid = par->mask->width*sizeof(ulong); + + if(src->data == dst->data && byteaddr(dst, par->r.min) > byteaddr(src, par->sr.min)){ + dir = -1; + }else + dir = 1; + + lm = 0xFF>>(par->r.min.x&7); + rm = 0xFF<<(8-(par->r.max.x&7)); + dx -= (8-(par->r.min.x&7)) + (par->r.max.x&7); + + if(dx < 0){ /* one byte wide */ + lm &= rm; + if(dir == -1){ + dp += dwid*(dy-1); + sp += swid*(dy-1); + mp += mwid*(dy-1); + dwid = -dwid; + swid = -swid; + mwid = -mwid; + } + for(y=0; ymask->flags, par->mask->depth, par->src->flags, + Dx(par->src->r), Dy(par->src->r), par->dst->depth, par->dst->data, par->src->data); + + mask = par->mask; + src = par->src; + dst = par->dst; + r = par->r; + mr = par->mr; + op = par->op; + + if((par->state&(Replsrc|Simplesrc|Replmask)) != (Replsrc|Simplesrc) + || mask->depth != 1 || src->flags&Falpha || dst->depth<8 || dst->data==src->data + || op != SoverD) + return 0; + +//if(drawdebug) iprint("chardraw..."); + + depth = mask->depth; + maskwid = mask->width*sizeof(ulong); + rp = byteaddr(mask, mr.min); + npack = 8/depth; + bsh = (mr.min.x % npack) * depth; + + wp = byteaddr(dst, r.min); + dstwid = dst->width*sizeof(ulong); +DBG print("bsh %d\n", bsh); + dy = Dy(r); + dx = Dx(r); + + ddepth = dst->depth; + + /* + * for loop counts from bsh to bsh+dx + * + * we want the bottom bits to be the amount + * to shift the pixels down, so for n≡0 (mod 8) we want + * bottom bits 7. for n≡1, 6, etc. + * the bits come from -n-1. + */ + + bx = -bsh-1; + ex = -bsh-1-dx; + SET(bits); + v = par->sdval; + + /* make little endian */ + sp[0] = v; + sp[1] = v>>8; + sp[2] = v>>16; + sp[3] = v>>24; + +//print("sp %x %x %x %x\n", sp[0], sp[1], sp[2], sp[3]); + for(y=0; yex; x--, wc++){ + i = x&7; + if(i == 8-1) + bits = *q++; +DBG print("bits %lux sh %d...", bits, i); + if((bits>>i)&1) + *wc = v; + } + break; + case 16: + ws = (ushort*)wp; + v = *(ushort*)sp; + for(x=bx; x>ex; x--, ws++){ + i = x&7; + if(i == 8-1) + bits = *q++; +DBG print("bits %lux sh %d...", bits, i); + if((bits>>i)&1) + *ws = v; + } + break; + case 24: + wc = wp; + for(x=bx; x>ex; x--, wc+=3){ + i = x&7; + if(i == 8-1) + bits = *q++; +DBG print("bits %lux sh %d...", bits, i); + if((bits>>i)&1){ + wc[0] = sp[0]; + wc[1] = sp[1]; + wc[2] = sp[2]; + } + } + break; + case 32: + wl = (ulong*)wp; + v = *(ulong*)sp; + for(x=bx; x>ex; x--, wl++){ + i = x&7; + if(i == 8-1) + bits = *q++; +DBG iprint("bits %lux sh %d...", bits, i); + if((bits>>i)&1) + *wl = v; + } + break; + } + } + +DBG print("\n"); + return 1; +} +#undef DBG + + +/* + * Fill entire byte with replicated (if necessary) copy of source pixel, + * assuming destination ldepth is >= source ldepth. + * + * This code is just plain wrong for >8bpp. + * +ulong +membyteval(Memimage *src) +{ + int i, val, bpp; + uchar uc; + + unloadmemimage(src, src->r, &uc, 1); + bpp = src->depth; + uc <<= (src->r.min.x&(7/src->depth))*src->depth; + uc &= ~(0xFF>>bpp); + /* pixel value is now in high part of byte. repeat throughout byte + val = uc; + for(i=bpp; i<8; i<<=1) + val |= val>>i; + return val; +} + * + */ + +void +_memfillcolor(Memimage *i, ulong val) +{ + ulong bits; + int d, y; + uchar p[4]; + + if(val == DNofill) + return; + + bits = _rgbatoimg(i, val); + switch(i->depth){ + case 24: /* 24-bit images suck */ + for(y=i->r.min.y; yr.max.y; y++) + memset24(byteaddr(i, Pt(i->r.min.x, y)), bits, Dx(i->r)); + break; + default: /* 1, 2, 4, 8, 16, 32 */ + for(d=i->depth; d<32; d*=2) + bits = (bits << d) | bits; + p[0] = bits; /* make little endian */ + p[1] = bits>>8; + p[2] = bits>>16; + p[3] = bits>>24; + bits = *(ulong*)p; + memsetl(wordaddr(i, i->r.min), bits, i->width*Dy(i->r)); + break; + } +} + diff --git a/libmemdraw/drawtest.c b/libmemdraw/drawtest.c new file mode 100644 index 0000000..d19e118 --- /dev/null +++ b/libmemdraw/drawtest.c @@ -0,0 +1,1004 @@ +#include +#include +#include +#include +#include + +#define DBG if(0) +#define RGB2K(r,g,b) ((299*((ulong)(r))+587*((ulong)(g))+114*((ulong)(b)))/1000) + +/* + * This program tests the 'memimagedraw' primitive stochastically. + * It tests the combination aspects of it thoroughly, but since the + * three images it uses are disjoint, it makes no check of the + * correct behavior when images overlap. That is, however, much + * easier to get right and to test. + */ + +void drawonepixel(Memimage*, Point, Memimage*, Point, Memimage*, Point); +void verifyone(void); +void verifyline(void); +void verifyrect(void); +void verifyrectrepl(int, int); +void putpixel(Memimage *img, Point pt, ulong nv); +ulong rgbatopix(uchar, uchar, uchar, uchar); + +char *dchan, *schan, *mchan; +int dbpp, sbpp, mbpp; + +int drawdebug=0; +int seed; +int niters = 100; +int dbpp; /* bits per pixel in destination */ +int sbpp; /* bits per pixel in src */ +int mbpp; /* bits per pixel in mask */ +int dpm; /* pixel mask at high part of byte, in destination */ +int nbytes; /* in destination */ + +int Xrange = 64; +int Yrange = 8; + +Memimage *dst; +Memimage *src; +Memimage *mask; +Memimage *stmp; +Memimage *mtmp; +Memimage *ones; +uchar *dstbits; +uchar *srcbits; +uchar *maskbits; +ulong *savedstbits; + +void +rdb(void) +{ +} + +int +iprint(char *fmt, ...) +{ + int n; + va_list va; + char buf[1024]; + + va_start(va, fmt); + n = doprint(buf, buf+sizeof buf, fmt, va) - buf; + va_end(va); + + write(1,buf,n); + return 1; +} + +void +main(int argc, char *argv[]) +{ + memimageinit(); + seed = time(0); + + ARGBEGIN{ + case 'x': + Xrange = atoi(ARGF()); + break; + case 'y': + Yrange = atoi(ARGF()); + break; + case 'n': + niters = atoi(ARGF()); + break; + case 's': + seed = atoi(ARGF()); + break; + }ARGEND + + dchan = "r8g8b8"; + schan = "r8g8b8"; + mchan = "r8g8b8"; + switch(argc){ + case 3: mchan = argv[2]; + case 2: schan = argv[1]; + case 1: dchan = argv[0]; + case 0: break; + default: goto Usage; + Usage: + fprint(2, "usage: dtest [dchan [schan [mchan]]]\n"); + exits("usage"); + } + + fmtinstall('b', numbconv); /* binary! */ + + fprint(2, "%s -x %d -y %d -s 0x%x %s %s %s\n", argv0, Xrange, Yrange, seed, dchan, schan, mchan); + srand(seed); + + dst = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(dchan)); + src = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(schan)); + mask = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan)); + stmp = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(schan)); + mtmp = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan)); + ones = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan)); +// print("chan %lux %lux %lux %lux %lux %lux\n", dst->chan, src->chan, mask->chan, stmp->chan, mtmp->chan, ones->chan); + if(dst==0 || src==0 || mask==0 || mtmp==0 || ones==0) { + Alloc: + fprint(2, "dtest: allocation failed: %r\n"); + exits("alloc"); + } + nbytes = (4*Xrange+4)*Yrange; + srcbits = malloc(nbytes); + dstbits = malloc(nbytes); + maskbits = malloc(nbytes); + savedstbits = malloc(nbytes); + if(dstbits==0 || srcbits==0 || maskbits==0 || savedstbits==0) + goto Alloc; + dbpp = dst->depth; + sbpp = src->depth; + mbpp = mask->depth; + dpm = 0xFF ^ (0xFF>>dbpp); + memset(ones->data->bdata, 0xFF, ones->width*sizeof(ulong)*Yrange); + + + fprint(2, "dtest: verify single pixel operation\n"); + verifyone(); + + fprint(2, "dtest: verify full line non-replicated\n"); + verifyline(); + + fprint(2, "dtest: verify full rectangle non-replicated\n"); + verifyrect(); + + fprint(2, "dtest: verify full rectangle source replicated\n"); + verifyrectrepl(1, 0); + + fprint(2, "dtest: verify full rectangle mask replicated\n"); + verifyrectrepl(0, 1); + + fprint(2, "dtest: verify full rectangle source and mask replicated\n"); + verifyrectrepl(1, 1); + + exits(0); +} + +/* + * Dump out an ASCII representation of an image. The label specifies + * a list of characters to put at various points in the picture. + */ +static void +Bprintr5g6b5(Biobuf *bio, char*, ulong v) +{ + int r,g,b; + r = (v>>11)&31; + g = (v>>5)&63; + b = v&31; + Bprint(bio, "%.2x%.2x%.2x", r,g,b); +} + +static void +Bprintr5g5b5a1(Biobuf *bio, char*, ulong v) +{ + int r,g,b,a; + r = (v>>11)&31; + g = (v>>6)&31; + b = (v>>1)&31; + a = v&1; + Bprint(bio, "%.2x%.2x%.2x%.2x", r,g,b,a); +} + +void +dumpimage(char *name, Memimage *img, void *vdata, Point labelpt) +{ + Biobuf b; + uchar *data; + uchar *p; + char *arg; + void (*fmt)(Biobuf*, char*, ulong); + int npr, x, y, nb, bpp; + ulong v, mask; + Rectangle r; + + fmt = nil; + arg = nil; + switch(img->depth){ + case 1: + case 2: + case 4: + fmt = (void(*)(Biobuf*,char*,ulong))Bprint; + arg = "%.1ux"; + break; + case 8: + fmt = (void(*)(Biobuf*,char*,ulong))Bprint; + arg = "%.2ux"; + break; + case 16: + arg = nil; + if(img->chan == RGB16) + fmt = Bprintr5g6b5; + else{ + fmt = (void(*)(Biobuf*,char*,ulong))Bprint; + arg = "%.4ux"; + } + break; + case 24: + fmt = (void(*)(Biobuf*,char*,ulong))Bprint; + arg = "%.6lux"; + break; + case 32: + fmt = (void(*)(Biobuf*,char*,ulong))Bprint; + arg = "%.8lux"; + break; + } + if(fmt == nil){ + fprint(2, "bad format\n"); + abort(); + } + + r = img->r; + Binit(&b, 2, OWRITE); + data = vdata; + bpp = img->depth; + Bprint(&b, "%s\t%d\tr %R clipr %R repl %d data %p *%P\n", name, r.min.x, r, img->clipr, (img->flags&Frepl) ? 1 : 0, vdata, labelpt); + mask = (1ULL<data->bdata); + Bprint(&b, "%-4d\t", y); +// for(x=r.min.x; x>nb)&mask); + } + Bprint(&b, "\n"); + } + Bterm(&b); +} + +/* + * Verify that the destination pixel has the specified value. + * The value is in the high bits of v, suitably masked, but must + * be extracted from the destination Memimage. + */ +void +checkone(Point p, Point sp, Point mp) +{ + int delta; + uchar *dp, *sdp; + + delta = (uchar*)byteaddr(dst, p)-(uchar*)dst->data->bdata; + dp = (uchar*)dst->data->bdata+delta; + sdp = (uchar*)savedstbits+delta; + + if(memcmp(dp, sdp, (dst->depth+7)/8) != 0) { + fprint(2, "dtest: one bad pixel drawing at dst %P from source %P mask %P\n", p, sp, mp); + fprint(2, " %.2ux %.2ux %.2ux %.2ux should be %.2ux %.2ux %.2ux %.2ux\n", + dp[0], dp[1], dp[2], dp[3], sdp[0], sdp[1], sdp[2], sdp[3]); + fprint(2, "addresses dst %p src %p mask %p\n", dp, byteaddr(src, sp), byteaddr(mask, mp)); + dumpimage("src", src, src->data->bdata, sp); + dumpimage("mask", mask, mask->data->bdata, mp); + dumpimage("origdst", dst, dstbits, p); + dumpimage("dst", dst, dst->data->bdata, p); + dumpimage("gooddst", dst, savedstbits, p); + abort(); + } +} + +/* + * Verify that the destination line has the same value as the saved line. + */ +#define RECTPTS(r) (r).min.x, (r).min.y, (r).max.x, (r).max.y +void +checkline(Rectangle r, Point sp, Point mp, int y, Memimage *stmp, Memimage *mtmp) +{ + ulong *dp; + int nb; + ulong *saved; + + dp = wordaddr(dst, Pt(0, y)); + saved = savedstbits + y*dst->width; + if(dst->depth < 8) + nb = Xrange/(8/dst->depth); + else + nb = Xrange*(dst->depth/8); + if(memcmp(dp, saved, nb) != 0){ + fprint(2, "dtest: bad line at y=%d; saved %p dp %p\n", y, saved, dp); + fprint(2, "draw dst %R src %P mask %P\n", r, sp, mp); + dumpimage("src", src, src->data->bdata, sp); + if(stmp) dumpimage("stmp", stmp, stmp->data->bdata, sp); + dumpimage("mask", mask, mask->data->bdata, mp); + if(mtmp) dumpimage("mtmp", mtmp, mtmp->data->bdata, mp); + dumpimage("origdst", dst, dstbits, r.min); + dumpimage("dst", dst, dst->data->bdata, r.min); + dumpimage("gooddst", dst, savedstbits, r.min); + abort(); + } +} + +/* + * Fill the bits of an image with random data. + * The Memimage parameter is used only to make sure + * the data is well formatted: only ucbits is written. + */ +void +fill(Memimage *img, uchar *ucbits) +{ + int i, x, y; + ushort *up; + uchar alpha, r, g, b; + void *data; + + if((img->flags&Falpha) == 0){ + up = (ushort*)ucbits; + for(i=0; i> 7; + if(i+i != nbytes) + *(uchar*)up = lrand() >> 7; + }else{ + data = img->data->bdata; + img->data->bdata = ucbits; + + for(x=img->r.min.x; xr.max.x; x++) + for(y=img->r.min.y; yr.max.y; y++){ + alpha = rand() >> 4; + r = rand()%(alpha+1); + g = rand()%(alpha+1); + b = rand()%(alpha+1); + putpixel(img, Pt(x,y), rgbatopix(r,g,b,alpha)); + } + img->data->bdata = data; + } + +} + +/* + * Mask is preset; do the rest + */ +void +verifyonemask(void) +{ + Point dp, sp, mp; + + fill(dst, dstbits); + fill(src, srcbits); + memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange); + memmove(src->data->bdata, srcbits, src->width*sizeof(ulong)*Yrange); + memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange); + + dp.x = nrand(Xrange); + dp.y = nrand(Yrange); + + sp.x = nrand(Xrange); + sp.y = nrand(Yrange); + + mp.x = nrand(Xrange); + mp.y = nrand(Yrange); + + drawonepixel(dst, dp, src, sp, mask, mp); + memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange); + memmove(savedstbits, dst->data->bdata, dst->width*sizeof(ulong)*Yrange); + + memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange); + memimagedraw(dst, Rect(dp.x, dp.y, dp.x+1, dp.y+1), src, sp, mask, mp, SoverD); + memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange); + + checkone(dp, sp, mp); +} + +void +verifyone(void) +{ + int i; + + /* mask all zeros */ + memset(maskbits, 0, nbytes); + for(i=0; idata->bdata, dstbits, dst->width*sizeof(ulong)*Yrange); + memmove(src->data->bdata, srcbits, src->width*sizeof(ulong)*Yrange); + memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange); + + dr.min.x = nrand(Xrange-1); + dr.min.y = nrand(Yrange-1); + dr.max.x = dr.min.x + 1 + nrand(Xrange-1-dr.min.x); + dr.max.y = dr.min.y + 1; + + sp.x = nrand(Xrange); + sp.y = nrand(Yrange); + + mp.x = nrand(Xrange); + mp.y = nrand(Yrange); + + tp = sp; + up = mp; + for(x=dr.min.x; xdata->bdata, dst->width*sizeof(ulong)*Yrange); + + memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange); + + memimagedraw(dst, dr, src, sp, mask, mp, SoverD); + checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), dr.min.y, nil, nil); +} + +void +verifyline(void) +{ + int i; + + /* mask all ones */ + memset(maskbits, 0xFF, nbytes); + for(i=0; idata->bdata, dstbits, dst->width*sizeof(ulong)*Yrange); + memmove(src->data->bdata, srcbits, src->width*sizeof(ulong)*Yrange); + memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange); + + dr.min.x = nrand(Xrange-1); + dr.min.y = nrand(Yrange-1); + dr.max.x = dr.min.x + 1 + nrand(Xrange-1-dr.min.x); + dr.max.y = dr.min.y + 1 + nrand(Yrange-1-dr.min.y); + + sp.x = nrand(Xrange); + sp.y = nrand(Yrange); + + mp.x = nrand(Xrange); + mp.y = nrand(Yrange); + + tp = sp; + up = mp; + for(y=dr.min.y; ydata->bdata, dst->width*sizeof(ulong)*Yrange); + + memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange); + + memimagedraw(dst, dr, src, sp, mask, mp, SoverD); + for(y=0; yr, sp), drawrepl(mask->r, mp), y, nil, nil); +} + +void +verifyrect(void) +{ + int i; + + /* mask all zeros */ + memset(maskbits, 0, nbytes); + for(i=0; ir) */ + r.min.x = nrand(Xrange-1); + r.min.y = nrand(Yrange-1); + /* make it trivial more often than pure chance allows */ + switch(lrand()&0){ + case 1: + r.max.x = r.min.x + 2; + r.max.y = r.min.y + 2; + if(r.max.x < Xrange && r.max.y < Yrange) + break; + /* fall through */ + case 0: + r.max.x = r.min.x + 1; + r.max.y = r.min.y + 1; + break; + default: + if(r.min.x+3 >= Xrange) + r.max.x = Xrange; + else + r.max.x = r.min.x+3 + nrand(Xrange-(r.min.x+3)); + + if(r.min.y+3 >= Yrange) + r.max.y = Yrange; + else + r.max.y = r.min.y+3 + nrand(Yrange-(r.min.y+3)); + } + assert(r.min.x >= 0); + assert(r.max.x <= Xrange); + assert(r.min.y >= 0); + assert(r.max.y <= Yrange); + /* copy from i to tmp so we have just the replicated bits */ + nb = tmp->width*sizeof(ulong)*Yrange; + memset(tmp->data->bdata, 0, nb); + memimagedraw(tmp, r, i, r.min, ones, r.min, SoverD); + memmove(i->data->bdata, tmp->data->bdata, nb); + /* i is now a non-replicated instance of the replication */ + /* replicate it by hand through tmp */ + memset(tmp->data->bdata, 0, nb); + x = -(tilexy(r.min.x, r.max.x, 0)-r.min.x); + for(; xflags |= Frepl; + i->r = r; + i->clipr = randrect(); +// fprint(2, "replicate [[%d %d] [%d %d]] [[%d %d][%d %d]]\n", r.min.x, r.min.y, r.max.x, r.max.y, +// i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y); + tmp->clipr = i->clipr; +} + +/* + * Mask is preset; do the rest + */ +void +verifyrectmaskrepl(int srcrepl, int maskrepl) +{ + Point sp, mp, tp, up; + Rectangle dr; + int x, y; + Memimage *s, *m; + +// print("verfrect %d %d\n", srcrepl, maskrepl); + src->flags &= ~Frepl; + src->r = Rect(0, 0, Xrange, Yrange); + src->clipr = src->r; + stmp->flags &= ~Frepl; + stmp->r = Rect(0, 0, Xrange, Yrange); + stmp->clipr = src->r; + mask->flags &= ~Frepl; + mask->r = Rect(0, 0, Xrange, Yrange); + mask->clipr = mask->r; + mtmp->flags &= ~Frepl; + mtmp->r = Rect(0, 0, Xrange, Yrange); + mtmp->clipr = mask->r; + + fill(dst, dstbits); + fill(src, srcbits); + + memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange); + memmove(src->data->bdata, srcbits, src->width*sizeof(ulong)*Yrange); + memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange); + + if(srcrepl){ + replicate(src, stmp); + s = stmp; + }else + s = src; + if(maskrepl){ + replicate(mask, mtmp); + m = mtmp; + }else + m = mask; + + dr = randrect(); + + sp.x = nrand(Xrange); + sp.y = nrand(Yrange); + + mp.x = nrand(Xrange); + mp.y = nrand(Yrange); + +DBG print("smalldraws\n"); + for(tp.y=sp.y,up.y=mp.y,y=dr.min.y; ydata->bdata, dst->width*sizeof(ulong)*Yrange); + + memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange); + +DBG print("bigdraw\n"); + memimagedraw(dst, dr, src, sp, mask, mp, SoverD); + for(y=0; yr, sp), drawrepl(mask->r, mp), y, srcrepl?stmp:nil, maskrepl?mtmp:nil); +} + +void +verifyrectrepl(int srcrepl, int maskrepl) +{ + int i; + + /* mask all ones */ + memset(maskbits, 0xFF, nbytes); + for(i=0; i>= (nhave-nwant); + return v & ((1<>24; + *r = v>>16; + *g = v>>8; + *b = v; +} + +/* + * Convert uchar channels into ulong pixel. + */ +ulong +rgbatopix(uchar r, uchar g, uchar b, uchar a) +{ + return (a<<24)|(r<<16)|(g<<8)|b; +} + +/* + * Retrieve the pixel value at pt in the image. + */ +ulong +getpixel(Memimage *img, Point pt) +{ + uchar r, g, b, a, *p; + int nbits, npack, bpp; + ulong v, c, rbits, bits; + + r = g = b = 0; + a = ~0; /* default alpha is full */ + + p = byteaddr(img, pt); + v = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24); + bpp = img->depth; + if(bpp<8){ + /* + * Sub-byte greyscale pixels. + * + * We want to throw away the top pt.x%npack pixels and then use the next bpp bits + * in the bottom byte of v. This madness is due to having big endian bits + * but little endian bytes. + */ + npack = 8/bpp; + v >>= 8 - bpp*(pt.x%npack+1); + v &= (1<chan; c; c>>=8){ + nbits = NBITS(c); + bits = v & ((1<>= nbits; + switch(TYPE(c)){ + case CRed: + r = rbits; + break; + case CGreen: + g = rbits; + break; + case CBlue: + b = rbits; + break; + case CGrey: + r = g = b = rbits; + break; + case CAlpha: + a = rbits; + break; + case CMap: + p = img->cmap->cmap2rgb + 3*bits; + r = p[0]; + g = p[1]; + b = p[2]; + break; + case CIgnore: + break; + default: + fprint(2, "unknown channel type %lud\n", TYPE(c)); + abort(); + } + } + } + return rgbatopix(r, g, b, a); +} + +/* + * Return the greyscale equivalent of a pixel. + */ +uchar +getgrey(Memimage *img, Point pt) +{ + uchar r, g, b, a; + pixtorgba(getpixel(img, pt), &r, &g, &b, &a); + return RGB2K(r, g, b); +} + +/* + * Return the value at pt in image, if image is interpreted + * as a mask. This means the alpha channel if present, else + * the greyscale or its computed equivalent. + */ +uchar +getmask(Memimage *img, Point pt) +{ + if(img->flags&Falpha) + return getpixel(img, pt)>>24; + else + return getgrey(img, pt); +} +#undef DBG + +#define DBG if(0) +/* + * Write a pixel to img at point pt. + * + * We do this by reading a 32-bit little endian + * value from p and then writing it back + * after tweaking the appropriate bits. Because + * the data is little endian, we don't have to worry + * about what the actual depth is, as long as it is + * less than 32 bits. + */ +void +putpixel(Memimage *img, Point pt, ulong nv) +{ + uchar r, g, b, a, *p, *q; + ulong c, mask, bits, v; + int bpp, sh, npack, nbits; + + pixtorgba(nv, &r, &g, &b, &a); + + p = byteaddr(img, pt); + v = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24); + bpp = img->depth; +DBG print("v %.8lux...", v); + if(bpp < 8){ + /* + * Sub-byte greyscale pixels. We need to skip the leftmost pt.x%npack pixels, + * which is equivalent to skipping the rightmost npack - pt.x%npack - 1 pixels. + */ + npack = 8/bpp; + sh = bpp*(npack - pt.x%npack - 1); + bits = RGB2K(r,g,b); +DBG print("repl %lux 8 %d = %lux...", bits, bpp, replbits(bits, 8, bpp)); + bits = replbits(bits, 8, bpp); + mask = (1<chan; c; c>>=8){ + nbits = NBITS(c); + switch(TYPE(c)){ + case CRed: + bits = r; + break; + case CGreen: + bits = g; + break; + case CBlue: + bits = b; + break; + case CGrey: + bits = RGB2K(r, g, b); + break; + case CAlpha: + bits = a; + break; + case CIgnore: + bits = 0; + break; + case CMap: + q = img->cmap->rgb2cmap; + bits = q[(r>>4)*16*16+(g>>4)*16+(b>>4)]; + break; + default: + SET(bits); + fprint(2, "unknown channel type %lud\n", TYPE(c)); + abort(); + } + +DBG print("repl %lux 8 %d = %lux...", bits, nbits, replbits(bits, 8, nbits)); + if(TYPE(c) != CMap) + bits = replbits(bits, 8, nbits); + mask = (1<>8; + p[2] = v>>16; + p[3] = v>>24; +} +#undef DBG + +#define DBG if(0) +void +drawonepixel(Memimage *dst, Point dp, Memimage *src, Point sp, Memimage *mask, Point mp) +{ + uchar m, M, sr, sg, sb, sa, sk, dr, dg, db, da, dk; + + pixtorgba(getpixel(dst, dp), &dr, &dg, &db, &da); + pixtorgba(getpixel(src, sp), &sr, &sg, &sb, &sa); + m = getmask(mask, mp); + M = 255-(sa*m)/255; + +DBG print("dst %x %x %x %x src %x %x %x %x m %x = ", dr,dg,db,da, sr,sg,sb,sa, m); + if(dst->flags&Fgrey){ + /* + * We need to do the conversion to grey before the alpha calculation + * because the draw operator does this, and we need to be operating + * at the same precision so we get exactly the same answers. + */ + sk = RGB2K(sr, sg, sb); + dk = RGB2K(dr, dg, db); + dk = (sk*m + dk*M)/255; + dr = dg = db = dk; + da = (sa*m + da*M)/255; + }else{ + /* + * True color alpha calculation treats all channels (including alpha) + * the same. It might have been nice to use an array, but oh well. + */ + dr = (sr*m + dr*M)/255; + dg = (sg*m + dg*M)/255; + db = (sb*m + db*M)/255; + da = (sa*m + da*M)/255; + } + +DBG print("%x %x %x %x\n", dr,dg,db,da); + putpixel(dst, dp, rgbatopix(dr, dg, db, da)); +} diff --git a/libmemdraw/ellipse.c b/libmemdraw/ellipse.c new file mode 100644 index 0000000..f3e6ee3 --- /dev/null +++ b/libmemdraw/ellipse.c @@ -0,0 +1,248 @@ +#include +#include +#include +#include +#include + +/* + * ellipse(dst, c, a, b, t, src, sp) + * draws an ellipse centered at c with semiaxes a,b>=0 + * and semithickness t>=0, or filled if t<0. point sp + * in src maps to c in dst + * + * very thick skinny ellipses are brushed with circles (slow) + * others are approximated by filling between 2 ellipses + * criterion for very thick when b 0.5*x/(1-x) + * where x = b/a + */ + +typedef struct Param Param; +typedef struct State State; + +static void bellipse(int, State*, Param*); +static void erect(int, int, int, int, Param*); +static void eline(int, int, int, int, Param*); + +struct Param { + Memimage *dst; + Memimage *src; + Point c; + int t; + Point sp; + Memimage *disc; + int op; +}; + +/* + * denote residual error by e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2 + * e(x,y) = 0 on ellipse, e(x,y) < 0 inside, e(x,y) > 0 outside + */ + +struct State { + int a; + int x; + vlong a2; /* a^2 */ + vlong b2; /* b^2 */ + vlong b2x; /* b^2 * x */ + vlong a2y; /* a^2 * y */ + vlong c1; + vlong c2; /* test criteria */ + vlong ee; /* ee = e(x+1/2,y-1/2) - (a^2+b^2)/4 */ + vlong dxe; + vlong dye; + vlong d2xe; + vlong d2ye; +}; + +static +State* +newstate(State *s, int a, int b) +{ + s->x = 0; + s->a = a; + s->a2 = (vlong)(a*a); + s->b2 = (vlong)(b*b); + s->b2x = (vlong)0; + s->a2y = s->a2*(vlong)b; + s->c1 = -((s->a2>>2) + (vlong)(a&1) + s->b2); + s->c2 = -((s->b2>>2) + (vlong)(b&1)); + s->ee = -s->a2y; + s->dxe = (vlong)0; + s->dye = s->ee<<1; + s->d2xe = s->b2<<1; + s->d2ye = s->a2<<1; + return s; +} + +/* + * return x coord of rightmost pixel on next scan line + */ +static +int +step(State *s) +{ + while(s->x < s->a) { + if(s->ee+s->b2x <= s->c1 || /* e(x+1,y-1/2) <= 0 */ + s->ee+s->a2y <= s->c2) { /* e(x+1/2,y) <= 0 (rare) */ + s->dxe += s->d2xe; + s->ee += s->dxe; + s->b2x += s->b2; + s->x++; + continue; + } + s->dye += s->d2ye; + s->ee += s->dye; + s->a2y -= s->a2; + if(s->ee-s->a2y <= s->c2) { /* e(x+1/2,y-1) <= 0 */ + s->dxe += s->d2xe; + s->ee += s->dxe; + s->b2x += s->b2; + return s->x++; + } + break; + } + return s->x; +} + +void +memellipse(Memimage *dst, Point c, int a, int b, int t, Memimage *src, Point sp, int op) +{ + State in, out; + int y, inb, inx, outx, u; + Param p; + + if(a < 0) + a = -a; + if(b < 0) + b = -b; + p.dst = dst; + p.src = src; + p.c = c; + p.t = t; + p.sp = subpt(sp, c); + p.disc = nil; + p.op = op; + + u = (t<<1)*(a-b); + if(bb*b || aa*a) { +/* if(bb*b/a || aa*a/b) # very thick */ + bellipse(b, newstate(&in, a, b), &p); + return; + } + + if(t < 0) { + inb = -1; + newstate(&out, a, y = b); + } else { + inb = b - t; + newstate(&out, a+t, y = b+t); + } + if(t > 0) + newstate(&in, a-t, inb); + inx = 0; + for( ; y>=0; y--) { + outx = step(&out); + if(y > inb) { + erect(-outx, y, outx, y, &p); + if(y != 0) + erect(-outx, -y, outx, -y, &p); + continue; + } + if(t > 0) { + inx = step(&in); + if(y == inb) + inx = 0; + } else if(inx > outx) + inx = outx; + erect(inx, y, outx, y, &p); + if(y != 0) + erect(inx, -y, outx, -y, &p); + erect(-outx, y, -inx, y, &p); + if(y != 0) + erect(-outx, -y, -inx, -y, &p); + inx = outx + 1; + } +} + +static Point p00 = {0, 0}; + +/* + * a brushed ellipse + */ +static +void +bellipse(int y, State *s, Param *p) +{ + int t, ox, oy, x, nx; + + t = p->t; + p->disc = allocmemimage(Rect(-t,-t,t+1,t+1), GREY1); + if(p->disc == nil) + return; + memfillcolor(p->disc, DTransparent); + memellipse(p->disc, p00, t, t, -1, memopaque, p00, p->op); + oy = y; + ox = 0; + nx = x = step(s); + do { + while(nx==x && y-->0) + nx = step(s); + y++; + eline(-x,-oy,-ox, -y, p); + eline(ox,-oy, x, -y, p); + eline(-x, y,-ox, oy, p); + eline(ox, y, x, oy, p); + ox = x+1; + x = nx; + y--; + oy = y; + } while(oy > 0); +} + +/* + * a rectangle with closed (not half-open) coordinates expressed + * relative to the center of the ellipse + */ +static +void +erect(int x0, int y0, int x1, int y1, Param *p) +{ + Rectangle r; + +/* print("R %d,%d %d,%d\n", x0, y0, x1, y1); /**/ + r = Rect(p->c.x+x0, p->c.y+y0, p->c.x+x1+1, p->c.y+y1+1); + memdraw(p->dst, r, p->src, addpt(p->sp, r.min), memopaque, p00, p->op); +} + +/* + * a brushed point similarly specified + */ +static +void +epoint(int x, int y, Param *p) +{ + Point p0; + Rectangle r; + +/* print("P%d %d,%d\n", p->t, x, y); /**/ + p0 = Pt(p->c.x+x, p->c.y+y); + r = Rpt(addpt(p0, p->disc->r.min), addpt(p0, p->disc->r.max)); + memdraw(p->dst, r, p->src, addpt(p->sp, r.min), p->disc, p->disc->r.min, p->op); +} + +/* + * a brushed horizontal or vertical line similarly specified + */ +static +void +eline(int x0, int y0, int x1, int y1, Param *p) +{ +/* print("L%d %d,%d %d,%d\n", p->t, x0, y0, x1, y1); /**/ + if(x1 > x0+1) + erect(x0+1, y0-p->t, x1-1, y1+p->t, p); + else if(y1 > y0+1) + erect(x0-p->t, y0+1, x1+p->t, y1-1, p); + epoint(x0, y0, p); + if(x1-x0 || y1-y0) + epoint(x1, y1, p); +} diff --git a/libmemdraw/fillpoly.c b/libmemdraw/fillpoly.c new file mode 100644 index 0000000..6d5370a --- /dev/null +++ b/libmemdraw/fillpoly.c @@ -0,0 +1,523 @@ +#include +#include +#include +#include +#include + +typedef struct Seg Seg; + +struct Seg +{ + Point p0; + Point p1; + long num; + long den; + long dz; + long dzrem; + long z; + long zerr; + long d; +}; + +static void zsort(Seg **seg, Seg **ep); +static int ycompare(void*, void*); +static int xcompare(void*, void*); +static int zcompare(void*, void*); +static void xscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src, Point sp, int, int, int, int); +static void yscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src, Point sp, int, int); + +static void +fillcolor(Memimage *dst, int left, int right, int y, Memimage *src, Point p) +{ + int srcval; + + USED(src); + srcval = p.x; + p.x = left; + p.y = y; + memset(byteaddr(dst, p), srcval, right-left); +} + +static void +fillline(Memimage *dst, int left, int right, int y, Memimage *src, Point p, int op) +{ + Rectangle r; + + r.min.x = left; + r.min.y = y; + r.max.x = right; + r.max.y = y+1; + p.x += left; + p.y += y; + memdraw(dst, r, src, p, memopaque, p, op); +} + +static void +fillpoint(Memimage *dst, int x, int y, Memimage *src, Point p, int op) +{ + Rectangle r; + + r.min.x = x; + r.min.y = y; + r.max.x = x+1; + r.max.y = y+1; + p.x += x; + p.y += y; + memdraw(dst, r, src, p, memopaque, p, op); +} + +void +memfillpoly(Memimage *dst, Point *vert, int nvert, int w, Memimage *src, Point sp, int op) +{ + _memfillpolysc(dst, vert, nvert, w, src, sp, 0, 0, 0, op); +} + +void +_memfillpolysc(Memimage *dst, Point *vert, int nvert, int w, Memimage *src, Point sp, int detail, int fixshift, int clipped, int op) +{ + Seg **seg, *segtab; + Point p0; + int i; + + if(nvert == 0) + return; + + seg = malloc((nvert+2)*sizeof(Seg*)); + if(seg == nil) + return; + segtab = malloc((nvert+1)*sizeof(Seg)); + if(segtab == nil) { + free(seg); + return; + } + + sp.x = (sp.x - vert[0].x) >> fixshift; + sp.y = (sp.y - vert[0].y) >> fixshift; + p0 = vert[nvert-1]; + if(!fixshift) { + p0.x <<= 1; + p0.y <<= 1; + } + for(i = 0; i < nvert; i++) { + segtab[i].p0 = p0; + p0 = vert[i]; + if(!fixshift) { + p0.x <<= 1; + p0.y <<= 1; + } + segtab[i].p1 = p0; + segtab[i].d = 1; + } + if(!fixshift) + fixshift = 1; + + xscan(dst, seg, segtab, nvert, w, src, sp, detail, fixshift, clipped, op); + if(detail) + yscan(dst, seg, segtab, nvert, w, src, sp, fixshift, op); + + free(seg); + free(segtab); +} + +static long +mod(long x, long y) +{ + long z; + + z = x%y; + if((long)(((ulong)z)^((ulong)y)) > 0 || z == 0) + return z; + return z + y; +} + +static long +sdiv(long x, long y) +{ + if((long)(((ulong)x)^((ulong)y)) >= 0 || x == 0) + return x/y; + + return (x+((y>>30)|1))/y-1; +} + +static long +smuldivmod(long x, long y, long z, long *mod) +{ + vlong vx; + + if(x == 0 || y == 0){ + *mod = 0; + return 0; + } + vx = x; + vx *= y; + *mod = vx % z; + if(*mod < 0) + *mod += z; /* z is always >0 */ + if((vx < 0) == (z < 0)) + return vx/z; + return -((-vx)/z); +} + +static void +xscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src, Point sp, int detail, int fixshift, int clipped, int op) +{ + long y, maxy, x, x2, xerr, xden, onehalf; + Seg **ep, **next, **p, **q, *s; + long n, i, iy, cnt, ix, ix2, minx, maxx; + Point pt; + void (*fill)(Memimage*, int, int, int, Memimage*, Point, int); + + fill = fillline; +/* + * This can only work on 8-bit destinations, since fillcolor is + * just using memset on sp.x. + * + * I'd rather not even enable it then, since then if the general + * code is too slow, someone will come up with a better improvement + * than this sleazy hack. -rsc + * + if(clipped && (src->flags&Frepl) && src->depth==8 && Dx(src->r)==1 && Dy(src->r)==1) { + fill = fillcolor; + sp.x = membyteval(src); + } + * + */ + USED(clipped); + + + for(i=0, s=segtab, p=seg; ip0.y == s->p1.y) + continue; + if(s->p0.y > s->p1.y) { + pt = s->p0; + s->p0 = s->p1; + s->p1 = pt; + s->d = -s->d; + } + s->num = s->p1.x - s->p0.x; + s->den = s->p1.y - s->p0.y; + s->dz = sdiv(s->num, s->den) << fixshift; + s->dzrem = mod(s->num, s->den) << fixshift; + s->dz += sdiv(s->dzrem, s->den); + s->dzrem = mod(s->dzrem, s->den); + p++; + } + n = p-seg; + if(n == 0) + return; + *p = 0; + qsort(seg, p-seg , sizeof(Seg*), ycompare); + + onehalf = 0; + if(fixshift) + onehalf = 1 << (fixshift-1); + + minx = dst->clipr.min.x; + maxx = dst->clipr.max.x; + + y = seg[0]->p0.y; + if(y < (dst->clipr.min.y << fixshift)) + y = dst->clipr.min.y << fixshift; + iy = (y + onehalf) >> fixshift; + y = (iy << fixshift) + onehalf; + maxy = dst->clipr.max.y << fixshift; + + ep = next = seg; + + while(yp1.y < y) + continue; + s->z += s->dz; + s->zerr += s->dzrem; + if(s->zerr >= s->den) { + s->z++; + s->zerr -= s->den; + if(s->zerr < 0 || s->zerr >= s->den) + print("bad ratzerr1: %ld den %ld dzrem %ld\n", s->zerr, s->den, s->dzrem); + } + *q++ = s; + } + + for(p = next; *p; p++) { + s = *p; + if(s->p0.y >= y) + break; + if(s->p1.y < y) + continue; + s->z = s->p0.x; + s->z += smuldivmod(y - s->p0.y, s->num, s->den, &s->zerr); + if(s->zerr < 0 || s->zerr >= s->den) + print("bad ratzerr2: %ld den %ld ratdzrem %ld\n", s->zerr, s->den, s->dzrem); + *q++ = s; + } + ep = q; + next = p; + + if(ep == seg) { + if(*next == 0) + break; + iy = (next[0]->p0.y + onehalf) >> fixshift; + y = (iy << fixshift) + onehalf; + continue; + } + + zsort(seg, ep); + + for(p = seg; p < ep; p++) { + cnt = 0; + x = p[0]->z; + xerr = p[0]->zerr; + xden = p[0]->den; + ix = (x + onehalf) >> fixshift; + if(ix >= maxx) + break; + if(ix < minx) + ix = minx; + cnt += p[0]->d; + p++; + for(;;) { + if(p == ep) { + print("xscan: fill to infinity"); + return; + } + cnt += p[0]->d; + if((cnt&wind) == 0) + break; + p++; + } + x2 = p[0]->z; + ix2 = (x2 + onehalf) >> fixshift; + if(ix2 <= minx) + continue; + if(ix2 > maxx) + ix2 = maxx; + if(ix == ix2 && detail) { + if(xerr*p[0]->den + p[0]->zerr*xden > p[0]->den*xden) + x++; + ix = (x + x2) >> (fixshift+1); + ix2 = ix+1; + } + (*fill)(dst, ix, ix2, iy, src, sp, op); + } + y += (1<p0.x == s->p1.x) + continue; + if(s->p0.x > s->p1.x) { + pt = s->p0; + s->p0 = s->p1; + s->p1 = pt; + s->d = -s->d; + } + s->num = s->p1.y - s->p0.y; + s->den = s->p1.x - s->p0.x; + s->dz = sdiv(s->num, s->den) << fixshift; + s->dzrem = mod(s->num, s->den) << fixshift; + s->dz += sdiv(s->dzrem, s->den); + s->dzrem = mod(s->dzrem, s->den); + p++; + } + n = p-seg; + if(n == 0) + return; + *p = 0; + qsort(seg, n , sizeof(Seg*), xcompare); + + onehalf = 0; + if(fixshift) + onehalf = 1 << (fixshift-1); + + miny = dst->clipr.min.y; + maxy = dst->clipr.max.y; + + x = seg[0]->p0.x; + if(x < (dst->clipr.min.x << fixshift)) + x = dst->clipr.min.x << fixshift; + ix = (x + onehalf) >> fixshift; + x = (ix << fixshift) + onehalf; + maxx = dst->clipr.max.x << fixshift; + + ep = next = seg; + + while(xp1.x < x) + continue; + s->z += s->dz; + s->zerr += s->dzrem; + if(s->zerr >= s->den) { + s->z++; + s->zerr -= s->den; + if(s->zerr < 0 || s->zerr >= s->den) + print("bad ratzerr1: %ld den %ld ratdzrem %ld\n", s->zerr, s->den, s->dzrem); + } + *q++ = s; + } + + for(p = next; *p; p++) { + s = *p; + if(s->p0.x >= x) + break; + if(s->p1.x < x) + continue; + s->z = s->p0.y; + s->z += smuldivmod(x - s->p0.x, s->num, s->den, &s->zerr); + if(s->zerr < 0 || s->zerr >= s->den) + print("bad ratzerr2: %ld den %ld ratdzrem %ld\n", s->zerr, s->den, s->dzrem); + *q++ = s; + } + ep = q; + next = p; + + if(ep == seg) { + if(*next == 0) + break; + ix = (next[0]->p0.x + onehalf) >> fixshift; + x = (ix << fixshift) + onehalf; + continue; + } + + zsort(seg, ep); + + for(p = seg; p < ep; p++) { + cnt = 0; + y = p[0]->z; + yerr = p[0]->zerr; + yden = p[0]->den; + iy = (y + onehalf) >> fixshift; + if(iy >= maxy) + break; + if(iy < miny) + iy = miny; + cnt += p[0]->d; + p++; + for(;;) { + if(p == ep) { + print("yscan: fill to infinity"); + return; + } + cnt += p[0]->d; + if((cnt&wind) == 0) + break; + p++; + } + y2 = p[0]->z; + iy2 = (y2 + onehalf) >> fixshift; + if(iy2 <= miny) + continue; + if(iy2 > maxy) + iy2 = maxy; + if(iy == iy2) { + if(yerr*p[0]->den + p[0]->zerr*yden > p[0]->den*yden) + y++; + iy = (y + y2) >> (fixshift+1); + fillpoint(dst, ix, iy, src, sp, op); + } + } + x += (1<z > p[1]->z) { + s = p[0]; + p[0] = p[1]; + p[1] = s; + done = 0; + } + } + } while(!done); + } else { + q = ep-1; + for(p = seg; p < q; p++) { + if(p[0]->z > p[1]->z) { + qsort(seg, ep-seg, sizeof(Seg*), zcompare); + break; + } + } + } +} + +static int +ycompare(void *a, void *b) +{ + Seg **s0, **s1; + long y0, y1; + + s0 = a; + s1 = b; + y0 = (*s0)->p0.y; + y1 = (*s1)->p0.y; + + if(y0 < y1) + return -1; + if(y0 == y1) + return 0; + return 1; +} + +static int +xcompare(void *a, void *b) +{ + Seg **s0, **s1; + long x0, x1; + + s0 = a; + s1 = b; + x0 = (*s0)->p0.x; + x1 = (*s1)->p0.x; + + if(x0 < x1) + return -1; + if(x0 == x1) + return 0; + return 1; +} + +static int +zcompare(void *a, void *b) +{ + Seg **s0, **s1; + long z0, z1; + + s0 = a; + s1 = b; + z0 = (*s0)->z; + z1 = (*s1)->z; + + if(z0 < z1) + return -1; + if(z0 == z1) + return 0; + return 1; +} diff --git a/libmemdraw/hwdraw.c b/libmemdraw/hwdraw.c new file mode 100644 index 0000000..3f36250 --- /dev/null +++ b/libmemdraw/hwdraw.c @@ -0,0 +1,12 @@ +#include +#include +#include +#include + +int +hwdraw(Memdrawparam *p) +{ + USED(p); + return 0; /* could not satisfy request */ +} + diff --git a/libmemdraw/iprint.c b/libmemdraw/iprint.c new file mode 100644 index 0000000..923b6b4 --- /dev/null +++ b/libmemdraw/iprint.c @@ -0,0 +1,12 @@ +#include +#include +#include +#include + +int +iprint(char *fmt,...) +{ + USED(fmt); + return -1; +} + diff --git a/libmemdraw/line.c b/libmemdraw/line.c new file mode 100644 index 0000000..55ec67d --- /dev/null +++ b/libmemdraw/line.c @@ -0,0 +1,485 @@ +#include +#include +#include +#include +#include + +enum +{ + Arrow1 = 8, + Arrow2 = 10, + Arrow3 = 3, +}; + +static +int +lmin(int a, int b) +{ + if(a < b) + return a; + return b; +} + +static +int +lmax(int a, int b) +{ + if(a > b) + return a; + return b; +} + +#ifdef NOTUSED +/* + * Rather than line clip, we run the Bresenham loop over the full line, + * and clip on each pixel. This is more expensive but means that + * lines look the same regardless of how the windowing has tiled them. + * For speed, we check for clipping outside the loop and make the + * test easy when possible. + */ + +static +void +horline1(Memimage *dst, Point p0, Point p1, int srcval, Rectangle clipr) +{ + int x, y, dy, deltay, deltax, maxx; + int dd, easy, e, bpp, m, m0; + uchar *d; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + dd = dst->width*sizeof(ulong); + dy = 1; + if(deltay < 0){ + dd = -dd; + deltay = -deltay; + dy = -1; + } + maxx = lmin(p1.x, clipr.max.x-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + m = m0 >> (p0.x&(7/dst->depth))*bpp; + easy = ptinrect(p0, clipr) && ptinrect(p1, clipr); + e = 2*deltay - deltax; + y = p0.y; + d = byteaddr(dst, p0); + deltay *= 2; + deltax = deltay - 2*deltax; + for(x=p0.x; x<=maxx; x++){ + if(easy || (clipr.min.x<=x && clipr.min.y<=y && y 0){ + y += dy; + d += dd; + e += deltax; + }else + e += deltay; + d++; + m >>= bpp; + if(m == 0) + m = m0; + } +} + +static +void +verline1(Memimage *dst, Point p0, Point p1, int srcval, Rectangle clipr) +{ + int x, y, deltay, deltax, maxy; + int easy, e, bpp, m, m0, dd; + uchar *d; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + dd = 1; + if(deltax < 0){ + dd = -1; + deltax = -deltax; + } + maxy = lmin(p1.y, clipr.max.y-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + m = m0 >> (p0.x&(7/dst->depth))*bpp; + easy = ptinrect(p0, clipr) && ptinrect(p1, clipr); + e = 2*deltax - deltay; + x = p0.x; + d = byteaddr(dst, p0); + deltax *= 2; + deltay = deltax - 2*deltay; + for(y=p0.y; y<=maxy; y++){ + if(easy || (clipr.min.y<=y && clipr.min.x<=x && x 0){ + x += dd; + d += dd; + e += deltay; + }else + e += deltax; + d += dst->width*sizeof(ulong); + m >>= bpp; + if(m == 0) + m = m0; + } +} + +static +void +horliner(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangle clipr) +{ + int x, y, sx, sy, deltay, deltax, minx, maxx; + int bpp, m, m0; + uchar *d, *s; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + sx = drawreplxy(src->r.min.x, src->r.max.x, p0.x+dsrc.x); + minx = lmax(p0.x, clipr.min.x); + maxx = lmin(p1.x, clipr.max.x-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + m = m0 >> (minx&(7/dst->depth))*bpp; + for(x=minx; x<=maxx; x++){ + y = p0.y + (deltay*(x-p0.x)+deltax/2)/deltax; + if(clipr.min.y<=y && yr.min.y, src->r.max.y, y+dsrc.y); + s = byteaddr(src, Pt(sx, sy)); + *d ^= (*d^*s) & m; + } + if(++sx >= src->r.max.x) + sx = src->r.min.x; + m >>= bpp; + if(m == 0) + m = m0; + } +} + +static +void +verliner(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangle clipr) +{ + int x, y, sx, sy, deltay, deltax, miny, maxy; + int bpp, m, m0; + uchar *d, *s; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + sy = drawreplxy(src->r.min.y, src->r.max.y, p0.y+dsrc.y); + miny = lmax(p0.y, clipr.min.y); + maxy = lmin(p1.y, clipr.max.y-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + for(y=miny; y<=maxy; y++){ + if(deltay == 0) /* degenerate line */ + x = p0.x; + else + x = p0.x + (deltax*(y-p0.y)+deltay/2)/deltay; + if(clipr.min.x<=x && x> (x&(7/dst->depth))*bpp; + d = byteaddr(dst, Pt(x, y)); + sx = drawreplxy(src->r.min.x, src->r.max.x, x+dsrc.x); + s = byteaddr(src, Pt(sx, sy)); + *d ^= (*d^*s) & m; + } + if(++sy >= src->r.max.y) + sy = src->r.min.y; + } +} + +static +void +horline(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangle clipr) +{ + int x, y, deltay, deltax, minx, maxx; + int bpp, m, m0; + uchar *d, *s; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + minx = lmax(p0.x, clipr.min.x); + maxx = lmin(p1.x, clipr.max.x-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + m = m0 >> (minx&(7/dst->depth))*bpp; + for(x=minx; x<=maxx; x++){ + y = p0.y + (deltay*(x-p0.x)+deltay/2)/deltax; + if(clipr.min.y<=y && y>= bpp; + if(m == 0) + m = m0; + } +} + +static +void +verline(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangle clipr) +{ + int x, y, deltay, deltax, miny, maxy; + int bpp, m, m0; + uchar *d, *s; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + miny = lmax(p0.y, clipr.min.y); + maxy = lmin(p1.y, clipr.max.y-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + for(y=miny; y<=maxy; y++){ + if(deltay == 0) /* degenerate line */ + x = p0.x; + else + x = p0.x + deltax*(y-p0.y)/deltay; + if(clipr.min.x<=x && x> (x&(7/dst->depth))*bpp; + d = byteaddr(dst, Pt(x, y)); + s = byteaddr(src, addpt(dsrc, Pt(x, y))); + *d ^= (*d^*s) & m; + } + } +} +#endif /* NOTUSED */ + +static Memimage* +membrush(int radius) +{ + static Memimage *brush; + static int brushradius; + + if(brush==nil || brushradius!=radius){ + freememimage(brush); + brush = allocmemimage(Rect(0, 0, 2*radius+1, 2*radius+1), memopaque->chan); + if(brush != nil){ + memfillcolor(brush, DTransparent); /* zeros */ + memellipse(brush, Pt(radius, radius), radius, radius, -1, memopaque, Pt(radius, radius), S); + } + brushradius = radius; + } + return brush; +} + +static +void +discend(Point p, int radius, Memimage *dst, Memimage *src, Point dsrc, int op) +{ + Memimage *disc; + Rectangle r; + + disc = membrush(radius); + if(disc != nil){ + r.min.x = p.x - radius; + r.min.y = p.y - radius; + r.max.x = p.x + radius+1; + r.max.y = p.y + radius+1; + memdraw(dst, r, src, addpt(r.min, dsrc), disc, Pt(0,0), op); + } +} + +static +void +arrowend(Point tip, Point *pp, int end, int sin, int cos, int radius) +{ + int x1, x2, x3; + + /* before rotation */ + if(end == Endarrow){ + x1 = Arrow1; + x2 = Arrow2; + x3 = Arrow3; + }else{ + x1 = (end>>5) & 0x1FF; /* distance along line from end of line to tip */ + x2 = (end>>14) & 0x1FF; /* distance along line from barb to tip */ + x3 = (end>>23) & 0x1FF; /* distance perpendicular from edge of line to barb */ + } + + /* comments follow track of right-facing arrowhead */ + pp->x = tip.x+((2*radius+1)*sin/2-x1*cos); /* upper side of shaft */ + pp->y = tip.y-((2*radius+1)*cos/2+x1*sin); + pp++; + pp->x = tip.x+((2*radius+2*x3+1)*sin/2-x2*cos); /* upper barb */ + pp->y = tip.y-((2*radius+2*x3+1)*cos/2+x2*sin); + pp++; + pp->x = tip.x; + pp->y = tip.y; + pp++; + pp->x = tip.x+(-(2*radius+2*x3+1)*sin/2-x2*cos); /* lower barb */ + pp->y = tip.y-(-(2*radius+2*x3+1)*cos/2+x2*sin); + pp++; + pp->x = tip.x+(-(2*radius+1)*sin/2-x1*cos); /* lower side of shaft */ + pp->y = tip.y+((2*radius+1)*cos/2-x1*sin); +} + +void +_memimageline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, Rectangle clipr, int op) +{ + /* + * BUG: We should really really pick off purely horizontal and purely + * vertical lines and handle them separately with calls to memimagedraw + * on rectangles. + */ + + int hor; + int sin, cos, dx, dy, t; + Rectangle oclipr, r; + Point q, pts[10], *pp, d; + + if(radius < 0) + return; + if(rectclip(&clipr, dst->r) == 0) + return; + if(rectclip(&clipr, dst->clipr) == 0) + return; + d = subpt(sp, p0); + if(rectclip(&clipr, rectsubpt(src->clipr, d)) == 0) + return; + if((src->flags&Frepl)==0 && rectclip(&clipr, rectsubpt(src->r, d))==0) + return; + /* this means that only verline() handles degenerate lines (p0==p1) */ + hor = (abs(p1.x-p0.x) > abs(p1.y-p0.y)); + /* + * Clipping is a little peculiar. We can't use Sutherland-Cohen + * clipping because lines are wide. But this is probably just fine: + * we do all math with the original p0 and p1, but clip when deciding + * what pixels to draw. This means the layer code can call this routine, + * using clipr to define the region being written, and get the same set + * of pixels regardless of the dicing. + */ + if((hor && p0.x>p1.x) || (!hor && p0.y>p1.y)){ + q = p0; + p0 = p1; + p1 = q; + t = end0; + end0 = end1; + end1 = t; + } + + if((p0.x == p1.x || p0.y == p1.y) && (end0&0x1F) == Endsquare && (end1&0x1F) == Endsquare){ + r.min = p0; + r.max = p1; + if(p0.x == p1.x){ + r.min.x -= radius; + r.max.x += radius+1; + } + else{ + r.min.y -= radius; + r.max.y += radius+1; + } + oclipr = dst->clipr; + dst->clipr = clipr; + memimagedraw(dst, r, src, sp, memopaque, sp, op); + dst->clipr = oclipr; + return; + } + +/* Hard: */ + /* draw thick line using polygon fill */ + icossin2(p1.x-p0.x, p1.y-p0.y, &cos, &sin); + dx = (sin*(2*radius+1))/2; + dy = (cos*(2*radius+1))/2; + pp = pts; + oclipr = dst->clipr; + dst->clipr = clipr; + q.x = ICOSSCALE*p0.x+ICOSSCALE/2-cos/2; + q.y = ICOSSCALE*p0.y+ICOSSCALE/2-sin/2; + switch(end0 & 0x1F){ + case Enddisc: + discend(p0, radius, dst, src, d, op); + /* fall through */ + case Endsquare: + default: + pp->x = q.x-dx; + pp->y = q.y+dy; + pp++; + pp->x = q.x+dx; + pp->y = q.y-dy; + pp++; + break; + case Endarrow: + arrowend(q, pp, end0, -sin, -cos, radius); + _memfillpolysc(dst, pts, 5, ~0, src, addpt(pts[0], mulpt(d, ICOSSCALE)), 1, 10, 1, op); + pp[1] = pp[4]; + pp += 2; + } + q.x = ICOSSCALE*p1.x+ICOSSCALE/2+cos/2; + q.y = ICOSSCALE*p1.y+ICOSSCALE/2+sin/2; + switch(end1 & 0x1F){ + case Enddisc: + discend(p1, radius, dst, src, d, op); + /* fall through */ + case Endsquare: + default: + pp->x = q.x+dx; + pp->y = q.y-dy; + pp++; + pp->x = q.x-dx; + pp->y = q.y+dy; + pp++; + break; + case Endarrow: + arrowend(q, pp, end1, sin, cos, radius); + _memfillpolysc(dst, pp, 5, ~0, src, addpt(pts[0], mulpt(d, ICOSSCALE)), 1, 10, 1, op); + pp[1] = pp[4]; + pp += 2; + } + _memfillpolysc(dst, pts, pp-pts, ~0, src, addpt(pts[0], mulpt(d, ICOSSCALE)), 0, 10, 1, op); + dst->clipr = oclipr; + return; +} + +void +memimageline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, int op) +{ + _memimageline(dst, p0, p1, end0, end1, radius, src, sp, dst->clipr, op); +} + +/* + * Simple-minded conservative code to compute bounding box of line. + * Result is probably a little larger than it needs to be. + */ +static +void +addbbox(Rectangle *r, Point p) +{ + if(r->min.x > p.x) + r->min.x = p.x; + if(r->min.y > p.y) + r->min.y = p.y; + if(r->max.x < p.x+1) + r->max.x = p.x+1; + if(r->max.y < p.y+1) + r->max.y = p.y+1; +} + +int +memlineendsize(int end) +{ + int x3; + + if((end&0x3F) != Endarrow) + return 0; + if(end == Endarrow) + x3 = Arrow3; + else + x3 = (end>>23) & 0x1FF; + return x3; +} + +Rectangle +memlinebbox(Point p0, Point p1, int end0, int end1, int radius) +{ + Rectangle r, r1; + int extra; + + r.min.x = 10000000; + r.min.y = 10000000; + r.max.x = -10000000; + r.max.y = -10000000; + extra = lmax(memlineendsize(end0), memlineendsize(end1)); + r1 = insetrect(canonrect(Rpt(p0, p1)), -(radius+extra)); + addbbox(&r, r1.min); + addbbox(&r, r1.max); + return r; +} diff --git a/libmemdraw/load.c b/libmemdraw/load.c new file mode 100644 index 0000000..4d724d3 --- /dev/null +++ b/libmemdraw/load.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include + +int +_loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) +{ + int y, l, lpart, rpart, mx, m, mr; + uchar *q; + + if(!rectinrect(r, i->r)) + return -1; + l = bytesperline(r, i->depth); + if(ndata < l*Dy(r)) + return -1; + ndata = l*Dy(r); + q = byteaddr(i, r.min); + mx = 7/i->depth; + lpart = (r.min.x & mx) * i->depth; + rpart = (r.max.x & mx) * i->depth; + m = 0xFF >> lpart; + /* may need to do bit insertion on edges */ + if(l == 1){ /* all in one byte */ + if(rpart) + m ^= 0xFF >> rpart; + for(y=r.min.y; ywidth*sizeof(ulong); + data++; + } + return ndata; + } + if(lpart==0 && rpart==0){ /* easy case */ + for(y=r.min.y; ywidth*sizeof(ulong); + data += l; + } + return ndata; + } + mr = 0xFF ^ (0xFF >> rpart); + if(lpart!=0 && rpart==0){ + for(y=r.min.y; y 1) + memmove(q+1, data+1, l-1); + q += i->width*sizeof(ulong); + data += l; + } + return ndata; + } + if(lpart==0 && rpart!=0){ + for(y=r.min.y; y 1) + memmove(q, data, l-1); + q[l-1] ^= (data[l-1]^q[l-1]) & mr; + q += i->width*sizeof(ulong); + data += l; + } + return ndata; + } + for(y=r.min.y; y 2) + memmove(q+1, data+1, l-2); + q[l-1] ^= (data[l-1]^q[l-1]) & mr; + q += i->width*sizeof(ulong); + data += l; + } + return ndata; +} diff --git a/libmemdraw/mkcmap.c b/libmemdraw/mkcmap.c new file mode 100644 index 0000000..e8d5efc --- /dev/null +++ b/libmemdraw/mkcmap.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include + +/* +struct Memcmap +{ + uchar cmap2rgb[3*256]; + uchar rgb2cmap[16*16*16]; +}; +*/ + +static Memcmap* +mkcmap(void) +{ + static Memcmap def; + + int i, rgb, r, g, b; + + for(i=0; i<256; i++){ + rgb = cmap2rgb(i); + r = (rgb>>16)&0xff; + g = (rgb>>8)&0xff; + b = rgb&0xff; + def.cmap2rgb[3*i] = r; + def.cmap2rgb[3*i+1] = g; + def.cmap2rgb[3*i+2] = b; + } + + for(r=0; r<16; r++) + for(g=0; g<16; g++) + for(b=0; b<16; b++) + def.rgb2cmap[r*16*16+g*16+b] = rgb2cmap(r*0x11, g*0x11, b*0x11); + return &def; +} + +void +main(int argc, char **argv) +{ + Memcmap *c; + int i, j, inferno; + + inferno = 0; + ARGBEGIN{ + case 'i': + inferno = 1; + }ARGEND + + memimageinit(); + c = mkcmap(); + if(!inferno) + print("#include \n#include \n"); + else + print("#include \"lib9.h\"\n"); + print("#include \n"); + print("#include \n\n"); + print("static Memcmap def = {\n"); + print("/* cmap2rgb */ {\n"); + for(i=0; icmap2rgb); ){ + print("\t"); + for(j=0; j<16; j++, i++) + print("0x%2.2ux,", c->cmap2rgb[i]); + print("\n"); + } + print("},\n"); + print("/* rgb2cmap */ {\n"); + for(i=0; irgb2cmap);){ + print("\t"); + for(j=0; j<16; j++, i++) + print("0x%2.2ux,", c->rgb2cmap[i]); + print("\n"); + } + print("}\n"); + print("};\n"); + print("Memcmap *memdefcmap = &def;\n"); + print("void _memmkcmap(void){}\n"); + exits(0); +} diff --git a/libmemdraw/mkfile b/libmemdraw/mkfile new file mode 100644 index 0000000..4ce9758 --- /dev/null +++ b/libmemdraw/mkfile @@ -0,0 +1,27 @@ +<$DSRC/mkfile-$CONF +TARG=libmemdraw.$L + +OFILES=\ + alloc.$O\ + arc.$O\ + cload.$O\ + cmap.$O\ + cread.$O\ + defont.$O\ + draw.$O\ + ellipse.$O\ + fillpoly.$O\ + hwdraw.$O\ + line.$O\ + load.$O\ + openmemsubfont.$O\ + poly.$O\ + read.$O\ + string.$O\ + subfont.$O\ + unload.$O\ + write.$O + +HFILE=\ + +<$DSRC/mklib-$CONF diff --git a/libmemdraw/openmemsubfont.c b/libmemdraw/openmemsubfont.c new file mode 100644 index 0000000..c8d926e --- /dev/null +++ b/libmemdraw/openmemsubfont.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +Memsubfont* +openmemsubfont(char *name) +{ + Memsubfont *sf; + Memimage *i; + Fontchar *fc; + int fd, n; + char hdr[3*12+4+1]; + uchar *p; + + fd = open(name, OREAD); + if(fd < 0) + return nil; + p = nil; + i = readmemimage(fd); + if(i == nil) + goto Err; + if(read(fd, hdr, 3*12) != 3*12){ + werrstr("openmemsubfont: header read error: %r"); + goto Err; + } + n = atoi(hdr); + p = malloc(6*(n+1)); + if(p == nil) + goto Err; + if(read(fd, p, 6*(n+1)) != 6*(n+1)){ + werrstr("openmemsubfont: fontchar read error: %r"); + goto Err; + } + fc = malloc(sizeof(Fontchar)*(n+1)); + if(fc == nil) + goto Err; + _unpackinfo(fc, p, n); + sf = allocmemsubfont(name, n, atoi(hdr+12), atoi(hdr+24), fc, i); + if(sf == nil){ + free(fc); + goto Err; + } + free(p); + return sf; +Err: + close(fd); + if (i != nil) + freememimage(i); + if (p != nil) + free(p); + return nil; +} diff --git a/libmemdraw/poly.c b/libmemdraw/poly.c new file mode 100644 index 0000000..572f154 --- /dev/null +++ b/libmemdraw/poly.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include + +void +mempoly(Memimage *dst, Point *vert, int nvert, int end0, int end1, int radius, Memimage *src, Point sp, int op) +{ + int i, e0, e1; + Point d; + + if(nvert < 2) + return; + d = subpt(sp, vert[0]); + for(i=1; i +#include +#include +#include + +Memimage* +readmemimage(int fd) +{ + char hdr[5*12+1]; + int dy; + ulong chan; + uint l, n; + int m, j; + int new, miny, maxy; + Rectangle r; + uchar *tmp; + int ldepth, chunk; + Memimage *i; + + if(readn(fd, hdr, 11) != 11){ + werrstr("readimage: short header"); + return nil; + } + if(memcmp(hdr, "compressed\n", 11) == 0) + return creadmemimage(fd); + if(readn(fd, hdr+11, 5*12-11) != 5*12-11){ + werrstr("readimage: short header (2)"); + return nil; + } + + /* + * distinguish new channel descriptor from old ldepth. + * channel descriptors have letters as well as numbers, + * while ldepths are a single digit formatted as %-11d. + */ + new = 0; + for(m=0; m<10; m++){ + if(hdr[m] != ' '){ + new = 1; + break; + } + } + if(hdr[11] != ' '){ + werrstr("readimage: bad format"); + return nil; + } + if(new){ + hdr[11] = '\0'; + if((chan = strtochan(hdr)) == 0){ + werrstr("readimage: bad channel string %s", hdr); + return nil; + } + }else{ + ldepth = ((int)hdr[10])-'0'; + if(ldepth<0 || ldepth>3){ + werrstr("readimage: bad ldepth %d", ldepth); + return nil; + } + chan = drawld2chan[ldepth]; + } + + r.min.x = atoi(hdr+1*12); + r.min.y = atoi(hdr+2*12); + r.max.x = atoi(hdr+3*12); + r.max.y = atoi(hdr+4*12); + if(r.min.x>r.max.x || r.min.y>r.max.y){ + werrstr("readimage: bad rectangle"); + return nil; + } + + miny = r.min.y; + maxy = r.max.y; + + l = bytesperline(r, chantodepth(chan)); + i = allocmemimage(r, chan); + if(i == nil) + return nil; + chunk = 32*1024; + if(chunk < l) + chunk = l; + tmp = malloc(chunk); + if(tmp == nil) + goto Err; + while(maxy > miny){ + dy = maxy - miny; + if(dy*l > chunk) + dy = chunk/l; + if(dy <= 0){ + werrstr("readmemimage: image too wide for buffer"); + goto Err; + } + n = dy*l; + m = readn(fd, tmp, n); + if(m != n){ + werrstr("readmemimage: read count %d not %d: %r", m, n); + Err: + freememimage(i); + free(tmp); + return nil; + } + if(!new) /* an old image: must flip all the bits */ + for(j=0; j +#include +#include +#include +#include + +Point +memimagestring(Memimage *b, Point p, Memimage *color, Point cp, Memsubfont *f, char *cs) +{ + int w, width; + uchar *s; + Rune c; + Fontchar *i; + + s = (uchar*)cs; + for(; c=*s; p.x+=width, cp.x+=width){ + width = 0; + if(c < Runeself) + s++; + else{ + w = chartorune(&c, (char*)s); + if(w == 0){ + s++; + continue; + } + s += w; + } + if(c >= f->n) + continue; +// i = f->info+c; + i = &(f->info[c]); + width = i->width; + memdraw(b, Rect(p.x+i->left, p.y+i->top, p.x+i->left+(i[1].x-i[0].x), p.y+i->bottom), + color, cp, f->bits, Pt(i->x, i->top), SoverD); + } + return p; +} + +Point +memsubfontwidth(Memsubfont *f, char *cs) +{ + Rune c; + Point p; + uchar *s; + Fontchar *i; + int w, width; + + p = Pt(0, f->height); + s = (uchar*)cs; + for(; c=*s; p.x+=width){ + width = 0; + if(c < Runeself) + s++; + else{ + w = chartorune(&c, (char*)s); + if(w == 0){ + s++; + continue; + } + s += w; + } + if(c >= f->n) + continue; + i = f->info+c; + width = i->width; + } + return p; +} diff --git a/libmemdraw/subfont.c b/libmemdraw/subfont.c new file mode 100644 index 0000000..e2bdee5 --- /dev/null +++ b/libmemdraw/subfont.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +Memsubfont* +allocmemsubfont(char *name, int n, int height, int ascent, Fontchar *info, Memimage *i) +{ + Memsubfont *f; + + f = malloc(sizeof(Memsubfont)); + if(f == 0) + return 0; + f->n = n; + f->height = height; + f->ascent = ascent; + f->info = info; + f->bits = i; + if(name) + f->name = strdup(name); + else + f->name = 0; + return f; +} + +void +freememsubfont(Memsubfont *f) +{ + if(f == 0) + return; + free(f->info); /* note: f->info must have been malloc'ed! */ + freememimage(f->bits); + free(f); +} diff --git a/libmemdraw/times b/libmemdraw/times new file mode 100644 index 0000000..41beff4 --- /dev/null +++ b/libmemdraw/times @@ -0,0 +1,19 @@ +draw1: 6M for draw 0,0,100,100 no repl +draw3: 4M for draw 0,0,100,100 no repl +just read src, dst - 250k +mask reading - 650k +write dst - 100k +alpha calculation - 3000k + +olddraw: 10M for draw 0, 0, 1000, 1000 no repl all ldepth 3 + 44M for draw 0, 0, 1000, 1000 src, mask ldepth 2 dst ldepth 3 +draw4: 160M for draw 0, 0, 1000, 1000 no repl all r8g8b8 + null loop: 10k + src, dst reading: 13-15M each + mask reading: 30M + alpha calculation loop: 90M + null alpha loop: 2M + minimal loop control +20M + alpha calculation with divides +190M + alpha calculation wtih shifts +70M + writeback: 11M diff --git a/libmemdraw/unload.c b/libmemdraw/unload.c new file mode 100644 index 0000000..b3f25c9 --- /dev/null +++ b/libmemdraw/unload.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +int +unloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) +{ + int y, l; + uchar *q; + + if(!rectinrect(r, i->r)) + return -1; + l = bytesperline(r, i->depth); + if(ndata < l*Dy(r)) + return -1; + ndata = l*Dy(r); + q = byteaddr(i, r.min); + for(y=r.min.y; ywidth*sizeof(ulong); + data += l; + } + return ndata; +} diff --git a/libmemdraw/write.c b/libmemdraw/write.c new file mode 100644 index 0000000..a8816ed --- /dev/null +++ b/libmemdraw/write.c @@ -0,0 +1,183 @@ +#include +#include +#include +#include + +#define CHUNK 8000 + +#define HSHIFT 3 /* HSHIFT==5 runs slightly faster, but hash table is 64x bigger */ +#define NHASH (1<<(HSHIFT*NMATCH)) +#define HMASK (NHASH-1) +#define hupdate(h, c) ((((h)<r; + bpl = bytesperline(r, i->depth); + n = Dy(r)*bpl; + data = malloc(n); + ncblock = _compblocksize(r, i->depth); + outbuf = malloc(ncblock); + hash = malloc(NHASH*sizeof(Hlist)); + chain = malloc(NMEM*sizeof(Hlist)); + if(data == 0 || outbuf == 0 || hash == 0 || chain == 0){ + ErrOut: + free(data); + free(outbuf); + free(hash); + free(chain); + return -1; + } + for(miny = r.min.y; miny != r.max.y; miny += dy){ + dy = r.max.y-miny; + if(dy*bpl > CHUNK) + dy = CHUNK/bpl; + nb = unloadmemimage(i, Rect(r.min.x, miny, r.max.x, miny+dy), + data+(miny-r.min.y)*bpl, dy*bpl); + if(nb != dy*bpl) + goto ErrOut; + } + sprint(hdr, "compressed\n%11s %11d %11d %11d %11d ", + chantostr(cbuf, i->chan), r.min.x, r.min.y, r.max.x, r.max.y); + if(write(fd, hdr, 11+5*12) != 11+5*12) + goto ErrOut; + edata = data+n; + eout = outbuf+ncblock; + line = data; + r.max.y = r.min.y; + while(line != edata){ + memset(hash, 0, NHASH*sizeof(Hlist)); + memset(chain, 0, NMEM*sizeof(Hlist)); + cp = chain; + h = 0; + outp = outbuf; + for(n = 0; n != NMATCH; n++) + h = hupdate(h, line[n]); + loutp = outbuf; + while(line != edata){ + ndump = 0; + eline = line+bpl; + for(p = line; p != eline; ){ + if(eline-p < NRUN) + es = eline; + else + es = p+NRUN; + q = 0; + runlen = 0; + for(hp = hash[h].next; hp; hp = hp->next){ + s = p + runlen; + if(s >= es) + continue; + t = hp->s + runlen; + for(; s >= p; s--) + if(*s != *t--) + goto matchloop; + t += runlen+2; + s += runlen+2; + for(; s < es; s++) + if(*s != *t++) + break; + n = s-p; + if(n > runlen){ + runlen = n; + q = hp->s; + if(n == NRUN) + break; + } + matchloop: ; + } + if(runlen < NMATCH){ + if(ndump == NDUMP){ + if(eout-outp < ndump+1) + goto Bfull; + *outp++ = ndump-1+128; + memmove(outp, dumpbuf, ndump); + outp += ndump; + ndump = 0; + } + dumpbuf[ndump++] = *p; + runlen = 1; + } + else{ + if(ndump != 0){ + if(eout-outp < ndump+1) + goto Bfull; + *outp++ = ndump-1+128; + memmove(outp, dumpbuf, ndump); + outp += ndump; + ndump = 0; + } + offs = p-q-1; + if(eout-outp < 2) + goto Bfull; + *outp++ = ((runlen-NMATCH)<<2) + (offs>>8); + *outp++ = offs&255; + } + for(q = p+runlen; p != q; p++){ + if(cp->prev) + cp->prev->next = 0; + cp->next = hash[h].next; + cp->prev = &hash[h]; + if(cp->next) + cp->next->prev = cp; + cp->prev->next = cp; + cp->s = p; + if(++cp == &chain[NMEM]) + cp = chain; + if(edata-p > NMATCH) + h = hupdate(h, p[NMATCH]); + } + } + if(ndump != 0){ + if(eout-outp < ndump+1) + goto Bfull; + *outp++ = ndump-1+128; + memmove(outp, dumpbuf, ndump); + outp += ndump; + } + line = eline; + loutp = outp; + r.max.y++; + } + Bfull: + if(loutp == outbuf) + goto ErrOut; + n = loutp-outbuf; + sprint(hdr, "%11d %11ld ", r.max.y, n); + write(fd, hdr, 2*12); + write(fd, outbuf, n); + r.min.y = r.max.y; + } + free(data); + free(outbuf); + free(hash); + free(chain); + return 0; +} diff --git a/libmemlayer/Makefile b/libmemlayer/Makefile new file mode 100644 index 0000000..824a4f5 --- /dev/null +++ b/libmemlayer/Makefile @@ -0,0 +1,26 @@ +LIB=libmemlayer.a +CC=gcc +CFLAGS=-I../include -I. -c -ggdb -D_THREAD_SAFE -pthread +O=o + +OFILES=\ + draw.$O\ + lalloc.$O\ + layerop.$O\ + ldelete.$O\ + lhide.$O\ + line.$O\ + load.$O\ + lorigin.$O\ + lsetrefresh.$O\ + ltofront.$O\ + ltorear.$O\ + unload.$O + +$(LIB): $(OFILES) + ar r $(LIB) $(OFILES) + ranlib $(LIB) + +%.$O: %.c + $(CC) $(CFLAGS) $*.c + diff --git a/libmemlayer/draw.c b/libmemlayer/draw.c new file mode 100644 index 0000000..c352a0b --- /dev/null +++ b/libmemlayer/draw.c @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include + +struct Draw +{ + Point deltas; + Point deltam; + Memlayer *dstlayer; + Memimage *src; + Memimage *mask; + int op; +}; + +static +void +ldrawop(Memimage *dst, Rectangle screenr, Rectangle clipr, void *etc, int insave) +{ + struct Draw *d; + Point p0, p1; + Rectangle oclipr, srcr, r, mr; + int ok; + + d = etc; + if(insave && d->dstlayer->save==nil) + return; + + p0 = addpt(screenr.min, d->deltas); + p1 = addpt(screenr.min, d->deltam); + + if(insave){ + r = rectsubpt(screenr, d->dstlayer->delta); + clipr = rectsubpt(clipr, d->dstlayer->delta); + }else + r = screenr; + + /* now in logical coordinates */ + + /* clipr may have narrowed what we should draw on, so clip if necessary */ + if(!rectinrect(r, clipr)){ + oclipr = dst->clipr; + dst->clipr = clipr; + ok = drawclip(dst, &r, d->src, &p0, d->mask, &p1, &srcr, &mr); + dst->clipr = oclipr; + if(!ok) + return; + } + memdraw(dst, r, d->src, p0, d->mask, p1, d->op); +} + +void +memdraw(Memimage *dst, Rectangle r, Memimage *src, Point p0, Memimage *mask, Point p1, int op) +{ + struct Draw d; + Rectangle srcr, tr, mr; + Memlayer *dl, *sl; + + if(drawdebug) + iprint("memdraw %p %R %p %P %p %P\n", dst, r, src, p0, mask, p1); + + if(mask == nil) + mask = memopaque; + + if(mask->layer){ +if(drawdebug) iprint("mask->layer != nil\n"); + return; /* too hard, at least for now */ + } + + Top: + if(dst->layer==nil && src->layer==nil){ + memimagedraw(dst, r, src, p0, mask, p1, op); + return; + } + + if(drawclip(dst, &r, src, &p0, mask, &p1, &srcr, &mr) == 0){ +if(drawdebug) iprint("drawclip dstcr %R srccr %R maskcr %R\n", dst->clipr, src->clipr, mask->clipr); + return; + } + + /* + * Convert to screen coordinates. + */ + dl = dst->layer; + if(dl != nil){ + r.min.x += dl->delta.x; + r.min.y += dl->delta.y; + r.max.x += dl->delta.x; + r.max.y += dl->delta.y; + } + Clearlayer: + if(dl!=nil && dl->clear){ + if(src == dst){ + p0.x += dl->delta.x; + p0.y += dl->delta.y; + src = dl->screen->image; + } + dst = dl->screen->image; + goto Top; + } + + sl = src->layer; + if(sl != nil){ + p0.x += sl->delta.x; + p0.y += sl->delta.y; + srcr.min.x += sl->delta.x; + srcr.min.y += sl->delta.y; + srcr.max.x += sl->delta.x; + srcr.max.y += sl->delta.y; + } + + /* + * Now everything is in screen coordinates. + * mask is an image. dst and src are images or obscured layers. + */ + + /* + * if dst and src are the same layer, just draw in save area and expose. + */ + if(dl!=nil && dst==src){ + if(dl->save == nil) + return; /* refresh function makes this case unworkable */ + if(rectXrect(r, srcr)){ + tr = r; + if(srcr.min.x < tr.min.x){ + p1.x += tr.min.x - srcr.min.x; + tr.min.x = srcr.min.x; + } + if(srcr.min.y < tr.min.y){ + p1.y += tr.min.x - srcr.min.x; + tr.min.y = srcr.min.y; + } + if(srcr.max.x > tr.max.x) + tr.max.x = srcr.max.x; + if(srcr.max.y > tr.max.y) + tr.max.y = srcr.max.y; + memlhide(dst, tr); + }else{ + memlhide(dst, r); + memlhide(dst, srcr); + } + memdraw(dl->save, rectsubpt(r, dl->delta), dl->save, + subpt(srcr.min, src->layer->delta), mask, p1, op); + memlexpose(dst, r); + return; + } + + if(sl){ + if(sl->clear){ + src = sl->screen->image; + if(dl != nil){ + r.min.x -= dl->delta.x; + r.min.y -= dl->delta.y; + r.max.x -= dl->delta.x; + r.max.y -= dl->delta.y; + } + goto Top; + } + /* relatively rare case; use save area */ + if(sl->save == nil) + return; /* refresh function makes this case unworkable */ + memlhide(src, srcr); + /* convert back to logical coordinates */ + p0.x -= sl->delta.x; + p0.y -= sl->delta.y; + srcr.min.x -= sl->delta.x; + srcr.min.y -= sl->delta.y; + srcr.max.x -= sl->delta.x; + srcr.max.y -= sl->delta.y; + src = src->layer->save; + } + + /* + * src is now an image. dst may be an image or a clear layer + */ + if(dst->layer==nil) + goto Top; + if(dst->layer->clear) + goto Clearlayer; + + /* + * dst is an obscured layer + */ + d.deltas = subpt(p0, r.min); + d.deltam = subpt(p1, r.min); + d.dstlayer = dl; + d.src = src; + d.op = op; + d.mask = mask; + _memlayerop(ldrawop, dst, r, r, &d); +} diff --git a/libmemlayer/lalloc.c b/libmemlayer/lalloc.c new file mode 100644 index 0000000..8f2e966 --- /dev/null +++ b/libmemlayer/lalloc.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include + +Memimage* +memlalloc(Memscreen *s, Rectangle screenr, Refreshfn refreshfn, void *refreshptr, ulong val) +{ + Memlayer *l; + Memimage *n; + static Memimage *paint; + + if(paint == nil){ + paint = allocmemimage(Rect(0,0,1,1), RGBA32); + if(paint == nil) + return nil; + paint->flags |= Frepl; + paint->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); + } + + n = allocmemimaged(screenr, s->image->chan, s->image->data, s->image->X); + if(n == nil) + return nil; + l = malloc(sizeof(Memlayer)); + if(l == nil){ + free(n); + return nil; + } + + l->screen = s; + if(refreshfn) + l->save = nil; + else{ + l->save = allocmemimage(screenr, s->image->chan); + if(l->save == nil){ + free(l); + free(n); + return nil; + } + /* allocmemimage doesn't initialize memory; this paints save area */ + if(val != DNofill) + memfillcolor(l->save, val); + } + l->refreshfn = refreshfn; + l->refreshptr = nil; /* don't set it until we're done */ + l->screenr = screenr; + l->delta = Pt(0,0); + + n->data->ref++; + n->zero = s->image->zero; + n->width = s->image->width; + n->layer = l; + + /* start with new window behind all existing ones */ + l->front = s->rearmost; + l->rear = nil; + if(s->rearmost) + s->rearmost->layer->rear = n; + s->rearmost = n; + if(s->frontmost == nil) + s->frontmost = n; + l->clear = 0; + + /* now pull new window to front */ + _memltofrontfill(n, val != DNofill); + l->refreshptr = refreshptr; + + /* + * paint with requested color; previously exposed areas are already right + * if this window has backing store, but just painting the whole thing is simplest. + */ + if(val != DNofill){ + memsetchan(paint, n->chan); + memfillcolor(paint, val); + memdraw(n, n->r, paint, n->r.min, nil, n->r.min, S); + } + return n; +} diff --git a/libmemlayer/layerop.c b/libmemlayer/layerop.c new file mode 100644 index 0000000..27fdcfc --- /dev/null +++ b/libmemlayer/layerop.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include + +#define RECUR(a,b,c,d) _layerop(fn, i, Rect(a.x, b.y, c.x, d.y), clipr, etc, front->layer->rear); + +static void +_layerop( + void (*fn)(Memimage*, Rectangle, Rectangle, void*, int), + Memimage *i, + Rectangle r, + Rectangle clipr, + void *etc, + Memimage *front) +{ + Rectangle fr; + + Top: + if(front == i){ + /* no one is in front of this part of window; use the screen */ + fn(i->layer->screen->image, r, clipr, etc, 0); + return; + } + fr = front->layer->screenr; + if(rectXrect(r, fr) == 0){ + /* r doesn't touch this window; continue on next rearmost */ + // assert(front && front->layer && front->layer->screen && front->layer->rear); + front = front->layer->rear; + goto Top; + } + if(fr.max.y < r.max.y){ + RECUR(r.min, fr.max, r.max, r.max); + r.max.y = fr.max.y; + } + if(r.min.y < fr.min.y){ + RECUR(r.min, r.min, r.max, fr.min); + r.min.y = fr.min.y; + } + if(fr.max.x < r.max.x){ + RECUR(fr.max, r.min, r.max, r.max); + r.max.x = fr.max.x; + } + if(r.min.x < fr.min.x){ + RECUR(r.min, r.min, fr.min, r.max); + r.min.x = fr.min.x; + } + /* r is covered by front, so put in save area */ + (*fn)(i->layer->save, r, clipr, etc, 1); +} + +/* + * Assumes incoming rectangle has already been clipped to i's logical r and clipr + */ +void +_memlayerop( + void (*fn)(Memimage*, Rectangle, Rectangle, void*, int), + Memimage *i, + Rectangle screenr, /* clipped to window boundaries */ + Rectangle clipr, /* clipped also to clipping rectangles of hierarchy */ + void *etc) +{ + Memlayer *l; + Rectangle r, scr; + + l = i->layer; + if(!rectclip(&screenr, l->screenr)) + return; + if(l->clear){ + fn(l->screen->image, screenr, clipr, etc, 0); + return; + } + r = screenr; + scr = l->screen->image->clipr; + + /* + * Do the piece on the screen + */ + if(rectclip(&screenr, scr)) + _layerop(fn, i, screenr, clipr, etc, l->screen->frontmost); + if(rectinrect(r, scr)) + return; + + /* + * Do the piece off the screen + */ + if(!rectXrect(r, scr)){ + /* completely offscreen; easy */ + fn(l->save, r, clipr, etc, 1); + return; + } + if(r.min.y < scr.min.y){ + /* above screen */ + fn(l->save, Rect(r.min.x, r.min.y, r.max.x, scr.min.y), clipr, etc, 1); + r.min.y = scr.min.y; + } + if(r.max.y > scr.max.y){ + /* below screen */ + fn(l->save, Rect(r.min.x, scr.max.y, r.max.x, r.max.y), clipr, etc, 1); + r.max.y = scr.max.y; + } + if(r.min.x < scr.min.x){ + /* left of screen */ + fn(l->save, Rect(r.min.x, r.min.y, scr.min.x, r.max.y), clipr, etc, 1); + r.min.x = scr.min.x; + } + if(r.max.x > scr.max.x){ + /* right of screen */ + fn(l->save, Rect(scr.max.x, r.min.y, r.max.x, r.max.y), clipr, etc, 1); + } +} diff --git a/libmemlayer/ldelete.c b/libmemlayer/ldelete.c new file mode 100644 index 0000000..34cd6ea --- /dev/null +++ b/libmemlayer/ldelete.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +void +memldelete(Memimage *i) +{ + Memscreen *s; + Memlayer *l; + + l = i->layer; + /* free backing store and disconnect refresh, to make pushback fast */ + freememimage(l->save); + l->save = nil; + l->refreshptr = nil; + memltorear(i); + + /* window is now the rearmost; clean up screen structures and deallocate */ + s = i->layer->screen; + if(s->fill){ + i->clipr = i->r; + memdraw(i, i->r, s->fill, i->r.min, nil, i->r.min, S); + } + if(l->front){ + l->front->layer->rear = nil; + s->rearmost = l->front; + }else{ + s->frontmost = nil; + s->rearmost = nil; + } + free(l); + freememimage(i); +} + +/* + * Just free the data structures, don't do graphics + */ +void +memlfree(Memimage *i) +{ + Memlayer *l; + + l = i->layer; + freememimage(l->save); + free(l); + freememimage(i); +} + +void +_memlsetclear(Memscreen *s) +{ + Memimage *i, *j; + Memlayer *l; + + for(i=s->rearmost; i; i=i->layer->front){ + l = i->layer; + l->clear = rectinrect(l->screenr, l->screen->image->clipr); + if(l->clear) + for(j=l->front; j; j=j->layer->front) + if(rectXrect(l->screenr, j->layer->screenr)){ + l->clear = 0; + break; + } + } +} diff --git a/libmemlayer/lhide.c b/libmemlayer/lhide.c new file mode 100644 index 0000000..d6aaa55 --- /dev/null +++ b/libmemlayer/lhide.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +/* + * Hide puts that portion of screenr now on the screen into the window's save area. + * Expose puts that portion of screenr now in the save area onto the screen. + * + * Hide and Expose both require that the layer structures in the screen + * match the geometry they are being asked to update, that is, they update the + * save area (hide) or screen (expose) based on what those structures tell them. + * This means they must be called at the correct time during window shuffles. + */ + +static +void +lhideop(Memimage *src, Rectangle screenr, Rectangle clipr, void *etc, int insave) +{ + Rectangle r; + Memlayer *l; + + USED(clipr.min.x); + USED(insave); + l = etc; + if(src != l->save){ /* do nothing if src is already in save area */ + r = rectsubpt(screenr, l->delta); + memdraw(l->save, r, src, screenr.min, nil, screenr.min, S); + } +} + +void +memlhide(Memimage *i, Rectangle screenr) +{ + if(i->layer->save == nil) + return; + if(rectclip(&screenr, i->layer->screen->image->r) == 0) + return; + _memlayerop(lhideop, i, screenr, screenr, i->layer); +} + +static +void +lexposeop(Memimage *dst, Rectangle screenr, Rectangle clipr, void *etc, int insave) +{ + Memlayer *l; + Rectangle r; + + USED(clipr.min.x); + if(insave) /* if dst is save area, don't bother */ + return; + l = etc; + r = rectsubpt(screenr, l->delta); + if(l->save) + memdraw(dst, screenr, l->save, r.min, nil, r.min, S); + else + l->refreshfn(dst, r, l->refreshptr); +} + +void +memlexpose(Memimage *i, Rectangle screenr) +{ + if(rectclip(&screenr, i->layer->screen->image->r) == 0) + return; + _memlayerop(lexposeop, i, screenr, screenr, i->layer); +} diff --git a/libmemlayer/line.c b/libmemlayer/line.c new file mode 100644 index 0000000..8c09a53 --- /dev/null +++ b/libmemlayer/line.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include + +struct Lline +{ + Point p0; + Point p1; + Point delta; + int end0; + int end1; + int radius; + Point sp; + Memlayer *dstlayer; + Memimage *src; + int op; +}; + +static void llineop(Memimage*, Rectangle, Rectangle, void*, int); + +static +void +_memline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, Rectangle clipr, int op) +{ + Rectangle r; + struct Lline ll; + Point d; + int srcclipped; + Memlayer *dl; + + if(radius < 0) + return; + if(src->layer) /* can't draw line with layered source */ + return; + srcclipped = 0; + + Top: + dl = dst->layer; + if(dl == nil){ + _memimageline(dst, p0, p1, end0, end1, radius, src, sp, clipr, op); + return; + } + if(!srcclipped){ + d = subpt(sp, p0); + if(rectclip(&clipr, rectsubpt(src->clipr, d)) == 0) + return; + if((src->flags&Frepl)==0 && rectclip(&clipr, rectsubpt(src->r, d))==0) + return; + srcclipped = 1; + } + + /* dst is known to be a layer */ + p0.x += dl->delta.x; + p0.y += dl->delta.y; + p1.x += dl->delta.x; + p1.y += dl->delta.y; + clipr.min.x += dl->delta.x; + clipr.min.y += dl->delta.y; + clipr.max.x += dl->delta.x; + clipr.max.y += dl->delta.y; + if(dl->clear){ + dst = dst->layer->screen->image; + goto Top; + } + + /* XXX */ + /* this is not the correct set of tests */ +// if(log2[dst->depth] != log2[src->depth] || log2[dst->depth]!=3) +// return; + + /* can't use sutherland-cohen clipping because lines are wide */ + r = memlinebbox(p0, p1, end0, end1, radius); + /* + * r is now a bounding box for the line; + * use it as a clipping rectangle for subdivision + */ + if(rectclip(&r, clipr) == 0) + return; + ll.p0 = p0; + ll.p1 = p1; + ll.end0 = end0; + ll.end1 = end1; + ll.sp = sp; + ll.dstlayer = dst->layer; + ll.src = src; + ll.radius = radius; + ll.delta = dl->delta; + ll.op = op; + _memlayerop(llineop, dst, r, r, &ll); +} + +static +void +llineop(Memimage *dst, Rectangle screenr, Rectangle clipr, void *etc, int insave) +{ + struct Lline *ll; + Point p0, p1; + + USED(screenr.min.x); + ll = etc; + if(insave && ll->dstlayer->save==nil) + return; + if(!rectclip(&clipr, screenr)) + return; + if(insave){ + p0 = subpt(ll->p0, ll->delta); + p1 = subpt(ll->p1, ll->delta); + clipr = rectsubpt(clipr, ll->delta); + }else{ + p0 = ll->p0; + p1 = ll->p1; + } + _memline(dst, p0, p1, ll->end0, ll->end1, ll->radius, ll->src, ll->sp, clipr, ll->op); +} + +void +memline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, int op) +{ + _memline(dst, p0, p1, end0, end1, radius, src, sp, dst->clipr, op); +} diff --git a/libmemlayer/load.c b/libmemlayer/load.c new file mode 100644 index 0000000..d211564 --- /dev/null +++ b/libmemlayer/load.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include + +int +memload(Memimage *dst, Rectangle r, uchar *data, int n, int iscompressed) +{ + int (*loadfn)(Memimage*, Rectangle, uchar*, int); + Memimage *tmp; + Memlayer *dl; + Rectangle lr; + int dx; + + loadfn = loadmemimage; + if(iscompressed) + loadfn = cloadmemimage; + + Top: + dl = dst->layer; + if(dl == nil) + return loadfn(dst, r, data, n); + + /* + * Convert to screen coordinates. + */ + lr = r; + r.min.x += dl->delta.x; + r.min.y += dl->delta.y; + r.max.x += dl->delta.x; + r.max.y += dl->delta.y; + dx = dl->delta.x&(7/dst->depth); + if(dl->clear && dx==0){ + dst = dl->screen->image; + goto Top; + } + + /* + * dst is an obscured layer or data is unaligned + */ + if(dl->save && dx==0){ + n = loadfn(dl->save, lr, data, n); + if(n > 0) + memlexpose(dst, r); + return n; + } + tmp = allocmemimage(lr, dst->chan); + if(tmp == nil) + return -1; + n = loadfn(tmp, lr, data, n); + memdraw(dst, lr, tmp, lr.min, nil, lr.min, S); + freememimage(tmp); + return n; +} diff --git a/libmemlayer/lorigin.c b/libmemlayer/lorigin.c new file mode 100644 index 0000000..0926ee8 --- /dev/null +++ b/libmemlayer/lorigin.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include + +/* + * Place i so i->r.min = log, i->layer->screenr.min == scr. +*/ +int +memlorigin(Memimage *i, Point log, Point scr) +{ + Memlayer *l; + Memscreen *s; + Memimage *t, *shad, *nsave; + Rectangle x, newr, oldr; + Point delta; + int overlap, eqlog, eqscr, wasclear; + + l = i->layer; + s = l->screen; + oldr = l->screenr; + newr = Rect(scr.x, scr.y, scr.x+Dx(oldr), scr.y+Dy(oldr)); + eqscr = eqpt(scr, oldr.min); + eqlog = eqpt(log, i->r.min); + if(eqscr && eqlog) + return 0; + nsave = nil; + if(eqlog==0 && l->save!=nil){ + nsave = allocmemimage(Rect(log.x, log.y, log.x+Dx(oldr), log.y+Dy(oldr)), i->chan); + if(nsave == nil) + return -1; + } + + /* + * Bring it to front and move logical coordinate system. + */ + memltofront(i); + wasclear = l->clear; + if(nsave){ + if(!wasclear) + memimagedraw(nsave, nsave->r, l->save, l->save->r.min, nil, Pt(0,0), S); + freememimage(l->save); + l->save = nsave; + } + delta = subpt(log, i->r.min); + i->r = rectaddpt(i->r, delta); + i->clipr = rectaddpt(i->clipr, delta); + l->delta = subpt(l->screenr.min, i->r.min); + if(eqscr) + return 0; + + /* + * To clean up old position, make a shadow window there, don't paint it, + * push it behind this one, and (later) delete it. Because the refresh function + * for this fake window is a no-op, this will cause no graphics action except + * to restore the background and expose the windows previously hidden. + */ + shad = memlalloc(s, oldr, memlnorefresh, nil, DNofill); + if(shad == nil) + return -1; + s->frontmost = i; + if(s->rearmost == i) + s->rearmost = shad; + else + l->rear->layer->front = shad; + shad->layer->front = i; + shad->layer->rear = l->rear; + l->rear = shad; + l->front = nil; + shad->layer->clear = 0; + + /* + * Shadow is now holding down the fort at the old position. + * Move the window and hide things obscured by new position. + */ + for(t=l->rear->layer->rear; t!=nil; t=t->layer->rear){ + x = newr; + overlap = rectclip(&x, t->layer->screenr); + if(overlap){ + memlhide(t, x); + t->layer->clear = 0; + } + } + l->screenr = newr; + l->delta = subpt(scr, i->r.min); + l->clear = rectinrect(newr, l->screen->image->clipr); + + /* + * Everything's covered. Copy to new position and delete shadow window. + */ + if(wasclear) + memdraw(s->image, newr, s->image, oldr.min, nil, Pt(0,0), S); + else + memlexpose(i, newr); + memldelete(shad); + + return 1; +} + +void +memlnorefresh(Memimage *l, Rectangle r, void *v) +{ + USED(l); + USED(r.min.x); + USED(v); +} diff --git a/libmemlayer/lsetrefresh.c b/libmemlayer/lsetrefresh.c new file mode 100644 index 0000000..44079be --- /dev/null +++ b/libmemlayer/lsetrefresh.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include + +int +memlsetrefresh(Memimage *i, Refreshfn fn, void *ptr) +{ + Memlayer *l; + + l = i->layer; + if(l->refreshfn!=nil && fn!=nil){ /* just change functions */ + l->refreshfn = fn; + l->refreshptr = ptr; + return 1; + } + + if(l->refreshfn == nil){ /* is using backup image; just free it */ + freememimage(l->save); + l->save = nil; + l->refreshfn = fn; + l->refreshptr = ptr; + return 1; + } + + l->save = allocmemimage(i->r, i->chan); + if(l->save == nil) + return 0; + /* easiest way is just to update the entire save area */ + l->refreshfn(i, i->r, l->refreshptr); + l->refreshfn = nil; + l->refreshptr = nil; + return 1; +} diff --git a/libmemlayer/ltofront.c b/libmemlayer/ltofront.c new file mode 100644 index 0000000..447b40b --- /dev/null +++ b/libmemlayer/ltofront.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include + +/* + * Pull i towards top of screen, just behind front +*/ +static +void +_memltofront(Memimage *i, Memimage *front, int fill) +{ + Memlayer *l; + Memscreen *s; + Memimage *f, *ff, *rr; + Rectangle x; + int overlap; + + l = i->layer; + s = l->screen; + while(l->front != front){ + f = l->front; + x = l->screenr; + overlap = rectclip(&x, f->layer->screenr); + if(overlap){ + memlhide(f, x); + f->layer->clear = 0; + } + /* swap l and f in screen's list */ + ff = f->layer->front; + rr = l->rear; + if(ff == nil) + s->frontmost = i; + else + ff->layer->rear = i; + if(rr == nil) + s->rearmost = f; + else + rr->layer->front = f; + l->front = ff; + l->rear = f; + f->layer->front = i; + f->layer->rear = rr; + if(overlap && fill) + memlexpose(i, x); + } +} + +void +_memltofrontfill(Memimage *i, int fill) +{ + _memltofront(i, nil, fill); + _memlsetclear(i->layer->screen); +} + +void +memltofront(Memimage *i) +{ + _memltofront(i, nil, 1); + _memlsetclear(i->layer->screen); +} + +void +memltofrontn(Memimage **ip, int n) +{ + Memimage *i, *front; + Memscreen *s; + + if(n == 0) + return; + front = nil; + while(--n >= 0){ + i = *ip++; + _memltofront(i, front, 1); + front = i; + } + s = front->layer->screen; + _memlsetclear(s); +} diff --git a/libmemlayer/ltorear.c b/libmemlayer/ltorear.c new file mode 100644 index 0000000..d53e8cc --- /dev/null +++ b/libmemlayer/ltorear.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +void +_memltorear(Memimage *i, Memimage *rear) +{ + Memlayer *l; + Memscreen *s; + Memimage *f, *r, *rr; + Rectangle x; + int overlap; + + l = i->layer; + s = l->screen; + while(l->rear != rear){ + r = l->rear; + x = l->screenr; + overlap = rectclip(&x, r->layer->screenr); + if(overlap){ + memlhide(i, x); + l->clear = 0; + } + /* swap l and r in screen's list */ + rr = r->layer->rear; + f = l->front; + if(rr == nil) + s->rearmost = i; + else + rr->layer->front = i; + if(f == nil) + s->frontmost = r; + else + f->layer->rear = r; + l->rear = rr; + l->front = r; + r->layer->rear = i; + r->layer->front = f; + if(overlap) + memlexpose(r, x); + } +} + +void +memltorear(Memimage *i) +{ + _memltorear(i, nil); + _memlsetclear(i->layer->screen); +} + +void +memltorearn(Memimage **ip, int n) +{ + Memimage *i, *rear; + Memscreen *s; + + if(n == 0) + return; + rear = nil; + while(--n >= 0){ + i = *ip++; + _memltorear(i, rear); + rear = i; + } + s = rear->layer->screen; + _memlsetclear(s); +} diff --git a/libmemlayer/mkfile b/libmemlayer/mkfile new file mode 100644 index 0000000..fa659a5 --- /dev/null +++ b/libmemlayer/mkfile @@ -0,0 +1,23 @@ +<$DSRC/mkfile-$CONF +TARG=libmemlayer.$L + +OFILES=\ + draw.$O\ + lalloc.$O\ + layerop.$O\ + ldelete.$O\ + lhide.$O\ + line.$O\ + load.$O\ + lorigin.$O\ + lsetrefresh.$O\ + ltofront.$O\ + ltorear.$O\ + unload.$O\ + +HFILES=\ + ../include/memlayer.h\ + ../include/memdraw.h\ + ../include/draw.h + +<$DSRC/mklib-$CONF diff --git a/libmemlayer/unload.c b/libmemlayer/unload.c new file mode 100644 index 0000000..23a8b29 --- /dev/null +++ b/libmemlayer/unload.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +int +memunload(Memimage *src, Rectangle r, uchar *data, int n) +{ + Memimage *tmp; + Memlayer *dl; + Rectangle lr; + int dx; + + Top: + dl = src->layer; + if(dl == nil) + return unloadmemimage(src, r, data, n); + + /* + * Convert to screen coordinates. + */ + lr = r; + r.min.x += dl->delta.x; + r.min.y += dl->delta.y; + r.max.x += dl->delta.x; + r.max.y += dl->delta.y; + dx = dl->delta.x&(7/src->depth); + if(dl->clear && dx==0){ + src = dl->screen->image; + goto Top; + } + + /* + * src is an obscured layer or data is unaligned + */ + if(dl->save && dx==0){ + if(dl->refreshfn != nil) + return -1; /* can't unload window if it's not Refbackup */ + if(n > 0) + memlhide(src, r); + n = unloadmemimage(dl->save, lr, data, n); + return n; + } + tmp = allocmemimage(lr, src->chan); + if(tmp == nil) + return -1; + memdraw(tmp, lr, src, lr.min, nil, lr.min, S); + n = unloadmemimage(tmp, lr, data, n); + freememimage(tmp); + return n; +} diff --git a/libmp/Makefile b/libmp/Makefile new file mode 100644 index 0000000..ddc9c11 --- /dev/null +++ b/libmp/Makefile @@ -0,0 +1,46 @@ +# N.B. This is used only for secstore. It needn't be fast. + +LIB=libmp.a +CC=gcc +CFLAGS=-I../include -I. -c -ggdb -D_THREAD_SAFE -pthread +O=o + +OFILES=\ + betomp.$O\ + crt.$O\ + letomp.$O\ + mpadd.$O\ + mpaux.$O\ + mpcmp.$O\ + mpdigdiv.$O\ + mpdiv.$O\ + mpeuclid.$O\ + mpexp.$O\ + mpextendedgcd.$O\ + mpfmt.$O\ + mpinvert.$O\ + mpleft.$O\ + mpmod.$O\ + mpmul.$O\ + mprand.$O\ + mpright.$O\ + mpsub.$O\ + mptobe.$O\ + mptoi.$O\ + mptole.$O\ + mptoui.$O\ + mptouv.$O\ + mptov.$O\ + mpvecadd.$O\ + mpveccmp.$O\ + mpvecdigmuladd.$O\ + mpvecsub.$O\ + strtomp.$O + +$(LIB): $(OFILES) + ar r $(LIB) $(OFILES) + ranlib $(LIB) + +%.$O: %.c + $(CC) $(CFLAGS) $*.c + diff --git a/libmp/betomp.c b/libmp/betomp.c new file mode 100644 index 0000000..95935fc --- /dev/null +++ b/libmp/betomp.c @@ -0,0 +1,40 @@ +#include "os.h" +#include +#include "dat.h" + +// convert a big-endian byte array (most significant byte first) to an mpint +mpint* +betomp(uchar *p, uint n, mpint *b) +{ + int m, s; + mpdigit x; + + if(b == nil) + b = mpnew(0); + + // dump leading zeros + while(*p == 0 && n > 1){ + p++; + n--; + } + + // get the space + mpbits(b, n*8); + b->top = DIGITS(n*8); + m = b->top-1; + + // first digit might not be Dbytes long + s = ((n-1)*8)%Dbits; + x = 0; + for(; n > 0; n--){ + x |= ((mpdigit)(*p++)) << s; + s -= 8; + if(s < 0){ + b->p[m--] = x; + s = Dbits-8; + x = 0; + } + } + + return b; +} diff --git a/libmp/crt.c b/libmp/crt.c new file mode 100644 index 0000000..ddf26ed --- /dev/null +++ b/libmp/crt.c @@ -0,0 +1,122 @@ +#include "os.h" +#include +#include + +// chinese remainder theorem +// +// handbook of applied cryptography, menezes et al, 1997, pp 610 - 613 + +struct CRTpre +{ + int n; // number of moduli + mpint **m; // pointer to moduli + mpint **c; // precomputed coefficients + mpint **p; // precomputed products + mpint *a[1]; // local storage +}; + +// setup crt info, returns a newly created structure +CRTpre* +crtpre(int n, mpint **m) +{ + CRTpre *crt; + int i, j; + mpint *u; + + crt = malloc(sizeof(CRTpre)+sizeof(mpint)*3*n); + if(crt == nil) + sysfatal("crtpre: %r"); + crt->m = crt->a; + crt->c = crt->a+n; + crt->p = crt->c+n; + crt->n = n; + + // make a copy of the moduli + for(i = 0; i < n; i++) + crt->m[i] = mpcopy(m[i]); + + // precompute the products + u = mpcopy(mpone); + for(i = 0; i < n; i++){ + mpmul(u, m[i], u); + crt->p[i] = mpcopy(u); + } + + // precompute the coefficients + for(i = 1; i < n; i++){ + crt->c[i] = mpcopy(mpone); + for(j = 0; j < i; j++){ + mpinvert(m[j], m[i], u); + mpmul(u, crt->c[i], u); + mpmod(u, m[i], crt->c[i]); + } + } + + mpfree(u); + + return crt; +} + +void +crtprefree(CRTpre *crt) +{ + int i; + + for(i = 0; i < crt->n; i++){ + if(i != 0) + mpfree(crt->c[i]); + mpfree(crt->p[i]); + mpfree(crt->m[i]); + } + free(crt); +} + +// convert to residues, returns a newly created structure +CRTres* +crtin(CRTpre *crt, mpint *x) +{ + int i; + CRTres *res; + + res = malloc(sizeof(CRTres)+sizeof(mpint)*crt->n); + if(res == nil) + sysfatal("crtin: %r"); + res->n = crt->n; + for(i = 0; i < res->n; i++){ + res->r[i] = mpnew(0); + mpmod(x, crt->m[i], res->r[i]); + } + return res; +} + +// garners algorithm for converting residue form to linear +void +crtout(CRTpre *crt, CRTres *res, mpint *x) +{ + mpint *u; + int i; + + u = mpnew(0); + mpassign(res->r[0], x); + + for(i = 1; i < crt->n; i++){ + mpsub(res->r[i], x, u); + mpmul(u, crt->c[i], u); + mpmod(u, crt->m[i], u); + mpmul(u, crt->p[i-1], u); + mpadd(x, u, x); + } + + mpfree(u); +} + +// free the residue +void +crtresfree(CRTres *res) +{ + int i; + + for(i = 0; i < res->n; i++) + mpfree(res->r[i]); + free(res); +} diff --git a/libmp/crttest.c b/libmp/crttest.c new file mode 100644 index 0000000..b58e173 --- /dev/null +++ b/libmp/crttest.c @@ -0,0 +1,54 @@ +#include "os.h" +#include +#include + +void +testcrt(mpint **p) +{ + CRTpre *crt; + CRTres *res; + mpint *m, *x, *y; + int i; + + fmtinstall('B', mpconv); + + // get a modulus and a test number + m = mpnew(1024+160); + mpmul(p[0], p[1], m); + x = mpnew(1024+160); + mpadd(m, mpone, x); + + // do the precomputation for crt conversion + crt = crtpre(2, p); + + // convert x to residues + res = crtin(crt, x); + + // convert back + y = mpnew(1024+160); + crtout(crt, res, y); + print("x %B\ny %B\n", x, y); + mpfree(m); + mpfree(x); + mpfree(y); +} + +void +main(void) +{ + int i; + mpint *p[2]; + long start; + + start = time(0); + for(i = 0; i < 10; i++){ + p[0] = mpnew(1024); + p[1] = mpnew(1024); + DSAprimes(p[0], p[1], nil); + testcrt(p); + mpfree(p[0]); + mpfree(p[1]); + } + print("%d secs with more\n", time(0)-start); + exits(0); +} diff --git a/libmp/dat.h b/libmp/dat.h new file mode 100644 index 0000000..69eef11 --- /dev/null +++ b/libmp/dat.h @@ -0,0 +1,15 @@ +#define mpdighi (mpdigit)(1<<(Dbits-1)) +#define DIGITS(x) ((Dbits - 1 + (x))/Dbits) + +// for converting between int's and mpint's +#define MAXUINT ((uint)-1) +#define MAXINT (MAXUINT>>1) +#define MININT (MAXINT+1) + +// for converting between vlongs's and mpint's +// #define MAXUVLONG (~0ULL) +// #define MAXVLONG (MAXUVLONG>>1) +// #define MINVLONG (MAXVLONG+1ULL) +#define MAXUVLONG ((uvlong) ~0) +#define MAXVLONG (MAXUVLONG>>1) +#define MINVLONG (MAXVLONG+((uvlong) 1)) diff --git a/libmp/letomp.c b/libmp/letomp.c new file mode 100644 index 0000000..5b8ee92 --- /dev/null +++ b/libmp/letomp.c @@ -0,0 +1,29 @@ +#include "os.h" +#include +#include "dat.h" + +// convert a little endian byte array (least significant byte first) to an mpint +mpint* +letomp(uchar *s, uint n, mpint *b) +{ + int i=0, m = 0; + mpdigit x=0; + + if(b == nil) + b = mpnew(0); + mpbits(b, 8*n); + b->top = DIGITS(8*n); + for(; n > 0; n--){ + x |= ((mpdigit)(*s++)) << i; + i += 8; + if(i == Dbits){ + b->p[m++] = x; + i = 0; + x = 0; + } + } + if(i > 0) + b->p[m++] = x; + b->top = m; + return b; +} diff --git a/libmp/mkfile b/libmp/mkfile new file mode 100644 index 0000000..77d325e --- /dev/null +++ b/libmp/mkfile @@ -0,0 +1,40 @@ +TARG=libmp.$L +<$DSRC/mkfile-$CONF + +OFILES=\ + betomp.$O\ + crt.$O\ + letomp.$O\ + mpadd.$O\ + mpaux.$O\ + mpcmp.$O\ + mpdigdiv.$O\ + mpdiv.$O\ + mpeuclid.$O\ + mpexp.$O\ + mpextendedgcd.$O\ + mpfmt.$O\ + mpinvert.$O\ + mpleft.$O\ + mpmod.$O\ + mpmul.$O\ + mprand.$O\ + mpright.$O\ + mpsub.$O\ + mptobe.$O\ + mptoi.$O\ + mptole.$O\ + mptoui.$O\ + mptouv.$O\ + mptov.$O\ + mpvecadd.$O\ + mpveccmp.$O\ + mpvecdigmuladd.$O\ + mpvecsub.$O\ + strtomp.$O + +HFILE=\ + dat.h\ + os.h + +<$DSRC/mklib-$CONF diff --git a/libmp/mpadd.c b/libmp/mpadd.c new file mode 100644 index 0000000..6022a64 --- /dev/null +++ b/libmp/mpadd.c @@ -0,0 +1,54 @@ +#include "os.h" +#include +#include "dat.h" + +// sum = abs(b1) + abs(b2), i.e., add the magnitudes +void +mpmagadd(mpint *b1, mpint *b2, mpint *sum) +{ + int m, n; + mpint *t; + + // get the sizes right + if(b2->top > b1->top){ + t = b1; + b1 = b2; + b2 = t; + } + n = b1->top; + m = b2->top; + if(n == 0){ + mpassign(mpzero, sum); + return; + } + if(m == 0){ + mpassign(b1, sum); + return; + } + mpbits(sum, (n+1)*Dbits); + sum->top = n+1; + + mpvecadd(b1->p, n, b2->p, m, sum->p); + sum->sign = 1; + + mpnorm(sum); +} + +// sum = b1 + b2 +void +mpadd(mpint *b1, mpint *b2, mpint *sum) +{ + int sign; + + if(b1->sign != b2->sign){ + if(b1->sign < 0) + mpmagsub(b2, b1, sum); + else + mpmagsub(b1, b2, sum); + } else { + sign = b1->sign; + mpmagadd(b1, b2, sum); + if(sum->top != 0) + sum->sign = sign; + } +} diff --git a/libmp/mpaux.c b/libmp/mpaux.c new file mode 100644 index 0000000..9d33138 --- /dev/null +++ b/libmp/mpaux.c @@ -0,0 +1,185 @@ +#include "os.h" +#include +#include "dat.h" + +static mpdigit _mptwodata[1] = { 2 }; +static mpint _mptwo = +{ + 1, + 1, + 1, + _mptwodata, + MPstatic +}; +mpint *mptwo = &_mptwo; + +static mpdigit _mponedata[1] = { 1 }; +static mpint _mpone = +{ + 1, + 1, + 1, + _mponedata, + MPstatic +}; +mpint *mpone = &_mpone; + +static mpdigit _mpzerodata[1] = { 0 }; +static mpint _mpzero = +{ + 1, + 1, + 0, + _mpzerodata, + MPstatic +}; +mpint *mpzero = &_mpzero; + +static int mpmindigits = 33; + +// set minimum digit allocation +void +mpsetminbits(int n) +{ + if(n == 0) + n = 1; + mpmindigits = DIGITS(n); +} + +// allocate an n bit 0'd number +mpint* +mpnew(int n) +{ + mpint *b; + + b = mallocz(sizeof(mpint), 1); + if(b == nil) + sysfatal("mpnew: %r"); + n = DIGITS(n); + if(n < mpmindigits) + n = mpmindigits; + n = n; + b->p = (mpdigit*)mallocz(n*Dbytes, 1); + if(b->p == nil) + sysfatal("mpnew: %r"); + b->size = n; + b->sign = 1; + + return b; +} + +// guarantee at least n significant bits +void +mpbits(mpint *b, int m) +{ + int n; + + n = DIGITS(m); + if(b->size >= n){ + if(b->top >= n) + return; + memset(&b->p[b->top], 0, Dbytes*(n - b->top)); + b->top = n; + return; + } + b->p = (mpdigit*)realloc(b->p, n*Dbytes); + if(b->p == nil) + sysfatal("mpbits: %r"); + memset(&b->p[b->top], 0, Dbytes*(n - b->top)); + b->size = n; + b->top = n; +} + +void +mpfree(mpint *b) +{ + if(b == nil) + return; + if(b->flags & MPstatic) + sysfatal("freeing mp constant"); + memset(b->p, 0, b->top*Dbytes); // information hiding + free(b->p); + free(b); +} + +void +mpnorm(mpint *b) +{ + int i; + + for(i = b->top-1; i >= 0; i--) + if(b->p[i] != 0) + break; + b->top = i+1; + if(b->top == 0) + b->sign = 1; +} + +mpint* +mpcopy(mpint *old) +{ + mpint *new; + + new = mpnew(Dbits*old->size); + new->top = old->top; + new->sign = old->sign; + memmove(new->p, old->p, Dbytes*old->top); + return new; +} + +void +mpassign(mpint *old, mpint *new) +{ + mpbits(new, Dbits*old->top); + new->sign = old->sign; + new->top = old->top; + memmove(new->p, old->p, Dbytes*old->top); +} + +// number of significant bits in mantissa +int +mpsignif(mpint *n) +{ + int i, j; + mpdigit d; + + if(n->top == 0) + return 0; + for(i = n->top-1; i >= 0; i--){ + d = n->p[i]; + for(j = Dbits-1; j >= 0; j--){ + if(d & (((mpdigit)1)<top==0) + return 0; + k = 0; + bit = 0; + digit = 0; + d = n->p[0]; + for(;;){ + if(d & (1<= n->top) + return 0; + d = n->p[digit]; + bit = 0; + } + } + return k; +} + diff --git a/libmp/mpcmp.c b/libmp/mpcmp.c new file mode 100644 index 0000000..38af081 --- /dev/null +++ b/libmp/mpcmp.c @@ -0,0 +1,28 @@ +#include "os.h" +#include +#include "dat.h" + +// return 1, 0, -1 as abs(b1)-abs(b2) is neg, 0, pos +int +mpmagcmp(mpint *b1, mpint *b2) +{ + int i; + + i = b1->top - b2->top; + if(i) + return i; + + return mpveccmp(b1->p, b1->top, b2->p, b2->top); +} + +// return neg, 0, pos as b1-b2 is neg, 0, pos +int +mpcmp(mpint *b1, mpint *b2) +{ + if(b1->sign != b2->sign) + return b1->sign - b2->sign; + if(b1->sign < 0) + return mpmagcmp(b2, b1); + else + return mpmagcmp(b1, b2); +} diff --git a/libmp/mpdigdiv.c b/libmp/mpdigdiv.c new file mode 100644 index 0000000..4a73bb3 --- /dev/null +++ b/libmp/mpdigdiv.c @@ -0,0 +1,43 @@ +#include "os.h" +#include +#include "dat.h" + +// +// divide two digits by one and return quotient +// +void +mpdigdiv(mpdigit *dividend, mpdigit divisor, mpdigit *quotient) +{ + mpdigit hi, lo, q, x, y; + int i; + + hi = dividend[1]; + lo = dividend[0]; + + // return highest digit value if the result >= 2**32 + if(hi >= divisor || divisor == 0){ + divisor = 0; + *quotient = ~divisor; + return; + } + + // at this point we know that hi < divisor + // just shift and subtract till we're done + q = 0; + x = divisor; + for(i = Dbits-1; hi > 0 && i >= 0; i--){ + x >>= 1; + if(x > hi) + continue; + y = divisor< lo) + continue; + if(y > lo) + hi--; + lo -= y; + hi -= x; + q |= 1< +#include "dat.h" + +// division ala knuth, seminumerical algorithms, pp 237-238 +// the numbers are stored backwards to what knuth expects so j +// counts down rather than up. + +void +mpdiv(mpint *dividend, mpint *divisor, mpint *quotient, mpint *remainder) +{ + int j, s, vn, sign; + mpdigit qd, *up, *vp, *qp; + mpint *u, *v, *t; + + // divide bv zero + if(divisor->top == 0) + abort(); + + // quick check + if(mpmagcmp(dividend, divisor) < 0){ + if(remainder != nil) + mpassign(dividend, remainder); + if(quotient != nil) + mpassign(mpzero, quotient); + return; + } + + // D1: shift until divisor, v, has hi bit set (needed to make trial + // divisor accurate) + qd = divisor->p[divisor->top-1]; + for(s = 0; (qd & mpdighi) == 0; s++) + qd <<= 1; + u = mpnew((dividend->top+2)*Dbits + s); + if(s == 0 && divisor != quotient && divisor != remainder) { + mpassign(dividend, u); + v = divisor; + } else { + mpleft(dividend, s, u); + v = mpnew(divisor->top*Dbits); + mpleft(divisor, s, v); + } + up = u->p+u->top-1; + vp = v->p+v->top-1; + vn = v->top; + + // D1a: make sure high digit of dividend is less than high digit of divisor + if(*up >= *vp){ + *++up = 0; + u->top++; + } + + // storage for multiplies + t = mpnew(4*Dbits); + + qp = nil; + if(quotient != nil){ + mpbits(quotient, (u->top - v->top)*Dbits); + quotient->top = u->top - v->top; + qp = quotient->p+quotient->top-1; + } + + // D2, D7: loop on length of dividend + for(j = u->top; j > vn; j--){ + + // D3: calculate trial divisor + mpdigdiv(up-1, *vp, &qd); + + // D3a: rule out trial divisors 2 greater than real divisor + if(vn > 1) for(;;){ + memset(t->p, 0, 3*Dbytes); // mpvecdigmuladd adds to what's there + mpvecdigmuladd(vp-1, 2, qd, t->p); + if(mpveccmp(t->p, 3, up-2, 3) > 0) + qd--; + else + break; + } + + // D4: u -= v*qd << j*Dbits + sign = mpvecdigmulsub(v->p, vn, qd, up-vn); + if(sign < 0){ + + // D6: trial divisor was too high, add back borrowed + // value and decrease divisor + mpvecadd(up-vn, vn+1, v->p, vn, up-vn); + qd--; + } + + // D5: save quotient digit + if(qp != nil) + *qp-- = qd; + + // push top of u down one + u->top--; + *up-- = 0; + } + if(qp != nil){ + mpnorm(quotient); + if(dividend->sign != divisor->sign) + quotient->sign = -1; + } + + if(remainder != nil){ + mpright(u, s, remainder); // u is the remainder shifted + remainder->sign = dividend->sign; + } + + mpfree(t); + mpfree(u); + if(v != divisor) + mpfree(v); +} diff --git a/libmp/mpeuclid.c b/libmp/mpeuclid.c new file mode 100644 index 0000000..b44e8ae --- /dev/null +++ b/libmp/mpeuclid.c @@ -0,0 +1,82 @@ +#include "os.h" +#include + +// extended euclid +// +// For a and b it solves, d = gcd(a,b) and finds x and y s.t. +// ax + by = d +// +// Handbook of Applied Cryptography, Menezes et al, 1997, pg 67 + +void +mpeuclid(mpint *a, mpint *b, mpint *d, mpint *x, mpint *y) +{ + mpint *tmp, *x0, *x1, *x2, *y0, *y1, *y2, *q, *r; + + if(mpcmp(a, b) < 0){ + tmp = a; + a = b; + b = tmp; + tmp = x; + x = y; + y = tmp; + } + + if(b->top == 0){ + mpassign(a, d); + mpassign(mpone, x); + mpassign(mpzero, y); + return; + } + + a = mpcopy(a); + b = mpcopy(b); + x0 = mpnew(0); + x1 = mpcopy(mpzero); + x2 = mpcopy(mpone); + y0 = mpnew(0); + y1 = mpcopy(mpone); + y2 = mpcopy(mpzero); + q = mpnew(0); + r = mpnew(0); + + while(b->top != 0 && b->sign > 0){ + // q = a/b + // r = a mod b + mpdiv(a, b, q, r); + // x0 = x2 - qx1 + mpmul(q, x1, x0); + mpsub(x2, x0, x0); + // y0 = y2 - qy1 + mpmul(q, y1, y0); + mpsub(y2, y0, y0); + // rotate values + tmp = a; + a = b; + b = r; + r = tmp; + tmp = x2; + x2 = x1; + x1 = x0; + x0 = tmp; + tmp = y2; + y2 = y1; + y1 = y0; + y0 = tmp; + } + + mpassign(a, d); + mpassign(x2, x); + mpassign(y2, y); + + mpfree(x0); + mpfree(x1); + mpfree(x2); + mpfree(y0); + mpfree(y1); + mpfree(y2); + mpfree(q); + mpfree(r); + mpfree(a); + mpfree(b); +} diff --git a/libmp/mpexp.c b/libmp/mpexp.c new file mode 100644 index 0000000..7450572 --- /dev/null +++ b/libmp/mpexp.c @@ -0,0 +1,86 @@ +#include "os.h" +#include +#include "dat.h" + +// res = b**e +// +// knuth, vol 2, pp 398-400 + +enum { + Freeb= 0x1, + Freee= 0x2, + Freem= 0x4, +}; + +//int expdebug; + +void +mpexp(mpint *b, mpint *e, mpint *m, mpint *res) +{ + mpint *t[2]; + int tofree; + mpdigit d, bit; + int i, j; + + t[0] = mpcopy(b); + t[1] = res; + + tofree = 0; + if(res == b){ + b = mpcopy(b); + tofree |= Freeb; + } + if(res == e){ + e = mpcopy(e); + tofree |= Freee; + } + if(res == m){ + m = mpcopy(m); + tofree |= Freem; + } + + // skip first bit + i = e->top-1; + d = e->p[i]; + for(bit = mpdighi; (bit & d) == 0; bit >>= 1) + ; + bit >>= 1; + + j = 0; + for(;;){ + for(; bit != 0; bit >>= 1){ + mpmul(t[j], t[j], t[j^1]); + if(bit & d) + mpmul(t[j^1], b, t[j]); + else + j ^= 1; + if(m != nil && t[j]->top > m->top){ + mpmod(t[j], m, t[j^1]); + j ^= 1; + } + } + if(--i < 0) + break; + bit = mpdighi; + d = e->p[i]; + } + if(m != nil){ + mpmod(t[j], m, t[j^1]); + j ^= 1; + } + if(t[j] == res){ + mpfree(t[j^1]); + } else { + mpassign(t[j], res); + mpfree(t[j]); + } + + if(tofree){ + if(tofree & Freeb) + mpfree(b); + if(tofree & Freee) + mpfree(e); + if(tofree & Freem) + mpfree(m); + } +} diff --git a/libmp/mpextendedgcd.c b/libmp/mpextendedgcd.c new file mode 100644 index 0000000..9d1557e --- /dev/null +++ b/libmp/mpextendedgcd.c @@ -0,0 +1,97 @@ +#include "os.h" +#include + +#define iseven(a) (((a)->p[0] & 1) == 0) + +// extended binary gcd +// +// For a anv b it solves, v = gcd(a,b) and finds x and y s.t. +// ax + by = v +// +// Handbook of Applied Cryptography, Menezes et al, 1997, pg 608. +void +mpextendedgcd(mpint *a, mpint *b, mpint *v, mpint *x, mpint *y) +{ + mpint *u, *A, *B, *C, *D; + int g; + + if(a->top == 0){ + mpassign(b, v); + mpassign(mpone, y); + mpassign(mpzero, x); + return; + } + if(b->top == 0){ + mpassign(a, v); + mpassign(mpone, x); + mpassign(mpzero, y); + return; + } + + g = 0; + a = mpcopy(a); + b = mpcopy(b); + + while(iseven(a) && iseven(b)){ + mpright(a, 1, a); + mpright(b, 1, b); + g++; + } + + u = mpcopy(a); + mpassign(b, v); + A = mpcopy(mpone); + B = mpcopy(mpzero); + C = mpcopy(mpzero); + D = mpcopy(mpone); + + for(;;) { +// print("%B %B %B %B %B %B\n", u, v, A, B, C, D); + while(iseven(u)){ + mpright(u, 1, u); + if(!iseven(A) || !iseven(B)) { + mpadd(A, b, A); + mpsub(B, a, B); + } + mpright(A, 1, A); + mpright(B, 1, B); + } + +// print("%B %B %B %B %B %B\n", u, v, A, B, C, D); + while(iseven(v)){ + mpright(v, 1, v); + if(!iseven(C) || !iseven(D)) { + mpadd(C, b, C); + mpsub(D, a, D); + } + mpright(C, 1, C); + mpright(D, 1, D); + } + +// print("%B %B %B %B %B %B\n", u, v, A, B, C, D); + if(mpcmp(u, v) >= 0){ + mpsub(u, v, u); + mpsub(A, C, A); + mpsub(B, D, B); + } else { + mpsub(v, u, v); + mpsub(C, A, C); + mpsub(D, B, D); + } + + if(u->top == 0) + break; + + } + mpassign(C, x); + mpassign(D, y); + mpleft(v, g, v); + + mpfree(A); + mpfree(B); + mpfree(C); + mpfree(D); + mpfree(u); + mpfree(a); + mpfree(b); +} diff --git a/libmp/mpfmt.c b/libmp/mpfmt.c new file mode 100644 index 0000000..f7c42a7 --- /dev/null +++ b/libmp/mpfmt.c @@ -0,0 +1,193 @@ +#include "os.h" +#include +#include +#include "dat.h" + +static int +to64(mpint *b, char *buf, int len) +{ + uchar *p; + int n, rv; + + p = nil; + n = mptobe(b, nil, 0, &p); + if(n < 0) + return -1; + rv = enc64(buf, len, p, n); + free(p); + return rv; +} + +static int +to32(mpint *b, char *buf, int len) +{ + uchar *p; + int n, rv; + + // leave room for a multiple of 5 buffer size + n = b->top*Dbytes + 5; + p = malloc(n); + if(p == nil) + return -1; + n = mptobe(b, p, n, nil); + if(n < 0) + return -1; + + // round up buffer size, enc32 only accepts a multiple of 5 + if(n%5) + n += 5 - (n%5); + rv = enc32(buf, len, p, n); + free(p); + return rv; +} + +static char set16[] = "0123456789ABCDEF"; + +static int +to16(mpint *b, char *buf, int len) +{ + mpdigit *p, x; + int i, j; + char *out, *eout; + + if(len < 1) + return -1; + + out = buf; + eout = buf+len; + for(p = &b->p[b->top-1]; p >= b->p; p--){ + x = *p; + for(i = Dbits-4; i >= 0; i -= 4){ + j = 0xf & (x>>i); + if(j != 0 || out != buf){ + if(out >= eout) + return -1; + *out++ = set16[j]; + } + } + } + if(out == buf) + *out++ = '0'; + if(out >= eout) + return -1; + *out = 0; + return 0; +} + +static char* +modbillion(int rem, ulong r, char *out, char *buf) +{ + ulong rr; + int i; + + for(i = 0; i < 9; i++){ + rr = r%10; + r /= 10; + if(out <= buf) + return nil; + *--out = '0' + rr; + if(rem == 0 && r == 0) + break; + } + return out; +} + +static int +to10(mpint *b, char *buf, int len) +{ + mpint *d, *r, *billion; + char *out; + + if(len < 1) + return -1; + + d = mpcopy(b); + r = mpnew(0); + billion = uitomp(1000000000, nil); + out = buf+len; + *--out = 0; + do { + mpdiv(d, billion, d, r); + out = modbillion(d->top, r->p[0], out, buf); + if(out == nil) + break; + } while(d->top != 0); + mpfree(d); + mpfree(r); + mpfree(billion); + + if(out == nil) + return -1; + len -= out-buf; + if(out != buf) + memmove(buf, out, len); + return 0; +} + +int +mpfmt(Fmt *fmt) +{ + mpint *b; + char *p; + + b = va_arg(fmt->args, mpint*); + if(b == nil) + return fmtstrcpy(fmt, "*"); + + p = mptoa(b, fmt->prec, nil, 0); + fmt->flags &= ~FmtPrec; + + if(p == nil) + return fmtstrcpy(fmt, "*"); + else{ + fmtstrcpy(fmt, p); + free(p); + return 0; + } +} + +char* +mptoa(mpint *b, int base, char *buf, int len) +{ + char *out; + int rv, alloced; + + alloced = 0; + if(buf == nil){ + len = ((b->top+1)*Dbits+2)/3 + 1; + buf = malloc(len); + if(buf == nil) + return nil; + alloced = 1; + } + + if(len < 2) + return nil; + + out = buf; + if(b->sign < 0){ + *out++ = '-'; + len--; + } + switch(base){ + case 64: + rv = to64(b, out, len); + break; + case 32: + rv = to32(b, out, len); + break; + default: + case 16: + rv = to16(b, out, len); + break; + case 10: + rv = to10(b, out, len); + break; + } + if(rv < 0){ + if(alloced) + free(buf); + return nil; + } + return buf; +} diff --git a/libmp/mpinvert.c b/libmp/mpinvert.c new file mode 100644 index 0000000..ee26307 --- /dev/null +++ b/libmp/mpinvert.c @@ -0,0 +1,21 @@ +#include "os.h" +#include + +#define iseven(a) (((a)->p[0] & 1) == 0) + +// use extended gcd to find the multiplicative inverse +// res = b**-1 mod m +void +mpinvert(mpint *b, mpint *m, mpint *res) +{ + mpint *dc1, *dc2; // don't care + + dc1 = mpnew(0); + dc2 = mpnew(0); + mpextendedgcd(b, m, dc1, res, dc2); + if(mpcmp(dc1, mpone) != 0) + abort(); + mpmod(res, m, res); + mpfree(dc1); + mpfree(dc2); +} diff --git a/libmp/mpleft.c b/libmp/mpleft.c new file mode 100644 index 0000000..23ff9da --- /dev/null +++ b/libmp/mpleft.c @@ -0,0 +1,46 @@ +#include "os.h" +#include +#include "dat.h" + +// res = b << shift +void +mpleft(mpint *b, int shift, mpint *res) +{ + int d, l, r, i, otop; + mpdigit this, last; + + // a negative left shift is a right shift + if(shift < 0){ + mpright(b, -shift, res); + return; + } + + // b and res may be the same so remember the old top + otop = b->top; + + // shift + mpbits(res, otop*Dbits + shift); // overkill + res->top = DIGITS(otop*Dbits + shift); + d = shift/Dbits; + l = shift - d*Dbits; + r = Dbits - l; + + if(l == 0){ + for(i = otop-1; i >= 0; i--) + res->p[i+d] = b->p[i]; + } else { + last = 0; + for(i = otop-1; i >= 0; i--) { + this = b->p[i]; + res->p[i+d+1] = (last<>r); + last = this; + } + res->p[d] = last<p[i] = 0; + + // normalize + while(res->top > 0 && res->p[res->top-1] == 0) + res->top--; +} diff --git a/libmp/mpmod.c b/libmp/mpmod.c new file mode 100644 index 0000000..91bebfa --- /dev/null +++ b/libmp/mpmod.c @@ -0,0 +1,15 @@ +#include "os.h" +#include +#include "dat.h" + +// remainder = b mod m +// +// knuth, vol 2, pp 398-400 + +void +mpmod(mpint *b, mpint *m, mpint *remainder) +{ + mpdiv(b, m, nil, remainder); + if(remainder->sign < 0) + mpadd(m, remainder, remainder); +} diff --git a/libmp/mpmul.c b/libmp/mpmul.c new file mode 100644 index 0000000..dedd474 --- /dev/null +++ b/libmp/mpmul.c @@ -0,0 +1,156 @@ +#include "os.h" +#include +#include "dat.h" + +// +// from knuth's 1969 seminumberical algorithms, pp 233-235 and pp 258-260 +// +// mpvecmul is an assembly language routine that performs the inner +// loop. +// +// the karatsuba trade off is set empiricly by measuring the algs on +// a 400 MHz Pentium II. +// + +// karatsuba like (see knuth pg 258) +// prereq: p is already zeroed +static void +mpkaratsuba(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *p) +{ + mpdigit *t, *u0, *u1, *v0, *v1, *u0v0, *u1v1, *res, *diffprod; + int u0len, u1len, v0len, v1len, reslen; + int sign, n; + + // divide each piece in half + n = alen/2; + if(alen&1) + n++; + u0len = n; + u1len = alen-n; + if(blen > n){ + v0len = n; + v1len = blen-n; + } else { + v0len = blen; + v1len = 0; + } + u0 = a; + u1 = a + u0len; + v0 = b; + v1 = b + v0len; + + // room for the partial products + t = mallocz(Dbytes*5*(2*n+1), 1); + if(t == nil) + sysfatal("mpkaratsuba: %r"); + u0v0 = t; + u1v1 = t + (2*n+1); + diffprod = t + 2*(2*n+1); + res = t + 3*(2*n+1); + reslen = 4*n+1; + + // t[0] = (u1-u0) + sign = 1; + if(mpveccmp(u1, u1len, u0, u0len) < 0){ + sign = -1; + mpvecsub(u0, u0len, u1, u1len, u0v0); + } else + mpvecsub(u1, u1len, u0, u1len, u0v0); + + // t[1] = (v0-v1) + if(mpveccmp(v0, v0len, v1, v1len) < 0){ + sign *= -1; + mpvecsub(v1, v1len, v0, v1len, u1v1); + } else + mpvecsub(v0, v0len, v1, v1len, u1v1); + + // t[4:5] = (u1-u0)*(v0-v1) + mpvecmul(u0v0, u0len, u1v1, v0len, diffprod); + + // t[0:1] = u1*v1 + memset(t, 0, 2*(2*n+1)*Dbytes); + if(v1len > 0) + mpvecmul(u1, u1len, v1, v1len, u1v1); + + // t[2:3] = u0v0 + mpvecmul(u0, u0len, v0, v0len, u0v0); + + // res = u0*v0< 0){ + mpvecadd(res+n, reslen-n, u1v1, u1len+v1len, res+n); + mpvecadd(res+2*n, reslen-2*n, u1v1, u1len+v1len, res+2*n); + } + + // res += (u1-u0)*(v0-v1)<= KARATSUBAMIN && blen > 1){ + // O(n^1.585) + mpkaratsuba(a, alen, b, blen, p); + } else { + // O(n^2) + for(i = 0; i < blen; i++){ + d = b[i]; + if(d != 0) + mpvecdigmuladd(a, alen, d, &p[i]); + } + } +} + +void +mpmul(mpint *b1, mpint *b2, mpint *prod) +{ + mpint *oprod; + + oprod = nil; + if(prod == b1 || prod == b2){ + oprod = prod; + prod = mpnew(0); + } + + prod->top = 0; + mpbits(prod, (b1->top+b2->top+1)*Dbits); + mpvecmul(b1->p, b1->top, b2->p, b2->top, prod->p); + prod->top = b1->top+b2->top+1; + prod->sign = b1->sign*b2->sign; + mpnorm(prod); + + if(oprod != nil){ + mpassign(prod, oprod); + mpfree(prod); + } +} diff --git a/libmp/mprand.c b/libmp/mprand.c new file mode 100644 index 0000000..fd288f2 --- /dev/null +++ b/libmp/mprand.c @@ -0,0 +1,42 @@ +#include "os.h" +#include +#include +#include "dat.h" + +mpint* +mprand(int bits, void (*gen)(uchar*, int), mpint *b) +{ + int n, m; + mpdigit mask; + uchar *p; + + n = DIGITS(bits); + if(b == nil) + b = mpnew(bits); + else + mpbits(b, bits); + + p = malloc(n*Dbytes); + if(p == nil) + return nil; + (*gen)(p, n*Dbytes); + betomp(p, n*Dbytes, b); + free(p); + + // make sure we don't give too many bits + m = bits%Dbits; + n--; + if(m > 0){ + mask = 1; + mask <<= m; + mask--; + b->p[n] &= mask; + } + + for(; n >= 0; n--) + if(b->p[n] != 0) + break; + b->top = n+1; + b->sign = 1; + return b; +} diff --git a/libmp/mpright.c b/libmp/mpright.c new file mode 100644 index 0000000..6c42de1 --- /dev/null +++ b/libmp/mpright.c @@ -0,0 +1,40 @@ +#include "os.h" +#include +#include "dat.h" + +// res = b >> shift +void +mpright(mpint *b, int shift, mpint *res) +{ + int d, l, r, i; + mpdigit this, last; + + // a negative right shift is a left shift + if(shift < 0){ + mpleft(b, -shift, res); + return; + } + + if(res != b) + mpbits(res, b->top*Dbits - shift); + d = shift/Dbits; + r = shift - d*Dbits; + l = Dbits - r; + + // special case digit shifts + if(r == 0){ + for(i = 0; i < b->top-d; i++) + res->p[i] = b->p[i+d]; + } else { + last = b->p[d]; + for(i = 0; i < b->top-d-1; i++){ + this = b->p[i+d+1]; + res->p[i] = (this<>r); + last = this; + } + res->p[i++] = last>>r; + } + while(i > 0 && res->p[i-1] == 0) + i--; + res->top = i; +} diff --git a/libmp/mpsub.c b/libmp/mpsub.c new file mode 100644 index 0000000..3fe6ca0 --- /dev/null +++ b/libmp/mpsub.c @@ -0,0 +1,52 @@ +#include "os.h" +#include +#include "dat.h" + +// diff = abs(b1) - abs(b2), i.e., subtract the magnitudes +void +mpmagsub(mpint *b1, mpint *b2, mpint *diff) +{ + int n, m, sign; + mpint *t; + + // get the sizes right + if(mpmagcmp(b1, b2) < 0){ + sign = -1; + t = b1; + b1 = b2; + b2 = t; + } else + sign = 1; + n = b1->top; + m = b2->top; + if(m == 0){ + mpassign(b1, diff); + diff->sign = sign; + return; + } + mpbits(diff, n*Dbits); + + mpvecsub(b1->p, n, b2->p, m, diff->p); + diff->sign = sign; + diff->top = n; + mpnorm(diff); +} + +// diff = b1 - b2 +void +mpsub(mpint *b1, mpint *b2, mpint *diff) +{ + int sign; + + if(b1->sign != b2->sign){ + sign = b1->sign; + mpmagadd(b1, b2, diff); + diff->sign = sign; + return; + } + + sign = b1->sign; + mpmagsub(b1, b2, diff); + if(diff->top != 0) + diff->sign *= sign; +} diff --git a/libmp/mptobe.c b/libmp/mptobe.c new file mode 100644 index 0000000..e08ae56 --- /dev/null +++ b/libmp/mptobe.c @@ -0,0 +1,57 @@ +#include "os.h" +#include +#include "dat.h" + +// convert an mpint into a big endian byte array (most significant byte first) +// return number of bytes converted +// if p == nil, allocate and result array +int +mptobe(mpint *b, uchar *p, uint n, uchar **pp) +{ + int i, j, suppress; + mpdigit x; + uchar *e, *s, c; + + if(p == nil){ + n = (b->top+1)*Dbytes; + p = malloc(n); + } + if(p == nil) + return -1; + if(pp != nil) + *pp = p; + memset(p, 0, n); + + // special case 0 + if(b->top == 0){ + if(n < 1) + return -1; + else + return 1; + } + + s = p; + e = s+n; + suppress = 1; + for(i = b->top-1; i >= 0; i--){ + x = b->p[i]; + for(j = Dbits-8; j >= 0; j -= 8){ + c = x>>j; + if(c == 0 && suppress) + continue; + if(p >= e) + return -1; + *p++ = c; + suppress = 0; + } + } + + // guarantee at least one byte + if(s == p){ + if(p >= e) + return -1; + *p++ = 0; + } + + return p - s; +} diff --git a/libmp/mptoi.c b/libmp/mptoi.c new file mode 100644 index 0000000..e636e24 --- /dev/null +++ b/libmp/mptoi.c @@ -0,0 +1,44 @@ +#include "os.h" +#include +#include "dat.h" + +/* + * this code assumes that mpdigit is at least as + * big as an int. + */ + +mpint* +itomp(int i, mpint *b) +{ + if(b == nil) + b = mpnew(0); + mpassign(mpzero, b); + if(i != 0) + b->top = 1; + if(i < 0){ + b->sign = -1; + *b->p = -i; + } else + *b->p = i; + return b; +} + +int +mptoi(mpint *b) +{ + uint x; + + x = *b->p; + if(b->sign > 0){ + if(b->top > 1 || (x > MAXINT)) + x = (int)MAXINT; + else + x = (int)x; + } else { + if(b->top > 1 || x > MAXINT+1) + x = (int)MININT; + else + x = -(int)x; + } + return x; +} diff --git a/libmp/mptole.c b/libmp/mptole.c new file mode 100644 index 0000000..9421d5f --- /dev/null +++ b/libmp/mptole.c @@ -0,0 +1,54 @@ +#include "os.h" +#include +#include "dat.h" + +// convert an mpint into a little endian byte array (least significant byte first) + +// return number of bytes converted +// if p == nil, allocate and result array +int +mptole(mpint *b, uchar *p, uint n, uchar **pp) +{ + int i, j; + mpdigit x; + uchar *e, *s; + + if(p == nil){ + n = (b->top+1)*Dbytes; + p = malloc(n); + } + if(pp != nil) + *pp = p; + if(p == nil) + return -1; + memset(p, 0, n); + + // special case 0 + if(b->top == 0){ + if(n < 1) + return -1; + else + return 0; + } + + s = p; + e = s+n; + for(i = 0; i < b->top-1; i++){ + x = b->p[i]; + for(j = 0; j < Dbytes; j++){ + if(p >= e) + return -1; + *p++ = x; + x >>= 8; + } + } + x = b->p[i]; + while(x > 0){ + if(p >= e) + return -1; + *p++ = x; + x >>= 8; + } + + return p - s; +} diff --git a/libmp/mptoui.c b/libmp/mptoui.c new file mode 100644 index 0000000..9d80c1d --- /dev/null +++ b/libmp/mptoui.c @@ -0,0 +1,35 @@ +#include "os.h" +#include +#include "dat.h" + +/* + * this code assumes that mpdigit is at least as + * big as an int. + */ + +mpint* +uitomp(uint i, mpint *b) +{ + if(b == nil) + b = mpnew(0); + mpassign(mpzero, b); + if(i != 0) + b->top = 1; + *b->p = i; + return b; +} + +uint +mptoui(mpint *b) +{ + uint x; + + x = *b->p; + if(b->sign < 0){ + x = 0; + } else { + if(b->top > 1 || x > MAXUINT) + x = MAXUINT; + } + return x; +} diff --git a/libmp/mptouv.c b/libmp/mptouv.c new file mode 100644 index 0000000..76f93dd --- /dev/null +++ b/libmp/mptouv.c @@ -0,0 +1,49 @@ +#include "os.h" +#include +#include "dat.h" + +#define VLDIGITS (sizeof(vlong)/sizeof(mpdigit)) + +/* + * this code assumes that a vlong is an integral number of + * mpdigits long. + */ +mpint* +uvtomp(uvlong v, mpint *b) +{ + int s; + + if(b == nil) + b = mpnew(VLDIGITS*sizeof(mpdigit)); + else + mpbits(b, VLDIGITS*sizeof(mpdigit)); + mpassign(mpzero, b); + if(v == 0) + return b; + for(s = 0; s < VLDIGITS && v != 0; s++){ + b->p[s] = v; + v >>= sizeof(mpdigit)*8; + } + b->top = s; + return b; +} + +uvlong +mptouv(mpint *b) +{ + uvlong v; + int s; + + if(b->top == 0) + return (vlong) 0; + + mpnorm(b); + if(b->top > VLDIGITS) + return MAXVLONG; + + v = (uvlong) 0; + for(s = 0; s < b->top; s++) + v |= b->p[s]<<(s*sizeof(mpdigit)*8); + + return v; +} diff --git a/libmp/mptov.c b/libmp/mptov.c new file mode 100644 index 0000000..fd371d8 --- /dev/null +++ b/libmp/mptov.c @@ -0,0 +1,69 @@ +#include "os.h" +#include +#include "dat.h" + +#define VLDIGITS (sizeof(vlong)/sizeof(mpdigit)) + +/* + * this code assumes that a vlong is an integral number of + * mpdigits long. + */ +mpint* +vtomp(vlong v, mpint *b) +{ + int s; + uvlong uv; + + if(b == nil) + b = mpnew(VLDIGITS*sizeof(mpdigit)); + else + mpbits(b, VLDIGITS*sizeof(mpdigit)); + mpassign(mpzero, b); + if(v == 0) + return b; + if(v < 0){ + b->sign = -1; + uv = -v; + } else + uv = v; + for(s = 0; s < VLDIGITS && uv != 0; s++){ + b->p[s] = uv; + uv >>= sizeof(mpdigit)*8; + } + b->top = s; + return b; +} + +vlong +mptov(mpint *b) +{ + uvlong v; + int s; + + if(b->top == 0) + return (vlong) 0; + + mpnorm(b); + if(b->top > VLDIGITS){ + if(b->sign > 0) + return (vlong)MAXVLONG; + else + return (vlong)MINVLONG; + } + + v = (uvlong) 0; + for(s = 0; s < b->top; s++) + v |= b->p[s]<<(s*sizeof(mpdigit)*8); + + if(b->sign > 0){ + if(v > MAXVLONG) + v = MAXVLONG; + } else { + if(v > MINVLONG) + v = MINVLONG; + else + v = -(vlong)v; + } + + return (vlong)v; +} diff --git a/libmp/mpvecadd.c b/libmp/mpvecadd.c new file mode 100644 index 0000000..98fdcc9 --- /dev/null +++ b/libmp/mpvecadd.c @@ -0,0 +1,35 @@ +#include "os.h" +#include +#include "dat.h" + +// prereq: alen >= blen, sum has at least blen+1 digits +void +mpvecadd(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *sum) +{ + int i, carry; + mpdigit x, y; + + carry = 0; + for(i = 0; i < blen; i++){ + x = *a++; + y = *b++; + x += carry; + if(x < carry) + carry = 1; + else + carry = 0; + x += y; + if(x < y) + carry++; + *sum++ = x; + } + for(; i < alen; i++){ + x = *a++ + carry; + if(x < carry) + carry = 1; + else + carry = 0; + *sum++ = x; + } + *sum = carry; +} diff --git a/libmp/mpveccmp.c b/libmp/mpveccmp.c new file mode 100644 index 0000000..9dcc598 --- /dev/null +++ b/libmp/mpveccmp.c @@ -0,0 +1,28 @@ +#include "os.h" +#include +#include "dat.h" + +// prereq: alen >= blen +int +mpveccmp(mpdigit *a, int alen, mpdigit *b, int blen) +{ + mpdigit x; + + while(alen > blen) + if(a[--alen] != 0) + return 1; + while(blen > alen) + if(b[--blen] != 0) + return -1; + while(alen > 0){ + --alen; + x = a[alen] - b[alen]; + if(x == 0) + continue; + if(x > a[alen]) + return -1; + else + return 1; + } + return 0; +} diff --git a/libmp/mpvecdigmuladd.c b/libmp/mpvecdigmuladd.c new file mode 100644 index 0000000..6b6c683 --- /dev/null +++ b/libmp/mpvecdigmuladd.c @@ -0,0 +1,103 @@ +#include "os.h" +#include +#include "dat.h" + +#define LO(x) ((x) & ((1<<(Dbits/2))-1)) +#define HI(x) ((x) >> (Dbits/2)) + +static void +mpdigmul(mpdigit a, mpdigit b, mpdigit *p) +{ + mpdigit x, ah, al, bh, bl, p1, p2, p3, p4; + int carry; + + // half digits + ah = HI(a); + al = LO(a); + bh = HI(b); + bl = LO(b); + + // partial products + p1 = ah*bl; + p2 = bh*al; + p3 = bl*al; + p4 = ah*bh; + + // p = ((p1+p2)<<(Dbits/2)) + (p4< x) + borrow = 1; + else + borrow = 0; + x = part[1]; + mpdigmul(*b++, m, part); + x += part[0]; + if(x < part[0]) + borrow++; + x = y - x; + if(x > y) + borrow++; + *p++ = x; + } + + x = *p; + y = x - borrow - part[1]; + *p = y; + if(y > x) + return -1; + else + return 1; +} diff --git a/libmp/mpvecsub.c b/libmp/mpvecsub.c new file mode 100644 index 0000000..db93b65 --- /dev/null +++ b/libmp/mpvecsub.c @@ -0,0 +1,34 @@ +#include "os.h" +#include +#include "dat.h" + +// prereq: a >= b, alen >= blen, diff has at least alen digits +void +mpvecsub(mpdigit *a, int alen, mpdigit *b, int blen, mpdigit *diff) +{ + int i, borrow; + mpdigit x, y; + + borrow = 0; + for(i = 0; i < blen; i++){ + x = *a++; + y = *b++; + y += borrow; + if(y < borrow) + borrow = 1; + else + borrow = 0; + if(x < y) + borrow++; + *diff++ = x - y; + } + for(; i < alen; i++){ + x = *a++; + y = x - borrow; + if(y > x) + borrow = 1; + else + borrow = 0; + *diff++ = y; + } +} diff --git a/libmp/os.h b/libmp/os.h new file mode 100644 index 0000000..dae875d --- /dev/null +++ b/libmp/os.h @@ -0,0 +1,3 @@ +#include +#include + diff --git a/libmp/reduce b/libmp/reduce new file mode 100644 index 0000000..a857a28 --- /dev/null +++ b/libmp/reduce @@ -0,0 +1,16 @@ +O=$1 +shift +objtype=$1 +shift + +ls -p ../$objtype/*.[cs] >[2]/dev/null | sed 's/..$//' > /tmp/reduce.$pid +# +# if empty directory, just return the input files +# +if (! ~ $status '|') { + echo $* + rm /tmp/reduce.$pid + exit 0 +} +echo $* | tr ' ' \012 | grep -v -f /tmp/reduce.$pid | tr \012 ' ' +rm /tmp/reduce.$pid diff --git a/libmp/strtomp.c b/libmp/strtomp.c new file mode 100644 index 0000000..1e4e9ca --- /dev/null +++ b/libmp/strtomp.c @@ -0,0 +1,205 @@ +#include "os.h" +#include +#include +#include "dat.h" + +static struct { + int inited; + + uchar t64[256]; + uchar t32[256]; + uchar t16[256]; + uchar t10[256]; +} tab; + +enum { + INVAL= 255 +}; + +static char set64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static char set32[] = "23456789abcdefghijkmnpqrstuvwxyz"; +static char set16[] = "0123456789ABCDEF0123456789abcdef"; +static char set10[] = "0123456789"; + +static void +init(void) +{ + char *p; + + memset(tab.t64, INVAL, sizeof(tab.t64)); + memset(tab.t32, INVAL, sizeof(tab.t32)); + memset(tab.t16, INVAL, sizeof(tab.t16)); + memset(tab.t10, INVAL, sizeof(tab.t10)); + + for(p = set64; *p; p++) + tab.t64[*p] = p-set64; + for(p = set32; *p; p++) + tab.t32[*p] = p-set32; + for(p = set16; *p; p++) + tab.t16[*p] = (p-set16)%16; + for(p = set10; *p; p++) + tab.t10[*p] = (p-set10); + + tab.inited = 1; +} + +static char* +from16(char *a, mpint *b) +{ + char *p, *next; + int i; + mpdigit x; + + b->top = 0; + for(p = a; *p; p++) + if(tab.t16[*p] == INVAL) + break; + mpbits(b, (p-a)*4); + b->top = 0; + next = p; + while(p > a){ + x = 0; + for(i = 0; i < Dbits; i += 4){ + if(p <= a) + break; + x |= tab.t16[*--p]<p[b->top++] = x; + } + return next; +} + +static ulong mppow10[] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 +}; + +static char* +from10(char *a, mpint *b) +{ + ulong x, y; + mpint *pow, *r; + int i; + + pow = mpnew(0); + r = mpnew(0); + + b->top = 0; + for(;;){ + // do a billion at a time in native arithmetic + x = 0; + for(i = 0; i < 9; i++){ + y = tab.t10[*a]; + if(y == INVAL) + break; + a++; + x *= 10; + x += y; + } + if(i == 0) + break; + + // accumulate into mpint + uitomp(mppow10[i], pow); + uitomp(x, r); + mpmul(b, pow, b); + mpadd(b, r, b); + if(i != 9) + break; + } + mpfree(pow); + mpfree(r); + return a; +} + +static char* +from64(char *a, mpint *b) +{ + char *buf = a; + uchar *p; + int n, m; + + for(; tab.t64[*a] != INVAL; a++) + ; + n = a-buf; + mpbits(b, n*6); + p = malloc(n); + if(p == nil) + return a; + m = dec64(p, n, buf, n); + betomp(p, m, b); + free(p); + return a; +} + +static char* +from32(char *a, mpint *b) +{ + char *buf = a; + uchar *p; + int n, m; + + for(; tab.t64[*a] != INVAL; a++) + ; + n = a-buf; + mpbits(b, n*5); + p = malloc(n); + if(p == nil) + return a; + m = dec32(p, n, buf, n); + betomp(p, m, b); + free(p); + return a; +} + +mpint* +strtomp(char *a, char **pp, int base, mpint *b) +{ + int sign; + char *e; + + if(b == nil) + b = mpnew(0); + + if(tab.inited == 0) + init(); + + while(*a==' ' || *a=='\t') + a++; + + sign = 1; + for(;; a++){ + switch(*a){ + case '-': + sign *= -1; + continue; + } + break; + } + + switch(base){ + case 10: + e = from10(a, b); + break; + default: + case 16: + e = from16(a, b); + break; + case 32: + e = from32(a, b); + break; + case 64: + e = from64(a, b); + break; + } + + // if no characters parsed, there wasn't a number to convert + if(e == a) + return nil; + + mpnorm(b); + b->sign = sign; + if(pp != nil) + *pp = e; + + return b; +} diff --git a/libsec/Makefile b/libsec/Makefile new file mode 100644 index 0000000..f651bb2 --- /dev/null +++ b/libsec/Makefile @@ -0,0 +1,60 @@ +LIB=libsec.a +CC=gcc +CFLAGS=-I../include -I. -c -ggdb -D_THREAD_SAFE -pthread +O=o + +OFILES=\ + aes.$O\ + blowfish.$O\ + decodepem.$O\ + des.$O\ + des3CBC.$O\ + des3ECB.$O\ + desCBC.$O\ + desECB.$O\ + desmodes.$O\ + dsaalloc.$O\ + dsagen.$O\ + dsaprimes.$O\ + dsaprivtopub.$O\ + dsasign.$O\ + dsaverify.$O\ + egalloc.$O\ + egdecrypt.$O\ + egencrypt.$O\ + eggen.$O\ + egprivtopub.$O\ + egsign.$O\ + egverify.$O\ + fastrand.$O\ + genprime.$O\ + genrandom.$O\ + gensafeprime.$O\ + genstrongprime.$O\ + hmac.$O\ + md4.$O\ + md5.$O\ + md5block.$O\ + md5pickle.$O\ + nfastrand.$O\ + prng.$O\ + probably_prime.$O\ + rc4.$O\ + rsaalloc.$O\ + rsadecrypt.$O\ + rsaencrypt.$O\ + rsafill.$O\ + rsagen.$O\ + rsaprivtopub.$O\ + sha1.$O\ + sha1block.$O\ + sha1pickle.$O\ + smallprimes.$O + +$(LIB): $(OFILES) + ar r $(LIB) $(OFILES) + ranlib $(LIB) + +%.$O: %.c + $(CC) $(CFLAGS) $*.c + diff --git a/libsec/aes.c b/libsec/aes.c new file mode 100644 index 0000000..dda9c8a --- /dev/null +++ b/libsec/aes.c @@ -0,0 +1,1544 @@ +/* + * this code is derived from the following source, + * and modified to fit into the plan 9 libsec interface. + * most of the changes are confined to the top section, + * with the exception of converting Te4 and Td4 into u8 rather than u32 arrays. + * + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include + +typedef uchar u8; +typedef u32int u32; +#define FULL_UNROLL + +static const u32 Td0[256]; +static const u32 Td1[256]; +static const u32 Td2[256]; +static const u32 Td3[256]; +static const u8 Te4[256]; + +static int rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits); +static int rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits); +static int rijndaelKeySetup(u32 erk[/*4*(Nr + 1)*/], u32 drk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits); +static void rijndaelEncrypt(const u32int rk[], int Nr, const uchar pt[16], uchar ct[16]); +static void rijndaelDecrypt(const u32int rk[], int Nr, const uchar ct[16], uchar pt[16]); + +void +setupAESstate(AESstate *s, uchar key[], int keybytes, uchar *ivec) +{ + memset(s, 0, sizeof(*s)); + if(keybytes > AESmaxkey) + keybytes = AESmaxkey; + memmove(s->key, key, keybytes); + s->keybytes = keybytes; + s->rounds = rijndaelKeySetup(s->ekey, s->dkey, s->key, keybytes * 8); + if(ivec != nil) + memmove(s->ivec, ivec, AESbsize); + if(keybytes==16 || keybytes==24 || keybytes==32) + s->setup = 0xcafebabe; + // else rijndaelKeySetup was invalid +} + +// Define by analogy with desCBCencrypt; AES modes are not standardized yet. +// Because of the way that non-multiple-of-16 buffers are handled, +// the decryptor must be fed buffers of the same size as the encryptor. +void +aesCBCencrypt(uchar *p, int len, AESstate *s) +{ + uchar *p2, *ip, *eip; + uchar q[AESbsize]; + + for(; len >= AESbsize; len -= AESbsize){ + p2 = p; + ip = s->ivec; + for(eip = ip+AESbsize; ip < eip; ) + *p2++ ^= *ip++; + rijndaelEncrypt(s->ekey, s->rounds, p, q); + memmove(s->ivec, q, AESbsize); + memmove(p, q, AESbsize); + p += AESbsize; + } + + if(len > 0){ + ip = s->ivec; + rijndaelEncrypt(s->ekey, s->rounds, ip, q); + memmove(s->ivec, q, AESbsize); + for(eip = ip+len; ip < eip; ) + *p++ ^= *ip++; + } +} + +void +aesCBCdecrypt(uchar *p, int len, AESstate *s) +{ + uchar *ip, *eip, *tp; + uchar tmp[AESbsize], q[AESbsize]; + + for(; len >= AESbsize; len -= AESbsize){ + memmove(tmp, p, AESbsize); + rijndaelDecrypt(s->dkey, s->rounds, p, q); + memmove(p, q, AESbsize); + tp = tmp; + ip = s->ivec; + for(eip = ip+AESbsize; ip < eip; ){ + *p++ ^= *ip; + *ip++ = *tp++; + } + } + + if(len > 0){ + ip = s->ivec; + rijndaelEncrypt(s->ekey, s->rounds, ip, q); + memmove(s->ivec, q, AESbsize); + for(eip = ip+len; ip < eip; ) + *p++ ^= *ip++; + } +} + +/* + * this function has been changed for plan 9. + * Expand the cipher key into the encryption and decryption key schedules. + * + * @return the number of rounds for the given cipher key size. + */ +static int rijndaelKeySetup(u32 erk[/*4*(Nr + 1)*/], u32 drk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) { + int Nr, i; + + /* expand the cipher key: */ + Nr = rijndaelKeySetupEnc(erk, cipherKey, keyBits); + + /* + * invert the order of the round keys and + * apply the inverse MixColumn transform to all round keys but the first and the last + */ + drk[0 ] = erk[4*Nr ]; + drk[1 ] = erk[4*Nr + 1]; + drk[2 ] = erk[4*Nr + 2]; + drk[3 ] = erk[4*Nr + 3]; + drk[4*Nr ] = erk[0 ]; + drk[4*Nr + 1] = erk[1 ]; + drk[4*Nr + 2] = erk[2 ]; + drk[4*Nr + 3] = erk[3 ]; + erk += 4 * Nr; + for (i = 1; i < Nr; i++) { + drk += 4; + erk -= 4; + drk[0] = + Td0[Te4[(erk[0] >> 24) ]] ^ + Td1[Te4[(erk[0] >> 16) & 0xff]] ^ + Td2[Te4[(erk[0] >> 8) & 0xff]] ^ + Td3[Te4[(erk[0] ) & 0xff]]; + drk[1] = + Td0[Te4[(erk[1] >> 24) ]] ^ + Td1[Te4[(erk[1] >> 16) & 0xff]] ^ + Td2[Te4[(erk[1] >> 8) & 0xff]] ^ + Td3[Te4[(erk[1] ) & 0xff]]; + drk[2] = + Td0[Te4[(erk[2] >> 24) ]] ^ + Td1[Te4[(erk[2] >> 16) & 0xff]] ^ + Td2[Te4[(erk[2] >> 8) & 0xff]] ^ + Td3[Te4[(erk[2] ) & 0xff]]; + drk[3] = + Td0[Te4[(erk[3] >> 24) ]] ^ + Td1[Te4[(erk[3] >> 16) & 0xff]] ^ + Td2[Te4[(erk[3] >> 8) & 0xff]] ^ + Td3[Te4[(erk[3] ) & 0xff]]; + } + return Nr; +} + +/* +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; +Te4[x] = S [x] + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x] +*/ + +static const u32 Te0[256] = { + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; +static const u32 Te1[256] = { + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; +static const u32 Te2[256] = { + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; +static const u32 Te3[256] = { + + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; +static const u8 Te4[256] = { + 0x63U, 0x7cU, 0x77U, 0x7bU, + 0xf2U, 0x6bU, 0x6fU, 0xc5U, + 0x30U, 0x01U, 0x67U, 0x2bU, + 0xfeU, 0xd7U, 0xabU, 0x76U, + 0xcaU, 0x82U, 0xc9U, 0x7dU, + 0xfaU, 0x59U, 0x47U, 0xf0U, + 0xadU, 0xd4U, 0xa2U, 0xafU, + 0x9cU, 0xa4U, 0x72U, 0xc0U, + 0xb7U, 0xfdU, 0x93U, 0x26U, + 0x36U, 0x3fU, 0xf7U, 0xccU, + 0x34U, 0xa5U, 0xe5U, 0xf1U, + 0x71U, 0xd8U, 0x31U, 0x15U, + 0x04U, 0xc7U, 0x23U, 0xc3U, + 0x18U, 0x96U, 0x05U, 0x9aU, + 0x07U, 0x12U, 0x80U, 0xe2U, + 0xebU, 0x27U, 0xb2U, 0x75U, + 0x09U, 0x83U, 0x2cU, 0x1aU, + 0x1bU, 0x6eU, 0x5aU, 0xa0U, + 0x52U, 0x3bU, 0xd6U, 0xb3U, + 0x29U, 0xe3U, 0x2fU, 0x84U, + 0x53U, 0xd1U, 0x00U, 0xedU, + 0x20U, 0xfcU, 0xb1U, 0x5bU, + 0x6aU, 0xcbU, 0xbeU, 0x39U, + 0x4aU, 0x4cU, 0x58U, 0xcfU, + 0xd0U, 0xefU, 0xaaU, 0xfbU, + 0x43U, 0x4dU, 0x33U, 0x85U, + 0x45U, 0xf9U, 0x02U, 0x7fU, + 0x50U, 0x3cU, 0x9fU, 0xa8U, + 0x51U, 0xa3U, 0x40U, 0x8fU, + 0x92U, 0x9dU, 0x38U, 0xf5U, + 0xbcU, 0xb6U, 0xdaU, 0x21U, + 0x10U, 0xffU, 0xf3U, 0xd2U, + 0xcdU, 0x0cU, 0x13U, 0xecU, + 0x5fU, 0x97U, 0x44U, 0x17U, + 0xc4U, 0xa7U, 0x7eU, 0x3dU, + 0x64U, 0x5dU, 0x19U, 0x73U, + 0x60U, 0x81U, 0x4fU, 0xdcU, + 0x22U, 0x2aU, 0x90U, 0x88U, + 0x46U, 0xeeU, 0xb8U, 0x14U, + 0xdeU, 0x5eU, 0x0bU, 0xdbU, + 0xe0U, 0x32U, 0x3aU, 0x0aU, + 0x49U, 0x06U, 0x24U, 0x5cU, + 0xc2U, 0xd3U, 0xacU, 0x62U, + 0x91U, 0x95U, 0xe4U, 0x79U, + 0xe7U, 0xc8U, 0x37U, 0x6dU, + 0x8dU, 0xd5U, 0x4eU, 0xa9U, + 0x6cU, 0x56U, 0xf4U, 0xeaU, + 0x65U, 0x7aU, 0xaeU, 0x08U, + 0xbaU, 0x78U, 0x25U, 0x2eU, + 0x1cU, 0xa6U, 0xb4U, 0xc6U, + 0xe8U, 0xddU, 0x74U, 0x1fU, + 0x4bU, 0xbdU, 0x8bU, 0x8aU, + 0x70U, 0x3eU, 0xb5U, 0x66U, + 0x48U, 0x03U, 0xf6U, 0x0eU, + 0x61U, 0x35U, 0x57U, 0xb9U, + 0x86U, 0xc1U, 0x1dU, 0x9eU, + 0xe1U, 0xf8U, 0x98U, 0x11U, + 0x69U, 0xd9U, 0x8eU, 0x94U, + 0x9bU, 0x1eU, 0x87U, 0xe9U, + 0xceU, 0x55U, 0x28U, 0xdfU, + 0x8cU, 0xa1U, 0x89U, 0x0dU, + 0xbfU, 0xe6U, 0x42U, 0x68U, + 0x41U, 0x99U, 0x2dU, 0x0fU, + 0xb0U, 0x54U, 0xbbU, 0x16U, +}; +static const u32 Td0[256] = { + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; +static const u32 Td1[256] = { + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; +static const u32 Td2[256] = { + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; +static const u32 Td3[256] = { + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; +static const u8 Td4[256] = { + 0x52U, 0x09U, 0x6aU, 0xd5U, + 0x30U, 0x36U, 0xa5U, 0x38U, + 0xbfU, 0x40U, 0xa3U, 0x9eU, + 0x81U, 0xf3U, 0xd7U, 0xfbU, + 0x7cU, 0xe3U, 0x39U, 0x82U, + 0x9bU, 0x2fU, 0xffU, 0x87U, + 0x34U, 0x8eU, 0x43U, 0x44U, + 0xc4U, 0xdeU, 0xe9U, 0xcbU, + 0x54U, 0x7bU, 0x94U, 0x32U, + 0xa6U, 0xc2U, 0x23U, 0x3dU, + 0xeeU, 0x4cU, 0x95U, 0x0bU, + 0x42U, 0xfaU, 0xc3U, 0x4eU, + 0x08U, 0x2eU, 0xa1U, 0x66U, + 0x28U, 0xd9U, 0x24U, 0xb2U, + 0x76U, 0x5bU, 0xa2U, 0x49U, + 0x6dU, 0x8bU, 0xd1U, 0x25U, + 0x72U, 0xf8U, 0xf6U, 0x64U, + 0x86U, 0x68U, 0x98U, 0x16U, + 0xd4U, 0xa4U, 0x5cU, 0xccU, + 0x5dU, 0x65U, 0xb6U, 0x92U, + 0x6cU, 0x70U, 0x48U, 0x50U, + 0xfdU, 0xedU, 0xb9U, 0xdaU, + 0x5eU, 0x15U, 0x46U, 0x57U, + 0xa7U, 0x8dU, 0x9dU, 0x84U, + 0x90U, 0xd8U, 0xabU, 0x00U, + 0x8cU, 0xbcU, 0xd3U, 0x0aU, + 0xf7U, 0xe4U, 0x58U, 0x05U, + 0xb8U, 0xb3U, 0x45U, 0x06U, + 0xd0U, 0x2cU, 0x1eU, 0x8fU, + 0xcaU, 0x3fU, 0x0fU, 0x02U, + 0xc1U, 0xafU, 0xbdU, 0x03U, + 0x01U, 0x13U, 0x8aU, 0x6bU, + 0x3aU, 0x91U, 0x11U, 0x41U, + 0x4fU, 0x67U, 0xdcU, 0xeaU, + 0x97U, 0xf2U, 0xcfU, 0xceU, + 0xf0U, 0xb4U, 0xe6U, 0x73U, + 0x96U, 0xacU, 0x74U, 0x22U, + 0xe7U, 0xadU, 0x35U, 0x85U, + 0xe2U, 0xf9U, 0x37U, 0xe8U, + 0x1cU, 0x75U, 0xdfU, 0x6eU, + 0x47U, 0xf1U, 0x1aU, 0x71U, + 0x1dU, 0x29U, 0xc5U, 0x89U, + 0x6fU, 0xb7U, 0x62U, 0x0eU, + 0xaaU, 0x18U, 0xbeU, 0x1bU, + 0xfcU, 0x56U, 0x3eU, 0x4bU, + 0xc6U, 0xd2U, 0x79U, 0x20U, + 0x9aU, 0xdbU, 0xc0U, 0xfeU, + 0x78U, 0xcdU, 0x5aU, 0xf4U, + 0x1fU, 0xddU, 0xa8U, 0x33U, + 0x88U, 0x07U, 0xc7U, 0x31U, + 0xb1U, 0x12U, 0x10U, 0x59U, + 0x27U, 0x80U, 0xecU, 0x5fU, + 0x60U, 0x51U, 0x7fU, 0xa9U, + 0x19U, 0xb5U, 0x4aU, 0x0dU, + 0x2dU, 0xe5U, 0x7aU, 0x9fU, + 0x93U, 0xc9U, 0x9cU, 0xefU, + 0xa0U, 0xe0U, 0x3bU, 0x4dU, + 0xaeU, 0x2aU, 0xf5U, 0xb0U, + 0xc8U, 0xebU, 0xbbU, 0x3cU, + 0x83U, 0x53U, 0x99U, 0x61U, + 0x17U, 0x2bU, 0x04U, 0x7eU, + 0xbaU, 0x77U, 0xd6U, 0x26U, + 0xe1U, 0x69U, 0x14U, 0x63U, + 0x55U, 0x21U, 0x0cU, 0x7dU, +}; +static const u32 rcon[] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) + +#ifdef _MSC_VER +#define GETU32(p) SWAP(*((u32 *)(p))) +#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); } +#else +#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) +#define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } +#endif + +/** + * Expand the cipher key into the encryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +static int rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) { + int i = 0; + u32 temp; + + rk[0] = GETU32(cipherKey ); + rk[1] = GETU32(cipherKey + 4); + rk[2] = GETU32(cipherKey + 8); + rk[3] = GETU32(cipherKey + 12); + if (keyBits == 128) { + for (;;) { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] << 24) ^ + (Te4[(temp >> 8) & 0xff] << 16) ^ + (Te4[(temp ) & 0xff] << 8) ^ + (Te4[(temp >> 24) ] ) ^ + rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) { + return 10; + } + rk += 4; + } + } + rk[4] = GETU32(cipherKey + 16); + rk[5] = GETU32(cipherKey + 20); + if (keyBits == 192) { + for (;;) { + temp = rk[ 5]; + rk[ 6] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] << 24) ^ + (Te4[(temp >> 8) & 0xff] << 16) ^ + (Te4[(temp ) & 0xff] << 8) ^ + (Te4[(temp >> 24) ] ) ^ + rcon[i]; + rk[ 7] = rk[ 1] ^ rk[ 6]; + rk[ 8] = rk[ 2] ^ rk[ 7]; + rk[ 9] = rk[ 3] ^ rk[ 8]; + if (++i == 8) { + return 12; + } + rk[10] = rk[ 4] ^ rk[ 9]; + rk[11] = rk[ 5] ^ rk[10]; + rk += 6; + } + } + rk[6] = GETU32(cipherKey + 24); + rk[7] = GETU32(cipherKey + 28); + if (keyBits == 256) { + for (;;) { + temp = rk[ 7]; + rk[ 8] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] << 24) ^ + (Te4[(temp >> 8) & 0xff] << 16) ^ + (Te4[(temp ) & 0xff] << 8) ^ + (Te4[(temp >> 24) ] ) ^ + rcon[i]; + rk[ 9] = rk[ 1] ^ rk[ 8]; + rk[10] = rk[ 2] ^ rk[ 9]; + rk[11] = rk[ 3] ^ rk[10]; + if (++i == 7) { + return 14; + } + temp = rk[11]; + rk[12] = rk[ 4] ^ + (Te4[(temp >> 24) ] << 24) ^ + (Te4[(temp >> 16) & 0xff] << 16) ^ + (Te4[(temp >> 8) & 0xff] << 8) ^ + (Te4[(temp ) & 0xff] ); + rk[13] = rk[ 5] ^ rk[12]; + rk[14] = rk[ 6] ^ rk[13]; + rk[15] = rk[ 7] ^ rk[14]; + + rk += 8; + } + } + return 0; +} + +/** + * Expand the cipher key into the decryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +static int rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) { + int Nr, i, j; + u32 temp; + + /* expand the cipher key: */ + Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits); + /* invert the order of the round keys: */ + for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) { + temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; + temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; + temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; + temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + for (i = 1; i < Nr; i++) { + rk += 4; + rk[0] = + Td0[Te4[(rk[0] >> 24) ]] ^ + Td1[Te4[(rk[0] >> 16) & 0xff]] ^ + Td2[Te4[(rk[0] >> 8) & 0xff]] ^ + Td3[Te4[(rk[0] ) & 0xff]]; + rk[1] = + Td0[Te4[(rk[1] >> 24) ]] ^ + Td1[Te4[(rk[1] >> 16) & 0xff]] ^ + Td2[Te4[(rk[1] >> 8) & 0xff]] ^ + Td3[Te4[(rk[1] ) & 0xff]]; + rk[2] = + Td0[Te4[(rk[2] >> 24) ]] ^ + Td1[Te4[(rk[2] >> 16) & 0xff]] ^ + Td2[Te4[(rk[2] >> 8) & 0xff]] ^ + Td3[Te4[(rk[2] ) & 0xff]]; + rk[3] = + Td0[Te4[(rk[3] >> 24) ]] ^ + Td1[Te4[(rk[3] >> 16) & 0xff]] ^ + Td2[Te4[(rk[3] >> 8) & 0xff]] ^ + Td3[Te4[(rk[3] ) & 0xff]]; + } + return Nr; +} + +static void rijndaelEncrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 pt[16], u8 ct[16]) { + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(pt ) ^ rk[0]; + s1 = GETU32(pt + 4) ^ rk[1]; + s2 = GETU32(pt + 8) ^ rk[2]; + s3 = GETU32(pt + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; + if (Nr > 10) { + /* round 10: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47]; + if (Nr > 12) { + /* round 12: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55]; + } + } + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Te0[(s0 >> 24) ] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3 ) & 0xff] ^ + rk[4]; + t1 = + Te0[(s1 >> 24) ] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0 ) & 0xff] ^ + rk[5]; + t2 = + Te0[(s2 >> 24) ] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1 ) & 0xff] ^ + rk[6]; + t3 = + Te0[(s3 >> 24) ] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Te0[(t0 >> 24) ] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ + Te3[(t3 ) & 0xff] ^ + rk[0]; + s1 = + Te0[(t1 >> 24) ] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ + Te3[(t0 ) & 0xff] ^ + rk[1]; + s2 = + Te0[(t2 >> 24) ] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ + Te3[(t1 ) & 0xff] ^ + rk[2]; + s3 = + Te0[(t3 >> 24) ] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ + Te3[(t2 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4[(t0 >> 24) ] << 24) ^ + (Te4[(t1 >> 16) & 0xff] << 16) ^ + (Te4[(t2 >> 8) & 0xff] << 8) ^ + (Te4[(t3 ) & 0xff] ) ^ + rk[0]; + PUTU32(ct , s0); + s1 = + (Te4[(t1 >> 24) ] << 24) ^ + (Te4[(t2 >> 16) & 0xff] << 16) ^ + (Te4[(t3 >> 8) & 0xff] << 8) ^ + (Te4[(t0 ) & 0xff] ) ^ + rk[1]; + PUTU32(ct + 4, s1); + s2 = + (Te4[(t2 >> 24) ] << 24) ^ + (Te4[(t3 >> 16) & 0xff] << 16) ^ + (Te4[(t0 >> 8) & 0xff] << 8) ^ + (Te4[(t1 ) & 0xff] ) ^ + rk[2]; + PUTU32(ct + 8, s2); + s3 = + (Te4[(t3 >> 24) ] << 24) ^ + (Te4[(t0 >> 16) & 0xff] << 16) ^ + (Te4[(t1 >> 8) & 0xff] << 8) ^ + (Te4[(t2 ) & 0xff] ) ^ + rk[3]; + PUTU32(ct + 12, s3); +} + +static void rijndaelDecrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 ct[16], u8 pt[16]) { + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(ct ) ^ rk[0]; + s1 = GETU32(ct + 4) ^ rk[1]; + s2 = GETU32(ct + 8) ^ rk[2]; + s3 = GETU32(ct + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39]; + if (Nr > 10) { + /* round 10: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47]; + if (Nr > 12) { + /* round 12: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55]; + } + } + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Td0[(s0 >> 24) ] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ + Td3[(s1 ) & 0xff] ^ + rk[4]; + t1 = + Td0[(s1 >> 24) ] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ + Td3[(s2 ) & 0xff] ^ + rk[5]; + t2 = + Td0[(s2 >> 24) ] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ + Td3[(s3 ) & 0xff] ^ + rk[6]; + t3 = + Td0[(s3 >> 24) ] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ + Td3[(s0 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Td0[(t0 >> 24) ] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ + Td3[(t1 ) & 0xff] ^ + rk[0]; + s1 = + Td0[(t1 >> 24) ] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ + Td3[(t2 ) & 0xff] ^ + rk[1]; + s2 = + Td0[(t2 >> 24) ] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ + Td3[(t3 ) & 0xff] ^ + rk[2]; + s3 = + Td0[(t3 >> 24) ] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ + Td3[(t0 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[(t0 >> 24) ] << 24) ^ + (Td4[(t3 >> 16) & 0xff] << 16) ^ + (Td4[(t2 >> 8) & 0xff] << 8) ^ + (Td4[(t1 ) & 0xff] ) ^ + rk[0]; + PUTU32(pt , s0); + s1 = + (Td4[(t1 >> 24) ] << 24) ^ + (Td4[(t0 >> 16) & 0xff] << 16) ^ + (Td4[(t3 >> 8) & 0xff] << 8) ^ + (Td4[(t2 ) & 0xff] ) ^ + rk[1]; + PUTU32(pt + 4, s1); + s2 = + (Td4[(t2 >> 24) ] << 24) ^ + (Td4[(t1 >> 16) & 0xff] << 16) ^ + (Td4[(t0 >> 8) & 0xff] << 8) ^ + (Td4[(t3 ) & 0xff] ) ^ + rk[2]; + PUTU32(pt + 8, s2); + s3 = + (Td4[(t3 >> 24) ] << 24) ^ + (Td4[(t2 >> 16) & 0xff] << 16) ^ + (Td4[(t1 >> 8) & 0xff] << 8) ^ + (Td4[(t0 ) & 0xff] ) ^ + rk[3]; + PUTU32(pt + 12, s3); +} + +#ifdef INTERMEDIATE_VALUE_KAT + +static void rijndaelEncryptRound(const u32 rk[/*4*(Nr + 1)*/], int Nr, u8 block[16], int rounds) { + int r; + u32 s0, s1, s2, s3, t0, t1, t2, t3; + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(block ) ^ rk[0]; + s1 = GETU32(block + 4) ^ rk[1]; + s2 = GETU32(block + 8) ^ rk[2]; + s3 = GETU32(block + 12) ^ rk[3]; + rk += 4; + + /* + * Nr - 1 full rounds: + */ + for (r = (rounds < Nr ? rounds : Nr - 1); r > 0; r--) { + t0 = + Te0[(s0 >> 24) ] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3 ) & 0xff] ^ + rk[0]; + t1 = + Te0[(s1 >> 24) ] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0 ) & 0xff] ^ + rk[1]; + t2 = + Te0[(s2 >> 24) ] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1 ) & 0xff] ^ + rk[2]; + t3 = + Te0[(s3 >> 24) ] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2 ) & 0xff] ^ + rk[3]; + + s0 = t0; + s1 = t1; + s2 = t2; + s3 = t3; + rk += 4; + + } + + /* + * apply last round and + * map cipher state to byte array block: + */ + if (rounds == Nr) { + t0 = + (Te4[(s0 >> 24) ] << 24) ^ + (Te4[(s1 >> 16) & 0xff] << 16) ^ + (Te4[(s2 >> 8) & 0xff] << 8) ^ + (Te4[(s3 ) & 0xff] ) ^ + rk[0]; + t1 = + (Te4[(s1 >> 24) ] << 24) ^ + (Te4[(s2 >> 16) & 0xff] << 16) ^ + (Te4[(s3 >> 8) & 0xff] << 8) ^ + (Te4[(s0 ) & 0xff] ) ^ + rk[1]; + t2 = + (Te4[(s2 >> 24) ] << 24) ^ + (Te4[(s3 >> 16) & 0xff] << 16) ^ + (Te4[(s0 >> 8) & 0xff] << 8) ^ + (Te4[(s1 ) & 0xff] ) ^ + rk[2]; + t3 = + (Te4[(s3 >> 24) ] << 24) ^ + (Te4[(s0 >> 16) & 0xff] << 16) ^ + (Te4[(s1 >> 8) & 0xff] << 8) ^ + (Te4[(s2 ) & 0xff] ) ^ + rk[3]; + + s0 = t0; + s1 = t1; + s2 = t2; + s3 = t3; + } + + PUTU32(block , s0); + PUTU32(block + 4, s1); + PUTU32(block + 8, s2); + PUTU32(block + 12, s3); +} + +static void rijndaelDecryptRound(const u32 rk[/*4*(Nr + 1)*/], int Nr, u8 block[16], int rounds) { + int r; + u32 s0, s1, s2, s3, t0, t1, t2, t3; + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(block ) ^ rk[0]; + s1 = GETU32(block + 4) ^ rk[1]; + s2 = GETU32(block + 8) ^ rk[2]; + s3 = GETU32(block + 12) ^ rk[3]; + rk += 4; + + /* + * Nr - 1 full rounds: + */ + for (r = (rounds < Nr ? rounds : Nr) - 1; r > 0; r--) { + t0 = + Td0[(s0 >> 24) ] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ + Td3[(s1 ) & 0xff] ^ + rk[0]; + t1 = + Td0[(s1 >> 24) ] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ + Td3[(s2 ) & 0xff] ^ + rk[1]; + t2 = + Td0[(s2 >> 24) ] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ + Td3[(s3 ) & 0xff] ^ + rk[2]; + t3 = + Td0[(s3 >> 24) ] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ + Td3[(s0 ) & 0xff] ^ + rk[3]; + + s0 = t0; + s1 = t1; + s2 = t2; + s3 = t3; + rk += 4; + + } + + /* + * complete the last round and + * map cipher state to byte array block: + */ + t0 = + (Td4[(s0 >> 24) ] << 24) ^ + (Td4[(s3 >> 16) & 0xff] << 16) ^ + (Td4[(s2 >> 8) & 0xff] << 8) ^ + (Td4[(s1 ) & 0xff] ); + t1 = + (Td4[(s1 >> 24) ] << 24) ^ + (Td4[(s0 >> 16) & 0xff] << 16) ^ + (Td4[(s3 >> 8) & 0xff] << 8) ^ + (Td4[(s2 ) & 0xff] ); + t2 = + (Td4[(s2 >> 24) ] << 24) ^ + (Td4[(s1 >> 16) & 0xff] << 16) ^ + (Td4[(s0 >> 8) & 0xff] << 8) ^ + (Td4[(s3 ) & 0xff] ); + t3 = + (Td4[(s3 >> 24) ] << 24) ^ + (Td4[(s2 >> 16) & 0xff] << 16) ^ + (Td4[(s1 >> 8) & 0xff] << 8) ^ + (Td4[(s0 ) & 0xff] ); + + if (rounds == Nr) { + t0 ^= rk[0]; + t1 ^= rk[1]; + t2 ^= rk[2]; + t3 ^= rk[3]; + } + + PUTU32(block , t0); + PUTU32(block + 4, t1); + PUTU32(block + 8, t2); + PUTU32(block + 12, t3); +} + +#endif /* INTERMEDIATE_VALUE_KAT */ diff --git a/libsec/blowfish.c b/libsec/blowfish.c new file mode 100644 index 0000000..5dcc677 --- /dev/null +++ b/libsec/blowfish.c @@ -0,0 +1,579 @@ +#include "os.h" +#include +#include + +// Blowfish block cipher. See: +// Lecture Notes in Computer Science 809 +// Fast Software Encryption +// Cambridge Security Workshop, Cambridge, England (1993) + +static u32int sbox[1024]; +static u32int pbox[BFrounds+2]; + +static void bfencrypt(u32int *, BFstate *); +static void bfdecrypt(u32int *, BFstate *); + +void +setupBFstate(BFstate *s, uchar key[], int keybytes, uchar *ivec) +{ + int i, j; + u32int n, buf[2]; + + memset(s, 0, sizeof(*s)); + memset(buf, 0, sizeof buf); + + if (keybytes > sizeof(s->key)) + keybytes = sizeof(s->key); + + memmove(s->key, key, keybytes); + + if (ivec != nil) + memmove(s->ivec, ivec, sizeof(s->ivec)); + else + memset(s->ivec, 0, sizeof(s->ivec)); + + memmove(s->pbox, pbox, sizeof(pbox)); + memmove(s->sbox, sbox, sizeof(sbox)); + + if (keybytes > 4*(BFrounds + 2)) + keybytes = 4*(BFrounds + 2); + + for(i=j=0; i < BFrounds+2; i++) { + n = key[j]; + j = (j+1) % keybytes; + + n <<= 8; + n |= key[j]; + j = (j+1) % keybytes; + + n <<= 8; + n |= key[j]; + j = (j+1) % keybytes; + + n <<= 8; + n |= key[j]; + j = (j+1) % keybytes; + + s->pbox[i] ^= n; + } + + for(i=0; i < BFrounds+2; i += 2) { + bfencrypt(buf, s); + s->pbox[i] = buf[0]; + s->pbox[i+1] = buf[1]; + } + + for(i=0; i < 1024; i += 2) { + bfencrypt(buf, s); + s->sbox[i] = buf[0]; + s->sbox[i+1] = buf[1]; + } + + s->setup = 0xcafebabe; +} + +void +bfCBCencrypt(uchar *buf, int n, BFstate *s) +{ + int i; + uchar *p; + u32int bo[2], bi[2], b; + + assert((n & 7) == 0); + + bo[0] = s->ivec[0] | ((u32int) s->ivec[1]<<8) | ((u32int)s->ivec[2]<<16) | ((u32int)s->ivec[3]<<24); + bo[1] = s->ivec[4] | ((u32int) s->ivec[5]<<8) | ((u32int)s->ivec[6]<<16) | ((u32int)s->ivec[7]<<24); + + for(i=0; i < n; i += 8, buf += 8) { + bi[0] = buf[0] | ((u32int) buf[1]<<8) | ((u32int)buf[2]<<16) | ((u32int)buf[3]<<24); + bi[1] = buf[4] | ((u32int) buf[5]<<8) | ((u32int)buf[6]<<16) | ((u32int)buf[7]<<24); + + bi[0] ^= bo[0]; + bi[1] ^= bo[1]; + + bfencrypt(bi, s); + + bo[0] = bi[0]; + bo[1] = bi[1]; + + p = buf; + b = bo[0]; + *p++ = b; + b >>= 8; + *p++ = b; + b >>= 8; + *p++ = b; + b >>= 8; + *p++ = b; + + b = bo[1]; + *p++ = b; + b >>= 8; + *p++ = b; + b >>= 8; + *p++ = b; + b >>= 8; + *p = b; + } + + s->ivec[7] = bo[1] >> 24; + s->ivec[6] = bo[1] >> 16; + s->ivec[5] = bo[1] >> 8; + s->ivec[4] = bo[1]; + + s->ivec[3] = bo[0] >> 24; + s->ivec[2] = bo[0] >> 16; + s->ivec[1] = bo[0] >> 8; + s->ivec[0] = bo[0]; + + return; +} + +void +bfCBCdecrypt(uchar *buf, int n, BFstate *s) +{ + int i; + uchar *p; + u32int b, bo[2], bi[2], xr[2]; + + assert((n & 7) == 0); + + bo[0] = s->ivec[0] | ((u32int) s->ivec[1]<<8) | ((u32int)s->ivec[2]<<16) | ((u32int)s->ivec[3]<<24); + bo[1] = s->ivec[4] | ((u32int) s->ivec[5]<<8) | ((u32int)s->ivec[6]<<16) | ((u32int)s->ivec[7]<<24); + + for(i=0; i < n; i += 8, buf += 8) { + bi[0] = buf[0] | ((u32int) buf[1]<<8) | ((u32int)buf[2]<<16) | ((u32int)buf[3]<<24); + bi[1] = buf[4] | ((u32int) buf[5]<<8) | ((u32int)buf[6]<<16) | ((u32int)buf[7]<<24); + + xr[0] = bi[0]; + xr[1] = bi[1]; + + bfdecrypt(bi, s); + + bo[0] ^= bi[0]; + bo[1] ^= bi[1]; + + p = buf; + b = bo[0]; + *p++ = b; + b >>= 8; + *p++ = b; + b >>= 8; + *p++ = b; + b >>= 8; + *p++ = b; + + b = bo[1]; + *p++ = b; + b >>= 8; + *p++ = b; + b >>= 8; + *p++ = b; + b >>= 8; + *p = b; + + bo[0] = xr[0]; + bo[1] = xr[1]; + } + + s->ivec[7] = bo[1] >> 24; + s->ivec[6] = bo[1] >> 16; + s->ivec[5] = bo[1] >> 8; + s->ivec[4] = bo[1]; + + s->ivec[3] = bo[0] >> 24; + s->ivec[2] = bo[0] >> 16; + s->ivec[1] = bo[0] >> 8; + s->ivec[0] = bo[0]; + + return; +} + +void +bfECBencrypt(uchar *buf, int n, BFstate *s) +{ + int i; + u32int b[2]; + + for(i=0; i < n; i += 8, buf += 8) { + b[0] = buf[0] | ((u32int) buf[1]<<8) | ((u32int)buf[2]<<16) | ((u32int)buf[3]<<24); + b[1] = buf[4] | ((u32int) buf[5]<<8) | ((u32int)buf[6]<<16) | ((u32int)buf[7]<<24); + + bfencrypt(b, s); + + buf[7] = b[1] >> 24; + buf[6] = b[1] >> 16; + buf[5] = b[1] >> 8; + buf[4] = b[1]; + + buf[3] = b[0] >> 24; + buf[2] = b[0] >> 16; + buf[1] = b[0] >> 8; + buf[0] = b[0]; + } + + return; +} + +void +bfECBdecrypt(uchar *buf, int n, BFstate *s) +{ + int i; + u32int b[2]; + + for(i=0; i < n; i += 8, buf += 8) { + b[0] = buf[0] | ((u32int) buf[1]<<8) | ((u32int)buf[2]<<16) | ((u32int)buf[3]<<24); + b[1] = buf[4] | ((u32int) buf[5]<<8) | ((u32int)buf[6]<<16) | ((u32int)buf[7]<<24); + + bfdecrypt(b, s); + + buf[7] = b[1] >> 24; + buf[6] = b[1] >> 16; + buf[5] = b[1] >> 8; + buf[4] = b[1]; + + buf[3] = b[0] >> 24; + buf[2] = b[0] >> 16; + buf[1] = b[0] >> 8; + buf[0] = b[0]; + } + + return; +} + +static void +bfencrypt(u32int *b, BFstate *s) +{ + int i; + u32int l, r; + u32int *pb, *sb; + + l = b[0]; + r = b[1]; + + pb = s->pbox; + sb = s->sbox; + + l ^= pb[0]; + + for(i=1; i<16; i += 2) { + r ^= pb[i]; + r ^= ( (sb[ (uchar) (l>>24)] + sb[256 + ((uchar) (l>>16))]) ^ + sb[512 + ((uchar) (l>>8))]) + sb[768 +((uchar) l)]; + + l ^= pb[i+1]; + l ^= ( (sb[ (uchar) (r>>24)] + sb[256 + ((uchar) (r>>16))]) ^ + sb[512 + ((uchar) (r>>8))]) + sb[768 +((uchar) r)]; + } + + r ^= pb[BFrounds+1]; + + /* sic */ + b[0] = r; + b[1] = l; + + return; +} + +static void +bfdecrypt(u32int *b, BFstate *s) +{ + int i; + u32int l, r; + u32int *pb, *sb; + + l = b[0]; + r = b[1]; + + pb = s->pbox; + sb = s->sbox; + + l ^= pb[BFrounds+1]; + + for(i=16; i > 0; i -= 2) { + r ^= pb[i]; + r ^= ( (sb[ (uchar) (l>>24)] + sb[256 + ((uchar) (l>>16))]) ^ + sb[512 + ((uchar) (l>>8))]) + sb[768 +((uchar) l)]; + + l ^= pb[i-1]; + l ^= ( (sb[ (uchar) (r>>24)] + sb[256 + ((uchar) (r>>16))]) ^ + sb[512 + ((uchar) (r>>8))]) + sb[768 +((uchar) r)]; + } + + r ^= pb[0]; + + /* sic */ + b[0] = r; + b[1] = l; + + return; +} + +static u32int pbox[BFrounds+2] = { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b +}; + +static u32int sbox[1024] = { + 0xd1310ba6L, 0x98dfb5acL, 0x2ffd72dbL, 0xd01adfb7L, + 0xb8e1afedL, 0x6a267e96L, 0xba7c9045L, 0xf12c7f99L, + 0x24a19947L, 0xb3916cf7L, 0x0801f2e2L, 0x858efc16L, + 0x636920d8L, 0x71574e69L, 0xa458fea3L, 0xf4933d7eL, + 0x0d95748fL, 0x728eb658L, 0x718bcd58L, 0x82154aeeL, + 0x7b54a41dL, 0xc25a59b5L, 0x9c30d539L, 0x2af26013L, + 0xc5d1b023L, 0x286085f0L, 0xca417918L, 0xb8db38efL, + 0x8e79dcb0L, 0x603a180eL, 0x6c9e0e8bL, 0xb01e8a3eL, + 0xd71577c1L, 0xbd314b27L, 0x78af2fdaL, 0x55605c60L, + 0xe65525f3L, 0xaa55ab94L, 0x57489862L, 0x63e81440L, + 0x55ca396aL, 0x2aab10b6L, 0xb4cc5c34L, 0x1141e8ceL, + 0xa15486afL, 0x7c72e993L, 0xb3ee1411L, 0x636fbc2aL, + 0x2ba9c55dL, 0x741831f6L, 0xce5c3e16L, 0x9b87931eL, + 0xafd6ba33L, 0x6c24cf5cL, 0x7a325381L, 0x28958677L, + 0x3b8f4898L, 0x6b4bb9afL, 0xc4bfe81bL, 0x66282193L, + 0x61d809ccL, 0xfb21a991L, 0x487cac60L, 0x5dec8032L, + 0xef845d5dL, 0xe98575b1L, 0xdc262302L, 0xeb651b88L, + 0x23893e81L, 0xd396acc5L, 0x0f6d6ff3L, 0x83f44239L, + 0x2e0b4482L, 0xa4842004L, 0x69c8f04aL, 0x9e1f9b5eL, + 0x21c66842L, 0xf6e96c9aL, 0x670c9c61L, 0xabd388f0L, + 0x6a51a0d2L, 0xd8542f68L, 0x960fa728L, 0xab5133a3L, + 0x6eef0b6cL, 0x137a3be4L, 0xba3bf050L, 0x7efb2a98L, + 0xa1f1651dL, 0x39af0176L, 0x66ca593eL, 0x82430e88L, + 0x8cee8619L, 0x456f9fb4L, 0x7d84a5c3L, 0x3b8b5ebeL, + 0xe06f75d8L, 0x85c12073L, 0x401a449fL, 0x56c16aa6L, + 0x4ed3aa62L, 0x363f7706L, 0x1bfedf72L, 0x429b023dL, + 0x37d0d724L, 0xd00a1248L, 0xdb0fead3L, 0x49f1c09bL, + 0x075372c9L, 0x80991b7bL, 0x25d479d8L, 0xf6e8def7L, + 0xe3fe501aL, 0xb6794c3bL, 0x976ce0bdL, 0x04c006baL, + 0xc1a94fb6L, 0x409f60c4L, 0x5e5c9ec2L, 0x196a2463L, + 0x68fb6fafL, 0x3e6c53b5L, 0x1339b2ebL, 0x3b52ec6fL, + 0x6dfc511fL, 0x9b30952cL, 0xcc814544L, 0xaf5ebd09L, + 0xbee3d004L, 0xde334afdL, 0x660f2807L, 0x192e4bb3L, + 0xc0cba857L, 0x45c8740fL, 0xd20b5f39L, 0xb9d3fbdbL, + 0x5579c0bdL, 0x1a60320aL, 0xd6a100c6L, 0x402c7279L, + 0x679f25feL, 0xfb1fa3ccL, 0x8ea5e9f8L, 0xdb3222f8L, + 0x3c7516dfL, 0xfd616b15L, 0x2f501ec8L, 0xad0552abL, + 0x323db5faL, 0xfd238760L, 0x53317b48L, 0x3e00df82L, + 0x9e5c57bbL, 0xca6f8ca0L, 0x1a87562eL, 0xdf1769dbL, + 0xd542a8f6L, 0x287effc3L, 0xac6732c6L, 0x8c4f5573L, + 0x695b27b0L, 0xbbca58c8L, 0xe1ffa35dL, 0xb8f011a0L, + 0x10fa3d98L, 0xfd2183b8L, 0x4afcb56cL, 0x2dd1d35bL, + 0x9a53e479L, 0xb6f84565L, 0xd28e49bcL, 0x4bfb9790L, + 0xe1ddf2daL, 0xa4cb7e33L, 0x62fb1341L, 0xcee4c6e8L, + 0xef20cadaL, 0x36774c01L, 0xd07e9efeL, 0x2bf11fb4L, + 0x95dbda4dL, 0xae909198L, 0xeaad8e71L, 0x6b93d5a0L, + 0xd08ed1d0L, 0xafc725e0L, 0x8e3c5b2fL, 0x8e7594b7L, + 0x8ff6e2fbL, 0xf2122b64L, 0x8888b812L, 0x900df01cL, + 0x4fad5ea0L, 0x688fc31cL, 0xd1cff191L, 0xb3a8c1adL, + 0x2f2f2218L, 0xbe0e1777L, 0xea752dfeL, 0x8b021fa1L, + 0xe5a0cc0fL, 0xb56f74e8L, 0x18acf3d6L, 0xce89e299L, + 0xb4a84fe0L, 0xfd13e0b7L, 0x7cc43b81L, 0xd2ada8d9L, + 0x165fa266L, 0x80957705L, 0x93cc7314L, 0x211a1477L, + 0xe6ad2065L, 0x77b5fa86L, 0xc75442f5L, 0xfb9d35cfL, + 0xebcdaf0cL, 0x7b3e89a0L, 0xd6411bd3L, 0xae1e7e49L, + 0x00250e2dL, 0x2071b35eL, 0x226800bbL, 0x57b8e0afL, + 0x2464369bL, 0xf009b91eL, 0x5563911dL, 0x59dfa6aaL, + 0x78c14389L, 0xd95a537fL, 0x207d5ba2L, 0x02e5b9c5L, + 0x83260376L, 0x6295cfa9L, 0x11c81968L, 0x4e734a41L, + 0xb3472dcaL, 0x7b14a94aL, 0x1b510052L, 0x9a532915L, + 0xd60f573fL, 0xbc9bc6e4L, 0x2b60a476L, 0x81e67400L, + 0x08ba6fb5L, 0x571be91fL, 0xf296ec6bL, 0x2a0dd915L, + 0xb6636521L, 0xe7b9f9b6L, 0xff34052eL, 0xc5855664L, + 0x53b02d5dL, 0xa99f8fa1L, 0x08ba4799L, 0x6e85076aL, + 0x4b7a70e9L, 0xb5b32944L, 0xdb75092eL, 0xc4192623L, + 0xad6ea6b0L, 0x49a7df7dL, 0x9cee60b8L, 0x8fedb266L, + 0xecaa8c71L, 0x699a17ffL, 0x5664526cL, 0xc2b19ee1L, + 0x193602a5L, 0x75094c29L, 0xa0591340L, 0xe4183a3eL, + 0x3f54989aL, 0x5b429d65L, 0x6b8fe4d6L, 0x99f73fd6L, + 0xa1d29c07L, 0xefe830f5L, 0x4d2d38e6L, 0xf0255dc1L, + 0x4cdd2086L, 0x8470eb26L, 0x6382e9c6L, 0x021ecc5eL, + 0x09686b3fL, 0x3ebaefc9L, 0x3c971814L, 0x6b6a70a1L, + 0x687f3584L, 0x52a0e286L, 0xb79c5305L, 0xaa500737L, + 0x3e07841cL, 0x7fdeae5cL, 0x8e7d44ecL, 0x5716f2b8L, + 0xb03ada37L, 0xf0500c0dL, 0xf01c1f04L, 0x0200b3ffL, + 0xae0cf51aL, 0x3cb574b2L, 0x25837a58L, 0xdc0921bdL, + 0xd19113f9L, 0x7ca92ff6L, 0x94324773L, 0x22f54701L, + 0x3ae5e581L, 0x37c2dadcL, 0xc8b57634L, 0x9af3dda7L, + 0xa9446146L, 0x0fd0030eL, 0xecc8c73eL, 0xa4751e41L, + 0xe238cd99L, 0x3bea0e2fL, 0x3280bba1L, 0x183eb331L, + 0x4e548b38L, 0x4f6db908L, 0x6f420d03L, 0xf60a04bfL, + 0x2cb81290L, 0x24977c79L, 0x5679b072L, 0xbcaf89afL, + 0xde9a771fL, 0xd9930810L, 0xb38bae12L, 0xdccf3f2eL, + 0x5512721fL, 0x2e6b7124L, 0x501adde6L, 0x9f84cd87L, + 0x7a584718L, 0x7408da17L, 0xbc9f9abcL, 0xe94b7d8cL, + 0xec7aec3aL, 0xdb851dfaL, 0x63094366L, 0xc464c3d2L, + 0xef1c1847L, 0x3215d908L, 0xdd433b37L, 0x24c2ba16L, + 0x12a14d43L, 0x2a65c451L, 0x50940002L, 0x133ae4ddL, + 0x71dff89eL, 0x10314e55L, 0x81ac77d6L, 0x5f11199bL, + 0x043556f1L, 0xd7a3c76bL, 0x3c11183bL, 0x5924a509L, + 0xf28fe6edL, 0x97f1fbfaL, 0x9ebabf2cL, 0x1e153c6eL, + 0x86e34570L, 0xeae96fb1L, 0x860e5e0aL, 0x5a3e2ab3L, + 0x771fe71cL, 0x4e3d06faL, 0x2965dcb9L, 0x99e71d0fL, + 0x803e89d6L, 0x5266c825L, 0x2e4cc978L, 0x9c10b36aL, + 0xc6150ebaL, 0x94e2ea78L, 0xa5fc3c53L, 0x1e0a2df4L, + 0xf2f74ea7L, 0x361d2b3dL, 0x1939260fL, 0x19c27960L, + 0x5223a708L, 0xf71312b6L, 0xebadfe6eL, 0xeac31f66L, + 0xe3bc4595L, 0xa67bc883L, 0xb17f37d1L, 0x018cff28L, + 0xc332ddefL, 0xbe6c5aa5L, 0x65582185L, 0x68ab9802L, + 0xeecea50fL, 0xdb2f953bL, 0x2aef7dadL, 0x5b6e2f84L, + 0x1521b628L, 0x29076170L, 0xecdd4775L, 0x619f1510L, + 0x13cca830L, 0xeb61bd96L, 0x0334fe1eL, 0xaa0363cfL, + 0xb5735c90L, 0x4c70a239L, 0xd59e9e0bL, 0xcbaade14L, + 0xeecc86bcL, 0x60622ca7L, 0x9cab5cabL, 0xb2f3846eL, + 0x648b1eafL, 0x19bdf0caL, 0xa02369b9L, 0x655abb50L, + 0x40685a32L, 0x3c2ab4b3L, 0x319ee9d5L, 0xc021b8f7L, + 0x9b540b19L, 0x875fa099L, 0x95f7997eL, 0x623d7da8L, + 0xf837889aL, 0x97e32d77L, 0x11ed935fL, 0x16681281L, + 0x0e358829L, 0xc7e61fd6L, 0x96dedfa1L, 0x7858ba99L, + 0x57f584a5L, 0x1b227263L, 0x9b83c3ffL, 0x1ac24696L, + 0xcdb30aebL, 0x532e3054L, 0x8fd948e4L, 0x6dbc3128L, + 0x58ebf2efL, 0x34c6ffeaL, 0xfe28ed61L, 0xee7c3c73L, + 0x5d4a14d9L, 0xe864b7e3L, 0x42105d14L, 0x203e13e0L, + 0x45eee2b6L, 0xa3aaabeaL, 0xdb6c4f15L, 0xfacb4fd0L, + 0xc742f442L, 0xef6abbb5L, 0x654f3b1dL, 0x41cd2105L, + 0xd81e799eL, 0x86854dc7L, 0xe44b476aL, 0x3d816250L, + 0xcf62a1f2L, 0x5b8d2646L, 0xfc8883a0L, 0xc1c7b6a3L, + 0x7f1524c3L, 0x69cb7492L, 0x47848a0bL, 0x5692b285L, + 0x095bbf00L, 0xad19489dL, 0x1462b174L, 0x23820e00L, + 0x58428d2aL, 0x0c55f5eaL, 0x1dadf43eL, 0x233f7061L, + 0x3372f092L, 0x8d937e41L, 0xd65fecf1L, 0x6c223bdbL, + 0x7cde3759L, 0xcbee7460L, 0x4085f2a7L, 0xce77326eL, + 0xa6078084L, 0x19f8509eL, 0xe8efd855L, 0x61d99735L, + 0xa969a7aaL, 0xc50c06c2L, 0x5a04abfcL, 0x800bcadcL, + 0x9e447a2eL, 0xc3453484L, 0xfdd56705L, 0x0e1e9ec9L, + 0xdb73dbd3L, 0x105588cdL, 0x675fda79L, 0xe3674340L, + 0xc5c43465L, 0x713e38d8L, 0x3d28f89eL, 0xf16dff20L, + 0x153e21e7L, 0x8fb03d4aL, 0xe6e39f2bL, 0xdb83adf7L, + 0xe93d5a68L, 0x948140f7L, 0xf64c261cL, 0x94692934L, + 0x411520f7L, 0x7602d4f7L, 0xbcf46b2eL, 0xd4a20068L, + 0xd4082471L, 0x3320f46aL, 0x43b7d4b7L, 0x500061afL, + 0x1e39f62eL, 0x97244546L, 0x14214f74L, 0xbf8b8840L, + 0x4d95fc1dL, 0x96b591afL, 0x70f4ddd3L, 0x66a02f45L, + 0xbfbc09ecL, 0x03bd9785L, 0x7fac6dd0L, 0x31cb8504L, + 0x96eb27b3L, 0x55fd3941L, 0xda2547e6L, 0xabca0a9aL, + 0x28507825L, 0x530429f4L, 0x0a2c86daL, 0xe9b66dfbL, + 0x68dc1462L, 0xd7486900L, 0x680ec0a4L, 0x27a18deeL, + 0x4f3ffea2L, 0xe887ad8cL, 0xb58ce006L, 0x7af4d6b6L, + 0xaace1e7cL, 0xd3375fecL, 0xce78a399L, 0x406b2a42L, + 0x20fe9e35L, 0xd9f385b9L, 0xee39d7abL, 0x3b124e8bL, + 0x1dc9faf7L, 0x4b6d1856L, 0x26a36631L, 0xeae397b2L, + 0x3a6efa74L, 0xdd5b4332L, 0x6841e7f7L, 0xca7820fbL, + 0xfb0af54eL, 0xd8feb397L, 0x454056acL, 0xba489527L, + 0x55533a3aL, 0x20838d87L, 0xfe6ba9b7L, 0xd096954bL, + 0x55a867bcL, 0xa1159a58L, 0xcca92963L, 0x99e1db33L, + 0xa62a4a56L, 0x3f3125f9L, 0x5ef47e1cL, 0x9029317cL, + 0xfdf8e802L, 0x04272f70L, 0x80bb155cL, 0x05282ce3L, + 0x95c11548L, 0xe4c66d22L, 0x48c1133fL, 0xc70f86dcL, + 0x07f9c9eeL, 0x41041f0fL, 0x404779a4L, 0x5d886e17L, + 0x325f51ebL, 0xd59bc0d1L, 0xf2bcc18fL, 0x41113564L, + 0x257b7834L, 0x602a9c60L, 0xdff8e8a3L, 0x1f636c1bL, + 0x0e12b4c2L, 0x02e1329eL, 0xaf664fd1L, 0xcad18115L, + 0x6b2395e0L, 0x333e92e1L, 0x3b240b62L, 0xeebeb922L, + 0x85b2a20eL, 0xe6ba0d99L, 0xde720c8cL, 0x2da2f728L, + 0xd0127845L, 0x95b794fdL, 0x647d0862L, 0xe7ccf5f0L, + 0x5449a36fL, 0x877d48faL, 0xc39dfd27L, 0xf33e8d1eL, + 0x0a476341L, 0x992eff74L, 0x3a6f6eabL, 0xf4f8fd37L, + 0xa812dc60L, 0xa1ebddf8L, 0x991be14cL, 0xdb6e6b0dL, + 0xc67b5510L, 0x6d672c37L, 0x2765d43bL, 0xdcd0e804L, + 0xf1290dc7L, 0xcc00ffa3L, 0xb5390f92L, 0x690fed0bL, + 0x667b9ffbL, 0xcedb7d9cL, 0xa091cf0bL, 0xd9155ea3L, + 0xbb132f88L, 0x515bad24L, 0x7b9479bfL, 0x763bd6ebL, + 0x37392eb3L, 0xcc115979L, 0x8026e297L, 0xf42e312dL, + 0x6842ada7L, 0xc66a2b3bL, 0x12754cccL, 0x782ef11cL, + 0x6a124237L, 0xb79251e7L, 0x06a1bbe6L, 0x4bfb6350L, + 0x1a6b1018L, 0x11caedfaL, 0x3d25bdd8L, 0xe2e1c3c9L, + 0x44421659L, 0x0a121386L, 0xd90cec6eL, 0xd5abea2aL, + 0x64af674eL, 0xda86a85fL, 0xbebfe988L, 0x64e4c3feL, + 0x9dbc8057L, 0xf0f7c086L, 0x60787bf8L, 0x6003604dL, + 0xd1fd8346L, 0xf6381fb0L, 0x7745ae04L, 0xd736fcccL, + 0x83426b33L, 0xf01eab71L, 0xb0804187L, 0x3c005e5fL, + 0x77a057beL, 0xbde8ae24L, 0x55464299L, 0xbf582e61L, + 0x4e58f48fL, 0xf2ddfda2L, 0xf474ef38L, 0x8789bdc2L, + 0x5366f9c3L, 0xc8b38e74L, 0xb475f255L, 0x46fcd9b9L, + 0x7aeb2661L, 0x8b1ddf84L, 0x846a0e79L, 0x915f95e2L, + 0x466e598eL, 0x20b45770L, 0x8cd55591L, 0xc902de4cL, + 0xb90bace1L, 0xbb8205d0L, 0x11a86248L, 0x7574a99eL, + 0xb77f19b6L, 0xe0a9dc09L, 0x662d09a1L, 0xc4324633L, + 0xe85a1f02L, 0x09f0be8cL, 0x4a99a025L, 0x1d6efe10L, + 0x1ab93d1dL, 0x0ba5a4dfL, 0xa186f20fL, 0x2868f169L, + 0xdcb7da83L, 0x573906feL, 0xa1e2ce9bL, 0x4fcd7f52L, + 0x50115e01L, 0xa70683faL, 0xa002b5c4L, 0x0de6d027L, + 0x9af88c27L, 0x773f8641L, 0xc3604c06L, 0x61a806b5L, + 0xf0177a28L, 0xc0f586e0L, 0x006058aaL, 0x30dc7d62L, + 0x11e69ed7L, 0x2338ea63L, 0x53c2dd94L, 0xc2c21634L, + 0xbbcbee56L, 0x90bcb6deL, 0xebfc7da1L, 0xce591d76L, + 0x6f05e409L, 0x4b7c0188L, 0x39720a3dL, 0x7c927c24L, + 0x86e3725fL, 0x724d9db9L, 0x1ac15bb4L, 0xd39eb8fcL, + 0xed545578L, 0x08fca5b5L, 0xd83d7cd3L, 0x4dad0fc4L, + 0x1e50ef5eL, 0xb161e6f8L, 0xa28514d9L, 0x6c51133cL, + 0x6fd5c7e7L, 0x56e14ec4L, 0x362abfceL, 0xddc6c837L, + 0xd79a3234L, 0x92638212L, 0x670efa8eL, 0x406000e0L, + 0x3a39ce37L, 0xd3faf5cfL, 0xabc27737L, 0x5ac52d1bL, + 0x5cb0679eL, 0x4fa33742L, 0xd3822740L, 0x99bc9bbeL, + 0xd5118e9dL, 0xbf0f7315L, 0xd62d1c7eL, 0xc700c47bL, + 0xb78c1b6bL, 0x21a19045L, 0xb26eb1beL, 0x6a366eb4L, + 0x5748ab2fL, 0xbc946e79L, 0xc6a376d2L, 0x6549c2c8L, + 0x530ff8eeL, 0x468dde7dL, 0xd5730a1dL, 0x4cd04dc6L, + 0x2939bbdbL, 0xa9ba4650L, 0xac9526e8L, 0xbe5ee304L, + 0xa1fad5f0L, 0x6a2d519aL, 0x63ef8ce2L, 0x9a86ee22L, + 0xc089c2b8L, 0x43242ef6L, 0xa51e03aaL, 0x9cf2d0a4L, + 0x83c061baL, 0x9be96a4dL, 0x8fe51550L, 0xba645bd6L, + 0x2826a2f9L, 0xa73a3ae1L, 0x4ba99586L, 0xef5562e9L, + 0xc72fefd3L, 0xf752f7daL, 0x3f046f69L, 0x77fa0a59L, + 0x80e4a915L, 0x87b08601L, 0x9b09e6adL, 0x3b3ee593L, + 0xe990fd5aL, 0x9e34d797L, 0x2cf0b7d9L, 0x022b8b51L, + 0x96d5ac3aL, 0x017da67dL, 0xd1cf3ed6L, 0x7c7d2d28L, + 0x1f9f25cfL, 0xadf2b89bL, 0x5ad6b472L, 0x5a88f54cL, + 0xe029ac71L, 0xe019a5e6L, 0x47b0acfdL, 0xed93fa9bL, + 0xe8d3c48dL, 0x283b57ccL, 0xf8d56629L, 0x79132e28L, + 0x785f0191L, 0xed756055L, 0xf7960e44L, 0xe3d35e8cL, + 0x15056dd4L, 0x88f46dbaL, 0x03a16125L, 0x0564f0bdL, + 0xc3eb9e15L, 0x3c9057a2L, 0x97271aecL, 0xa93a072aL, + 0x1b3f6d9bL, 0x1e6321f5L, 0xf59c66fbL, 0x26dcf319L, + 0x7533d928L, 0xb155fdf5L, 0x03563482L, 0x8aba3cbbL, + 0x28517711L, 0xc20ad9f8L, 0xabcc5167L, 0xccad925fL, + 0x4de81751L, 0x3830dc8eL, 0x379d5862L, 0x9320f991L, + 0xea7a90c2L, 0xfb3e7bceL, 0x5121ce64L, 0x774fbe32L, + 0xa8b6e37eL, 0xc3293d46L, 0x48de5369L, 0x6413e680L, + 0xa2ae0810L, 0xdd6db224L, 0x69852dfdL, 0x09072166L, + 0xb39a460aL, 0x6445c0ddL, 0x586cdecfL, 0x1c20c8aeL, + 0x5bbef7ddL, 0x1b588d40L, 0xccd2017fL, 0x6bb4e3bbL, + 0xdda26a7eL, 0x3a59ff45L, 0x3e350a44L, 0xbcb4cdd5L, + 0x72eacea8L, 0xfa6484bbL, 0x8d6612aeL, 0xbf3c6f47L, + 0xd29be463L, 0x542f5d9eL, 0xaec2771bL, 0xf64e6370L, + 0x740e0d8dL, 0xe75b1357L, 0xf8721671L, 0xaf537d5dL, + 0x4040cb08L, 0x4eb4e2ccL, 0x34d2466aL, 0x0115af84L, + 0xe1b00428L, 0x95983a1dL, 0x06b89fb4L, 0xce6ea048L, + 0x6f3f3b82L, 0x3520ab82L, 0x011a1d4bL, 0x277227f8L, + 0x611560b1L, 0xe7933fdcL, 0xbb3a792bL, 0x344525bdL, + 0xa08839e1L, 0x51ce794bL, 0x2f32c9b7L, 0xa01fbac9L, + 0xe01cc87eL, 0xbcc7d1f6L, 0xcf0111c3L, 0xa1e8aac7L, + 0x1a908749L, 0xd44fbd9aL, 0xd0dadecbL, 0xd50ada38L, + 0x0339c32aL, 0xc6913667L, 0x8df9317cL, 0xe0b12b4fL, + 0xf79e59b7L, 0x43f5bb3aL, 0xf2d519ffL, 0x27d9459cL, + 0xbf97222cL, 0x15e6fc2aL, 0x0f91fc71L, 0x9b941525L, + 0xfae59361L, 0xceb69cebL, 0xc2a86459L, 0x12baa8d1L, + 0xb6c1075eL, 0xe3056a0cL, 0x10d25065L, 0xcb03a442L, + 0xe0ec6e0eL, 0x1698db3bL, 0x4c98a0beL, 0x3278e964L, + 0x9f1f9532L, 0xe0d392dfL, 0xd3a0342bL, 0x8971f21eL, + 0x1b0a7441L, 0x4ba3348cL, 0xc5be7120L, 0xc37632d8L, + 0xdf359f8dL, 0x9b992f2eL, 0xe60b6f47L, 0x0fe3f11dL, + 0xe54cda54L, 0x1edad891L, 0xce6279cfL, 0xcd3e7e6fL, + 0x1618b166L, 0xfd2c1d05L, 0x848fd2c5L, 0xf6fb2299L, + 0xf523f357L, 0xa6327623L, 0x93a83531L, 0x56cccd02L, + 0xacf08162L, 0x5a75ebb5L, 0x6e163697L, 0x88d273ccL, + 0xde966292L, 0x81b949d0L, 0x4c50901bL, 0x71c65614L, + 0xe6c6c7bdL, 0x327a140aL, 0x45e1d006L, 0xc3f27b9aL, + 0xc9aa53fdL, 0x62a80f00L, 0xbb25bfe2L, 0x35bdd2f6L, + 0x71126905L, 0xb2040222L, 0xb6cbcf7cL, 0xcd769c2bL, + 0x53113ec0L, 0x1640e3d3L, 0x38abbd60L, 0x2547adf0L, + 0xba38209cL, 0xf746ce76L, 0x77afa1c5L, 0x20756060L, + 0x85cbfe4eL, 0x8ae88dd8L, 0x7aaaf9b0L, 0x4cf9aa7eL, + 0x1948c25cL, 0x02fb8a8cL, 0x01c36ae4L, 0xd6ebe1f9L, + 0x90d4f869L, 0xa65cdea0L, 0x3f09252dL, 0xc208e69fL, + 0xb74e6132L, 0xce77e25bL, 0x578fdfe3L, 0x3ac372e6L, +}; + + diff --git a/libsec/decodepem.c b/libsec/decodepem.c new file mode 100644 index 0000000..7e8f83b --- /dev/null +++ b/libsec/decodepem.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include + +#define STRLEN(s) (sizeof(s)-1) + +uchar* +decodepem(char *s, char *type, int *len) +{ + uchar *d; + char *t, *e, *tt; + int n; + + /* + * find the correct section of the file, stripping garbage at the beginning and end. + * the data is delimited by -----BEGIN -----\n and -----END -----\n + */ + n = strlen(type); + e = strchr(s, '\0'); + for(t = s; t != nil && t < e; ){ + tt = t; + t = strchr(tt, '\n'); + if(t != nil) + t++; + if(strncmp(tt, "-----BEGIN ", STRLEN("-----BEGIN ")) == 0 + && strncmp(&tt[STRLEN("-----BEGIN ")], type, n) == 0 + && strncmp(&tt[STRLEN("-----BEGIN ")+n], "-----\n", STRLEN("-----\n")) == 0) + break; + } + for(tt = t; tt != nil && tt < e; tt++){ + if(strncmp(tt, "-----END ", STRLEN("-----END ")) == 0 + && strncmp(&tt[STRLEN("-----END ")], type, n) == 0 + && strncmp(&tt[STRLEN("-----END ")+n], "-----\n", STRLEN("-----\n")) == 0) + break; + tt = strchr(tt, '\n'); + if(tt == nil) + break; + } + if(tt == nil || tt == e){ + werrstr("incorrect .pem file format: bad header or trailer"); + return nil; + } + + n = ((tt - t) * 6 + 7) / 8; + d = malloc(n); + if(d == nil){ + werrstr("out of memory"); + return nil; + } + n = dec64(d, n, t, tt - t); + if(n < 0){ + free(d); + werrstr("incorrect .pem file format: bad base64 encoded data"); + return nil; + } + *len = n; + return d; +} diff --git a/libsec/des.c b/libsec/des.c new file mode 100644 index 0000000..c54541c --- /dev/null +++ b/libsec/des.c @@ -0,0 +1,480 @@ +#include "os.h" +#include + +/* + * integrated sbox & p perm + */ +static u32int spbox[] = { + +0x00808200,0x00000000,0x00008000,0x00808202,0x00808002,0x00008202,0x00000002,0x00008000, +0x00000200,0x00808200,0x00808202,0x00000200,0x00800202,0x00808002,0x00800000,0x00000002, +0x00000202,0x00800200,0x00800200,0x00008200,0x00008200,0x00808000,0x00808000,0x00800202, +0x00008002,0x00800002,0x00800002,0x00008002,0x00000000,0x00000202,0x00008202,0x00800000, +0x00008000,0x00808202,0x00000002,0x00808000,0x00808200,0x00800000,0x00800000,0x00000200, +0x00808002,0x00008000,0x00008200,0x00800002,0x00000200,0x00000002,0x00800202,0x00008202, +0x00808202,0x00008002,0x00808000,0x00800202,0x00800002,0x00000202,0x00008202,0x00808200, +0x00000202,0x00800200,0x00800200,0x00000000,0x00008002,0x00008200,0x00000000,0x00808002, + +0x40084010,0x40004000,0x00004000,0x00084010,0x00080000,0x00000010,0x40080010,0x40004010, +0x40000010,0x40084010,0x40084000,0x40000000,0x40004000,0x00080000,0x00000010,0x40080010, +0x00084000,0x00080010,0x40004010,0x00000000,0x40000000,0x00004000,0x00084010,0x40080000, +0x00080010,0x40000010,0x00000000,0x00084000,0x00004010,0x40084000,0x40080000,0x00004010, +0x00000000,0x00084010,0x40080010,0x00080000,0x40004010,0x40080000,0x40084000,0x00004000, +0x40080000,0x40004000,0x00000010,0x40084010,0x00084010,0x00000010,0x00004000,0x40000000, +0x00004010,0x40084000,0x00080000,0x40000010,0x00080010,0x40004010,0x40000010,0x00080010, +0x00084000,0x00000000,0x40004000,0x00004010,0x40000000,0x40080010,0x40084010,0x00084000, + +0x00000104,0x04010100,0x00000000,0x04010004,0x04000100,0x00000000,0x00010104,0x04000100, +0x00010004,0x04000004,0x04000004,0x00010000,0x04010104,0x00010004,0x04010000,0x00000104, +0x04000000,0x00000004,0x04010100,0x00000100,0x00010100,0x04010000,0x04010004,0x00010104, +0x04000104,0x00010100,0x00010000,0x04000104,0x00000004,0x04010104,0x00000100,0x04000000, +0x04010100,0x04000000,0x00010004,0x00000104,0x00010000,0x04010100,0x04000100,0x00000000, +0x00000100,0x00010004,0x04010104,0x04000100,0x04000004,0x00000100,0x00000000,0x04010004, +0x04000104,0x00010000,0x04000000,0x04010104,0x00000004,0x00010104,0x00010100,0x04000004, +0x04010000,0x04000104,0x00000104,0x04010000,0x00010104,0x00000004,0x04010004,0x00010100, + +0x80401000,0x80001040,0x80001040,0x00000040,0x00401040,0x80400040,0x80400000,0x80001000, +0x00000000,0x00401000,0x00401000,0x80401040,0x80000040,0x00000000,0x00400040,0x80400000, +0x80000000,0x00001000,0x00400000,0x80401000,0x00000040,0x00400000,0x80001000,0x00001040, +0x80400040,0x80000000,0x00001040,0x00400040,0x00001000,0x00401040,0x80401040,0x80000040, +0x00400040,0x80400000,0x00401000,0x80401040,0x80000040,0x00000000,0x00000000,0x00401000, +0x00001040,0x00400040,0x80400040,0x80000000,0x80401000,0x80001040,0x80001040,0x00000040, +0x80401040,0x80000040,0x80000000,0x00001000,0x80400000,0x80001000,0x00401040,0x80400040, +0x80001000,0x00001040,0x00400000,0x80401000,0x00000040,0x00400000,0x00001000,0x00401040, + +0x00000080,0x01040080,0x01040000,0x21000080,0x00040000,0x00000080,0x20000000,0x01040000, +0x20040080,0x00040000,0x01000080,0x20040080,0x21000080,0x21040000,0x00040080,0x20000000, +0x01000000,0x20040000,0x20040000,0x00000000,0x20000080,0x21040080,0x21040080,0x01000080, +0x21040000,0x20000080,0x00000000,0x21000000,0x01040080,0x01000000,0x21000000,0x00040080, +0x00040000,0x21000080,0x00000080,0x01000000,0x20000000,0x01040000,0x21000080,0x20040080, +0x01000080,0x20000000,0x21040000,0x01040080,0x20040080,0x00000080,0x01000000,0x21040000, +0x21040080,0x00040080,0x21000000,0x21040080,0x01040000,0x00000000,0x20040000,0x21000000, +0x00040080,0x01000080,0x20000080,0x00040000,0x00000000,0x20040000,0x01040080,0x20000080, + +0x10000008,0x10200000,0x00002000,0x10202008,0x10200000,0x00000008,0x10202008,0x00200000, +0x10002000,0x00202008,0x00200000,0x10000008,0x00200008,0x10002000,0x10000000,0x00002008, +0x00000000,0x00200008,0x10002008,0x00002000,0x00202000,0x10002008,0x00000008,0x10200008, +0x10200008,0x00000000,0x00202008,0x10202000,0x00002008,0x00202000,0x10202000,0x10000000, +0x10002000,0x00000008,0x10200008,0x00202000,0x10202008,0x00200000,0x00002008,0x10000008, +0x00200000,0x10002000,0x10000000,0x00002008,0x10000008,0x10202008,0x00202000,0x10200000, +0x00202008,0x10202000,0x00000000,0x10200008,0x00000008,0x00002000,0x10200000,0x00202008, +0x00002000,0x00200008,0x10002008,0x00000000,0x10202000,0x10000000,0x00200008,0x10002008, + +0x00100000,0x02100001,0x02000401,0x00000000,0x00000400,0x02000401,0x00100401,0x02100400, +0x02100401,0x00100000,0x00000000,0x02000001,0x00000001,0x02000000,0x02100001,0x00000401, +0x02000400,0x00100401,0x00100001,0x02000400,0x02000001,0x02100000,0x02100400,0x00100001, +0x02100000,0x00000400,0x00000401,0x02100401,0x00100400,0x00000001,0x02000000,0x00100400, +0x02000000,0x00100400,0x00100000,0x02000401,0x02000401,0x02100001,0x02100001,0x00000001, +0x00100001,0x02000000,0x02000400,0x00100000,0x02100400,0x00000401,0x00100401,0x02100400, +0x00000401,0x02000001,0x02100401,0x02100000,0x00100400,0x00000000,0x00000001,0x02100401, +0x00000000,0x00100401,0x02100000,0x00000400,0x02000001,0x02000400,0x00000400,0x00100001, + +0x08000820,0x00000800,0x00020000,0x08020820,0x08000000,0x08000820,0x00000020,0x08000000, +0x00020020,0x08020000,0x08020820,0x00020800,0x08020800,0x00020820,0x00000800,0x00000020, +0x08020000,0x08000020,0x08000800,0x00000820,0x00020800,0x00020020,0x08020020,0x08020800, +0x00000820,0x00000000,0x00000000,0x08020020,0x08000020,0x08000800,0x00020820,0x00020000, +0x00020820,0x00020000,0x08020800,0x00000800,0x00000020,0x08020020,0x00000800,0x00020820, +0x08000800,0x00000020,0x08000020,0x08020000,0x08020020,0x08000000,0x00020000,0x08000820, +0x00000000,0x08020820,0x00020020,0x08000020,0x08020000,0x08000800,0x08000820,0x00000000, +0x08020820,0x00020800,0x00020800,0x00000820,0x00000820,0x00020020,0x08000000,0x08020800, +}; + +/* + * for manual index calculation + * #define fetch(box, i, sh) (*((u32int*)((uchar*)spbox + (box << 8) + ((i >> (sh)) & 0xfc)))) + */ +#define fetch(box, i, sh) ((spbox+(box << 6))[((i >> (sh + 2)) & 0x3f)]) + +/* + * DES electronic codebook encryption of one block + */ +void +block_cipher(ulong key[32], uchar text[8], int decrypting) +{ + u32int right, left, v0, v1; + int i, keystep; + + /* + * initial permutation + */ + v0 = text[0] | ((u32int)text[2]<<8) | ((u32int)text[4]<<16) | ((u32int)text[6]<<24); + left = text[1] | ((u32int)text[3]<<8) | ((u32int)text[5]<<16) | ((u32int)text[7]<<24); + right = (left & 0xaaaaaaaa) | ((v0 >> 1) & 0x55555555); + left = ((left << 1) & 0xaaaaaaaa) | (v0 & 0x55555555); + left = ((left << 6) & 0x33003300) + | (left & 0xcc33cc33) + | ((left >> 6) & 0x00cc00cc); + left = ((left << 12) & 0x0f0f0000) + | (left & 0xf0f00f0f) + | ((left >> 12) & 0x0000f0f0); + right = ((right << 6) & 0x33003300) + | (right & 0xcc33cc33) + | ((right >> 6) & 0x00cc00cc); + right = ((right << 12) & 0x0f0f0000) + | (right & 0xf0f00f0f) + | ((right >> 12) & 0x0000f0f0); + + if (decrypting) { + keystep = -2; + key = key + 32 - 2; + } else + keystep = 2; + for (i = 0; i < 8; i++) { + v0 = key[0]; + v0 ^= (right >> 1) | (right << 31); + left ^= fetch(0, v0, 24) + ^ fetch(2, v0, 16) + ^ fetch(4, v0, 8) + ^ fetch(6, v0, 0); + v1 = key[1]; + v1 ^= (right << 3) | (right >> 29); + left ^= fetch(1, v1, 24) + ^ fetch(3, v1, 16) + ^ fetch(5, v1, 8) + ^ fetch(7, v1, 0); + key += keystep; + + v0 = key[0]; + v0 ^= (left >> 1) | (left << 31); + right ^= fetch(0, v0, 24) + ^ fetch(2, v0, 16) + ^ fetch(4, v0, 8) + ^ fetch(6, v0, 0); + v1 = key[1]; + v1 ^= (left << 3) | (left >> 29); + right ^= fetch(1, v1, 24) + ^ fetch(3, v1, 16) + ^ fetch(5, v1, 8) + ^ fetch(7, v1, 0); + key += keystep; + } + + /* + * final permutation, inverse initial permutation + */ + v0 = ((left << 1) & 0xaaaaaaaa) | (right & 0x55555555); + v1 = (left & 0xaaaaaaaa) | ((right >> 1) & 0x55555555); + v1 = ((v1 << 6) & 0x33003300) + | (v1 & 0xcc33cc33) + | ((v1 >> 6) & 0x00cc00cc); + v1 = ((v1 << 12) & 0x0f0f0000) + | (v1 & 0xf0f00f0f) + | ((v1 >> 12) & 0x0000f0f0); + v0 = ((v0 << 6) & 0x33003300) + | (v0 & 0xcc33cc33) + | ((v0 >> 6) & 0x00cc00cc); + v0 = ((v0 << 12) & 0x0f0f0000) + | (v0 & 0xf0f00f0f) + | ((v0 >> 12) & 0x0000f0f0); + text[0] = v0; + text[2] = v0 >> 8; + text[4] = v0 >> 16; + text[6] = v0 >> 24; + text[1] = v1; + text[3] = v1 >> 8; + text[5] = v1 >> 16; + text[7] = v1 >> 24; +} + +/* + * triple DES electronic codebook encryption of one block + */ +void +triple_block_cipher(ulong expanded_key[3][32], uchar text[8], int ende) +{ + ulong *key; + u32int right, left, v0, v1; + int i, j, keystep; + + /* + * initial permutation + */ + v0 = text[0] | ((u32int)text[2]<<8) | ((u32int)text[4]<<16) | ((u32int)text[6]<<24); + left = text[1] | ((u32int)text[3]<<8) | ((u32int)text[5]<<16) | ((u32int)text[7]<<24); + right = (left & 0xaaaaaaaa) | ((v0 >> 1) & 0x55555555); + left = ((left << 1) & 0xaaaaaaaa) | (v0 & 0x55555555); + left = ((left << 6) & 0x33003300) + | (left & 0xcc33cc33) + | ((left >> 6) & 0x00cc00cc); + left = ((left << 12) & 0x0f0f0000) + | (left & 0xf0f00f0f) + | ((left >> 12) & 0x0000f0f0); + right = ((right << 6) & 0x33003300) + | (right & 0xcc33cc33) + | ((right >> 6) & 0x00cc00cc); + right = ((right << 12) & 0x0f0f0000) + | (right & 0xf0f00f0f) + | ((right >> 12) & 0x0000f0f0); + + for(j = 0; j < 3; j++){ + if((ende & 1) == DES3D) { + key = &expanded_key[2-j][32-2]; + keystep = -2; + } else { + key = &expanded_key[j][0]; + keystep = 2; + } + ende >>= 1; + for (i = 0; i < 8; i++) { + v0 = key[0]; + v0 ^= (right >> 1) | (right << 31); + left ^= fetch(0, v0, 24) + ^ fetch(2, v0, 16) + ^ fetch(4, v0, 8) + ^ fetch(6, v0, 0); + v1 = key[1]; + v1 ^= (right << 3) | (right >> 29); + left ^= fetch(1, v1, 24) + ^ fetch(3, v1, 16) + ^ fetch(5, v1, 8) + ^ fetch(7, v1, 0); + key += keystep; + + v0 = key[0]; + v0 ^= (left >> 1) | (left << 31); + right ^= fetch(0, v0, 24) + ^ fetch(2, v0, 16) + ^ fetch(4, v0, 8) + ^ fetch(6, v0, 0); + v1 = key[1]; + v1 ^= (left << 3) | (left >> 29); + right ^= fetch(1, v1, 24) + ^ fetch(3, v1, 16) + ^ fetch(5, v1, 8) + ^ fetch(7, v1, 0); + key += keystep; + } + + v0 = left; + left = right; + right = v0; + } + + /* + * final permutation, inverse initial permutation + * left and right are swapped here + */ + v0 = ((right << 1) & 0xaaaaaaaa) | (left & 0x55555555); + v1 = (right & 0xaaaaaaaa) | ((left >> 1) & 0x55555555); + v1 = ((v1 << 6) & 0x33003300) + | (v1 & 0xcc33cc33) + | ((v1 >> 6) & 0x00cc00cc); + v1 = ((v1 << 12) & 0x0f0f0000) + | (v1 & 0xf0f00f0f) + | ((v1 >> 12) & 0x0000f0f0); + v0 = ((v0 << 6) & 0x33003300) + | (v0 & 0xcc33cc33) + | ((v0 >> 6) & 0x00cc00cc); + v0 = ((v0 << 12) & 0x0f0f0000) + | (v0 & 0xf0f00f0f) + | ((v0 >> 12) & 0x0000f0f0); + text[0] = v0; + text[2] = v0 >> 8; + text[4] = v0 >> 16; + text[6] = v0 >> 24; + text[1] = v1; + text[3] = v1 >> 8; + text[5] = v1 >> 16; + text[7] = v1 >> 24; +} + +/* + * key compression permutation, 4 bits at a time + */ +static u32int comptab[] = { + +0x000000,0x010000,0x000008,0x010008,0x000080,0x010080,0x000088,0x010088, +0x000000,0x010000,0x000008,0x010008,0x000080,0x010080,0x000088,0x010088, + +0x000000,0x100000,0x000800,0x100800,0x000000,0x100000,0x000800,0x100800, +0x002000,0x102000,0x002800,0x102800,0x002000,0x102000,0x002800,0x102800, + +0x000000,0x000004,0x000400,0x000404,0x000000,0x000004,0x000400,0x000404, +0x400000,0x400004,0x400400,0x400404,0x400000,0x400004,0x400400,0x400404, + +0x000000,0x000020,0x008000,0x008020,0x800000,0x800020,0x808000,0x808020, +0x000002,0x000022,0x008002,0x008022,0x800002,0x800022,0x808002,0x808022, + +0x000000,0x000200,0x200000,0x200200,0x001000,0x001200,0x201000,0x201200, +0x000000,0x000200,0x200000,0x200200,0x001000,0x001200,0x201000,0x201200, + +0x000000,0x000040,0x000010,0x000050,0x004000,0x004040,0x004010,0x004050, +0x040000,0x040040,0x040010,0x040050,0x044000,0x044040,0x044010,0x044050, + +0x000000,0x000100,0x020000,0x020100,0x000001,0x000101,0x020001,0x020101, +0x080000,0x080100,0x0a0000,0x0a0100,0x080001,0x080101,0x0a0001,0x0a0101, + +0x000000,0x000100,0x040000,0x040100,0x000000,0x000100,0x040000,0x040100, +0x000040,0x000140,0x040040,0x040140,0x000040,0x000140,0x040040,0x040140, + +0x000000,0x400000,0x008000,0x408000,0x000008,0x400008,0x008008,0x408008, +0x000400,0x400400,0x008400,0x408400,0x000408,0x400408,0x008408,0x408408, + +0x000000,0x001000,0x080000,0x081000,0x000020,0x001020,0x080020,0x081020, +0x004000,0x005000,0x084000,0x085000,0x004020,0x005020,0x084020,0x085020, + +0x000000,0x000800,0x000000,0x000800,0x000010,0x000810,0x000010,0x000810, +0x800000,0x800800,0x800000,0x800800,0x800010,0x800810,0x800010,0x800810, + +0x000000,0x010000,0x000200,0x010200,0x000000,0x010000,0x000200,0x010200, +0x100000,0x110000,0x100200,0x110200,0x100000,0x110000,0x100200,0x110200, + +0x000000,0x000004,0x000000,0x000004,0x000080,0x000084,0x000080,0x000084, +0x002000,0x002004,0x002000,0x002004,0x002080,0x002084,0x002080,0x002084, + +0x000000,0x000001,0x200000,0x200001,0x020000,0x020001,0x220000,0x220001, +0x000002,0x000003,0x200002,0x200003,0x020002,0x020003,0x220002,0x220003, +}; + +static int keysh[] = +{ + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, +}; + +static void +keycompperm(u32int left, u32int right, ulong *ek) +{ + u32int v0, v1; + int i; + + for(i = 0; i < 16; i++){ + left = (left << keysh[i]) | (left >> (28 - keysh[i])); + left &= 0xfffffff0; + right = (right << keysh[i]) | (right >> (28 - keysh[i])); + right &= 0xfffffff0; + v0 = comptab[6 * (1 << 4) + ((left >> (32-4)) & 0xf)] + | comptab[5 * (1 << 4) + ((left >> (32-8)) & 0xf)] + | comptab[4 * (1 << 4) + ((left >> (32-12)) & 0xf)] + | comptab[3 * (1 << 4) + ((left >> (32-16)) & 0xf)] + | comptab[2 * (1 << 4) + ((left >> (32-20)) & 0xf)] + | comptab[1 * (1 << 4) + ((left >> (32-24)) & 0xf)] + | comptab[0 * (1 << 4) + ((left >> (32-28)) & 0xf)]; + v1 = comptab[13 * (1 << 4) + ((right >> (32-4)) & 0xf)] + | comptab[12 * (1 << 4) + ((right >> (32-8)) & 0xf)] + | comptab[11 * (1 << 4) + ((right >> (32-12)) & 0xf)] + | comptab[10 * (1 << 4) + ((right >> (32-16)) & 0xf)] + | comptab[9 * (1 << 4) + ((right >> (32-20)) & 0xf)] + | comptab[8 * (1 << 4) + ((right >> (32-24)) & 0xf)] + | comptab[7 * (1 << 4) + ((right >> (32-28)) & 0xf)]; + ek[0] = (((v0 >> (24-6)) & 0x3f) << 26) + | (((v0 >> (24-18)) & 0x3f) << 18) + | (((v1 >> (24-6)) & 0x3f) << 10) + | (((v1 >> (24-18)) & 0x3f) << 2); + ek[1] = (((v0 >> (24-12)) & 0x3f) << 26) + | (((v0 >> (24-24)) & 0x3f) << 18) + | (((v1 >> (24-12)) & 0x3f) << 10) + | (((v1 >> (24-24)) & 0x3f) << 2); + ek += 2; + } +} + +void +des_key_setup(uchar key[8], ulong *ek) +{ + u32int left, right, v0, v1; + + v0 = key[0] | ((u32int)key[2] << 8) | ((u32int)key[4] << 16) | ((u32int)key[6] << 24); + v1 = key[1] | ((u32int)key[3] << 8) | ((u32int)key[5] << 16) | ((u32int)key[7] << 24); + left = ((v0 >> 1) & 0x40404040) + | ((v0 >> 2) & 0x10101010) + | ((v0 >> 3) & 0x04040404) + | ((v0 >> 4) & 0x01010101) + | ((v1 >> 0) & 0x80808080) + | ((v1 >> 1) & 0x20202020) + | ((v1 >> 2) & 0x08080808) + | ((v1 >> 3) & 0x02020202); + right = ((v0 >> 1) & 0x04040404) + | ((v0 << 2) & 0x10101010) + | ((v0 << 5) & 0x40404040) + | ((v1 << 0) & 0x08080808) + | ((v1 << 3) & 0x20202020) + | ((v1 << 6) & 0x80808080); + left = ((left << 6) & 0x33003300) + | (left & 0xcc33cc33) + | ((left >> 6) & 0x00cc00cc); + v0 = ((left << 12) & 0x0f0f0000) + | (left & 0xf0f00f0f) + | ((left >> 12) & 0x0000f0f0); + right = ((right << 6) & 0x33003300) + | (right & 0xcc33cc33) + | ((right >> 6) & 0x00cc00cc); + v1 = ((right << 12) & 0x0f0f0000) + | (right & 0xf0f00f0f) + | ((right >> 12) & 0x0000f0f0); + left = v0 & 0xfffffff0; + right = (v1 & 0xffffff00) | ((v0 << 4) & 0xf0); + + keycompperm(left, right, ek); +} + +static uchar parity[128] = +{ + 0x01, 0x02, 0x04, 0x07, 0x08, 0x0b, 0x0d, 0x0e, + 0x10, 0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c, 0x1f, + 0x20, 0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x2f, + 0x31, 0x32, 0x34, 0x37, 0x38, 0x3b, 0x3d, 0x3e, + 0x40, 0x43, 0x45, 0x46, 0x49, 0x4a, 0x4c, 0x4f, + 0x51, 0x52, 0x54, 0x57, 0x58, 0x5b, 0x5d, 0x5e, + 0x61, 0x62, 0x64, 0x67, 0x68, 0x6b, 0x6d, 0x6e, + 0x70, 0x73, 0x75, 0x76, 0x79, 0x7a, 0x7c, 0x7f, + 0x80, 0x83, 0x85, 0x86, 0x89, 0x8a, 0x8c, 0x8f, + 0x91, 0x92, 0x94, 0x97, 0x98, 0x9b, 0x9d, 0x9e, + 0xa1, 0xa2, 0xa4, 0xa7, 0xa8, 0xab, 0xad, 0xae, + 0xb0, 0xb3, 0xb5, 0xb6, 0xb9, 0xba, 0xbc, 0xbf, + 0xc1, 0xc2, 0xc4, 0xc7, 0xc8, 0xcb, 0xcd, 0xce, + 0xd0, 0xd3, 0xd5, 0xd6, 0xd9, 0xda, 0xdc, 0xdf, + 0xe0, 0xe3, 0xe5, 0xe6, 0xe9, 0xea, 0xec, 0xef, + 0xf1, 0xf2, 0xf4, 0xf7, 0xf8, 0xfb, 0xfd, 0xfe, +}; + +/* + * convert a 7 byte key to an 8 byte one + */ +void +des56to64(uchar *k56, uchar *k64) +{ + u32int hi, lo; + + hi = ((u32int)k56[0]<<24)|((u32int)k56[1]<<16)|((u32int)k56[2]<<8)|k56[3]; + lo = ((u32int)k56[4]<<24)|((u32int)k56[5]<<16)|((u32int)k56[6]<<8); + + k64[0] = parity[(hi>>25)&0x7f]; + k64[1] = parity[(hi>>18)&0x7f]; + k64[2] = parity[(hi>>11)&0x7f]; + k64[3] = parity[(hi>>4)&0x7f]; + k64[4] = parity[((hi<<3)|(lo>>29))&0x7f]; + k64[5] = parity[(lo>>22)&0x7f]; + k64[6] = parity[(lo>>15)&0x7f]; + k64[7] = parity[(lo>>8)&0x7f]; +} + +/* + * convert an 8 byte key to a 7 byte one + */ +void +des64to56(uchar *k64, uchar *k56) +{ + u32int hi, lo; + + hi = (((u32int)k64[0]&0xfe)<<24)|(((u32int)k64[1]&0xfe)<<17)|(((u32int)k64[2]&0xfe)<<10) + |((k64[3]&0xfe)<<3)|(k64[4]>>4); + lo = (((u32int)k64[4]&0xfe)<<28)|(((u32int)k64[5]&0xfe)<<21)|(((u32int)k64[6]&0xfe)<<14) + |(((u32int)k64[7]&0xfe)<<7); + + k56[0] = hi>>24; + k56[1] = hi>>16; + k56[2] = hi>>8; + k56[3] = hi>>0; + k56[4] = lo>>24; + k56[5] = lo>>16; + k56[6] = lo>>8; +} + +void +key_setup(uchar key[7], ulong *ek) +{ + uchar k64[8]; + + des56to64(key, k64); + des_key_setup(k64, ek); +} diff --git a/libsec/des3CBC.c b/libsec/des3CBC.c new file mode 100644 index 0000000..2632930 --- /dev/null +++ b/libsec/des3CBC.c @@ -0,0 +1,59 @@ +#include "os.h" +#include +#include + +// Because of the way that non multiple of 8 +// buffers are handled, the decryptor must +// be fed buffers of the same size as the +// encryptor + + +// If the length is not a multiple of 8, I encrypt +// the overflow to be compatible with lacy's cryptlib +void +des3CBCencrypt(uchar *p, int len, DES3state *s) +{ + uchar *p2, *ip, *eip; + + for(; len >= 8; len -= 8){ + p2 = p; + ip = s->ivec; + for(eip = ip+8; ip < eip; ) + *p2++ ^= *ip++; + triple_block_cipher(s->expanded, p, DES3EDE); + memmove(s->ivec, p, 8); + p += 8; + } + + if(len > 0){ + ip = s->ivec; + triple_block_cipher(s->expanded, ip, DES3EDE); + for(eip = ip+len; ip < eip; ) + *p++ ^= *ip++; + } +} + +void +des3CBCdecrypt(uchar *p, int len, DES3state *s) +{ + uchar *ip, *eip, *tp; + uchar tmp[8]; + + for(; len >= 8; len -= 8){ + memmove(tmp, p, 8); + triple_block_cipher(s->expanded, p, DES3DED); + tp = tmp; + ip = s->ivec; + for(eip = ip+8; ip < eip; ){ + *p++ ^= *ip; + *ip++ = *tp++; + } + } + + if(len > 0){ + ip = s->ivec; + triple_block_cipher(s->expanded, ip, DES3EDE); + for(eip = ip+len; ip < eip; ) + *p++ ^= *ip++; + } +} diff --git a/libsec/des3ECB.c b/libsec/des3ECB.c new file mode 100644 index 0000000..324254b --- /dev/null +++ b/libsec/des3ECB.c @@ -0,0 +1,48 @@ +#include "os.h" +#include +#include + +// I wasn't sure what to do when the buffer was not +// a multiple of 8. I did what lacy's cryptolib did +// to be compatible, but it looks dangerous to me +// since its encrypting plain text with the key. -- presotto + +void +des3ECBencrypt(uchar *p, int len, DES3state *s) +{ + int i; + uchar tmp[8]; + + for(; len >= 8; len -= 8){ + triple_block_cipher(s->expanded, p, DES3EDE); + p += 8; + } + + if(len > 0){ + for (i=0; i<8; i++) + tmp[i] = i; + triple_block_cipher(s->expanded, tmp, DES3EDE); + for (i = 0; i < len; i++) + p[i] ^= tmp[i]; + } +} + +void +des3ECBdecrypt(uchar *p, int len, DES3state *s) +{ + int i; + uchar tmp[8]; + + for(; len >= 8; len -= 8){ + triple_block_cipher(s->expanded, p, DES3DED); + p += 8; + } + + if(len > 0){ + for (i=0; i<8; i++) + tmp[i] = i; + triple_block_cipher(s->expanded, tmp, DES3EDE); + for (i = 0; i < len; i++) + p[i] ^= tmp[i]; + } +} diff --git a/libsec/desCBC.c b/libsec/desCBC.c new file mode 100644 index 0000000..ecee29e --- /dev/null +++ b/libsec/desCBC.c @@ -0,0 +1,59 @@ +#include "os.h" +#include +#include + +// Because of the way that non multiple of 8 +// buffers are handled, the decryptor must +// be fed buffers of the same size as the +// encryptor + + +// If the length is not a multiple of 8, I encrypt +// the overflow to be compatible with lacy's cryptlib +void +desCBCencrypt(uchar *p, int len, DESstate *s) +{ + uchar *p2, *ip, *eip; + + for(; len >= 8; len -= 8){ + p2 = p; + ip = s->ivec; + for(eip = ip+8; ip < eip; ) + *p2++ ^= *ip++; + block_cipher(s->expanded, p, 0); + memmove(s->ivec, p, 8); + p += 8; + } + + if(len > 0){ + ip = s->ivec; + block_cipher(s->expanded, ip, 0); + for(eip = ip+len; ip < eip; ) + *p++ ^= *ip++; + } +} + +void +desCBCdecrypt(uchar *p, int len, DESstate *s) +{ + uchar *ip, *eip, *tp; + uchar tmp[8]; + + for(; len >= 8; len -= 8){ + memmove(tmp, p, 8); + block_cipher(s->expanded, p, 1); + tp = tmp; + ip = s->ivec; + for(eip = ip+8; ip < eip; ){ + *p++ ^= *ip; + *ip++ = *tp++; + } + } + + if(len > 0){ + ip = s->ivec; + block_cipher(s->expanded, ip, 0); + for(eip = ip+len; ip < eip; ) + *p++ ^= *ip++; + } +} diff --git a/libsec/desECB.c b/libsec/desECB.c new file mode 100644 index 0000000..a9ad001 --- /dev/null +++ b/libsec/desECB.c @@ -0,0 +1,48 @@ +#include "os.h" +#include +#include + +// I wasn't sure what to do when the buffer was not +// a multiple of 8. I did what lacy's cryptolib did +// to be compatible, but it looks dangerous to me +// since its encrypting plain text with the key. -- presotto + +void +desECBencrypt(uchar *p, int len, DESstate *s) +{ + int i; + uchar tmp[8]; + + for(; len >= 8; len -= 8){ + block_cipher(s->expanded, p, 0); + p += 8; + } + + if(len > 0){ + for (i=0; i<8; i++) + tmp[i] = i; + block_cipher(s->expanded, tmp, 0); + for (i = 0; i < len; i++) + p[i] ^= tmp[i]; + } +} + +void +desECBdecrypt(uchar *p, int len, DESstate *s) +{ + int i; + uchar tmp[8]; + + for(; len >= 8; len -= 8){ + block_cipher(s->expanded, p, 1); + p += 8; + } + + if(len > 0){ + for (i=0; i<8; i++) + tmp[i] = i; + block_cipher(s->expanded, tmp, 0); + for (i = 0; i < len; i++) + p[i] ^= tmp[i]; + } +} diff --git a/libsec/desmodes.c b/libsec/desmodes.c new file mode 100644 index 0000000..da6d984 --- /dev/null +++ b/libsec/desmodes.c @@ -0,0 +1,31 @@ +#include "os.h" +#include + +/* + * these routines use the 64bit format for + * DES keys. + */ + +void +setupDESstate(DESstate *s, uchar key[8], uchar *ivec) +{ + memset(s, 0, sizeof(*s)); + memmove(s->key, key, sizeof(s->key)); + des_key_setup(key, s->expanded); + if(ivec) + memmove(s->ivec, ivec, 8); + s->setup = 0xdeadbeef; +} + +void +setupDES3state(DES3state *s, uchar key[3][8], uchar *ivec) +{ + memset(s, 0, sizeof(*s)); + memmove(s->key, key, sizeof(s->key)); + des_key_setup(key[0], s->expanded[0]); + des_key_setup(key[1], s->expanded[1]); + des_key_setup(key[2], s->expanded[2]); + if(ivec) + memmove(s->ivec, ivec, 8); + s->setup = 0xdeadbeef; +} diff --git a/libsec/dsaalloc.c b/libsec/dsaalloc.c new file mode 100644 index 0000000..6a48f03 --- /dev/null +++ b/libsec/dsaalloc.c @@ -0,0 +1,69 @@ +#include "os.h" +#include +#include + +DSApub* +dsapuballoc(void) +{ + DSApub *dsa; + + dsa = mallocz(sizeof(*dsa), 1); + if(dsa == nil) + sysfatal("dsapuballoc"); + return dsa; +} + +void +dsapubfree(DSApub *dsa) +{ + if(dsa == nil) + return; + mpfree(dsa->p); + mpfree(dsa->q); + mpfree(dsa->alpha); + mpfree(dsa->key); +} + + +DSApriv* +dsaprivalloc(void) +{ + DSApriv *dsa; + + dsa = mallocz(sizeof(*dsa), 1); + if(dsa == nil) + sysfatal("dsaprivalloc"); + return dsa; +} + +void +dsaprivfree(DSApriv *dsa) +{ + if(dsa == nil) + return; + mpfree(dsa->pub.p); + mpfree(dsa->pub.q); + mpfree(dsa->pub.alpha); + mpfree(dsa->pub.key); + mpfree(dsa->secret); +} + +DSAsig* +dsasigalloc(void) +{ + DSAsig *dsa; + + dsa = mallocz(sizeof(*dsa), 1); + if(dsa == nil) + sysfatal("dsasigalloc"); + return dsa; +} + +void +dsasigfree(DSAsig *dsa) +{ + if(dsa == nil) + return; + mpfree(dsa->r); + mpfree(dsa->s); +} diff --git a/libsec/dsagen.c b/libsec/dsagen.c new file mode 100644 index 0000000..ccdd918 --- /dev/null +++ b/libsec/dsagen.c @@ -0,0 +1,61 @@ +#include "os.h" +#include +#include + +DSApriv* +dsagen(DSApub *opub) +{ + DSApub *pub; + DSApriv *priv; + mpint *exp; + mpint *g; + mpint *r; + int bits; + + priv = dsaprivalloc(); + pub = &priv->pub; + + if(opub != nil){ + pub->p = mpcopy(opub->p); + pub->q = mpcopy(opub->q); + } else { + pub->p = mpnew(0); + pub->q = mpnew(0); + DSAprimes(pub->q, pub->p, nil); + } + bits = Dbits*pub->p->top; + + pub->alpha = mpnew(0); + pub->key = mpnew(0); + priv->secret = mpnew(0); + + // find a generator alpha of the multiplicative + // group Z*p, i.e., of order n = p-1. We use the + // fact that q divides p-1 to reduce the exponent. + // + // This isn't very efficient. If anyone has a better + // idea, mail presotto@closedmind.org + exp = mpnew(0); + g = mpnew(0); + r = mpnew(0); + mpsub(pub->p, mpone, exp); + mpdiv(exp, pub->q, exp, r); + if(mpcmp(r, mpzero) != 0) + sysfatal("dsagen foul up"); + while(1){ + mprand(bits, genrandom, g); + mpmod(g, pub->p, g); + mpexp(g, exp, pub->p, pub->alpha); + if(mpcmp(pub->alpha, mpone) != 0) + break; + } + mpfree(g); + mpfree(exp); + + // create the secret key + mprand(bits, genrandom, priv->secret); + mpmod(priv->secret, pub->p, priv->secret); + mpexp(pub->alpha, priv->secret, pub->p, pub->key); + + return priv; +} diff --git a/libsec/dsaprimes.c b/libsec/dsaprimes.c new file mode 100644 index 0000000..ff1dd5d --- /dev/null +++ b/libsec/dsaprimes.c @@ -0,0 +1,97 @@ +#include "os.h" +#include +#include + +// NIST algorithm for generating DSA primes +// Menezes et al (1997) Handbook of Applied Cryptography, p.151 +// q is a 160-bit prime; p is a 1024-bit prime; q divides p-1 + +// arithmetic on unsigned ints mod 2**160, represented +// as 20-byte, little-endian uchar array + +static void +Hrand(uchar *s) +{ + ulong *u = (ulong*)s; + *u++ = fastrand(); + *u++ = fastrand(); + *u++ = fastrand(); + *u++ = fastrand(); + *u = fastrand(); +} + +static void +Hincr(uchar *s) +{ + int i; + for(i=0; i<20; i++) + if(++s[i]!=0) + break; +} + +// this can run for quite a while; be patient +void +DSAprimes(mpint *q, mpint *p, uchar seed[SHA1dlen]) +{ + int i, j, k, n = 6, b = 63; + uchar s[SHA1dlen], Hs[SHA1dlen], Hs1[SHA1dlen], sj[SHA1dlen], sjk[SHA1dlen]; + mpint *two1023, *mb, *Vk, *W, *X, *q2; + + two1023 = mpnew(1024); + mpleft(mpone, 1023, two1023); + mb = mpnew(0); + mpleft(mpone, b, mb); + W = mpnew(1024); + Vk = mpnew(1024); + X = mpnew(0); + q2 = mpnew(0); +forever: + do{ + Hrand(s); + memcpy(sj, s, 20); + sha1(s, 20, Hs, 0); + Hincr(sj); + sha1(sj, 20, Hs1, 0); + for(i=0; i<20; i++) + Hs[i] ^= Hs1[i]; + Hs[0] |= 1; + Hs[19] |= 0x80; + letomp(Hs, 20, q); + }while(!probably_prime(q, 18)); + if(seed != nil) // allow skeptics to confirm computation + memmove(seed, s, SHA1dlen); + i = 0; + j = 2; + Hincr(sj); + mpleft(q, 1, q2); + while(i<4096){ + memcpy(sjk, sj, 20); + for(k=0; k <= n; k++){ + sha1(sjk, 20, Hs, 0); + letomp(Hs, 20, Vk); + if(k == n) + mpmod(Vk, mb, Vk); + mpleft(Vk, 160*k, Vk); + mpadd(W, Vk, W); + Hincr(sjk); + } + mpadd(W, two1023, X); + mpmod(X, q2, W); + mpsub(W, mpone, W); + mpsub(X, W, p); + if(mpcmp(p, two1023)>=0 && probably_prime(p, 5)) + goto done; + i += 1; + j += n+1; + for(k=0; k +#include + +DSApub* +dsaprivtopub(DSApriv *priv) +{ + DSApub *pub; + + pub = dsapuballoc(); + pub->p = mpcopy(priv->pub.p); + pub->q = mpcopy(priv->pub.q); + pub->alpha = mpcopy(priv->pub.alpha); + pub->key = mpcopy(priv->pub.key); + return pub; +} diff --git a/libsec/dsasign.c b/libsec/dsasign.c new file mode 100644 index 0000000..abca3eb --- /dev/null +++ b/libsec/dsasign.c @@ -0,0 +1,52 @@ +#include "os.h" +#include +#include + +DSAsig* +dsasign(DSApriv *priv, mpint *m) +{ + DSApub *pub = &priv->pub; + DSAsig *sig; + mpint *qm1, *k, *kinv, *r, *s; + mpint *q = pub->q, *p = pub->p, *alpha = pub->alpha; + int qlen = mpsignif(q); + + qm1 = mpnew(0); + kinv = mpnew(0); + r = mpnew(0); + s = mpnew(0); + k = mpnew(0); + mpsub(pub->q, mpone, qm1); + + // find a k that has an inverse mod q + while(1){ + mprand(qlen, genrandom, k); + if((mpcmp(mpone, k) > 0) || (mpcmp(k, qm1) >= 0)) + continue; + mpextendedgcd(k, q, r, kinv, s); + if(mpcmp(r, mpone) != 0) + continue; + break; + } + + // make kinv positive + mpmod(kinv, qm1, kinv); + + // r = ((alpha**k) mod p) mod q + mpexp(alpha, k, p, r); + mpmod(r, q, r); + + // s = (kinv*(m + ar)) mod q + mpmul(r, priv->secret, s); + mpadd(s, m, s); + mpmul(s, kinv, s); + mpmod(s, q, s); + + sig = dsasigalloc(); + sig->r = r; + sig->s = s; + mpfree(qm1); + mpfree(k); + mpfree(kinv); + return sig; +} diff --git a/libsec/dsaverify.c b/libsec/dsaverify.c new file mode 100644 index 0000000..23cee50 --- /dev/null +++ b/libsec/dsaverify.c @@ -0,0 +1,46 @@ +#include "os.h" +#include +#include + +int +dsaverify(DSApub *pub, DSAsig *sig, mpint *m) +{ + int rv = -1; + mpint *u1, *u2, *v, *sinv; + + if(sig->r->sign < 0 || mpcmp(sig->r, pub->q) >= 0) + return rv; + if(sig->s->sign < 0 || mpcmp(sig->s, pub->q) >= 0) + return rv; + u1 = mpnew(0); + u2 = mpnew(0); + v = mpnew(0); + sinv = mpnew(0); + + // find (s**-1) mod q, make sure it exists + mpextendedgcd(sig->s, pub->q, u1, sinv, v); + if(mpcmp(u1, mpone) != 0) + goto out; + + // u1 = (sinv * m) mod q, u2 = (r * sinv) mod q + mpmul(sinv, m, u1); + mpmod(u1, pub->q, u1); + mpmul(sig->r, sinv, u2); + mpmod(u2, pub->q, u2); + + // v = (((alpha**u1)*(key**u2)) mod p) mod q + mpexp(pub->alpha, u1, pub->p, sinv); + mpexp(pub->key, u2, pub->p, v); + mpmul(sinv, v, v); + mpmod(v, pub->p, v); + mpmod(v, pub->q, v); + + if(mpcmp(v, sig->r) == 0) + rv = 0; +out: + mpfree(v); + mpfree(u1); + mpfree(u2); + mpfree(sinv); + return rv; +} diff --git a/libsec/egalloc.c b/libsec/egalloc.c new file mode 100644 index 0000000..d7c940a --- /dev/null +++ b/libsec/egalloc.c @@ -0,0 +1,67 @@ +#include "os.h" +#include +#include + +EGpub* +egpuballoc(void) +{ + EGpub *eg; + + eg = mallocz(sizeof(*eg), 1); + if(eg == nil) + sysfatal("egpuballoc"); + return eg; +} + +void +egpubfree(EGpub *eg) +{ + if(eg == nil) + return; + mpfree(eg->p); + mpfree(eg->alpha); + mpfree(eg->key); +} + + +EGpriv* +egprivalloc(void) +{ + EGpriv *eg; + + eg = mallocz(sizeof(*eg), 1); + if(eg == nil) + sysfatal("egprivalloc"); + return eg; +} + +void +egprivfree(EGpriv *eg) +{ + if(eg == nil) + return; + mpfree(eg->pub.p); + mpfree(eg->pub.alpha); + mpfree(eg->pub.key); + mpfree(eg->secret); +} + +EGsig* +egsigalloc(void) +{ + EGsig *eg; + + eg = mallocz(sizeof(*eg), 1); + if(eg == nil) + sysfatal("egsigalloc"); + return eg; +} + +void +egsigfree(EGsig *eg) +{ + if(eg == nil) + return; + mpfree(eg->r); + mpfree(eg->s); +} diff --git a/libsec/egdecrypt.c b/libsec/egdecrypt.c new file mode 100644 index 0000000..2457880 --- /dev/null +++ b/libsec/egdecrypt.c @@ -0,0 +1,28 @@ +#include "os.h" +#include +#include + +mpint* +egdecrypt(EGpriv *priv, mpint *in, mpint *out) +{ + EGpub *pub = &priv->pub; + mpint *gamma, *delta; + mpint *p = pub->p; + int plen = mpsignif(p)+1; + int shift = ((plen+Dbits-1)/Dbits)*Dbits; + + if(out == nil) + out = mpnew(0); + gamma = mpnew(0); + delta = mpnew(0); + mpright(in, shift, gamma); + mpleft(gamma, shift, delta); + mpsub(in, delta, delta); + mpexp(gamma, priv->secret, p, out); + mpinvert(out, p, gamma); + mpmul(gamma, delta, out); + mpmod(out, p, out); + mpfree(gamma); + mpfree(delta); + return out; +} diff --git a/libsec/egencrypt.c b/libsec/egencrypt.c new file mode 100644 index 0000000..9b6b12c --- /dev/null +++ b/libsec/egencrypt.c @@ -0,0 +1,38 @@ +#include "os.h" +#include +#include + +mpint* +egencrypt(EGpub *pub, mpint *in, mpint *out) +{ + mpint *m, *k, *gamma, *delta, *pm1; + mpint *p = pub->p, *alpha = pub->alpha; + int plen = mpsignif(p); + int shift = ((plen+Dbits)/Dbits)*Dbits; + // in libcrypt version, (int)(LENGTH(pub->p)*sizeof(NumType)*CHARBITS); + + if(out == nil) + out = mpnew(0); + pm1 = mpnew(0); + m = mpnew(0); + gamma = mpnew(0); + delta = mpnew(0); + mpmod(in, p, m); + while(1){ + k = mprand(plen, genrandom, nil); + if((mpcmp(mpone, k) <= 0) && (mpcmp(k, pm1) < 0)) + break; + } + mpexp(alpha, k, p, gamma); + mpexp(pub->key, k, p, delta); + mpmul(m, delta, delta); + mpmod(delta, p, delta); + mpleft(gamma, shift, out); + mpadd(delta, out, out); + mpfree(pm1); + mpfree(m); + mpfree(k); + mpfree(gamma); + mpfree(delta); + return out; +} diff --git a/libsec/eggen.c b/libsec/eggen.c new file mode 100644 index 0000000..9ea7d99 --- /dev/null +++ b/libsec/eggen.c @@ -0,0 +1,21 @@ +#include "os.h" +#include +#include + +EGpriv* +eggen(int nlen, int rounds) +{ + EGpub *pub; + EGpriv *priv; + + priv = egprivalloc(); + pub = &priv->pub; + pub->p = mpnew(0); + pub->alpha = mpnew(0); + pub->key = mpnew(0); + priv->secret = mpnew(0); + gensafeprime(pub->p, pub->alpha, nlen, rounds); + mprand(nlen-1, genrandom, priv->secret); + mpexp(pub->alpha, priv->secret, pub->p, pub->key); + return priv; +} diff --git a/libsec/egprivtopub.c b/libsec/egprivtopub.c new file mode 100644 index 0000000..e22c5c3 --- /dev/null +++ b/libsec/egprivtopub.c @@ -0,0 +1,17 @@ +#include "os.h" +#include +#include + +EGpub* +egprivtopub(EGpriv *priv) +{ + EGpub *pub; + + pub = egpuballoc(); + if(pub == nil) + return nil; + pub->p = mpcopy(priv->pub.p); + pub->alpha = mpcopy(priv->pub.alpha); + pub->key = mpcopy(priv->pub.key); + return pub; +} diff --git a/libsec/egsign.c b/libsec/egsign.c new file mode 100644 index 0000000..1054004 --- /dev/null +++ b/libsec/egsign.c @@ -0,0 +1,43 @@ +#include "os.h" +#include +#include + +EGsig* +egsign(EGpriv *priv, mpint *m) +{ + EGpub *pub = &priv->pub; + EGsig *sig; + mpint *pm1, *k, *kinv, *r, *s; + mpint *p = pub->p, *alpha = pub->alpha; + int plen = mpsignif(p); + + pm1 = mpnew(0); + kinv = mpnew(0); + r = mpnew(0); + s = mpnew(0); + k = mpnew(0); + mpsub(p, mpone, pm1); + while(1){ + mprand(plen, genrandom, k); + if((mpcmp(mpone, k) > 0) || (mpcmp(k, pm1) >= 0)) + continue; + mpextendedgcd(k, pm1, r, kinv, s); + if(mpcmp(r, mpone) != 0) + continue; + break; + } + mpmod(kinv, pm1, kinv); // make kinv positive + mpexp(alpha, k, p, r); + mpmul(priv->secret, r, s); + mpmod(s, pm1, s); + mpsub(m, s, s); + mpmul(kinv, s, s); + mpmod(s, pm1, s); + sig = egsigalloc(); + sig->r = r; + sig->s = s; + mpfree(pm1); + mpfree(k); + mpfree(kinv); + return sig; +} diff --git a/libsec/egtest.c b/libsec/egtest.c new file mode 100644 index 0000000..41b438d --- /dev/null +++ b/libsec/egtest.c @@ -0,0 +1,34 @@ +#include "os.h" +#include +#include + +void +main(void) +{ + EGpriv *sk; + mpint *m, *gamma, *delta, *in, *out; + int plen, shift; + + fmtinstall('B', mpconv); + + sk = egprivalloc(); + sk->pub.p = uitomp(2357, nil); + sk->pub.alpha = uitomp(2, nil); + sk->pub.key = uitomp(1185, nil); + sk->secret = uitomp(1751, nil); + + m = uitomp(2035, nil); + + plen = mpsignif(sk->pub.p)+1; + shift = ((plen+Dbits-1)/Dbits)*Dbits; + gamma = uitomp(1430, nil); + delta = uitomp(697, nil); + out = mpnew(0); + in = mpnew(0); + mpleft(gamma, shift, in); + mpadd(delta, in, in); + egdecrypt(sk, in, out); + + if(mpcmp(m, out) != 0) + print("decrypt failed to recover message\n"); +} diff --git a/libsec/egverify.c b/libsec/egverify.c new file mode 100644 index 0000000..29a9515 --- /dev/null +++ b/libsec/egverify.c @@ -0,0 +1,29 @@ +#include "os.h" +#include +#include + +int +egverify(EGpub *pub, EGsig *sig, mpint *m) +{ + mpint *p = pub->p, *alpha = pub->alpha; + mpint *r = sig->r, *s = sig->s; + mpint *v1, *v2, *rs; + int rv = -1; + + if(mpcmp(r, mpone) < 0 || mpcmp(r, p) >= 0) + return rv; + v1 = mpnew(0); + rs = mpnew(0); + v2 = mpnew(0); + mpexp(pub->key, r, p, v1); + mpexp(r, s, p, rs); + mpmul(v1, rs, v1); + mpmod(v1, p, v1); + mpexp(alpha, m, p, v2); + if(mpcmp(v1, v2) == 0) + rv = 0; + mpfree(v1); + mpfree(rs); + mpfree(v2); + return rv; +} diff --git a/libsec/fastrand.c b/libsec/fastrand.c new file mode 100644 index 0000000..1457127 --- /dev/null +++ b/libsec/fastrand.c @@ -0,0 +1,16 @@ +#include +#include +#include + +/* + * use the X917 random number generator to create random + * numbers (faster than truerand() but not as random). + */ +ulong +fastrand(void) +{ + ulong x; + + genrandom((uchar*)&x, sizeof x); + return x; +} diff --git a/libsec/genprime.c b/libsec/genprime.c new file mode 100644 index 0000000..c0e16d9 --- /dev/null +++ b/libsec/genprime.c @@ -0,0 +1,27 @@ +#include "os.h" +#include +#include + +// generate a probable prime. accuracy is the miller-rabin interations +void +genprime(mpint *p, int n, int accuracy) +{ + mpdigit x; + + // generate n random bits with high and low bits set + mpbits(p, n); + genrandom((uchar*)p->p, (n+7)/8); + p->top = (n+Dbits-1)/Dbits; + x = 1; + x <<= ((n-1)%Dbits); + p->p[p->top-1] &= (x-1); + p->p[p->top-1] |= x; + p->p[0] |= 1; + + // keep icrementing till it looks prime + for(;;){ + if(probably_prime(p, accuracy)) + break; + mpadd(p, mptwo, p); + } +} diff --git a/libsec/genrandom.c b/libsec/genrandom.c new file mode 100644 index 0000000..2cbaeb8 --- /dev/null +++ b/libsec/genrandom.c @@ -0,0 +1,62 @@ +#include "os.h" +#include +#include + +typedef struct State{ + QLock lock; + int seeded; + uvlong seed; + DES3state des3; +} State; +static State x917state; + +static void +X917(uchar *rand, int nrand) +{ + int i, m, n8; + uvlong I, x; + + /* 1. Compute intermediate value I = Ek(time). */ + I = nsec(); + triple_block_cipher(x917state.des3.expanded, (uchar*)&I, 0); /* two-key EDE */ + + /* 2. x[i] = Ek(I^seed); seed = Ek(x[i]^I); */ + m = (nrand+7)/8; + for(i=0; i8) ? 8 : nrand; + memcpy(rand, (uchar*)&x, n8); + rand += 8; + nrand -= 8; + x ^= I; + triple_block_cipher(x917state.des3.expanded, (uchar*)&x, 0); + x917state.seed = x; + } +} + +static void +X917init(void) +{ + int n; + uchar mix[128]; + uchar key3[3][8]; + ulong *ulp; + + ulp = (ulong*)key3; + for(n = 0; n < sizeof(key3)/sizeof(ulong); n++) + ulp[n] = truerand(); + setupDES3state(&x917state.des3, key3, nil); + X917(mix, sizeof mix); + x917state.seeded = 1; +} + +void +genrandom(uchar *p, int n) +{ + qlock(&x917state.lock); + if(x917state.seeded == 0) + X917init(); + X917(p, n); + qunlock(&x917state.lock); +} diff --git a/libsec/gensafeprime.c b/libsec/gensafeprime.c new file mode 100644 index 0000000..e95c94c --- /dev/null +++ b/libsec/gensafeprime.c @@ -0,0 +1,36 @@ +#include "os.h" +#include +#include + +// find a prime p of length n and a generator alpha of Z^*_p +// Alg 4.86 Menezes et al () Handbook, p.164 +void +gensafeprime(mpint *p, mpint *alpha, int n, int accuracy) +{ + mpint *q, *b; + + q = mpnew(n-1); + while(1){ + genprime(q, n-1, accuracy); + mpleft(q, 1, p); + mpadd(p, mpone, p); // p = 2*q+1 + if(probably_prime(p, accuracy)) + break; + } + // now find a generator alpha of the multiplicative + // group Z*_p of order p-1=2q + b = mpnew(0); + while(1){ + mprand(n, genrandom, alpha); + mpmod(alpha, p, alpha); + mpmul(alpha, alpha, b); + mpmod(b, p, b); + if(mpcmp(b, mpone) == 0) + continue; + mpexp(alpha, q, p, b); + if(mpcmp(b, mpone) != 0) + break; + } + mpfree(b); + mpfree(q); +} diff --git a/libsec/genstrongprime.c b/libsec/genstrongprime.c new file mode 100644 index 0000000..27c43a9 --- /dev/null +++ b/libsec/genstrongprime.c @@ -0,0 +1,57 @@ +#include "os.h" +#include +#include + +// Gordon's algorithm for generating a strong prime +// Menezes et al () Handbook, p.150 +void +genstrongprime(mpint *p, int n, int accuracy) +{ + mpint *s, *t, *r, *i; + + if(n < 64) + n = 64; + + s = mpnew(n/2); + genprime(s, (n/2)-16, accuracy); + t = mpnew(n/2); + genprime(t, n-mpsignif(s)-32, accuracy); + + // first r = 2it + 1 that's prime + i = mpnew(16); + r = mpnew(0); + itomp(0x8000, i); + mpleft(t, 1, t); // 2t + mpmul(i, t, r); // 2it + mpadd(r, mpone, r); // 2it + 1 + for(;;){ + if(probably_prime(r, 18)) + break; + mpadd(r, t, r); // r += 2t + } + + // p0 = 2(s**(r-2) mod r)s - 1 + itomp(2, p); + mpsub(r, p, p); + mpexp(s, p, r, p); + mpmul(s, p, p); + mpleft(p, 1, p); + mpsub(p, mpone, p); + + // first p = p0 + 2irs that's prime + itomp(0x8000, i); + mpleft(r, 1, r); // 2r + mpmul(r, s, r); // 2rs + mpmul(r, i, i); // 2irs + mpadd(p, i, p); // p0 + 2irs + for(;;){ + if(probably_prime(p, accuracy)) + break; + mpadd(p, r, p); // p += 2rs + } + + mpfree(i); + mpfree(s); + mpfree(r); + mpfree(t); +} diff --git a/libsec/hmac.c b/libsec/hmac.c new file mode 100644 index 0000000..c723973 --- /dev/null +++ b/libsec/hmac.c @@ -0,0 +1,56 @@ +#include "os.h" +#include + +/* rfc2104 */ +static DigestState* +hmac_x(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s, + DigestState*(*x)(uchar*, ulong, uchar*, DigestState*), int xlen) +{ + int i; + uchar pad[65], innerdigest[256]; + + if(xlen > sizeof(innerdigest)) + return nil; + + if(klen>64) + return nil; + + /* first time through */ + if(s == nil){ + for(i=0; i<64; i++) + pad[i] = 0x36; + pad[64] = 0; + for(i=0; i +#include + +uchar key[] = "Jefe"; +uchar data[] = "what do ya want for nothing?"; + +void +main(void) +{ + int i; + uchar hash[MD5dlen]; + + hmac_md5(data, strlen((char*)data), key, 4, hash, nil); + for(i=0; i + +/* + * This MD4 is implemented from the description in Stinson's Cryptography, + * theory and practice. -- presotto + */ + +/* + * Rotate ammounts used in the algorithm + */ +enum +{ + S11= 3, + S12= 7, + S13= 11, + S14= 19, + + S21= 3, + S22= 5, + S23= 9, + S24= 13, + + S31= 3, + S32= 9, + S33= 11, + S34= 15, +}; + +typedef struct MD4Table MD4Table; +struct MD4Table +{ + uchar x; /* index into data block */ + uchar rot; /* amount to rotate left by */ +}; + +static MD4Table tab[] = +{ + /* round 1 */ +/*[0]*/ { 0, S11}, + { 1, S12}, + { 2, S13}, + { 3, S14}, + { 4, S11}, + { 5, S12}, + { 6, S13}, + { 7, S14}, + { 8, S11}, + { 9, S12}, + { 10, S13}, + { 11, S14}, + { 12, S11}, + { 13, S12}, + { 14, S13}, + { 15, S14}, + + /* round 2 */ +/*[16]*/{ 0, S21}, + { 4, S22}, + { 8, S23}, + { 12, S24}, + { 1, S21}, + { 5, S22}, + { 9, S23}, + { 13, S24}, + { 2, S21}, + { 6, S22}, + { 10, S23}, + { 14, S24}, + { 3, S21}, + { 7, S22}, + { 11, S23}, + { 15, S24}, + + /* round 3 */ +/*[32]*/{ 0, S31}, + { 8, S32}, + { 4, S33}, + { 12, S34}, + { 2, S31}, + { 10, S32}, + { 6, S33}, + { 14, S34}, + { 1, S31}, + { 9, S32}, + { 5, S33}, + { 13, S34}, + { 3, S31}, + { 11, S32}, + { 7, S33}, + { 15, S34}, +}; + +static void encode(uchar*, u32int*, ulong); +static void decode(u32int*, uchar*, ulong); + +static void +md4block(uchar *p, ulong len, MD4state *s) +{ + int i; + u32int a, b, c, d, tmp; + MD4Table *t; + uchar *end; + u32int x[16]; + + for(end = p+len; p < end; p += 64){ + a = s->state[0]; + b = s->state[1]; + c = s->state[2]; + d = s->state[3]; + + decode(x, p, 64); + + for(i = 0; i < 48; i++){ + t = tab + i; + switch(i>>4){ + case 0: + a += (b & c) | (~b & d); + break; + case 1: + a += ((b & c) | (b & d) | (c & d)) + 0x5A827999; + break; + case 2: + a += (b ^ c ^ d) + 0x6ED9EBA1; + break; + } + a += x[t->x]; + a = (a << t->rot) | (a >> (32 - t->rot)); + + /* rotate variables */ + tmp = d; + d = c; + c = b; + b = a; + a = tmp; + } + + s->state[0] += a; + s->state[1] += b; + s->state[2] += c; + s->state[3] += d; + + s->len += 64; + } +} + +MD4state* +md4(uchar *p, ulong len, uchar *digest, MD4state *s) +{ + u32int x[16]; + uchar buf[128]; + int i; + uchar *e; + + if(s == nil){ + s = malloc(sizeof(*s)); + if(s == nil) + return nil; + memset(s, 0, sizeof(*s)); + s->malloced = 1; + } + + if(s->seeded == 0){ + /* seed the state, these constants would look nicer big-endian */ + s->state[0] = 0x67452301; + s->state[1] = 0xefcdab89; + s->state[2] = 0x98badcfe; + s->state[3] = 0x10325476; + s->seeded = 1; + } + + /* fill out the partial 64 byte block from previous calls */ + if(s->blen){ + i = 64 - s->blen; + if(len < i) + i = len; + memmove(s->buf + s->blen, p, i); + len -= i; + s->blen += i; + p += i; + if(s->blen == 64){ + md4block(s->buf, s->blen, s); + s->blen = 0; + } + } + + /* do 64 byte blocks */ + i = len & ~0x3f; + if(i){ + md4block(p, i, s); + len -= i; + p += i; + } + + /* save the left overs if not last call */ + if(digest == 0){ + if(len){ + memmove(s->buf, p, len); + s->blen += len; + } + return s; + } + + /* + * this is the last time through, pad what's left with 0x80, + * 0's, and the input count to create a multiple of 64 bytes + */ + if(s->blen){ + p = s->buf; + len = s->blen; + } else { + memmove(buf, p, len); + p = buf; + } + s->len += len; + e = p + len; + if(len < 56) + i = 56 - len; + else + i = 120 - len; + memset(e, 0, i); + *e = 0x80; + len += i; + + /* append the count */ + x[0] = s->len<<3; + x[1] = s->len>>29; + encode(p+len, x, 8); + + /* digest the last part */ + md4block(p, len+8, s); + + /* return result and free state */ + encode(digest, s->state, MD4dlen); + if(s->malloced == 1) + free(s); + return nil; +} + +/* + * encodes input (u32int) into output (uchar). Assumes len is + * a multiple of 4. + */ +static void +encode(uchar *output, u32int *input, ulong len) +{ + u32int x; + uchar *e; + + for(e = output + len; output < e;) { + x = *input++; + *output++ = x; + *output++ = x >> 8; + *output++ = x >> 16; + *output++ = x >> 24; + } +} + +/* + * decodes input (uchar) into output (u32int). Assumes len is + * a multiple of 4. + */ +static void +decode(u32int *output, uchar *input, ulong len) +{ + uchar *e; + + for(e = input+len; input < e; input += 4) + *output++ = input[0] | (input[1] << 8) | + (input[2] << 16) | (input[3] << 24); +} diff --git a/libsec/md4test.c b/libsec/md4test.c new file mode 100644 index 0000000..417cb33 --- /dev/null +++ b/libsec/md4test.c @@ -0,0 +1,31 @@ +#include "os.h" +#include +#include + +char *tests[] = { + "", + "a", + "abc", + "message digest", + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890", + 0 +}; + +void +main(void) +{ + char **pp; + uchar *p; + int i; + uchar digest[MD5dlen]; + + for(pp = tests; *pp; pp++){ + p = (uchar*)*pp; + md4(p, strlen(*pp), digest, 0); + for(i = 0; i < MD5dlen; i++) + print("%2.2ux", digest[i]); + print("\n"); + } +} diff --git a/libsec/md5.c b/libsec/md5.c new file mode 100644 index 0000000..bb2f3cb --- /dev/null +++ b/libsec/md5.c @@ -0,0 +1,148 @@ +#include "os.h" +#include + +/* + * rfc1321 requires that I include this. The code is new. The constants + * all come from the rfc (hence the copyright). We trade a table for the + * macros in rfc. The total size is a lot less. -- presotto + * + * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + * rights reserved. + * + * License to copy and use this software is granted provided that it + * is identified as the "RSA Data Security, Inc. MD5 Message-Digest + * Algorithm" in all material mentioning or referencing this software + * or this function. + * + * License is also granted to make and use derivative works provided + * that such works are identified as "derived from the RSA Data + * Security, Inc. MD5 Message-Digest Algorithm" in all material + * mentioning or referencing the derived work. + * + * RSA Data Security, Inc. makes no representations concerning either + * the merchantability of this software or the suitability of this + * software forany particular purpose. It is provided "as is" + * without express or implied warranty of any kind. + * These notices must be retained in any copies of any part of this + * documentation and/or software. + */ + +static void encode(uchar*, u32int*, ulong); +static void decode(u32int*, uchar*, ulong); + +extern void _md5block(uchar*, ulong, u32int*); + +MD5state* +md5(uchar *p, ulong len, uchar *digest, MD5state *s) +{ + u32int x[16]; + uchar buf[128]; + int i; + uchar *e; + + if(s == nil){ + s = malloc(sizeof(*s)); + if(s == nil) + return nil; + memset(s, 0, sizeof(*s)); + s->malloced = 1; + } + + if(s->seeded == 0){ + /* seed the state, these constants would look nicer big-endian */ + s->state[0] = 0x67452301; + s->state[1] = 0xefcdab89; + s->state[2] = 0x98badcfe; + s->state[3] = 0x10325476; + s->seeded = 1; + } + + /* fill out the partial 64 byte block from previous calls */ + if(s->blen){ + i = 64 - s->blen; + if(len < i) + i = len; + memmove(s->buf + s->blen, p, i); + len -= i; + s->blen += i; + p += i; + if(s->blen == 64){ + _md5block(s->buf, s->blen, s->state); + s->len += s->blen; + s->blen = 0; + } + } + + /* do 64 byte blocks */ + i = len & ~0x3f; + if(i){ + _md5block(p, i, s->state); + s->len += i; + len -= i; + p += i; + } + + /* save the left overs if not last call */ + if(digest == 0){ + if(len){ + memmove(s->buf, p, len); + s->blen += len; + } + return s; + } + + /* + * this is the last time through, pad what's left with 0x80, + * 0's, and the input count to create a multiple of 64 bytes + */ + if(s->blen){ + p = s->buf; + len = s->blen; + } else { + memmove(buf, p, len); + p = buf; + } + s->len += len; + e = p + len; + if(len < 56) + i = 56 - len; + else + i = 120 - len; + memset(e, 0, i); + *e = 0x80; + len += i; + + /* append the count */ + x[0] = s->len<<3; + x[1] = s->len>>29; + encode(p+len, x, 8); + + /* digest the last part */ + _md5block(p, len+8, s->state); + s->len += len; + + /* return result and free state */ + encode(digest, s->state, MD5dlen); + if(s->malloced == 1) + free(s); + return nil; +} + +/* + * encodes input (u32int) into output (uchar). Assumes len is + * a multiple of 4. + */ +static void +encode(uchar *output, u32int *input, ulong len) +{ + u32int x; + uchar *e; + + for(e = output + len; output < e;) { + x = *input++; + *output++ = x; + *output++ = x >> 8; + *output++ = x >> 16; + *output++ = x >> 24; + } +} diff --git a/libsec/md5block.c b/libsec/md5block.c new file mode 100644 index 0000000..07fe223 --- /dev/null +++ b/libsec/md5block.c @@ -0,0 +1,267 @@ +#include "os.h" +#include + +/* + * rfc1321 requires that I include this. The code is new. The constants + * all come from the rfc (hence the copyright). We trade a table for the + * macros in rfc. The total size is a lot less. -- presotto + * + * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + * rights reserved. + * + * License to copy and use this software is granted provided that it + * is identified as the "RSA Data Security, Inc. MD5 Message-Digest + * Algorithm" in all material mentioning or referencing this software + * or this function. + * + * License is also granted to make and use derivative works provided + * that such works are identified as "derived from the RSA Data + * Security, Inc. MD5 Message-Digest Algorithm" in all material + * mentioning or referencing the derived work. + * + * RSA Data Security, Inc. makes no representations concerning either + * the merchantability of this software or the suitability of this + * software forany particular purpose. It is provided "as is" + * without express or implied warranty of any kind. + * These notices must be retained in any copies of any part of this + * documentation and/or software. + */ + +/* + * Rotate ammounts used in the algorithm + */ +enum +{ + S11= 7, + S12= 12, + S13= 17, + S14= 22, + + S21= 5, + S22= 9, + S23= 14, + S24= 20, + + S31= 4, + S32= 11, + S33= 16, + S34= 23, + + S41= 6, + S42= 10, + S43= 15, + S44= 21, +}; + +static u32int md5tab[] = +{ + /* round 1 */ +/*[0]*/ 0xd76aa478, + 0xe8c7b756, + 0x242070db, + 0xc1bdceee, + 0xf57c0faf, + 0x4787c62a, + 0xa8304613, + 0xfd469501, + 0x698098d8, + 0x8b44f7af, + 0xffff5bb1, + 0x895cd7be, + 0x6b901122, + 0xfd987193, + 0xa679438e, + 0x49b40821, + + /* round 2 */ +/*[16]*/0xf61e2562, + 0xc040b340, + 0x265e5a51, + 0xe9b6c7aa, + 0xd62f105d, + 0x2441453, + 0xd8a1e681, + 0xe7d3fbc8, + 0x21e1cde6, + 0xc33707d6, + 0xf4d50d87, + 0x455a14ed, + 0xa9e3e905, + 0xfcefa3f8, + 0x676f02d9, + 0x8d2a4c8a, + + /* round 3 */ +/*[32]*/0xfffa3942, + 0x8771f681, + 0x6d9d6122, + 0xfde5380c, + 0xa4beea44, + 0x4bdecfa9, + 0xf6bb4b60, + 0xbebfbc70, + 0x289b7ec6, + 0xeaa127fa, + 0xd4ef3085, + 0x4881d05, + 0xd9d4d039, + 0xe6db99e5, + 0x1fa27cf8, + 0xc4ac5665, + + /* round 4 */ +/*[48]*/0xf4292244, + 0x432aff97, + 0xab9423a7, + 0xfc93a039, + 0x655b59c3, + 0x8f0ccc92, + 0xffeff47d, + 0x85845dd1, + 0x6fa87e4f, + 0xfe2ce6e0, + 0xa3014314, + 0x4e0811a1, + 0xf7537e82, + 0xbd3af235, + 0x2ad7d2bb, + 0xeb86d391, +}; + +static void decode(u32int*, uchar*, ulong); +extern void _md5block(uchar *p, ulong len, u32int *s); + +void +_md5block(uchar *p, ulong len, u32int *s) +{ + u32int a, b, c, d, sh; + u32int *t; + uchar *end; + u32int x[16]; + + for(end = p+len; p < end; p += 64){ + a = s[0]; + b = s[1]; + c = s[2]; + d = s[3]; + + decode(x, p, 64); + + t = md5tab; + sh = 0; + for(; sh != 16; t += 4){ + a += ((c ^ d) & b) ^ d; + a += x[sh] + t[0]; + a = (a << S11) | (a >> (32 - S11)); + a += b; + + d += ((b ^ c) & a) ^ c; + d += x[sh + 1] + t[1]; + d = (d << S12) | (d >> (32 - S12)); + d += a; + + c += ((a ^ b) & d) ^ b; + c += x[sh + 2] + t[2]; + c = (c << S13) | (c >> (32 - S13)); + c += d; + + b += ((d ^ a) & c) ^ a; + b += x[sh + 3] + t[3]; + b = (b << S14) | (b >> (32 - S14)); + b += c; + + sh += 4; + } + sh = 1; + for(; sh != 1+20*4; t += 4){ + a += ((b ^ c) & d) ^ c; + a += x[sh & 0xf] + t[0]; + a = (a << S21) | (a >> (32 - S21)); + a += b; + + d += ((a ^ b) & c) ^ b; + d += x[(sh + 5) & 0xf] + t[1]; + d = (d << S22) | (d >> (32 - S22)); + d += a; + + c += ((d ^ a) & b) ^ a; + c += x[(sh + 10) & 0xf] + t[2]; + c = (c << S23) | (c >> (32 - S23)); + c += d; + + b += ((c ^ d) & a) ^ d; + b += x[(sh + 15) & 0xf] + t[3]; + b = (b << S24) | (b >> (32 - S24)); + b += c; + + sh += 20; + } + sh = 5; + for(; sh != 5+12*4; t += 4){ + a += b ^ c ^ d; + a += x[sh & 0xf] + t[0]; + a = (a << S31) | (a >> (32 - S31)); + a += b; + + d += a ^ b ^ c; + d += x[(sh + 3) & 0xf] + t[1]; + d = (d << S32) | (d >> (32 - S32)); + d += a; + + c += d ^ a ^ b; + c += x[(sh + 6) & 0xf] + t[2]; + c = (c << S33) | (c >> (32 - S33)); + c += d; + + b += c ^ d ^ a; + b += x[(sh + 9) & 0xf] + t[3]; + b = (b << S34) | (b >> (32 - S34)); + b += c; + + sh += 12; + } + sh = 0; + for(; sh != 28*4; t += 4){ + a += c ^ (b | ~d); + a += x[sh & 0xf] + t[0]; + a = (a << S41) | (a >> (32 - S41)); + a += b; + + d += b ^ (a | ~c); + d += x[(sh + 7) & 0xf] + t[1]; + d = (d << S42) | (d >> (32 - S42)); + d += a; + + c += a ^ (d | ~b); + c += x[(sh + 14) & 0xf] + t[2]; + c = (c << S43) | (c >> (32 - S43)); + c += d; + + b += d ^ (c | ~a); + b += x[(sh + 21) & 0xf] + t[3]; + b = (b << S44) | (b >> (32 - S44)); + b += c; + + sh += 28; + } + + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + } +} + +/* + * decodes input (uchar) into output (u32int). Assumes len is + * a multiple of 4. + */ +static void +decode(u32int *output, uchar *input, ulong len) +{ + uchar *e; + + for(e = input+len; input < e; input += 4) + *output++ = input[0] | (input[1] << 8) | + (input[2] << 16) | (input[3] << 24); +} diff --git a/libsec/md5pickle.c b/libsec/md5pickle.c new file mode 100644 index 0000000..5b353b5 --- /dev/null +++ b/libsec/md5pickle.c @@ -0,0 +1,37 @@ +#include "os.h" +#include + +char* +md5pickle(MD5state *s) +{ + char *p; + int m, n; + + m = 4*9+4*((s->blen+3)/3); + p = malloc(m); + if(p == nil) + return p; + n = sprint(p, "%8.8ux %8.8ux %8.8ux %8.8ux ", + s->state[0], s->state[1], s->state[2], + s->state[3]); + enc64(p+n, m-n, s->buf, s->blen); + return p; +} + +MD5state* +md5unpickle(char *p) +{ + MD5state *s; + + s = malloc(sizeof(*s)); + if(s == nil) + return nil; + s->state[0] = strtoul(p, &p, 16); + s->state[1] = strtoul(p, &p, 16); + s->state[2] = strtoul(p, &p, 16); + s->state[3] = strtoul(p, &p, 16); + s->blen = dec64(s->buf, sizeof(s->buf), p, strlen(p)); + s->malloced = 1; + s->seeded = 1; + return s; +} diff --git a/libsec/mkfile b/libsec/mkfile new file mode 100644 index 0000000..5e1f5d6 --- /dev/null +++ b/libsec/mkfile @@ -0,0 +1,55 @@ +<$DSRC/mkfile-$CONF +TARG=libsec.$L + +OFILES=\ + aes.$O\ + blowfish.$O\ + decodepem.$O\ + des.$O\ + des3CBC.$O\ + des3ECB.$O\ + desCBC.$O\ + desECB.$O\ + desmodes.$O\ + dsaalloc.$O\ + dsagen.$O\ + dsaprimes.$O\ + dsaprivtopub.$O\ + dsasign.$O\ + dsaverify.$O\ + egalloc.$O\ + egdecrypt.$O\ + egencrypt.$O\ + eggen.$O\ + egprivtopub.$O\ + egsign.$O\ + egverify.$O\ + fastrand.$O\ + genprime.$O\ + genrandom.$O\ + gensafeprime.$O\ + genstrongprime.$O\ + hmac.$O\ + md4.$O\ + md5.$O\ + md5block.$O\ + md5pickle.$O\ + nfastrand.$O\ + prng.$O\ + probably_prime.$O\ + rc4.$O\ + rsaalloc.$O\ + rsadecrypt.$O\ + rsaencrypt.$O\ + rsafill.$O\ + rsagen.$O\ + rsaprivtopub.$O\ + sha1.$O\ + sha1block.$O\ + sha1pickle.$O\ + smallprimes.$O + +HFILE=\ + os.h + +<$DSRC/mklib-$CONF diff --git a/libsec/nfastrand.c b/libsec/nfastrand.c new file mode 100644 index 0000000..fa042b0 --- /dev/null +++ b/libsec/nfastrand.c @@ -0,0 +1,23 @@ +#include +#include +#include + +#define Maxrand ((1UL<<31)-1) + +ulong +nfastrand(ulong n) +{ + ulong m, r; + + /* + * set m to the maximum multiple of n <= 2^31-1 + * so we want a random number < m. + */ + if(n > Maxrand) + abort(); + + m = Maxrand - Maxrand % n; + while((r = fastrand()) >= m) + ; + return r%n; +} diff --git a/libsec/os.h b/libsec/os.h new file mode 100644 index 0000000..07a1f97 --- /dev/null +++ b/libsec/os.h @@ -0,0 +1,2 @@ +#include +#include diff --git a/libsec/primetest.c b/libsec/primetest.c new file mode 100644 index 0000000..2d082e6 --- /dev/null +++ b/libsec/primetest.c @@ -0,0 +1,41 @@ +#include "os.h" +#include +#include + +void +main(void) +{ + mpint *z = mpnew(0); + mpint *p = mpnew(0); + mpint *q = mpnew(0); + mpint *nine = mpnew(0); + + fmtinstall('B', mpconv); + strtomp("2492491", nil, 16, z); // 38347921 = x*y = (2**28-9)/7, + // an example of 3**(n-1)=1 mod n + strtomp("15662C00E811", nil, 16, p);// 23528569104401, a prime + uitomp(9, nine); + + if(probably_prime(z, 5) == 1) + fprint(2, "tricked primality test\n"); + if(probably_prime(nine, 5) == 1) + fprint(2, "9 passed primality test!\n"); + if(probably_prime(p, 25) == 1) + fprint(2, "ok\n"); + + DSAprimes(q, p, nil); + print("q=%B\np=%B\n", q, p); + + exits(0); +} + +// example output, checked with Maple: +// seed EB7B6E35F7CD37B511D96C67D6688CC4DD440E1E +// q=E0F0EF284E10796C5A2A511E94748BA03C795C13 +// = 1284186945063585093695748280224501481698995297299 +// p=C41CFBE4D4846F67A3DF7DE9921A49D3B42DC33728427AB159CEC8CBBDB12B5F0C244F1A734AEB9840804EA3C25036AD1B61AFF3ABBC247CD4B384224567A863A6F020E7EE9795554BCD08ABAD7321AF27E1E92E3DB1C6E7E94FAAE590AE9C48F96D93D178E809401ABE8A534A1EC44359733475A36A70C7B425125062B1142D +// = 137715385439333164327584575331308277462546592976152006175830654712456008630139443747529133857837818585400418619916530061955288983751958831927807888408309879880101870216437711393638413509484569804814373511469405934988856674935304074081350525593807908358867354528898618574659752879015380013845760006721861915693 +// r=DF310F4E54A5FEC5D86D3E14863921E834113E060F90052AD332B3241CEF2497EFA0303D6344F7C819691A0F9C4A773815AF8EAECFB7EC1D98F039F17A32A7E887D97251A927D093F44A55577F4D70444AEBD06B9B45695EC23962B175F266895C67D21C4656848614D888A4 +// = 107239359478548771267308764204625458348785444483302647285245969203446101233421655396874997253111222983406676955642093641709149748793954493558324738441197139556917622937892491175016280660608595599724194374948056515856812347094848443460715881455884639869144172708 +// g=2F1C308DC46B9A44B52DF7DACCE1208CCEF72F69C743ADD4D2327173444ED6E65E074694246E07F9FD4AE26E0FDDD9F54F813C40CB9BCD4338EA6F242AB94CD410E676C290368A16B1A3594877437E516C53A6EEE5493A038A017E955E218E7819734E3E2A6E0BAE08B14258F8C03CC1B30E0DDADFCF7CEDF0727684D3D255F1 +// = 33081848392740465806285326014906437543653045153885419334085917570615301913274531387168723847139029827598735376746057461417880810924280288611116213062512408829164220104555543445909528701551198146080221790002337033997295756585193926863581671466708482411159477816144226847280417522524922667065714073338662508017 diff --git a/libsec/prng.c b/libsec/prng.c new file mode 100644 index 0000000..fc2e508 --- /dev/null +++ b/libsec/prng.c @@ -0,0 +1,15 @@ +#include "os.h" +#include +#include + +// +// just use the libc prng to fill a buffer +// +void +prng(uchar *p, int n) +{ + uchar *e; + + for(e = p+n; p < e; p++) + *p = rand(); +} diff --git a/libsec/probably_prime.c b/libsec/probably_prime.c new file mode 100644 index 0000000..4eaccba --- /dev/null +++ b/libsec/probably_prime.c @@ -0,0 +1,84 @@ +#include "os.h" +#include +#include + +// Miller-Rabin probabilistic primality testing +// Knuth (1981) Seminumerical Algorithms, p.379 +// Menezes et al () Handbook, p.39 +// 0 if composite; 1 if almost surely prime, Pr(err)<1/4**nrep +int +probably_prime(mpint *n, int nrep) +{ + int j, k, rep, nbits, isprime = 1; + mpint *nm1, *q, *x, *y, *r; + + if(n->sign < 0) + sysfatal("negative prime candidate"); + + if(nrep <= 0) + nrep = 18; + + k = mptoi(n); + if(k == 2) // 2 is prime + return 1; + if(k < 2) // 1 is not prime + return 0; + if((n->p[0] & 1) == 0) // even is not prime + return 0; + + // test against small prime numbers + if(smallprimetest(n) < 0) + return 0; + + // fermat test, 2^n mod n == 2 if p is prime + x = uitomp(2, nil); + y = mpnew(0); + mpexp(x, n, n, y); + k = mptoi(y); + if(k != 2){ + mpfree(x); + mpfree(y); + return 0; + } + + nbits = mpsignif(n); + nm1 = mpnew(nbits); + mpsub(n, mpone, nm1); // nm1 = n - 1 */ + k = mplowbits0(nm1); + q = mpnew(0); + mpright(nm1, k, q); // q = (n-1)/2**k + + for(rep = 0; rep < nrep; rep++){ + + // x = random in [2, n-2] + r = mprand(nbits, prng, nil); + mpmod(r, nm1, x); + mpfree(r); + if(mpcmp(x, mpone) <= 0) + continue; + + // y = x**q mod n + mpexp(x, q, n, y); + + if(mpcmp(y, mpone) == 0 || mpcmp(y, nm1) == 0) + goto done; + + for(j = 1; j < k; j++){ + mpmul(y, y, x); + mpmod(x, n, y); // y = y*y mod n + if(mpcmp(y, nm1) == 0) + goto done; + if(mpcmp(y, mpone) == 0){ + isprime = 0; + goto done; + } + } + isprime = 0; + } +done: + mpfree(y); + mpfree(x); + mpfree(q); + mpfree(nm1); + return isprime; +} diff --git a/libsec/ranlib.core b/libsec/ranlib.core new file mode 100644 index 0000000000000000000000000000000000000000..8a0e4c4c1a23e1a05f95f0990c71361107140ae5 GIT binary patch literal 430080 zcmeI530zd=+x{OE;jpA==2DttO5(=8nkKTT2||M9RxrS@C|fh2VwO~9T4q#cW|o$g zWs7EJRu)uNR$6LlR#sG|R;FfF=92&GIrAJC$pM#sKi>cQUgz_wP`&ZyBDzrSgNG_8)WqqYQ%QvdzY9X5G zGSH86S1ez9a-dpV zQ9I|botzyQmKC5?dq>w>EX23&(PGE@ddJn0ti>7wg<^lF0-18 z3i2~CV@&^8V632%011!)36KB@kN^pg011!)3H&z+thhnbo`IG@OQ46L`=Hs-45$ja z1)2(#K@*@XXbf~Ulmrch;-Nv%04NFygZ!Z$P#5ST$Qx<_9WT+e-=Lo$+3OzoF6dL} zY>)pD_SpdW#+ZC9!Z%a@YBu@C=a_s)nS95Xd`F9vBrzyS_!b8EW(W9|3*RCAe3Sb5 zrixT^X_+#Z_em<@i!$5rays`2`}$Y|Y9O z36@N2nc0dp<|5ruQ5F-ZBWnokEq9fBVwZBcv)ongDEASuA|*L>=qUWiA|Np~J#A=G zd;>R>v2n_vv?1w<$+3eH<3&K3%^G0KGg~bI1;ttB0*fjYn>IQnUZAt&;qhW*T0*dh z9~moRh7JxBNvXrkMdS0u2y~NV&dskqVt4gJj(#SaH7fu=8j#8gu$5Z#i*lu96&Dov zXZ4wA3i1yM@(=b8G6e?)1_cC01O!Hzf_2^>9i*(f5y}al74AD zcebo^z3q(sPPcs_ey~#nl@#0ZCtJ%3EH;rg2KyTmZh74K2mo?`I3N{B74AaZjJPLp zYvK;Yjf(pfw=M2o+{Cz-aZ3*zI8fH`^$)R`u&B7? z2CJ>KY~rNJ{ZheCJlFeJA#+;N|SIF>OEEv}n4mPvP9UhQNX$L)>z^*HV&>iF{5Qb%w+xy;x` zsQGb4?pg$DXF!@-R;i99mmAy5&(J86J>pDGJE9 z;rU5D0EL@^qN1V#0wV*0Bji(eSX95@AXAB@)M7o&Gq-UB-J|WJgH?8~8Tdfh;<+bJ zelsxahZSLX^GA(89*At`7v-0h+XHUjT8JY9&9TK<%kJ1Z@Wl-oclq7dzpH)&%(`4K!#)fu*1B*POevKt31tNnX%5%Lmk$aV1A|=FIK+R zVZGyia(-6>$9Glx#Wk?MtkKr=ZXD-WmQxJqAO z%D0)!1y+kWyWEt6_YytvN=ZLid5B5Y;-XxWEQ!VV`p=E!E(Q6vQn@DEf|tX^`8NcW5p2I&xq6wy&JVU!rNFpb=0aRPmUmt;Bgp zdwOU=7EQYx>I}7p+@Qa+HSG{2_l=cb8kqjKua_JfQhhY`chc?e{mON6-)O&XJ!`*x z@z3&z@DGa+*A7jL8x#{W0_#UtYMPYX&TPw)-{k09MPQxr4Ws@&CC1LCv?zCNI&|VB zwlGejA&ORoYg{j zLEg{>P)n#4Bxd2f{!KaQ(X%6t4|ziuKrNwGkm!ix|C_3Re0iNX{hucwA9v)fAfFR( z#|hkd`sat_XME%XyZk_td>)mb;qitpfLcPWAo;w}8fpVw2(^VSg5+~fd#D4{5xN+< z1d=~rkv}sreokV-d{^i)s2kKB@`WykdO&_qPpB8v8|nk~h5RA;IiNr&2nvQmAo)2f zyvP;dkbF#xgyatcP=9CubR{HT&_+XMC=<$pvLOqU1LZ<_ko*bUcxVD7KbIrF zIWB^Vp%Ul@$O_q@Qm71?2u*?}L*)>@{T5@PanNhfBXclxQbQd%mx*M7U-2+uZ_d@qU_d|1`2cUV-gV22FA!q^g zFtiYQ1bP%&1TBUhgC2*LKue(~peLba&{NRU&~oS*=vnAF=y_-bv=Vv&S_Qobt%hEL z)<7>qYoS-5SE1LSb|jUqU;fub{7?Z=f3JTj)FJduSK*1GF331MP)=gnoj4 zhW0_fK>MKs&_U=|=n(W9bQt;_`UCnCIs*L#9fgiT$DtF@-_S`&PA*V0s5#^cwSe3p zcSwUgAWz5(@`f&eT0*TLAE-6d2D%Vx3ta@YgW5wKppMYR&?QhOs55ja)CDp@U7^dM zZcul~7rGqk0r^2ap5E1=rd>s^f~ke^d+meR>H2ls z;h~rRKEXRyRMbS2zda~cEZ%YZwV@MZ#fIUzg>OC+D+b(~v1`<~vC*?%n%sHCMR6$y zzT3X*;$d<3ZA)_d_+Ukxv0wY57B9WD_Wp$5{~GvX^W?~+;Ld@64ViT7nEq9^Xl<7AY_^}<22Y%4?bI4` z(R)t+Gl1HSFyrbtko!EUY1Oa0Ysc5&=R5F7sNN=LG}QD*`?^i%|4Vf}MYFo1m^*kL zbxqi}IcmtBrQ|uvdZ!$q!NI|4|6J`-yGZ^lLmc$h>&c&m$Q6dyZgieAr_wm{V%>d> zZ7;_YbF=2IE!^BS54FtG%lm?st$bRyxv=d;?b>(fc=08jI$zqw)b+A%a)TbiuV=5` zefs(b)V&SX?~48dt{fPxN60ALWL|d#{&S}wk1_Xl*R!}+{&VMYy2|qW`oDW_**#so z|K_sTMh^BreSIUR&($PA0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmzAUps~L%(!N{54IgfQdHUa<^}ptpI|lB*^pmSQRQLEk_lx+8K4?4P*vfmB zb?+Nkxuo|M2P<}0WiEg1?%00oUNBGTxPDZ`(;2UL-dlE}`ExH#y6canUe6D2HF(y{ zM>_xd^^4ZoKaPCtx{K%gxNTVY#IC7BJRZ7o+l+TE_jMh2*(E_2yj?ym@7bY~uP=@3 zle%bgK)%auZQj@sob31TZ$Cx-b@cuxKY!Cbvdh>xH?K)~z4f56^J}Ip+!ZKfHVA z>pisZVGrotp5Iy+^@pEuj5yrdVg7IKK>TdS=Dtc#(5GT z0TLjAGa_I-Ki^(q|C^fn{~WLXhwjd(5;nr$Z!_V4npfYXY3q;L|9|+I?%LRP^6%XX zlloix4jX5k$CV*EU~A!w%k4nHgIYX7|y$NjWu`?A2mfBw!&Lv{b{b<2I{CCL6y zSJ&fo3$e`TV|NQD8(^{Q|6l@olRq1|%z2N||HJw>dXt?s*8gR04fh{6SoPn{dH*R* z+<%QcM~Cb0*nd^-Kl_vGrR1;tUa$K1e!xSkkD}s2O`C7@-~FHcYnAq88BN@O{~^x% z|7Z91B?hDAw>jIwe*Ygf`uF{3|HhsrP2B&&^XPxZ6Gj*RbeaD&SzG4+a_9Y5G;#mO z&!hh-&;7G+V~Om4wb6eM=lxeUasQ)+;@q6%-{_^j8~6X5E%n#bjb;BnvbMbbe$M+} z+{FE_J&*olKCA1u{+#T;#OS}L^Zr*hasM5z!nvK}^&h{pe%Ezl+5cvv|6b1fuWsW0 zC!a_E?Y^t)xBi^$KTy_|_kVBa{cmpK{&$>5|KWS;cU?D@{Z|_O_i^5TO%wMYISl7^ zj`#od`|A3wKPUS?X!PILdH)BSxc~X*(ZBz1^}DVc%l=bjZF&FuJMUivHL$L(+<$ED z$a(ZX=a}=;lKrnV`VVm4qfZm}KPnODc8>S|6gT6Qz&|<2{(WR^dHv=4-%ch|6Zfy5 z7BVRR#?ZUsgmIR~9XeJ|d#$E1~bXggYA2Ck1Q)EF+u>lS)w-SKCU zM5S84SgnWa+pkn(xIV5{W4J!vtj2JCT%*QteSA=j;rdv3+Rq2BkA2h_u8&P>4A;kj zY7E!M(P|9W$0=$I*T*Gl4A;jMY7E!Mm1+#v$BWe%u8&u$F4A;kjY7E!M(P|9W$0=$I*T)%Z4A;jcY7E!Mm1+#v$BWe% zu8&u$F*GK*hU?>K zHHPcs6g7tH;|w*1>*EqNhU?=BHHPcs#cB-K$1Bwsu8*tL7_N^ut1(=T3-U8 zoR^cWG-<@OgG1x&BO&_Kcx$U=p5VqZIth>f36KB@kN^pg011!)3H+M`mg9T+Ncnyp zrasXgS{i(#eE-^E+8E=Z$@hG}P`+3BAMhf)7uY=3?mo&pEAOQ|0)7?VGaCfI67L-i zSDvZ7MEMlu)0NMGkHC8^k0^f@?uPdsUQ_-7+#f%S-=Tar{EKgeIIO%`oQHOFH-0-o zc^CL)_}OJ|<>AU>l_x77t2_sO=#UVF@Dcc#*ChCr_}R~Nc#fxwm3ihefW%JE@GSV z8s)zz{|mnLIrM}3YRZc)q7^*mWf##NzM$GgTq^wo7ttGj=SMCg5Ps`+7jXr=(4GC9X|JO7f}S?;MPpoETv^7gW^+drRGwLFk5V~F|n4SENgj5sTyTl>?3{4 zm<0QVnK=&I=>uc4%$)ex7^8wdFxJ3}vn?f-LXlZeoHZdmE59VqVil#<{E`Ao`YFq@ zEw=OtmhyD{m;$4)@Y3S&P&pSKT3Q?yE>2k+;&3w9L=Ni?Y722VA-NVqhqb5fdg`91 z?&z?eeqM0~HDeTFXOnF+my~6i3(%yn&*xYz7Jb&5Ke28(2F40oes1k@%o~eyEkzO7gQN6j(i_yn@-IERK51_+%g;4?GE1n4+t=w z)f5tLY@XpM#Aj~u64f2dzgT#ldOW%0IF2Kq(&Xp9>K`sBFf=MOC@M4(bp!R%_OE?| zBJ{~Vk}EPi<@2S$=h|lDIWt8)Z=P>W{qtm(d1!l4Zz<$+6MhT`$0M14vBSv*1%`(N z2ZuyPMw(1PpB$c`q%Q{NY-s()6in};?;ekKE2+*Jtq*&kfW zNjdqpJmUcN+M$7=X!dlqBf|r6R5@jM*6k~VeEt{RJ+!A#GXq-dE` zL{ATGI%@WZcH#Wx=LWEiehS8|*=YUXFvt4F=?nQrd1l{pBGVs~;2?=*UMt@2Gu| zG5KC8-Ur@*x)o3<9v{}(k0GD?YaumyjHtTDADo|kU!1;t9WF9FHNPYetp;^>;2L+r zJhrhvxz}eDbo$4)pa_S1v~FJ^U*8L}r#2HcqoGl#KNW7Vzu5Wb`8%s09$d?)c3+&o z{JueCdTM)7bFmaMyi>2Qo~6(9{6pmThFVyRw5`nsV$QhrYsM^PW{RJCp%kPPV#Z%Lg zasQ*{X561g^x6)$7#@Ub?;R(#YoA(>Ux$~0`eUg4ehW8|mWN}QphVoC4UjLNav$lR@h?Dy>G}xiOv9A#F`$LiE zsf`o%_f6$7&t%%VaG>qb$nuZccBqPY&-T-%KU2ppKat+Hc)&L^zKolE%Wo?_xN?2o zD}6${N2W(FdCU5C=XTz{->tiAXn(&!4_t8a2A8LzzJAeLTexfWj>{_@Wzrg z!P`p56%2{FcJ1b6hp$et47xdWY5R-czP?Alc@v(w;=}kyy8Jro=(Eo>|9;s0M*~eG z3nxDJ$Ha{@?@es>yYFW|Bp?5<*E^rZgYJvY2R zCvMhTW#h}|9G-mTsD~!p@Jf%blD~ap&2uvkwtv?2eonuSZd!fSH(4EmF8<-xkgFfe zn3kFuKPmf#n&&6HHhu9ee|PGw{WUjlNXe)Z@%MBr_!pC^H@-7tffn(}6LgWu&3f9&uVmQ8Q8aNCv{x~0qCPp-c+)j#x+k-t5@_}e>M1poZU(z1QGc|{$_ zh+e+vhU?vYieA6^&aPMf<@VLQ0WGfl>iO4qCnemScJE8qt-qngr*5vr7o}vJJT|{$ z_a9&1{$OnMtc{O;o;xXK@53WlF-dE!{)?X9la_WXcLqxy#T_`H>8_LJlCx~7eNc#D5{_kOva z-Y@LA_WoA$_OIT0@57fo`c8F4vT3LJ@nMt3m#@;pY-EJT5JpRj?ykB>%&Dt6C*$200#rRzI;Dynx-<@vie%0+a4R~$qk>J0c zZ~a)$_X-0Z?K~`S)z05W%(!6R_is+x-0sER1+SDm{m_uPt&e8^IQX*J$Ddk1@ZjQ$ z``mQRj>?eB4h$c9$9r2MKY#P_4`%=E_38ZZTYi7??_{E2w z-EhTC9ekE$E!c_w>?$#z;4U#lgn@ZcwJX*??nf8 zKfLGF&%=wa`p9F^PoGX**>n4QQIJ|WB#3UUmUab%lk%L>&qDmm5&tU@{~HkhWW@hD#Q#~u zzaQd%72+R+_=h0=(-8kji2w75|6;_yH{zd%_@6-h3lRS!i2o;ue>24Y9>o6{#NQ9` ze+%)y5%Jd$|2q)>ClLSfi2tpKe<#F$HR68=@&69-KaBXlf%s=4{(mF>sfhnb#Q$5w z|7XO1AL4%i@n3}a`yl>zBK~fOe+$I_b;SQ}#Q!?P|5L>OBE@efD*JrMt?i2o|YzZ>E|7xB+Q{MRA= zA0Yml5&s>C|9r&%3&cMK@&5zy-;VgNMEsKwe^u_%_@^NL^AZ2o z5&vk!|8vBDFXBH7@$ZQEZ$SK~BK|Rm|8$vu#J>XZ?~VAkM*PPh{w)#z+Y$fg5&yo3 ze=Ed)9O6F~@efD*JrVzF5&!*&|HFuXHR8V$@t=hFw?X{xNBkEf{+A*CmmvNgi2q%P z{~E;qQpEpP#J>#je+coPhWNW6{#L|42k}3K_{Spt-4Op@5dU3>e-PrIh4?>+_`i$z zUxoO;hWP)5`1eHoA4U9EA^tNE|2Gl;7ZLvw#D6a0|0Cjm9PvMh_}_&1hamn#5&uZU z{{zJTQ^fyw#D5Cn|1#qL9OB;(@t=(Nrz8G1BmQ?I{yPx=D8&D5#Q!?Pe>3904e|Fy z{GUbqJ0SiG5&tU?|H~2o(TM*7#6KDFe*p16iTJNW{4YZM_aOeoi2owQeq#J@A*{~hArAMw8c@qY^O_eT6zBmUPQ z{vRU#!H9nW;=gu;$M|i5;ZH1l{fQ5F#T&;y*ymLao zds`1m&fWRbgNHu;=-}$zwt3H%Ubg4Cu-m`;D`Ib_vTuq8|9bHaoqN7BtN7*2mdiW5 zamn#(t{nF6)^gK=FRTBaylKqgE?+$rGSmINpcmTsjP6mfFFoVxX-{{2>8cC*{xD_H z{qHY4u(;-mr00A6zH-CQul2tz^&xZn-mW*lI%)gm-{!Z?8rjtwKfl#~SycYgG`=4e z@+Z&oPo$ZkXs80Z1PX^vKMjtJ?Pu~EJbYwNQ%KPt>W#1tGD7!(*16lU_XS_&*? zo292|Yy#euiAjyS4nN1Sl^2$pGvTFHyXP5mrIyL1!XG>N+vl>)rDoxuX|oCcqGG&b z=5H;wFUl$|EVLArp8k|hw^TNP?>_~8sGv`w`1(|w8o*@pa_R~>{#J2d0e->_B66AB z-+sn*pI5)Z4itUo*jGBz?PB|vlP0j}wEW{7`L`O*WD{mah z$+6z32|{ecGGj988Dqm8m%EBzPx%{q&1=urzIF3cjv$ZcdI~$)T{~>*hZ9)=HN&_E z#zWBTb2wQ!j==axue$MIjO9<{x)s2Cz7KB`n zwuR^nsqN*^n3`j~{HeY^sr5EFkPy{)k{rt$M~!ckWAD20G&$}Hsq?!{j&UFO>5JUN zO!#qZKMecJejKOf_)Ch8Q*+Q;s1{Vg+hF_U4jahp`a-?&D;QTp0VplEe-HBqF(0AE z^6hN@ERma)6`YQ+mgE;$vi*fMe|%B4*-|jfQl2W`3a?!duigPQHi%PigBxRG2W%(O zETJMTI}i$jf}s#B!`Onc1!D`wcyrU5RgCus>m3n=>%9wKXeq>-m;zQ^@2P-O86Je^vRF z$b{VfI^~m<&r!airG5Pl*@^t0J%BL%zt9*s>D&U3)JWhAzp~{n$rz^KAzg77nu0#luuGVTlw?K-%M)?iO=O|yI{2k>xmH(x@C7w|9^HM%odA9O$c zyr1%9y&R%{+n`7JVweoJ(TxXe!cQC<@YIHru=>7HOfyYZ-d8B{k)V9Q=X^% zCgqEiuTlPq^1aGEJK5`WReq)NROMyLXDfeN`5Ve>l>e!`EuQ1$dG%C2OnIjAY0Bp+ zU!#1J@}HESRNfUo1(Wq6m5)?DUiob0OO#hD-=_Si^44AKb^MjbD$i3sP5DCQFDT!x z{1@e(CVQPO%Hx!eRX$ny-O5)fe_#1-<;Rqpy4veSC{I(KuYA7p70Nd%|5mvhUgJnU zot5`fo~*o7`CZDNQvSN~@09MJi8LUZDIIuKZW!?s%;)@0&5oZOZ2;U#h%wANzKp%CAb zlhRNgY!UZ=P6Xyuv8%azYl{*3ZX%6BPu39;8{r#w=5vho7uw*D5bpUa9;!q9rTm!k4q^6seU&FG&ryDx^7+bNRlY^} ze&x->)p;rJuY9!fBIT9JmnmPb{Bz~Ul(&hn*9%Y{r#wgbP0AN2U!i=9^1aGiL<&wM zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J|7!?Li%(2& zadGje-%xcfK53v9y5M&K@WBX7e!>eMpRb&2|659G^(iLzY38t`1`B*J_Jr^uM*TM& zTpiZ8#(Z;!agxKloRz;hVVuW099N#GXhF=s@+h{7~3y=(7ruBz;nuP)3mL(gWO)8*RJ{Y?fYY1j*Toi{x*`nYrGI9 z%=B4c-yXZ`+w1G_1s}9JWs<+CBBRNiNC!At%d&W-*x*8k1MhlImzoA0qC+{W0Lop(%`!@Hg4!Vt%?YmOEpaJQuwVPQbXAoz?wMXTsTWoh{#kbC>s(WGCce))>o!VVOJ^{kS**KBmjZ zN%?P$9XZO!;QEilj>kTm`*GnziW8U>V1Ar2mIbj)o{L@^Cty6rJ08n%{%oHs9QXZC z%pbwIOHQtkeEvwS$5B2vIX*wNsDBc8t~%3cmwG;v%vNK5sWFxXuuPtdUJD$J=RRk( z&-OXgS)G4vIUegTLNS9+{rgcqXV-t;c09Ix4nNbW5B~p_zAcy?$2Hrhp7)Jq!g$^R zN8>etv)X6-+QL~KE++vJAOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH* zAOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8} z0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq z5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH* zAOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8} z0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DJCz`8^aO|;fD zW9(wgG~mWIk9!((|J>NL-(4$}wM~%Ov$lTyEdztFz6qyJN$1}Gh&B!P|EbY`rt|)z zo4Eg}=h1&o$A>N)z|L^E~=L+NI(C=NkQIJMTZEiTm$A{M_&V?Y<57 zf7s~X;=KQoChmXXdG!BvpN9KSleOjjpX0p$iYD&=$a(btZcxMhuQdA4b>4qv6ZbzZ z`P}dStzixK-&WR^*FVpB|BIWr|8?il|Cj*{_g`xCpYOc?l}+4#itsw;*FU{t8}46| zwdM66@4Wx&ChmXbdGx<3q2d1XjQ%G$?|*X>_wVC!?$`gVVGZ}c!RWuhdH*#{+<(b= z^gnS#!~Ofo+VcJ{bl(5LChmV@0?zLozyE1Dy5at38~qnK?_UJt_)R$ZH9PnFKgQf} z|20Pc#m@WpY2yB;pGW^Ka~tlzzpO3q{|dO1$<)OCJ2@>bCIJ#40TLhq5+DH*AOR8} z0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq z5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH* zAOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8} z0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLjA|6K&4Jv7-+Tj8Z8 zHWwlVo@2I^TFph-{>1?%IRyoVhXeMF!ac&??W#F}4dIn(+w=N}Xv=2+j@SIiiO`V&003e*gR#-VHA3EYL#Kr^R;J{F|M~3ItQK(tysRcIk()OWd zWeXwvFrQaw&N7(G+Ktr=3Jq2lHpA1tuMlo#)GqSWW~1H^NO%e{9bT9n>R7tb+QDJr zwdbqvi+gZu4r&*BYI{*{DYOzSYV_J+XH`2WD!i6=VK(+f?Upy7b_LG4InEi{=^;da zIM!zt6lYB^4$v5r;IKL-b^8i&bTaDS;-x)>ni)`A+$#(1^-J=zCKMPqdt>#3!s_lt z)Hn7OZEi#TTfH>R)k~{J-KEItad=T(;PkLIRzE5vSlw0`o(}tp%vq>^o0pc2`X(q1 z^~b?WtVOv7n=`p*BE#zH+xHbB=5ExU?xn3oy{S+E*nchW@gQ}X)EPB)?!lo!syOcP zO4Pg^oLhKln^7|oyuC4R%{sgLFeoglwx+%>?v*JEPoF86i&;&E{?>feFKEYzG0`Jny=S>I+h7vN?*zg{YeX&BpyQ+kOm7 z(ODcLD$uwG^d4&WmB*O!B93vVmv$U=S3{j}-yF0bBPYM0z`#`dSmW$E1cw+my?zY) zzNjDbGV0%j>+6pD2wIQ&DRA_Pr;yt6jn@wgtbHsq_Lb+qpc?gOBd@4g3atjCmG=60 z?wD9wTvC=PkJ8w6hzdQ0k;A??#s@gY-C(16X**DR9Ue2=V&0Z#4yx;*_OU)VD9Xvb z8WmaFgMDA1{|L3`AhW0!3MIG1^Pj5i_@JrI-)ZfT@Nm3xkW+@ItgZGHw{A!Md%Uz7 z)SL^=L=HBq`o;sFd{Aqweo%NsZH8riV_$gCoKN!Y@?I0%~kS$symrX z``xwqvbM=Xlm8dp$z~!j80(vGlK%t#T>B3%Z@B-%M*sM~?ao?^ZsPvs|ENFL{)fx| zN$+Hm*FR0xme(KuSKi5HBBhD@KYkwlKRT=7{#P6QPj}vbMicix&h^}{|IK$d+<#kH zTVDU$o%dhT#QlGrfb%=Y$G?{MHr#)S(fmsw!HsmI`4mR6ZgO2JoJfHqQXt@78qyIV1`xhbL(}Yu7?cA^byB{^& z|3;(#dz|<0)5QIkoJap}Y;U-KKUrJe|CQcOud%6#`)|TqkB24!5+DH*AOR8}0TLhq z5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH* zAOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8} z0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq z5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH* zAOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8} z0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq z5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH* z_#Z@Iv*x9V)|zIVl8Z6ZfE(Lf^@B0@&y7v{-L;vrw#h>i_j=dXufJtr2-Y{@6zXyA z{kPoLaR2*^{_k_%e{>V~zcB&)&++76U@ z|D{I%PPY;wqlx>kkjKaMpT9@bYOkieW>T1Hm#H3P$IiP}8^L7%btBAm^{A_lp;lwR zCn}q`|5MJ90}>zs5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH* zAOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8} z0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TTGP2?XNre*4(}_O~p( z*kN8j6vokVS)hkz{4Mb2SYgbci)WzTx$IYepNfB>j_kL7T{-4N0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmz|K2z2w%gw$!K&4u=bQ(t#K?fTQ~ zqAyCqSE;p{yS8qwhqn1T4^58sNgdA^_gUznNkuO3&}#m0)#v4Ql~D8^&3*L$IX`kWvQoAq?|It-nC%0L7pS|a3 z9~|QWoa6l-T4p;pO^&rn4{a3mRtI~1xqawoRaM)2+s6`y3$zm3d%)%P218^23>*`y z#OrWbs}%b$_qXpa_mNti(?YAndQZ&n#d_JV9DAwpVVu`H7|U95JF2R#E^474M{cjz zN1xG$aBU=0OxL@~uR_S{ReN6cMW=j~=U9RM@r}~p_2d!kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^q%_X!Ms8t?J?cq-W84w%+w1SQr{FzrlMuVHLcT{o9qWb3 zL(|W%2JeMqT8-_~;@tK3@P*o5?l0FF@LGKvE{9b#r*S5Jh=ZA;7 zYnAH#{Vm6;suF&~=PB_1zcG>pPN{NKJzg)w(pM53xvJwu1$x__0mVXbwOxAC9)V{s^{Ke0aLPVCX7xMEEa{I$r|G>SjdcX1* za(n_~SxHrASI*1(VI|H%UNEd(aoqniEx3Z$UNC zPtakg#otv`7eFSc4-^h%LnTlJv;dOFG(JlsGiick?&UK5V>>?h91-T_BtLtz7CpMl zL4}ql?R=(yGMhD^AV1SG*^(7tDaZ-1nu{t$nAA9Z`?R#t0|K%w69Y=I zDoTt@jUO=p$Jf^l8ksgEJvBLY*noi2!jb@+EwAsOkf9R>1zE!b%(<4LQvayPz{n_( zGH5XN$|^1}_07Z}ZAfBr^01L9sfmMd-l+qUhl_-?l=PHAsi{M+iBC^Tj*A~KIzCk- z4H}A`CYr4Qh35PM*|om2W1HsUveLd;)?%AYcHHk&M3R9m?=%Zle*qzp;q}%=Wd%5_ zwAZxhmGv+3I@yPEu69wbxzJ+k=Rm)1n*!r%*jJAV3K|i9`coA#i6EIebWHq!VIt+) zxO&$+yEJ%$yhm_Di1@gnX~`o7Sh7Vztelr~DXBqqS9-t|CfpK<*CY-I5<`-c;!mw5 z5|alHAB3fn=ru!AM-EDq74*9!dHC@7*tDU^!%uUE823z+yjO}d$LseB7>acJC6T-f z%@ZslIWg|^4T$8Js}hn&Bn`qT7v!8zWd2v|dor!N&)sp|+ot>(ohBav%bL&0YrA$7 q9)43cRs8&SrNjCT#`?JTq7&O~a}jl$&H6(=hRVlJ`54>@{{H~R-GrI| literal 0 HcmV?d00001 diff --git a/libsec/rc4.c b/libsec/rc4.c new file mode 100644 index 0000000..beafa48 --- /dev/null +++ b/libsec/rc4.c @@ -0,0 +1,104 @@ +#include "os.h" +#include + +void +setupRC4state(RC4state *key, uchar *start, int n) +{ + int t; + int index2; + uchar *state; + uchar *p, *e, *sp, *se; + + state = key->state; + se = &state[256]; + for(sp = state; sp < se; sp++) + *sp = sp - state; + + key->x = 0; + key->y = 0; + index2 = 0; + e = start + n; + p = start; + for(sp = state; sp < se; sp++) + { + t = *sp; + index2 = (*p + t + index2) & 255; + *sp = state[index2]; + state[index2] = t; + if(++p >= e) + p = start; + } +} + +void +rc4(RC4state *key, uchar *p, int len) +{ + int tx, ty; + int x, y; + uchar *state; + uchar *e; + + x = key->x; + y = key->y; + state = &key->state[0]; + for(e = p + len; p < e; p++) + { + x = (x+1)&255; + tx = state[x]; + y = (y+tx)&255; + ty = state[y]; + state[x] = ty; + state[y] = tx; + *p ^= state[(tx+ty)&255]; + } + key->x = x; + key->y = y; +} + +void +rc4skip(RC4state *key, int len) +{ + int tx, ty; + int x, y; + uchar *state; + int i; + + x = key->x; + y = key->y; + state = &key->state[0]; + for(i=0; ix = x; + key->y = y; +} + +void +rc4back(RC4state *key, int len) +{ + int tx, ty; + int x, y; + uchar *state; + int i; + + x = key->x; + y = key->y; + state = &key->state[0]; + for(i=0; ix = x; + key->y = y; +} diff --git a/libsec/readcert.c b/libsec/readcert.c new file mode 100644 index 0000000..9fe5d73 --- /dev/null +++ b/libsec/readcert.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +static char* +readfile(char *name) +{ + int fd; + char *s; + Dir *d; + + fd = open(name, OREAD); + if(fd < 0) + return nil; + if((d = dirfstat(fd)) == nil) + return nil; + s = malloc(d->length + 1); + if(s == nil || readn(fd, s, d->length) != d->length){ + free(s); + free(d); + close(fd); + return nil; + } + close(fd); + s[d->length] = '\0'; + free(d); + return s; +} + +uchar* +readcert(char *filename, int *pcertlen) +{ + char *pem; + uchar *binary; + + pem = readfile(filename); + if(pem == nil){ + werrstr("can't read %s", filename); + return nil; + } + binary = decodepem(pem, "CERTIFICATE", pcertlen); + free(pem); + if(binary == nil){ + werrstr("can't parse %s", filename); + return nil; + } + return binary; +} + diff --git a/libsec/rsaalloc.c b/libsec/rsaalloc.c new file mode 100644 index 0000000..0caa96d --- /dev/null +++ b/libsec/rsaalloc.c @@ -0,0 +1,52 @@ +#include "os.h" +#include +#include + +RSApub* +rsapuballoc(void) +{ + RSApub *rsa; + + rsa = mallocz(sizeof(*rsa), 1); + if(rsa == nil) + sysfatal("rsapuballoc"); + return rsa; +} + +void +rsapubfree(RSApub *rsa) +{ + if(rsa == nil) + return; + mpfree(rsa->ek); + mpfree(rsa->n); + free(rsa); +} + + +RSApriv* +rsaprivalloc(void) +{ + RSApriv *rsa; + + rsa = mallocz(sizeof(*rsa), 1); + if(rsa == nil) + sysfatal("rsaprivalloc"); + return rsa; +} + +void +rsaprivfree(RSApriv *rsa) +{ + if(rsa == nil) + return; + mpfree(rsa->pub.ek); + mpfree(rsa->pub.n); + mpfree(rsa->dk); + mpfree(rsa->p); + mpfree(rsa->q); + mpfree(rsa->kp); + mpfree(rsa->kq); + mpfree(rsa->c2); + free(rsa); +} diff --git a/libsec/rsadecrypt.c b/libsec/rsadecrypt.c new file mode 100644 index 0000000..1e937be --- /dev/null +++ b/libsec/rsadecrypt.c @@ -0,0 +1,37 @@ +#include "os.h" +#include +#include + +// decrypt rsa using garner's algorithm for the chinese remainder theorem +// seminumerical algorithms, knuth, pp 253-254 +// applied cryptography, menezes et al, pg 612 +mpint* +rsadecrypt(RSApriv *rsa, mpint *in, mpint *out) +{ + mpint *v1, *v2; + + if(out == nil) + out = mpnew(0); + + // convert in to modular representation + v1 = mpnew(0); + mpmod(in, rsa->p, v1); + v2 = mpnew(0); + mpmod(in, rsa->q, v2); + + // exponentiate the modular rep + mpexp(v1, rsa->kp, rsa->p, v1); + mpexp(v2, rsa->kq, rsa->q, v2); + + // out = v1 + p*((v2-v1)*c2 mod q) + mpsub(v2, v1, v2); + mpmul(v2, rsa->c2, v2); + mpmod(v2, rsa->q, v2); + mpmul(v2, rsa->p, out); + mpadd(v1, out, out); + + mpfree(v1); + mpfree(v2); + + return out; +} diff --git a/libsec/rsaencrypt.c b/libsec/rsaencrypt.c new file mode 100644 index 0000000..ade686d --- /dev/null +++ b/libsec/rsaencrypt.c @@ -0,0 +1,12 @@ +#include "os.h" +#include +#include + +mpint* +rsaencrypt(RSApub *rsa, mpint *in, mpint *out) +{ + if(out == nil) + out = mpnew(0); + mpexp(in, rsa->ek, rsa->n, out); + return out; +} diff --git a/libsec/rsafill.c b/libsec/rsafill.c new file mode 100644 index 0000000..f514b07 --- /dev/null +++ b/libsec/rsafill.c @@ -0,0 +1,61 @@ +#include "os.h" +#include +#include + +RSApriv* +rsafill(mpint *n, mpint *e, mpint *d, mpint *p, mpint *q) +{ + mpint *c2, *kq, *kp, *x; + RSApriv *rsa; + + // make sure we're not being hoodwinked + if(!probably_prime(p, 10) || !probably_prime(q, 10)){ + werrstr("rsafill: p or q not prime"); + return nil; + } + x = mpnew(0); + mpmul(p, q, x); + if(mpcmp(n, x) != 0){ + werrstr("rsafill: n != p*q"); + mpfree(x); + return nil; + } + c2 = mpnew(0); + mpsub(p, mpone, c2); + mpsub(q, mpone, x); + mpmul(c2, x, x); + mpmul(e, d, c2); + mpmod(c2, x, x); + if(mpcmp(x, mpone) != 0){ + werrstr("rsafill: e*d != 1 mod (p-1)*(q-1)"); + mpfree(x); + mpfree(c2); + return nil; + } + + // compute chinese remainder coefficient + mpinvert(p, q, c2); + + // for crt a**k mod p == (a**(k mod p-1)) mod p + kq = mpnew(0); + kp = mpnew(0); + mpsub(p, mpone, x); + mpmod(d, x, kp); + mpsub(q, mpone, x); + mpmod(d, x, kq); + + rsa = rsaprivalloc(); + rsa->pub.ek = mpcopy(e); + rsa->pub.n = mpcopy(n); + rsa->dk = mpcopy(d); + rsa->kp = kp; + rsa->kq = kq; + rsa->p = mpcopy(p); + rsa->q = mpcopy(q); + rsa->c2 = c2; + + mpfree(x); + + return rsa; +} + diff --git a/libsec/rsagen.c b/libsec/rsagen.c new file mode 100644 index 0000000..bdfc37f --- /dev/null +++ b/libsec/rsagen.c @@ -0,0 +1,82 @@ +#include "os.h" +#include +#include + +static void +genrand(mpint *p, int n) +{ + mpdigit x; + + // generate n random bits with high set + mpbits(p, n); + genrandom((uchar*)p->p, (n+7)/8); + p->top = (n+Dbits-1)/Dbits; + x = 1; + x <<= ((n-1)%Dbits); + p->p[p->top-1] &= (x-1); + p->p[p->top-1] |= x; +} + +RSApriv* +rsagen(int nlen, int elen, int rounds) +{ + mpint *p, *q, *e, *d, *phi, *n, *t1, *t2, *kp, *kq, *c2; + RSApriv *rsa; + + p = mpnew(nlen/2); + q = mpnew(nlen/2); + n = mpnew(nlen); + e = mpnew(elen); + d = mpnew(0); + phi = mpnew(nlen); + + // create the prime factors and euclid's function + genstrongprime(p, nlen/2, rounds); + genstrongprime(q, nlen - mpsignif(p) + 1, rounds); + mpmul(p, q, n); + mpsub(p, mpone, e); + mpsub(q, mpone, d); + mpmul(e, d, phi); + + // find an e relatively prime to phi + t1 = mpnew(0); + t2 = mpnew(0); + genrand(e, elen); + for(;;){ + mpextendedgcd(e, phi, d, t1, t2); + if(mpcmp(d, mpone) == 0) + break; + mpadd(mpone, e, e); + } + mpfree(t1); + mpfree(t2); + + // d = e**-1 mod phi + mpinvert(e, phi, d); + + // compute chinese remainder coefficient + c2 = mpnew(0); + mpinvert(p, q, c2); + + // for crt a**k mod p == (a**(k mod p-1)) mod p + kq = mpnew(0); + kp = mpnew(0); + mpsub(p, mpone, phi); + mpmod(d, phi, kp); + mpsub(q, mpone, phi); + mpmod(d, phi, kq); + + rsa = rsaprivalloc(); + rsa->pub.ek = e; + rsa->pub.n = n; + rsa->dk = d; + rsa->kp = kp; + rsa->kq = kq; + rsa->p = p; + rsa->q = q; + rsa->c2 = c2; + + mpfree(phi); + + return rsa; +} diff --git a/libsec/rsaprivtopub.c b/libsec/rsaprivtopub.c new file mode 100644 index 0000000..d464c87 --- /dev/null +++ b/libsec/rsaprivtopub.c @@ -0,0 +1,16 @@ +#include "os.h" +#include +#include + +RSApub* +rsaprivtopub(RSApriv *priv) +{ + RSApub *pub; + + pub = rsapuballoc(); + if(pub == nil) + return nil; + pub->n = mpcopy(priv->pub.n); + pub->ek = mpcopy(priv->pub.ek); + return pub; +} diff --git a/libsec/rsatest.c b/libsec/rsatest.c new file mode 100644 index 0000000..9ff66b5 --- /dev/null +++ b/libsec/rsatest.c @@ -0,0 +1,57 @@ +#include "os.h" +#include +#include +#include + +void +main(void) +{ + RSApriv *rsa; + Biobuf b; + char *p; + int n; + mpint *clr, *enc, *clr2; + uchar buf[4096]; + uchar *e; + vlong start; + + fmtinstall('B', mpconv); + + rsa = rsagen(1024, 16, 0); + if(rsa == nil) + sysfatal("rsagen"); + Binit(&b, 0, OREAD); + clr = mpnew(0); + clr2 = mpnew(0); + enc = mpnew(0); + + strtomp("123456789abcdef123456789abcdef123456789abcdef123456789abcdef", nil, 16, clr); + rsaencrypt(&rsa->pub, clr, enc); + + start = nsec(); + for(n = 0; n < 10; n++) + rsadecrypt(rsa, enc, clr); + print("%lld\n", nsec()-start); + + start = nsec(); + for(n = 0; n < 10; n++) + mpexp(enc, rsa->dk, rsa->pub.n, clr2); + print("%lld\n", nsec()-start); + + if(mpcmp(clr, clr2) != 0) + print("%B != %B\n", clr, clr2); + + print("> "); + while(p = Brdline(&b, '\n')){ + n = Blinelen(&b); + letomp((uchar*)p, n, clr); + print("clr %B\n", clr); + rsaencrypt(&rsa->pub, clr, enc); + print("enc %B\n", enc); + rsadecrypt(rsa, enc, clr); + print("clr %B\n", clr); + n = mptole(clr, buf, sizeof(buf), nil); + write(1, buf, n); + print("> "); + } +} diff --git a/libsec/sha1.c b/libsec/sha1.c new file mode 100644 index 0000000..946f028 --- /dev/null +++ b/libsec/sha1.c @@ -0,0 +1,127 @@ +#include "os.h" +#include + +static void encode(uchar*, u32int*, ulong); + +extern void _sha1block(uchar*, ulong, u32int*); + +/* + * we require len to be a multiple of 64 for all but + * the last call. There must be room in the input buffer + * to pad. + */ +SHA1state* +sha1(uchar *p, ulong len, uchar *digest, SHA1state *s) +{ + uchar buf[128]; + u32int x[16]; + int i; + uchar *e; + + if(s == nil){ + s = malloc(sizeof(*s)); + if(s == nil) + return nil; + memset(s, 0, sizeof(*s)); + s->malloced = 1; + } + + if(s->seeded == 0){ + /* seed the state, these constants would look nicer big-endian */ + s->state[0] = 0x67452301; + s->state[1] = 0xefcdab89; + s->state[2] = 0x98badcfe; + s->state[3] = 0x10325476; + s->state[4] = 0xc3d2e1f0; + s->seeded = 1; + } + + /* fill out the partial 64 byte block from previous calls */ + if(s->blen){ + i = 64 - s->blen; + if(len < i) + i = len; + memmove(s->buf + s->blen, p, i); + len -= i; + s->blen += i; + p += i; + if(s->blen == 64){ + _sha1block(s->buf, s->blen, s->state); + s->len += s->blen; + s->blen = 0; + } + } + + /* do 64 byte blocks */ + i = len & ~0x3f; + if(i){ + _sha1block(p, i, s->state); + s->len += i; + len -= i; + p += i; + } + + /* save the left overs if not last call */ + if(digest == 0){ + if(len){ + memmove(s->buf, p, len); + s->blen += len; + } + return s; + } + + /* + * this is the last time through, pad what's left with 0x80, + * 0's, and the input count to create a multiple of 64 bytes + */ + if(s->blen){ + p = s->buf; + len = s->blen; + } else { + memmove(buf, p, len); + p = buf; + } + s->len += len; + e = p + len; + if(len < 56) + i = 56 - len; + else + i = 120 - len; + memset(e, 0, i); + *e = 0x80; + len += i; + + /* append the count */ + x[0] = s->len>>29; + x[1] = s->len<<3; + encode(p+len, x, 8); + + /* digest the last part */ + _sha1block(p, len+8, s->state); + s->len += len+8; + + /* return result and free state */ + encode(digest, s->state, SHA1dlen); + if(s->malloced == 1) + free(s); + return nil; +} + +/* + * encodes input (ulong) into output (uchar). Assumes len is + * a multiple of 4. + */ +static void +encode(uchar *output, u32int *input, ulong len) +{ + u32int x; + uchar *e; + + for(e = output + len; output < e;) { + x = *input++; + *output++ = x >> 24; + *output++ = x >> 16; + *output++ = x >> 8; + *output++ = x; + } +} diff --git a/libsec/sha1block.c b/libsec/sha1block.c new file mode 100644 index 0000000..82566f2 --- /dev/null +++ b/libsec/sha1block.c @@ -0,0 +1,187 @@ +#include "os.h" + +void +_sha1block(uchar *p, ulong len, u32int *s) +{ + u32int a, b, c, d, e, x; + uchar *end; + u32int *wp, *wend; + u32int w[80]; + + /* at this point, we have a multiple of 64 bytes */ + for(end = p+len; p < end;){ + a = s[0]; + b = s[1]; + c = s[2]; + d = s[3]; + e = s[4]; + + wend = w + 15; + for(wp = w; wp < wend; wp += 5){ + wp[0] = (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; + e += ((a<<5) | (a>>27)) + wp[0]; + e += 0x5a827999 + (((c^d)&b)^d); + b = (b<<30)|(b>>2); + + wp[1] = (p[4]<<24) | (p[5]<<16) | (p[6]<<8) | p[7]; + d += ((e<<5) | (e>>27)) + wp[1]; + d += 0x5a827999 + (((b^c)&a)^c); + a = (a<<30)|(a>>2); + + wp[2] = (p[8]<<24) | (p[9]<<16) | (p[10]<<8) | p[11]; + c += ((d<<5) | (d>>27)) + wp[2]; + c += 0x5a827999 + (((a^b)&e)^b); + e = (e<<30)|(e>>2); + + wp[3] = (p[12]<<24) | (p[13]<<16) | (p[14]<<8) | p[15]; + b += ((c<<5) | (c>>27)) + wp[3]; + b += 0x5a827999 + (((e^a)&d)^a); + d = (d<<30)|(d>>2); + + wp[4] = (p[16]<<24) | (p[17]<<16) | (p[18]<<8) | p[19]; + a += ((b<<5) | (b>>27)) + wp[4]; + a += 0x5a827999 + (((d^e)&c)^e); + c = (c<<30)|(c>>2); + + p += 20; + } + + wp[0] = (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; + e += ((a<<5) | (a>>27)) + wp[0]; + e += 0x5a827999 + (((c^d)&b)^d); + b = (b<<30)|(b>>2); + + x = wp[-2] ^ wp[-7] ^ wp[-13] ^ wp[-15]; + wp[1] = (x<<1) | (x>>31); + d += ((e<<5) | (e>>27)) + wp[1]; + d += 0x5a827999 + (((b^c)&a)^c); + a = (a<<30)|(a>>2); + + x = wp[-1] ^ wp[-6] ^ wp[-12] ^ wp[-14]; + wp[2] = (x<<1) | (x>>31); + c += ((d<<5) | (d>>27)) + wp[2]; + c += 0x5a827999 + (((a^b)&e)^b); + e = (e<<30)|(e>>2); + + x = wp[0] ^ wp[-5] ^ wp[-11] ^ wp[-13]; + wp[3] = (x<<1) | (x>>31); + b += ((c<<5) | (c>>27)) + wp[3]; + b += 0x5a827999 + (((e^a)&d)^a); + d = (d<<30)|(d>>2); + + x = wp[1] ^ wp[-4] ^ wp[-10] ^ wp[-12]; + wp[4] = (x<<1) | (x>>31); + a += ((b<<5) | (b>>27)) + wp[4]; + a += 0x5a827999 + (((d^e)&c)^e); + c = (c<<30)|(c>>2); + + wp += 5; + p += 4; + + wend = w + 40; + for(; wp < wend; wp += 5){ + x = wp[-3] ^ wp[-8] ^ wp[-14] ^ wp[-16]; + wp[0] = (x<<1) | (x>>31); + e += ((a<<5) | (a>>27)) + wp[0]; + e += 0x6ed9eba1 + (b^c^d); + b = (b<<30)|(b>>2); + + x = wp[-2] ^ wp[-7] ^ wp[-13] ^ wp[-15]; + wp[1] = (x<<1) | (x>>31); + d += ((e<<5) | (e>>27)) + wp[1]; + d += 0x6ed9eba1 + (a^b^c); + a = (a<<30)|(a>>2); + + x = wp[-1] ^ wp[-6] ^ wp[-12] ^ wp[-14]; + wp[2] = (x<<1) | (x>>31); + c += ((d<<5) | (d>>27)) + wp[2]; + c += 0x6ed9eba1 + (e^a^b); + e = (e<<30)|(e>>2); + + x = wp[0] ^ wp[-5] ^ wp[-11] ^ wp[-13]; + wp[3] = (x<<1) | (x>>31); + b += ((c<<5) | (c>>27)) + wp[3]; + b += 0x6ed9eba1 + (d^e^a); + d = (d<<30)|(d>>2); + + x = wp[1] ^ wp[-4] ^ wp[-10] ^ wp[-12]; + wp[4] = (x<<1) | (x>>31); + a += ((b<<5) | (b>>27)) + wp[4]; + a += 0x6ed9eba1 + (c^d^e); + c = (c<<30)|(c>>2); + } + + wend = w + 60; + for(; wp < wend; wp += 5){ + x = wp[-3] ^ wp[-8] ^ wp[-14] ^ wp[-16]; + wp[0] = (x<<1) | (x>>31); + e += ((a<<5) | (a>>27)) + wp[0]; + e += 0x8f1bbcdc + ((b&c)|((b|c)&d)); + b = (b<<30)|(b>>2); + + x = wp[-2] ^ wp[-7] ^ wp[-13] ^ wp[-15]; + wp[1] = (x<<1) | (x>>31); + d += ((e<<5) | (e>>27)) + wp[1]; + d += 0x8f1bbcdc + ((a&b)|((a|b)&c)); + a = (a<<30)|(a>>2); + + x = wp[-1] ^ wp[-6] ^ wp[-12] ^ wp[-14]; + wp[2] = (x<<1) | (x>>31); + c += ((d<<5) | (d>>27)) + wp[2]; + c += 0x8f1bbcdc + ((e&a)|((e|a)&b)); + e = (e<<30)|(e>>2); + + x = wp[0] ^ wp[-5] ^ wp[-11] ^ wp[-13]; + wp[3] = (x<<1) | (x>>31); + b += ((c<<5) | (c>>27)) + wp[3]; + b += 0x8f1bbcdc + ((d&e)|((d|e)&a)); + d = (d<<30)|(d>>2); + + x = wp[1] ^ wp[-4] ^ wp[-10] ^ wp[-12]; + wp[4] = (x<<1) | (x>>31); + a += ((b<<5) | (b>>27)) + wp[4]; + a += 0x8f1bbcdc + ((c&d)|((c|d)&e)); + c = (c<<30)|(c>>2); + } + + wend = w + 80; + for(; wp < wend; wp += 5){ + x = wp[-3] ^ wp[-8] ^ wp[-14] ^ wp[-16]; + wp[0] = (x<<1) | (x>>31); + e += ((a<<5) | (a>>27)) + wp[0]; + e += 0xca62c1d6 + (b^c^d); + b = (b<<30)|(b>>2); + + x = wp[-2] ^ wp[-7] ^ wp[-13] ^ wp[-15]; + wp[1] = (x<<1) | (x>>31); + d += ((e<<5) | (e>>27)) + wp[1]; + d += 0xca62c1d6 + (a^b^c); + a = (a<<30)|(a>>2); + + x = wp[-1] ^ wp[-6] ^ wp[-12] ^ wp[-14]; + wp[2] = (x<<1) | (x>>31); + c += ((d<<5) | (d>>27)) + wp[2]; + c += 0xca62c1d6 + (e^a^b); + e = (e<<30)|(e>>2); + + x = wp[0] ^ wp[-5] ^ wp[-11] ^ wp[-13]; + wp[3] = (x<<1) | (x>>31); + b += ((c<<5) | (c>>27)) + wp[3]; + b += 0xca62c1d6 + (d^e^a); + d = (d<<30)|(d>>2); + + x = wp[1] ^ wp[-4] ^ wp[-10] ^ wp[-12]; + wp[4] = (x<<1) | (x>>31); + a += ((b<<5) | (b>>27)) + wp[4]; + a += 0xca62c1d6 + (c^d^e); + c = (c<<30)|(c>>2); + } + + /* save state */ + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + s[4] += e; + } +} diff --git a/libsec/sha1pickle.c b/libsec/sha1pickle.c new file mode 100644 index 0000000..6139567 --- /dev/null +++ b/libsec/sha1pickle.c @@ -0,0 +1,38 @@ +#include "os.h" +#include + +char* +sha1pickle(SHA1state *s) +{ + char *p; + int m, n; + + m = 5*9+4*((s->blen+3)/3); + p = malloc(m); + if(p == nil) + return p; + n = sprint(p, "%8.8ux %8.8ux %8.8ux %8.8ux %8.8ux ", + s->state[0], s->state[1], s->state[2], + s->state[3], s->state[4]); + enc64(p+n, m-n, s->buf, s->blen); + return p; +} + +SHA1state* +sha1unpickle(char *p) +{ + SHA1state *s; + + s = malloc(sizeof(*s)); + if(s == nil) + return nil; + s->state[0] = strtoul(p, &p, 16); + s->state[1] = strtoul(p, &p, 16); + s->state[2] = strtoul(p, &p, 16); + s->state[3] = strtoul(p, &p, 16); + s->state[4] = strtoul(p, &p, 16); + s->blen = dec64(s->buf, sizeof(s->buf), p, strlen(p)); + s->malloced = 1; + s->seeded = 1; + return s; +} diff --git a/libsec/smallprimes.c b/libsec/smallprimes.c new file mode 100644 index 0000000..ac23a2f --- /dev/null +++ b/libsec/smallprimes.c @@ -0,0 +1,1004 @@ +#include "os.h" + +ulong smallprimes[1000] = { + 2, + 3, + 5, + 7, + 11, + 13, + 17, + 19, + 23, + 29, + 31, + 37, + 41, + 43, + 47, + 53, + 59, + 61, + 67, + 71, + 73, + 79, + 83, + 89, + 97, + 101, + 103, + 107, + 109, + 113, + 127, + 131, + 137, + 139, + 149, + 151, + 157, + 163, + 167, + 173, + 179, + 181, + 191, + 193, + 197, + 199, + 211, + 223, + 227, + 229, + 233, + 239, + 241, + 251, + 257, + 263, + 269, + 271, + 277, + 281, + 283, + 293, + 307, + 311, + 313, + 317, + 331, + 337, + 347, + 349, + 353, + 359, + 367, + 373, + 379, + 383, + 389, + 397, + 401, + 409, + 419, + 421, + 431, + 433, + 439, + 443, + 449, + 457, + 461, + 463, + 467, + 479, + 487, + 491, + 499, + 503, + 509, + 521, + 523, + 541, + 547, + 557, + 563, + 569, + 571, + 577, + 587, + 593, + 599, + 601, + 607, + 613, + 617, + 619, + 631, + 641, + 643, + 647, + 653, + 659, + 661, + 673, + 677, + 683, + 691, + 701, + 709, + 719, + 727, + 733, + 739, + 743, + 751, + 757, + 761, + 769, + 773, + 787, + 797, + 809, + 811, + 821, + 823, + 827, + 829, + 839, + 853, + 857, + 859, + 863, + 877, + 881, + 883, + 887, + 907, + 911, + 919, + 929, + 937, + 941, + 947, + 953, + 967, + 971, + 977, + 983, + 991, + 997, + 1009, + 1013, + 1019, + 1021, + 1031, + 1033, + 1039, + 1049, + 1051, + 1061, + 1063, + 1069, + 1087, + 1091, + 1093, + 1097, + 1103, + 1109, + 1117, + 1123, + 1129, + 1151, + 1153, + 1163, + 1171, + 1181, + 1187, + 1193, + 1201, + 1213, + 1217, + 1223, + 1229, + 1231, + 1237, + 1249, + 1259, + 1277, + 1279, + 1283, + 1289, + 1291, + 1297, + 1301, + 1303, + 1307, + 1319, + 1321, + 1327, + 1361, + 1367, + 1373, + 1381, + 1399, + 1409, + 1423, + 1427, + 1429, + 1433, + 1439, + 1447, + 1451, + 1453, + 1459, + 1471, + 1481, + 1483, + 1487, + 1489, + 1493, + 1499, + 1511, + 1523, + 1531, + 1543, + 1549, + 1553, + 1559, + 1567, + 1571, + 1579, + 1583, + 1597, + 1601, + 1607, + 1609, + 1613, + 1619, + 1621, + 1627, + 1637, + 1657, + 1663, + 1667, + 1669, + 1693, + 1697, + 1699, + 1709, + 1721, + 1723, + 1733, + 1741, + 1747, + 1753, + 1759, + 1777, + 1783, + 1787, + 1789, + 1801, + 1811, + 1823, + 1831, + 1847, + 1861, + 1867, + 1871, + 1873, + 1877, + 1879, + 1889, + 1901, + 1907, + 1913, + 1931, + 1933, + 1949, + 1951, + 1973, + 1979, + 1987, + 1993, + 1997, + 1999, + 2003, + 2011, + 2017, + 2027, + 2029, + 2039, + 2053, + 2063, + 2069, + 2081, + 2083, + 2087, + 2089, + 2099, + 2111, + 2113, + 2129, + 2131, + 2137, + 2141, + 2143, + 2153, + 2161, + 2179, + 2203, + 2207, + 2213, + 2221, + 2237, + 2239, + 2243, + 2251, + 2267, + 2269, + 2273, + 2281, + 2287, + 2293, + 2297, + 2309, + 2311, + 2333, + 2339, + 2341, + 2347, + 2351, + 2357, + 2371, + 2377, + 2381, + 2383, + 2389, + 2393, + 2399, + 2411, + 2417, + 2423, + 2437, + 2441, + 2447, + 2459, + 2467, + 2473, + 2477, + 2503, + 2521, + 2531, + 2539, + 2543, + 2549, + 2551, + 2557, + 2579, + 2591, + 2593, + 2609, + 2617, + 2621, + 2633, + 2647, + 2657, + 2659, + 2663, + 2671, + 2677, + 2683, + 2687, + 2689, + 2693, + 2699, + 2707, + 2711, + 2713, + 2719, + 2729, + 2731, + 2741, + 2749, + 2753, + 2767, + 2777, + 2789, + 2791, + 2797, + 2801, + 2803, + 2819, + 2833, + 2837, + 2843, + 2851, + 2857, + 2861, + 2879, + 2887, + 2897, + 2903, + 2909, + 2917, + 2927, + 2939, + 2953, + 2957, + 2963, + 2969, + 2971, + 2999, + 3001, + 3011, + 3019, + 3023, + 3037, + 3041, + 3049, + 3061, + 3067, + 3079, + 3083, + 3089, + 3109, + 3119, + 3121, + 3137, + 3163, + 3167, + 3169, + 3181, + 3187, + 3191, + 3203, + 3209, + 3217, + 3221, + 3229, + 3251, + 3253, + 3257, + 3259, + 3271, + 3299, + 3301, + 3307, + 3313, + 3319, + 3323, + 3329, + 3331, + 3343, + 3347, + 3359, + 3361, + 3371, + 3373, + 3389, + 3391, + 3407, + 3413, + 3433, + 3449, + 3457, + 3461, + 3463, + 3467, + 3469, + 3491, + 3499, + 3511, + 3517, + 3527, + 3529, + 3533, + 3539, + 3541, + 3547, + 3557, + 3559, + 3571, + 3581, + 3583, + 3593, + 3607, + 3613, + 3617, + 3623, + 3631, + 3637, + 3643, + 3659, + 3671, + 3673, + 3677, + 3691, + 3697, + 3701, + 3709, + 3719, + 3727, + 3733, + 3739, + 3761, + 3767, + 3769, + 3779, + 3793, + 3797, + 3803, + 3821, + 3823, + 3833, + 3847, + 3851, + 3853, + 3863, + 3877, + 3881, + 3889, + 3907, + 3911, + 3917, + 3919, + 3923, + 3929, + 3931, + 3943, + 3947, + 3967, + 3989, + 4001, + 4003, + 4007, + 4013, + 4019, + 4021, + 4027, + 4049, + 4051, + 4057, + 4073, + 4079, + 4091, + 4093, + 4099, + 4111, + 4127, + 4129, + 4133, + 4139, + 4153, + 4157, + 4159, + 4177, + 4201, + 4211, + 4217, + 4219, + 4229, + 4231, + 4241, + 4243, + 4253, + 4259, + 4261, + 4271, + 4273, + 4283, + 4289, + 4297, + 4327, + 4337, + 4339, + 4349, + 4357, + 4363, + 4373, + 4391, + 4397, + 4409, + 4421, + 4423, + 4441, + 4447, + 4451, + 4457, + 4463, + 4481, + 4483, + 4493, + 4507, + 4513, + 4517, + 4519, + 4523, + 4547, + 4549, + 4561, + 4567, + 4583, + 4591, + 4597, + 4603, + 4621, + 4637, + 4639, + 4643, + 4649, + 4651, + 4657, + 4663, + 4673, + 4679, + 4691, + 4703, + 4721, + 4723, + 4729, + 4733, + 4751, + 4759, + 4783, + 4787, + 4789, + 4793, + 4799, + 4801, + 4813, + 4817, + 4831, + 4861, + 4871, + 4877, + 4889, + 4903, + 4909, + 4919, + 4931, + 4933, + 4937, + 4943, + 4951, + 4957, + 4967, + 4969, + 4973, + 4987, + 4993, + 4999, + 5003, + 5009, + 5011, + 5021, + 5023, + 5039, + 5051, + 5059, + 5077, + 5081, + 5087, + 5099, + 5101, + 5107, + 5113, + 5119, + 5147, + 5153, + 5167, + 5171, + 5179, + 5189, + 5197, + 5209, + 5227, + 5231, + 5233, + 5237, + 5261, + 5273, + 5279, + 5281, + 5297, + 5303, + 5309, + 5323, + 5333, + 5347, + 5351, + 5381, + 5387, + 5393, + 5399, + 5407, + 5413, + 5417, + 5419, + 5431, + 5437, + 5441, + 5443, + 5449, + 5471, + 5477, + 5479, + 5483, + 5501, + 5503, + 5507, + 5519, + 5521, + 5527, + 5531, + 5557, + 5563, + 5569, + 5573, + 5581, + 5591, + 5623, + 5639, + 5641, + 5647, + 5651, + 5653, + 5657, + 5659, + 5669, + 5683, + 5689, + 5693, + 5701, + 5711, + 5717, + 5737, + 5741, + 5743, + 5749, + 5779, + 5783, + 5791, + 5801, + 5807, + 5813, + 5821, + 5827, + 5839, + 5843, + 5849, + 5851, + 5857, + 5861, + 5867, + 5869, + 5879, + 5881, + 5897, + 5903, + 5923, + 5927, + 5939, + 5953, + 5981, + 5987, + 6007, + 6011, + 6029, + 6037, + 6043, + 6047, + 6053, + 6067, + 6073, + 6079, + 6089, + 6091, + 6101, + 6113, + 6121, + 6131, + 6133, + 6143, + 6151, + 6163, + 6173, + 6197, + 6199, + 6203, + 6211, + 6217, + 6221, + 6229, + 6247, + 6257, + 6263, + 6269, + 6271, + 6277, + 6287, + 6299, + 6301, + 6311, + 6317, + 6323, + 6329, + 6337, + 6343, + 6353, + 6359, + 6361, + 6367, + 6373, + 6379, + 6389, + 6397, + 6421, + 6427, + 6449, + 6451, + 6469, + 6473, + 6481, + 6491, + 6521, + 6529, + 6547, + 6551, + 6553, + 6563, + 6569, + 6571, + 6577, + 6581, + 6599, + 6607, + 6619, + 6637, + 6653, + 6659, + 6661, + 6673, + 6679, + 6689, + 6691, + 6701, + 6703, + 6709, + 6719, + 6733, + 6737, + 6761, + 6763, + 6779, + 6781, + 6791, + 6793, + 6803, + 6823, + 6827, + 6829, + 6833, + 6841, + 6857, + 6863, + 6869, + 6871, + 6883, + 6899, + 6907, + 6911, + 6917, + 6947, + 6949, + 6959, + 6961, + 6967, + 6971, + 6977, + 6983, + 6991, + 6997, + 7001, + 7013, + 7019, + 7027, + 7039, + 7043, + 7057, + 7069, + 7079, + 7103, + 7109, + 7121, + 7127, + 7129, + 7151, + 7159, + 7177, + 7187, + 7193, + 7207, + 7211, + 7213, + 7219, + 7229, + 7237, + 7243, + 7247, + 7253, + 7283, + 7297, + 7307, + 7309, + 7321, + 7331, + 7333, + 7349, + 7351, + 7369, + 7393, + 7411, + 7417, + 7433, + 7451, + 7457, + 7459, + 7477, + 7481, + 7487, + 7489, + 7499, + 7507, + 7517, + 7523, + 7529, + 7537, + 7541, + 7547, + 7549, + 7559, + 7561, + 7573, + 7577, + 7583, + 7589, + 7591, + 7603, + 7607, + 7621, + 7639, + 7643, + 7649, + 7669, + 7673, + 7681, + 7687, + 7691, + 7699, + 7703, + 7717, + 7723, + 7727, + 7741, + 7753, + 7757, + 7759, + 7789, + 7793, + 7817, + 7823, + 7829, + 7841, + 7853, + 7867, + 7873, + 7877, + 7879, + 7883, + 7901, + 7907, + 7919, +}; diff --git a/libsec/smallprimetest.c b/libsec/smallprimetest.c new file mode 100644 index 0000000..cf94dac --- /dev/null +++ b/libsec/smallprimetest.c @@ -0,0 +1,1039 @@ +#include "os.h" +#include +#include + +static ulong smallprimes[] = { + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, + 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, + 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, + 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, + 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, + 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, + 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, + 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, + 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, + 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, + 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, + 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, + 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, + 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, + 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, + 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, + 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, + 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, + 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, + 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, + 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, + 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, + 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, + 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, + 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, + 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, + 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, + 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, + 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, + 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, + 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, + 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, + 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, + 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, + 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, + 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, + 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, + 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, + 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, + 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, + 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, + 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, + 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, + 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, + 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, + 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, + 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, + 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, + 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, + 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, + 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, + 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, + 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, + 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, + 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, + 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, + 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, + 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231, + 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, + 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, + 4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, + 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, + 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, + 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, + 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, + 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, + 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, + 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, + 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, + 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, + 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, + 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, + 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, + 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, + 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, + 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, + 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, + 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, + 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, + 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, + 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, + 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, + 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, + 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, + 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, + 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673, + 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761, + 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, + 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, + 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, + 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, + 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, + 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, + 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411, + 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, + 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, + 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643, + 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, + 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, + 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919, + 7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 8011, 8017, + 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111, + 8117, 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209, 8219, + 8221, 8231, 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291, + 8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 8377, 8387, + 8389, 8419, 8423, 8429, 8431, 8443, 8447, 8461, 8467, 8501, + 8513, 8521, 8527, 8537, 8539, 8543, 8563, 8573, 8581, 8597, + 8599, 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677, + 8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741, + 8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819, 8821, 8831, + 8837, 8839, 8849, 8861, 8863, 8867, 8887, 8893, 8923, 8929, + 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011, + 9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109, + 9127, 9133, 9137, 9151, 9157, 9161, 9173, 9181, 9187, 9199, + 9203, 9209, 9221, 9227, 9239, 9241, 9257, 9277, 9281, 9283, + 9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377, + 9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 9437, 9439, + 9461, 9463, 9467, 9473, 9479, 9491, 9497, 9511, 9521, 9533, + 9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629, 9631, + 9643, 9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733, + 9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 9803, 9811, + 9817, 9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887, + 9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973, 10007, + 10009, 10037, 10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099, + 10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163, 10169, 10177, + 10181, 10193, 10211, 10223, 10243, 10247, 10253, 10259, 10267, 10271, + 10273, 10289, 10301, 10303, 10313, 10321, 10331, 10333, 10337, 10343, + 10357, 10369, 10391, 10399, 10427, 10429, 10433, 10453, 10457, 10459, + 10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531, 10559, 10567, + 10589, 10597, 10601, 10607, 10613, 10627, 10631, 10639, 10651, 10657, + 10663, 10667, 10687, 10691, 10709, 10711, 10723, 10729, 10733, 10739, + 10753, 10771, 10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859, + 10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937, 10939, 10949, + 10957, 10973, 10979, 10987, 10993, 11003, 11027, 11047, 11057, 11059, + 11069, 11071, 11083, 11087, 11093, 11113, 11117, 11119, 11131, 11149, + 11159, 11161, 11171, 11173, 11177, 11197, 11213, 11239, 11243, 11251, + 11257, 11261, 11273, 11279, 11287, 11299, 11311, 11317, 11321, 11329, + 11351, 11353, 11369, 11383, 11393, 11399, 11411, 11423, 11437, 11443, + 11447, 11467, 11471, 11483, 11489, 11491, 11497, 11503, 11519, 11527, + 11549, 11551, 11579, 11587, 11593, 11597, 11617, 11621, 11633, 11657, + 11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743, 11777, + 11779, 11783, 11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833, + 11839, 11863, 11867, 11887, 11897, 11903, 11909, 11923, 11927, 11933, + 11939, 11941, 11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011, + 12037, 12041, 12043, 12049, 12071, 12073, 12097, 12101, 12107, 12109, + 12113, 12119, 12143, 12149, 12157, 12161, 12163, 12197, 12203, 12211, + 12227, 12239, 12241, 12251, 12253, 12263, 12269, 12277, 12281, 12289, + 12301, 12323, 12329, 12343, 12347, 12373, 12377, 12379, 12391, 12401, + 12409, 12413, 12421, 12433, 12437, 12451, 12457, 12473, 12479, 12487, + 12491, 12497, 12503, 12511, 12517, 12527, 12539, 12541, 12547, 12553, + 12569, 12577, 12583, 12589, 12601, 12611, 12613, 12619, 12637, 12641, + 12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713, 12721, 12739, + 12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829, + 12841, 12853, 12889, 12893, 12899, 12907, 12911, 12917, 12919, 12923, + 12941, 12953, 12959, 12967, 12973, 12979, 12983, 13001, 13003, 13007, + 13009, 13033, 13037, 13043, 13049, 13063, 13093, 13099, 13103, 13109, + 13121, 13127, 13147, 13151, 13159, 13163, 13171, 13177, 13183, 13187, + 13217, 13219, 13229, 13241, 13249, 13259, 13267, 13291, 13297, 13309, + 13313, 13327, 13331, 13337, 13339, 13367, 13381, 13397, 13399, 13411, + 13417, 13421, 13441, 13451, 13457, 13463, 13469, 13477, 13487, 13499, + 13513, 13523, 13537, 13553, 13567, 13577, 13591, 13597, 13613, 13619, + 13627, 13633, 13649, 13669, 13679, 13681, 13687, 13691, 13693, 13697, + 13709, 13711, 13721, 13723, 13729, 13751, 13757, 13759, 13763, 13781, + 13789, 13799, 13807, 13829, 13831, 13841, 13859, 13873, 13877, 13879, + 13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 13963, 13967, + 13997, 13999, 14009, 14011, 14029, 14033, 14051, 14057, 14071, 14081, + 14083, 14087, 14107, 14143, 14149, 14153, 14159, 14173, 14177, 14197, + 14207, 14221, 14243, 14249, 14251, 14281, 14293, 14303, 14321, 14323, + 14327, 14341, 14347, 14369, 14387, 14389, 14401, 14407, 14411, 14419, + 14423, 14431, 14437, 14447, 14449, 14461, 14479, 14489, 14503, 14519, + 14533, 14537, 14543, 14549, 14551, 14557, 14561, 14563, 14591, 14593, + 14621, 14627, 14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699, + 14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753, 14759, 14767, + 14771, 14779, 14783, 14797, 14813, 14821, 14827, 14831, 14843, 14851, + 14867, 14869, 14879, 14887, 14891, 14897, 14923, 14929, 14939, 14947, + 14951, 14957, 14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073, + 15077, 15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149, + 15161, 15173, 15187, 15193, 15199, 15217, 15227, 15233, 15241, 15259, + 15263, 15269, 15271, 15277, 15287, 15289, 15299, 15307, 15313, 15319, + 15329, 15331, 15349, 15359, 15361, 15373, 15377, 15383, 15391, 15401, + 15413, 15427, 15439, 15443, 15451, 15461, 15467, 15473, 15493, 15497, + 15511, 15527, 15541, 15551, 15559, 15569, 15581, 15583, 15601, 15607, + 15619, 15629, 15641, 15643, 15647, 15649, 15661, 15667, 15671, 15679, + 15683, 15727, 15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773, + 15787, 15791, 15797, 15803, 15809, 15817, 15823, 15859, 15877, 15881, + 15887, 15889, 15901, 15907, 15913, 15919, 15923, 15937, 15959, 15971, + 15973, 15991, 16001, 16007, 16033, 16057, 16061, 16063, 16067, 16069, + 16073, 16087, 16091, 16097, 16103, 16111, 16127, 16139, 16141, 16183, + 16187, 16189, 16193, 16217, 16223, 16229, 16231, 16249, 16253, 16267, + 16273, 16301, 16319, 16333, 16339, 16349, 16361, 16363, 16369, 16381, + 16411, 16417, 16421, 16427, 16433, 16447, 16451, 16453, 16477, 16481, + 16487, 16493, 16519, 16529, 16547, 16553, 16561, 16567, 16573, 16603, + 16607, 16619, 16631, 16633, 16649, 16651, 16657, 16661, 16673, 16691, + 16693, 16699, 16703, 16729, 16741, 16747, 16759, 16763, 16787, 16811, + 16823, 16829, 16831, 16843, 16871, 16879, 16883, 16889, 16901, 16903, + 16921, 16927, 16931, 16937, 16943, 16963, 16979, 16981, 16987, 16993, + 17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, 17077, 17093, + 17099, 17107, 17117, 17123, 17137, 17159, 17167, 17183, 17189, 17191, + 17203, 17207, 17209, 17231, 17239, 17257, 17291, 17293, 17299, 17317, + 17321, 17327, 17333, 17341, 17351, 17359, 17377, 17383, 17387, 17389, + 17393, 17401, 17417, 17419, 17431, 17443, 17449, 17467, 17471, 17477, + 17483, 17489, 17491, 17497, 17509, 17519, 17539, 17551, 17569, 17573, + 17579, 17581, 17597, 17599, 17609, 17623, 17627, 17657, 17659, 17669, + 17681, 17683, 17707, 17713, 17729, 17737, 17747, 17749, 17761, 17783, + 17789, 17791, 17807, 17827, 17837, 17839, 17851, 17863, 17881, 17891, + 17903, 17909, 17911, 17921, 17923, 17929, 17939, 17957, 17959, 17971, + 17977, 17981, 17987, 17989, 18013, 18041, 18043, 18047, 18049, 18059, + 18061, 18077, 18089, 18097, 18119, 18121, 18127, 18131, 18133, 18143, + 18149, 18169, 18181, 18191, 18199, 18211, 18217, 18223, 18229, 18233, + 18251, 18253, 18257, 18269, 18287, 18289, 18301, 18307, 18311, 18313, + 18329, 18341, 18353, 18367, 18371, 18379, 18397, 18401, 18413, 18427, + 18433, 18439, 18443, 18451, 18457, 18461, 18481, 18493, 18503, 18517, + 18521, 18523, 18539, 18541, 18553, 18583, 18587, 18593, 18617, 18637, + 18661, 18671, 18679, 18691, 18701, 18713, 18719, 18731, 18743, 18749, + 18757, 18773, 18787, 18793, 18797, 18803, 18839, 18859, 18869, 18899, + 18911, 18913, 18917, 18919, 18947, 18959, 18973, 18979, 19001, 19009, + 19013, 19031, 19037, 19051, 19069, 19073, 19079, 19081, 19087, 19121, + 19139, 19141, 19157, 19163, 19181, 19183, 19207, 19211, 19213, 19219, + 19231, 19237, 19249, 19259, 19267, 19273, 19289, 19301, 19309, 19319, + 19333, 19373, 19379, 19381, 19387, 19391, 19403, 19417, 19421, 19423, + 19427, 19429, 19433, 19441, 19447, 19457, 19463, 19469, 19471, 19477, + 19483, 19489, 19501, 19507, 19531, 19541, 19543, 19553, 19559, 19571, + 19577, 19583, 19597, 19603, 19609, 19661, 19681, 19687, 19697, 19699, + 19709, 19717, 19727, 19739, 19751, 19753, 19759, 19763, 19777, 19793, + 19801, 19813, 19819, 19841, 19843, 19853, 19861, 19867, 19889, 19891, + 19913, 19919, 19927, 19937, 19949, 19961, 19963, 19973, 19979, 19991, + 19993, 19997, 20011, 20021, 20023, 20029, 20047, 20051, 20063, 20071, + 20089, 20101, 20107, 20113, 20117, 20123, 20129, 20143, 20147, 20149, + 20161, 20173, 20177, 20183, 20201, 20219, 20231, 20233, 20249, 20261, + 20269, 20287, 20297, 20323, 20327, 20333, 20341, 20347, 20353, 20357, + 20359, 20369, 20389, 20393, 20399, 20407, 20411, 20431, 20441, 20443, + 20477, 20479, 20483, 20507, 20509, 20521, 20533, 20543, 20549, 20551, + 20563, 20593, 20599, 20611, 20627, 20639, 20641, 20663, 20681, 20693, + 20707, 20717, 20719, 20731, 20743, 20747, 20749, 20753, 20759, 20771, + 20773, 20789, 20807, 20809, 20849, 20857, 20873, 20879, 20887, 20897, + 20899, 20903, 20921, 20929, 20939, 20947, 20959, 20963, 20981, 20983, + 21001, 21011, 21013, 21017, 21019, 21023, 21031, 21059, 21061, 21067, + 21089, 21101, 21107, 21121, 21139, 21143, 21149, 21157, 21163, 21169, + 21179, 21187, 21191, 21193, 21211, 21221, 21227, 21247, 21269, 21277, + 21283, 21313, 21317, 21319, 21323, 21341, 21347, 21377, 21379, 21383, + 21391, 21397, 21401, 21407, 21419, 21433, 21467, 21481, 21487, 21491, + 21493, 21499, 21503, 21517, 21521, 21523, 21529, 21557, 21559, 21563, + 21569, 21577, 21587, 21589, 21599, 21601, 21611, 21613, 21617, 21647, + 21649, 21661, 21673, 21683, 21701, 21713, 21727, 21737, 21739, 21751, + 21757, 21767, 21773, 21787, 21799, 21803, 21817, 21821, 21839, 21841, + 21851, 21859, 21863, 21871, 21881, 21893, 21911, 21929, 21937, 21943, + 21961, 21977, 21991, 21997, 22003, 22013, 22027, 22031, 22037, 22039, + 22051, 22063, 22067, 22073, 22079, 22091, 22093, 22109, 22111, 22123, + 22129, 22133, 22147, 22153, 22157, 22159, 22171, 22189, 22193, 22229, + 22247, 22259, 22271, 22273, 22277, 22279, 22283, 22291, 22303, 22307, + 22343, 22349, 22367, 22369, 22381, 22391, 22397, 22409, 22433, 22441, + 22447, 22453, 22469, 22481, 22483, 22501, 22511, 22531, 22541, 22543, + 22549, 22567, 22571, 22573, 22613, 22619, 22621, 22637, 22639, 22643, + 22651, 22669, 22679, 22691, 22697, 22699, 22709, 22717, 22721, 22727, + 22739, 22741, 22751, 22769, 22777, 22783, 22787, 22807, 22811, 22817, + 22853, 22859, 22861, 22871, 22877, 22901, 22907, 22921, 22937, 22943, + 22961, 22963, 22973, 22993, 23003, 23011, 23017, 23021, 23027, 23029, + 23039, 23041, 23053, 23057, 23059, 23063, 23071, 23081, 23087, 23099, + 23117, 23131, 23143, 23159, 23167, 23173, 23189, 23197, 23201, 23203, + 23209, 23227, 23251, 23269, 23279, 23291, 23293, 23297, 23311, 23321, + 23327, 23333, 23339, 23357, 23369, 23371, 23399, 23417, 23431, 23447, + 23459, 23473, 23497, 23509, 23531, 23537, 23539, 23549, 23557, 23561, + 23563, 23567, 23581, 23593, 23599, 23603, 23609, 23623, 23627, 23629, + 23633, 23663, 23669, 23671, 23677, 23687, 23689, 23719, 23741, 23743, + 23747, 23753, 23761, 23767, 23773, 23789, 23801, 23813, 23819, 23827, + 23831, 23833, 23857, 23869, 23873, 23879, 23887, 23893, 23899, 23909, + 23911, 23917, 23929, 23957, 23971, 23977, 23981, 23993, 24001, 24007, + 24019, 24023, 24029, 24043, 24049, 24061, 24071, 24077, 24083, 24091, + 24097, 24103, 24107, 24109, 24113, 24121, 24133, 24137, 24151, 24169, + 24179, 24181, 24197, 24203, 24223, 24229, 24239, 24247, 24251, 24281, + 24317, 24329, 24337, 24359, 24371, 24373, 24379, 24391, 24407, 24413, + 24419, 24421, 24439, 24443, 24469, 24473, 24481, 24499, 24509, 24517, + 24527, 24533, 24547, 24551, 24571, 24593, 24611, 24623, 24631, 24659, + 24671, 24677, 24683, 24691, 24697, 24709, 24733, 24749, 24763, 24767, + 24781, 24793, 24799, 24809, 24821, 24841, 24847, 24851, 24859, 24877, + 24889, 24907, 24917, 24919, 24923, 24943, 24953, 24967, 24971, 24977, + 24979, 24989, 25013, 25031, 25033, 25037, 25057, 25073, 25087, 25097, + 25111, 25117, 25121, 25127, 25147, 25153, 25163, 25169, 25171, 25183, + 25189, 25219, 25229, 25237, 25243, 25247, 25253, 25261, 25301, 25303, + 25307, 25309, 25321, 25339, 25343, 25349, 25357, 25367, 25373, 25391, + 25409, 25411, 25423, 25439, 25447, 25453, 25457, 25463, 25469, 25471, + 25523, 25537, 25541, 25561, 25577, 25579, 25583, 25589, 25601, 25603, + 25609, 25621, 25633, 25639, 25643, 25657, 25667, 25673, 25679, 25693, + 25703, 25717, 25733, 25741, 25747, 25759, 25763, 25771, 25793, 25799, + 25801, 25819, 25841, 25847, 25849, 25867, 25873, 25889, 25903, 25913, + 25919, 25931, 25933, 25939, 25943, 25951, 25969, 25981, 25997, 25999, + 26003, 26017, 26021, 26029, 26041, 26053, 26083, 26099, 26107, 26111, + 26113, 26119, 26141, 26153, 26161, 26171, 26177, 26183, 26189, 26203, + 26209, 26227, 26237, 26249, 26251, 26261, 26263, 26267, 26293, 26297, + 26309, 26317, 26321, 26339, 26347, 26357, 26371, 26387, 26393, 26399, + 26407, 26417, 26423, 26431, 26437, 26449, 26459, 26479, 26489, 26497, + 26501, 26513, 26539, 26557, 26561, 26573, 26591, 26597, 26627, 26633, + 26641, 26647, 26669, 26681, 26683, 26687, 26693, 26699, 26701, 26711, + 26713, 26717, 26723, 26729, 26731, 26737, 26759, 26777, 26783, 26801, + 26813, 26821, 26833, 26839, 26849, 26861, 26863, 26879, 26881, 26891, + 26893, 26903, 26921, 26927, 26947, 26951, 26953, 26959, 26981, 26987, + 26993, 27011, 27017, 27031, 27043, 27059, 27061, 27067, 27073, 27077, + 27091, 27103, 27107, 27109, 27127, 27143, 27179, 27191, 27197, 27211, + 27239, 27241, 27253, 27259, 27271, 27277, 27281, 27283, 27299, 27329, + 27337, 27361, 27367, 27397, 27407, 27409, 27427, 27431, 27437, 27449, + 27457, 27479, 27481, 27487, 27509, 27527, 27529, 27539, 27541, 27551, + 27581, 27583, 27611, 27617, 27631, 27647, 27653, 27673, 27689, 27691, + 27697, 27701, 27733, 27737, 27739, 27743, 27749, 27751, 27763, 27767, + 27773, 27779, 27791, 27793, 27799, 27803, 27809, 27817, 27823, 27827, + 27847, 27851, 27883, 27893, 27901, 27917, 27919, 27941, 27943, 27947, + 27953, 27961, 27967, 27983, 27997, 28001, 28019, 28027, 28031, 28051, + 28057, 28069, 28081, 28087, 28097, 28099, 28109, 28111, 28123, 28151, + 28163, 28181, 28183, 28201, 28211, 28219, 28229, 28277, 28279, 28283, + 28289, 28297, 28307, 28309, 28319, 28349, 28351, 28387, 28393, 28403, + 28409, 28411, 28429, 28433, 28439, 28447, 28463, 28477, 28493, 28499, + 28513, 28517, 28537, 28541, 28547, 28549, 28559, 28571, 28573, 28579, + 28591, 28597, 28603, 28607, 28619, 28621, 28627, 28631, 28643, 28649, + 28657, 28661, 28663, 28669, 28687, 28697, 28703, 28711, 28723, 28729, + 28751, 28753, 28759, 28771, 28789, 28793, 28807, 28813, 28817, 28837, + 28843, 28859, 28867, 28871, 28879, 28901, 28909, 28921, 28927, 28933, + 28949, 28961, 28979, 29009, 29017, 29021, 29023, 29027, 29033, 29059, + 29063, 29077, 29101, 29123, 29129, 29131, 29137, 29147, 29153, 29167, + 29173, 29179, 29191, 29201, 29207, 29209, 29221, 29231, 29243, 29251, + 29269, 29287, 29297, 29303, 29311, 29327, 29333, 29339, 29347, 29363, + 29383, 29387, 29389, 29399, 29401, 29411, 29423, 29429, 29437, 29443, + 29453, 29473, 29483, 29501, 29527, 29531, 29537, 29567, 29569, 29573, + 29581, 29587, 29599, 29611, 29629, 29633, 29641, 29663, 29669, 29671, + 29683, 29717, 29723, 29741, 29753, 29759, 29761, 29789, 29803, 29819, + 29833, 29837, 29851, 29863, 29867, 29873, 29879, 29881, 29917, 29921, + 29927, 29947, 29959, 29983, 29989, 30011, 30013, 30029, 30047, 30059, + 30071, 30089, 30091, 30097, 30103, 30109, 30113, 30119, 30133, 30137, + 30139, 30161, 30169, 30181, 30187, 30197, 30203, 30211, 30223, 30241, + 30253, 30259, 30269, 30271, 30293, 30307, 30313, 30319, 30323, 30341, + 30347, 30367, 30389, 30391, 30403, 30427, 30431, 30449, 30467, 30469, + 30491, 30493, 30497, 30509, 30517, 30529, 30539, 30553, 30557, 30559, + 30577, 30593, 30631, 30637, 30643, 30649, 30661, 30671, 30677, 30689, + 30697, 30703, 30707, 30713, 30727, 30757, 30763, 30773, 30781, 30803, + 30809, 30817, 30829, 30839, 30841, 30851, 30853, 30859, 30869, 30871, + 30881, 30893, 30911, 30931, 30937, 30941, 30949, 30971, 30977, 30983, + 31013, 31019, 31033, 31039, 31051, 31063, 31069, 31079, 31081, 31091, + 31121, 31123, 31139, 31147, 31151, 31153, 31159, 31177, 31181, 31183, + 31189, 31193, 31219, 31223, 31231, 31237, 31247, 31249, 31253, 31259, + 31267, 31271, 31277, 31307, 31319, 31321, 31327, 31333, 31337, 31357, + 31379, 31387, 31391, 31393, 31397, 31469, 31477, 31481, 31489, 31511, + 31513, 31517, 31531, 31541, 31543, 31547, 31567, 31573, 31583, 31601, + 31607, 31627, 31643, 31649, 31657, 31663, 31667, 31687, 31699, 31721, + 31723, 31727, 31729, 31741, 31751, 31769, 31771, 31793, 31799, 31817, + 31847, 31849, 31859, 31873, 31883, 31891, 31907, 31957, 31963, 31973, + 31981, 31991, 32003, 32009, 32027, 32029, 32051, 32057, 32059, 32063, + 32069, 32077, 32083, 32089, 32099, 32117, 32119, 32141, 32143, 32159, + 32173, 32183, 32189, 32191, 32203, 32213, 32233, 32237, 32251, 32257, + 32261, 32297, 32299, 32303, 32309, 32321, 32323, 32327, 32341, 32353, + 32359, 32363, 32369, 32371, 32377, 32381, 32401, 32411, 32413, 32423, + 32429, 32441, 32443, 32467, 32479, 32491, 32497, 32503, 32507, 32531, + 32533, 32537, 32561, 32563, 32569, 32573, 32579, 32587, 32603, 32609, + 32611, 32621, 32633, 32647, 32653, 32687, 32693, 32707, 32713, 32717, + 32719, 32749, 32771, 32779, 32783, 32789, 32797, 32801, 32803, 32831, + 32833, 32839, 32843, 32869, 32887, 32909, 32911, 32917, 32933, 32939, + 32941, 32957, 32969, 32971, 32983, 32987, 32993, 32999, 33013, 33023, + 33029, 33037, 33049, 33053, 33071, 33073, 33083, 33091, 33107, 33113, + 33119, 33149, 33151, 33161, 33179, 33181, 33191, 33199, 33203, 33211, + 33223, 33247, 33287, 33289, 33301, 33311, 33317, 33329, 33331, 33343, + 33347, 33349, 33353, 33359, 33377, 33391, 33403, 33409, 33413, 33427, + 33457, 33461, 33469, 33479, 33487, 33493, 33503, 33521, 33529, 33533, + 33547, 33563, 33569, 33577, 33581, 33587, 33589, 33599, 33601, 33613, + 33617, 33619, 33623, 33629, 33637, 33641, 33647, 33679, 33703, 33713, + 33721, 33739, 33749, 33751, 33757, 33767, 33769, 33773, 33791, 33797, + 33809, 33811, 33827, 33829, 33851, 33857, 33863, 33871, 33889, 33893, + 33911, 33923, 33931, 33937, 33941, 33961, 33967, 33997, 34019, 34031, + 34033, 34039, 34057, 34061, 34123, 34127, 34129, 34141, 34147, 34157, + 34159, 34171, 34183, 34211, 34213, 34217, 34231, 34253, 34259, 34261, + 34267, 34273, 34283, 34297, 34301, 34303, 34313, 34319, 34327, 34337, + 34351, 34361, 34367, 34369, 34381, 34403, 34421, 34429, 34439, 34457, + 34469, 34471, 34483, 34487, 34499, 34501, 34511, 34513, 34519, 34537, + 34543, 34549, 34583, 34589, 34591, 34603, 34607, 34613, 34631, 34649, + 34651, 34667, 34673, 34679, 34687, 34693, 34703, 34721, 34729, 34739, + 34747, 34757, 34759, 34763, 34781, 34807, 34819, 34841, 34843, 34847, + 34849, 34871, 34877, 34883, 34897, 34913, 34919, 34939, 34949, 34961, + 34963, 34981, 35023, 35027, 35051, 35053, 35059, 35069, 35081, 35083, + 35089, 35099, 35107, 35111, 35117, 35129, 35141, 35149, 35153, 35159, + 35171, 35201, 35221, 35227, 35251, 35257, 35267, 35279, 35281, 35291, + 35311, 35317, 35323, 35327, 35339, 35353, 35363, 35381, 35393, 35401, + 35407, 35419, 35423, 35437, 35447, 35449, 35461, 35491, 35507, 35509, + 35521, 35527, 35531, 35533, 35537, 35543, 35569, 35573, 35591, 35593, + 35597, 35603, 35617, 35671, 35677, 35729, 35731, 35747, 35753, 35759, + 35771, 35797, 35801, 35803, 35809, 35831, 35837, 35839, 35851, 35863, + 35869, 35879, 35897, 35899, 35911, 35923, 35933, 35951, 35963, 35969, + 35977, 35983, 35993, 35999, 36007, 36011, 36013, 36017, 36037, 36061, + 36067, 36073, 36083, 36097, 36107, 36109, 36131, 36137, 36151, 36161, + 36187, 36191, 36209, 36217, 36229, 36241, 36251, 36263, 36269, 36277, + 36293, 36299, 36307, 36313, 36319, 36341, 36343, 36353, 36373, 36383, + 36389, 36433, 36451, 36457, 36467, 36469, 36473, 36479, 36493, 36497, + 36523, 36527, 36529, 36541, 36551, 36559, 36563, 36571, 36583, 36587, + 36599, 36607, 36629, 36637, 36643, 36653, 36671, 36677, 36683, 36691, + 36697, 36709, 36713, 36721, 36739, 36749, 36761, 36767, 36779, 36781, + 36787, 36791, 36793, 36809, 36821, 36833, 36847, 36857, 36871, 36877, + 36887, 36899, 36901, 36913, 36919, 36923, 36929, 36931, 36943, 36947, + 36973, 36979, 36997, 37003, 37013, 37019, 37021, 37039, 37049, 37057, + 37061, 37087, 37097, 37117, 37123, 37139, 37159, 37171, 37181, 37189, + 37199, 37201, 37217, 37223, 37243, 37253, 37273, 37277, 37307, 37309, + 37313, 37321, 37337, 37339, 37357, 37361, 37363, 37369, 37379, 37397, + 37409, 37423, 37441, 37447, 37463, 37483, 37489, 37493, 37501, 37507, + 37511, 37517, 37529, 37537, 37547, 37549, 37561, 37567, 37571, 37573, + 37579, 37589, 37591, 37607, 37619, 37633, 37643, 37649, 37657, 37663, + 37691, 37693, 37699, 37717, 37747, 37781, 37783, 37799, 37811, 37813, + 37831, 37847, 37853, 37861, 37871, 37879, 37889, 37897, 37907, 37951, + 37957, 37963, 37967, 37987, 37991, 37993, 37997, 38011, 38039, 38047, + 38053, 38069, 38083, 38113, 38119, 38149, 38153, 38167, 38177, 38183, + 38189, 38197, 38201, 38219, 38231, 38237, 38239, 38261, 38273, 38281, + 38287, 38299, 38303, 38317, 38321, 38327, 38329, 38333, 38351, 38371, + 38377, 38393, 38431, 38447, 38449, 38453, 38459, 38461, 38501, 38543, + 38557, 38561, 38567, 38569, 38593, 38603, 38609, 38611, 38629, 38639, + 38651, 38653, 38669, 38671, 38677, 38693, 38699, 38707, 38711, 38713, + 38723, 38729, 38737, 38747, 38749, 38767, 38783, 38791, 38803, 38821, + 38833, 38839, 38851, 38861, 38867, 38873, 38891, 38903, 38917, 38921, + 38923, 38933, 38953, 38959, 38971, 38977, 38993, 39019, 39023, 39041, + 39043, 39047, 39079, 39089, 39097, 39103, 39107, 39113, 39119, 39133, + 39139, 39157, 39161, 39163, 39181, 39191, 39199, 39209, 39217, 39227, + 39229, 39233, 39239, 39241, 39251, 39293, 39301, 39313, 39317, 39323, + 39341, 39343, 39359, 39367, 39371, 39373, 39383, 39397, 39409, 39419, + 39439, 39443, 39451, 39461, 39499, 39503, 39509, 39511, 39521, 39541, + 39551, 39563, 39569, 39581, 39607, 39619, 39623, 39631, 39659, 39667, + 39671, 39679, 39703, 39709, 39719, 39727, 39733, 39749, 39761, 39769, + 39779, 39791, 39799, 39821, 39827, 39829, 39839, 39841, 39847, 39857, + 39863, 39869, 39877, 39883, 39887, 39901, 39929, 39937, 39953, 39971, + 39979, 39983, 39989, 40009, 40013, 40031, 40037, 40039, 40063, 40087, + 40093, 40099, 40111, 40123, 40127, 40129, 40151, 40153, 40163, 40169, + 40177, 40189, 40193, 40213, 40231, 40237, 40241, 40253, 40277, 40283, + 40289, 40343, 40351, 40357, 40361, 40387, 40423, 40427, 40429, 40433, + 40459, 40471, 40483, 40487, 40493, 40499, 40507, 40519, 40529, 40531, + 40543, 40559, 40577, 40583, 40591, 40597, 40609, 40627, 40637, 40639, + 40693, 40697, 40699, 40709, 40739, 40751, 40759, 40763, 40771, 40787, + 40801, 40813, 40819, 40823, 40829, 40841, 40847, 40849, 40853, 40867, + 40879, 40883, 40897, 40903, 40927, 40933, 40939, 40949, 40961, 40973, + 40993, 41011, 41017, 41023, 41039, 41047, 41051, 41057, 41077, 41081, + 41113, 41117, 41131, 41141, 41143, 41149, 41161, 41177, 41179, 41183, + 41189, 41201, 41203, 41213, 41221, 41227, 41231, 41233, 41243, 41257, + 41263, 41269, 41281, 41299, 41333, 41341, 41351, 41357, 41381, 41387, + 41389, 41399, 41411, 41413, 41443, 41453, 41467, 41479, 41491, 41507, + 41513, 41519, 41521, 41539, 41543, 41549, 41579, 41593, 41597, 41603, + 41609, 41611, 41617, 41621, 41627, 41641, 41647, 41651, 41659, 41669, + 41681, 41687, 41719, 41729, 41737, 41759, 41761, 41771, 41777, 41801, + 41809, 41813, 41843, 41849, 41851, 41863, 41879, 41887, 41893, 41897, + 41903, 41911, 41927, 41941, 41947, 41953, 41957, 41959, 41969, 41981, + 41983, 41999, 42013, 42017, 42019, 42023, 42043, 42061, 42071, 42073, + 42083, 42089, 42101, 42131, 42139, 42157, 42169, 42179, 42181, 42187, + 42193, 42197, 42209, 42221, 42223, 42227, 42239, 42257, 42281, 42283, + 42293, 42299, 42307, 42323, 42331, 42337, 42349, 42359, 42373, 42379, + 42391, 42397, 42403, 42407, 42409, 42433, 42437, 42443, 42451, 42457, + 42461, 42463, 42467, 42473, 42487, 42491, 42499, 42509, 42533, 42557, + 42569, 42571, 42577, 42589, 42611, 42641, 42643, 42649, 42667, 42677, + 42683, 42689, 42697, 42701, 42703, 42709, 42719, 42727, 42737, 42743, + 42751, 42767, 42773, 42787, 42793, 42797, 42821, 42829, 42839, 42841, + 42853, 42859, 42863, 42899, 42901, 42923, 42929, 42937, 42943, 42953, + 42961, 42967, 42979, 42989, 43003, 43013, 43019, 43037, 43049, 43051, + 43063, 43067, 43093, 43103, 43117, 43133, 43151, 43159, 43177, 43189, + 43201, 43207, 43223, 43237, 43261, 43271, 43283, 43291, 43313, 43319, + 43321, 43331, 43391, 43397, 43399, 43403, 43411, 43427, 43441, 43451, + 43457, 43481, 43487, 43499, 43517, 43541, 43543, 43573, 43577, 43579, + 43591, 43597, 43607, 43609, 43613, 43627, 43633, 43649, 43651, 43661, + 43669, 43691, 43711, 43717, 43721, 43753, 43759, 43777, 43781, 43783, + 43787, 43789, 43793, 43801, 43853, 43867, 43889, 43891, 43913, 43933, + 43943, 43951, 43961, 43963, 43969, 43973, 43987, 43991, 43997, 44017, + 44021, 44027, 44029, 44041, 44053, 44059, 44071, 44087, 44089, 44101, + 44111, 44119, 44123, 44129, 44131, 44159, 44171, 44179, 44189, 44201, + 44203, 44207, 44221, 44249, 44257, 44263, 44267, 44269, 44273, 44279, + 44281, 44293, 44351, 44357, 44371, 44381, 44383, 44389, 44417, 44449, + 44453, 44483, 44491, 44497, 44501, 44507, 44519, 44531, 44533, 44537, + 44543, 44549, 44563, 44579, 44587, 44617, 44621, 44623, 44633, 44641, + 44647, 44651, 44657, 44683, 44687, 44699, 44701, 44711, 44729, 44741, + 44753, 44771, 44773, 44777, 44789, 44797, 44809, 44819, 44839, 44843, + 44851, 44867, 44879, 44887, 44893, 44909, 44917, 44927, 44939, 44953, + 44959, 44963, 44971, 44983, 44987, 45007, 45013, 45053, 45061, 45077, + 45083, 45119, 45121, 45127, 45131, 45137, 45139, 45161, 45179, 45181, + 45191, 45197, 45233, 45247, 45259, 45263, 45281, 45289, 45293, 45307, + 45317, 45319, 45329, 45337, 45341, 45343, 45361, 45377, 45389, 45403, + 45413, 45427, 45433, 45439, 45481, 45491, 45497, 45503, 45523, 45533, + 45541, 45553, 45557, 45569, 45587, 45589, 45599, 45613, 45631, 45641, + 45659, 45667, 45673, 45677, 45691, 45697, 45707, 45737, 45751, 45757, + 45763, 45767, 45779, 45817, 45821, 45823, 45827, 45833, 45841, 45853, + 45863, 45869, 45887, 45893, 45943, 45949, 45953, 45959, 45971, 45979, + 45989, 46021, 46027, 46049, 46051, 46061, 46073, 46091, 46093, 46099, + 46103, 46133, 46141, 46147, 46153, 46171, 46181, 46183, 46187, 46199, + 46219, 46229, 46237, 46261, 46271, 46273, 46279, 46301, 46307, 46309, + 46327, 46337, 46349, 46351, 46381, 46399, 46411, 46439, 46441, 46447, + 46451, 46457, 46471, 46477, 46489, 46499, 46507, 46511, 46523, 46549, + 46559, 46567, 46573, 46589, 46591, 46601, 46619, 46633, 46639, 46643, + 46649, 46663, 46679, 46681, 46687, 46691, 46703, 46723, 46727, 46747, + 46751, 46757, 46769, 46771, 46807, 46811, 46817, 46819, 46829, 46831, + 46853, 46861, 46867, 46877, 46889, 46901, 46919, 46933, 46957, 46993, + 46997, 47017, 47041, 47051, 47057, 47059, 47087, 47093, 47111, 47119, + 47123, 47129, 47137, 47143, 47147, 47149, 47161, 47189, 47207, 47221, + 47237, 47251, 47269, 47279, 47287, 47293, 47297, 47303, 47309, 47317, + 47339, 47351, 47353, 47363, 47381, 47387, 47389, 47407, 47417, 47419, + 47431, 47441, 47459, 47491, 47497, 47501, 47507, 47513, 47521, 47527, + 47533, 47543, 47563, 47569, 47581, 47591, 47599, 47609, 47623, 47629, + 47639, 47653, 47657, 47659, 47681, 47699, 47701, 47711, 47713, 47717, + 47737, 47741, 47743, 47777, 47779, 47791, 47797, 47807, 47809, 47819, + 47837, 47843, 47857, 47869, 47881, 47903, 47911, 47917, 47933, 47939, + 47947, 47951, 47963, 47969, 47977, 47981, 48017, 48023, 48029, 48049, + 48073, 48079, 48091, 48109, 48119, 48121, 48131, 48157, 48163, 48179, + 48187, 48193, 48197, 48221, 48239, 48247, 48259, 48271, 48281, 48299, + 48311, 48313, 48337, 48341, 48353, 48371, 48383, 48397, 48407, 48409, + 48413, 48437, 48449, 48463, 48473, 48479, 48481, 48487, 48491, 48497, + 48523, 48527, 48533, 48539, 48541, 48563, 48571, 48589, 48593, 48611, + 48619, 48623, 48647, 48649, 48661, 48673, 48677, 48679, 48731, 48733, + 48751, 48757, 48761, 48767, 48779, 48781, 48787, 48799, 48809, 48817, + 48821, 48823, 48847, 48857, 48859, 48869, 48871, 48883, 48889, 48907, + 48947, 48953, 48973, 48989, 48991, 49003, 49009, 49019, 49031, 49033, + 49037, 49043, 49057, 49069, 49081, 49103, 49109, 49117, 49121, 49123, + 49139, 49157, 49169, 49171, 49177, 49193, 49199, 49201, 49207, 49211, + 49223, 49253, 49261, 49277, 49279, 49297, 49307, 49331, 49333, 49339, + 49363, 49367, 49369, 49391, 49393, 49409, 49411, 49417, 49429, 49433, + 49451, 49459, 49463, 49477, 49481, 49499, 49523, 49529, 49531, 49537, + 49547, 49549, 49559, 49597, 49603, 49613, 49627, 49633, 49639, 49663, + 49667, 49669, 49681, 49697, 49711, 49727, 49739, 49741, 49747, 49757, + 49783, 49787, 49789, 49801, 49807, 49811, 49823, 49831, 49843, 49853, + 49871, 49877, 49891, 49919, 49921, 49927, 49937, 49939, 49943, 49957, + 49991, 49993, 49999, 50021, 50023, 50033, 50047, 50051, 50053, 50069, + 50077, 50087, 50093, 50101, 50111, 50119, 50123, 50129, 50131, 50147, + 50153, 50159, 50177, 50207, 50221, 50227, 50231, 50261, 50263, 50273, + 50287, 50291, 50311, 50321, 50329, 50333, 50341, 50359, 50363, 50377, + 50383, 50387, 50411, 50417, 50423, 50441, 50459, 50461, 50497, 50503, + 50513, 50527, 50539, 50543, 50549, 50551, 50581, 50587, 50591, 50593, + 50599, 50627, 50647, 50651, 50671, 50683, 50707, 50723, 50741, 50753, + 50767, 50773, 50777, 50789, 50821, 50833, 50839, 50849, 50857, 50867, + 50873, 50891, 50893, 50909, 50923, 50929, 50951, 50957, 50969, 50971, + 50989, 50993, 51001, 51031, 51043, 51047, 51059, 51061, 51071, 51109, + 51131, 51133, 51137, 51151, 51157, 51169, 51193, 51197, 51199, 51203, + 51217, 51229, 51239, 51241, 51257, 51263, 51283, 51287, 51307, 51329, + 51341, 51343, 51347, 51349, 51361, 51383, 51407, 51413, 51419, 51421, + 51427, 51431, 51437, 51439, 51449, 51461, 51473, 51479, 51481, 51487, + 51503, 51511, 51517, 51521, 51539, 51551, 51563, 51577, 51581, 51593, + 51599, 51607, 51613, 51631, 51637, 51647, 51659, 51673, 51679, 51683, + 51691, 51713, 51719, 51721, 51749, 51767, 51769, 51787, 51797, 51803, + 51817, 51827, 51829, 51839, 51853, 51859, 51869, 51871, 51893, 51899, + 51907, 51913, 51929, 51941, 51949, 51971, 51973, 51977, 51991, 52009, + 52021, 52027, 52051, 52057, 52067, 52069, 52081, 52103, 52121, 52127, + 52147, 52153, 52163, 52177, 52181, 52183, 52189, 52201, 52223, 52237, + 52249, 52253, 52259, 52267, 52289, 52291, 52301, 52313, 52321, 52361, + 52363, 52369, 52379, 52387, 52391, 52433, 52453, 52457, 52489, 52501, + 52511, 52517, 52529, 52541, 52543, 52553, 52561, 52567, 52571, 52579, + 52583, 52609, 52627, 52631, 52639, 52667, 52673, 52691, 52697, 52709, + 52711, 52721, 52727, 52733, 52747, 52757, 52769, 52783, 52807, 52813, + 52817, 52837, 52859, 52861, 52879, 52883, 52889, 52901, 52903, 52919, + 52937, 52951, 52957, 52963, 52967, 52973, 52981, 52999, 53003, 53017, + 53047, 53051, 53069, 53077, 53087, 53089, 53093, 53101, 53113, 53117, + 53129, 53147, 53149, 53161, 53171, 53173, 53189, 53197, 53201, 53231, + 53233, 53239, 53267, 53269, 53279, 53281, 53299, 53309, 53323, 53327, + 53353, 53359, 53377, 53381, 53401, 53407, 53411, 53419, 53437, 53441, + 53453, 53479, 53503, 53507, 53527, 53549, 53551, 53569, 53591, 53593, + 53597, 53609, 53611, 53617, 53623, 53629, 53633, 53639, 53653, 53657, + 53681, 53693, 53699, 53717, 53719, 53731, 53759, 53773, 53777, 53783, + 53791, 53813, 53819, 53831, 53849, 53857, 53861, 53881, 53887, 53891, + 53897, 53899, 53917, 53923, 53927, 53939, 53951, 53959, 53987, 53993, + 54001, 54011, 54013, 54037, 54049, 54059, 54083, 54091, 54101, 54121, + 54133, 54139, 54151, 54163, 54167, 54181, 54193, 54217, 54251, 54269, + 54277, 54287, 54293, 54311, 54319, 54323, 54331, 54347, 54361, 54367, + 54371, 54377, 54401, 54403, 54409, 54413, 54419, 54421, 54437, 54443, + 54449, 54469, 54493, 54497, 54499, 54503, 54517, 54521, 54539, 54541, + 54547, 54559, 54563, 54577, 54581, 54583, 54601, 54617, 54623, 54629, + 54631, 54647, 54667, 54673, 54679, 54709, 54713, 54721, 54727, 54751, + 54767, 54773, 54779, 54787, 54799, 54829, 54833, 54851, 54869, 54877, + 54881, 54907, 54917, 54919, 54941, 54949, 54959, 54973, 54979, 54983, + 55001, 55009, 55021, 55049, 55051, 55057, 55061, 55073, 55079, 55103, + 55109, 55117, 55127, 55147, 55163, 55171, 55201, 55207, 55213, 55217, + 55219, 55229, 55243, 55249, 55259, 55291, 55313, 55331, 55333, 55337, + 55339, 55343, 55351, 55373, 55381, 55399, 55411, 55439, 55441, 55457, + 55469, 55487, 55501, 55511, 55529, 55541, 55547, 55579, 55589, 55603, + 55609, 55619, 55621, 55631, 55633, 55639, 55661, 55663, 55667, 55673, + 55681, 55691, 55697, 55711, 55717, 55721, 55733, 55763, 55787, 55793, + 55799, 55807, 55813, 55817, 55819, 55823, 55829, 55837, 55843, 55849, + 55871, 55889, 55897, 55901, 55903, 55921, 55927, 55931, 55933, 55949, + 55967, 55987, 55997, 56003, 56009, 56039, 56041, 56053, 56081, 56087, + 56093, 56099, 56101, 56113, 56123, 56131, 56149, 56167, 56171, 56179, + 56197, 56207, 56209, 56237, 56239, 56249, 56263, 56267, 56269, 56299, + 56311, 56333, 56359, 56369, 56377, 56383, 56393, 56401, 56417, 56431, + 56437, 56443, 56453, 56467, 56473, 56477, 56479, 56489, 56501, 56503, + 56509, 56519, 56527, 56531, 56533, 56543, 56569, 56591, 56597, 56599, + 56611, 56629, 56633, 56659, 56663, 56671, 56681, 56687, 56701, 56711, + 56713, 56731, 56737, 56747, 56767, 56773, 56779, 56783, 56807, 56809, + 56813, 56821, 56827, 56843, 56857, 56873, 56891, 56893, 56897, 56909, + 56911, 56921, 56923, 56929, 56941, 56951, 56957, 56963, 56983, 56989, + 56993, 56999, 57037, 57041, 57047, 57059, 57073, 57077, 57089, 57097, + 57107, 57119, 57131, 57139, 57143, 57149, 57163, 57173, 57179, 57191, + 57193, 57203, 57221, 57223, 57241, 57251, 57259, 57269, 57271, 57283, + 57287, 57301, 57329, 57331, 57347, 57349, 57367, 57373, 57383, 57389, + 57397, 57413, 57427, 57457, 57467, 57487, 57493, 57503, 57527, 57529, + 57557, 57559, 57571, 57587, 57593, 57601, 57637, 57641, 57649, 57653, + 57667, 57679, 57689, 57697, 57709, 57713, 57719, 57727, 57731, 57737, + 57751, 57773, 57781, 57787, 57791, 57793, 57803, 57809, 57829, 57839, + 57847, 57853, 57859, 57881, 57899, 57901, 57917, 57923, 57943, 57947, + 57973, 57977, 57991, 58013, 58027, 58031, 58043, 58049, 58057, 58061, + 58067, 58073, 58099, 58109, 58111, 58129, 58147, 58151, 58153, 58169, + 58171, 58189, 58193, 58199, 58207, 58211, 58217, 58229, 58231, 58237, + 58243, 58271, 58309, 58313, 58321, 58337, 58363, 58367, 58369, 58379, + 58391, 58393, 58403, 58411, 58417, 58427, 58439, 58441, 58451, 58453, + 58477, 58481, 58511, 58537, 58543, 58549, 58567, 58573, 58579, 58601, + 58603, 58613, 58631, 58657, 58661, 58679, 58687, 58693, 58699, 58711, + 58727, 58733, 58741, 58757, 58763, 58771, 58787, 58789, 58831, 58889, + 58897, 58901, 58907, 58909, 58913, 58921, 58937, 58943, 58963, 58967, + 58979, 58991, 58997, 59009, 59011, 59021, 59023, 59029, 59051, 59053, + 59063, 59069, 59077, 59083, 59093, 59107, 59113, 59119, 59123, 59141, + 59149, 59159, 59167, 59183, 59197, 59207, 59209, 59219, 59221, 59233, + 59239, 59243, 59263, 59273, 59281, 59333, 59341, 59351, 59357, 59359, + 59369, 59377, 59387, 59393, 59399, 59407, 59417, 59419, 59441, 59443, + 59447, 59453, 59467, 59471, 59473, 59497, 59509, 59513, 59539, 59557, + 59561, 59567, 59581, 59611, 59617, 59621, 59627, 59629, 59651, 59659, + 59663, 59669, 59671, 59693, 59699, 59707, 59723, 59729, 59743, 59747, + 59753, 59771, 59779, 59791, 59797, 59809, 59833, 59863, 59879, 59887, + 59921, 59929, 59951, 59957, 59971, 59981, 59999, 60013, 60017, 60029, + 60037, 60041, 60077, 60083, 60089, 60091, 60101, 60103, 60107, 60127, + 60133, 60139, 60149, 60161, 60167, 60169, 60209, 60217, 60223, 60251, + 60257, 60259, 60271, 60289, 60293, 60317, 60331, 60337, 60343, 60353, + 60373, 60383, 60397, 60413, 60427, 60443, 60449, 60457, 60493, 60497, + 60509, 60521, 60527, 60539, 60589, 60601, 60607, 60611, 60617, 60623, + 60631, 60637, 60647, 60649, 60659, 60661, 60679, 60689, 60703, 60719, + 60727, 60733, 60737, 60757, 60761, 60763, 60773, 60779, 60793, 60811, + 60821, 60859, 60869, 60887, 60889, 60899, 60901, 60913, 60917, 60919, + 60923, 60937, 60943, 60953, 60961, 61001, 61007, 61027, 61031, 61043, + 61051, 61057, 61091, 61099, 61121, 61129, 61141, 61151, 61153, 61169, + 61211, 61223, 61231, 61253, 61261, 61283, 61291, 61297, 61331, 61333, + 61339, 61343, 61357, 61363, 61379, 61381, 61403, 61409, 61417, 61441, + 61463, 61469, 61471, 61483, 61487, 61493, 61507, 61511, 61519, 61543, + 61547, 61553, 61559, 61561, 61583, 61603, 61609, 61613, 61627, 61631, + 61637, 61643, 61651, 61657, 61667, 61673, 61681, 61687, 61703, 61717, + 61723, 61729, 61751, 61757, 61781, 61813, 61819, 61837, 61843, 61861, + 61871, 61879, 61909, 61927, 61933, 61949, 61961, 61967, 61979, 61981, + 61987, 61991, 62003, 62011, 62017, 62039, 62047, 62053, 62057, 62071, + 62081, 62099, 62119, 62129, 62131, 62137, 62141, 62143, 62171, 62189, + 62191, 62201, 62207, 62213, 62219, 62233, 62273, 62297, 62299, 62303, + 62311, 62323, 62327, 62347, 62351, 62383, 62401, 62417, 62423, 62459, + 62467, 62473, 62477, 62483, 62497, 62501, 62507, 62533, 62539, 62549, + 62563, 62581, 62591, 62597, 62603, 62617, 62627, 62633, 62639, 62653, + 62659, 62683, 62687, 62701, 62723, 62731, 62743, 62753, 62761, 62773, + 62791, 62801, 62819, 62827, 62851, 62861, 62869, 62873, 62897, 62903, + 62921, 62927, 62929, 62939, 62969, 62971, 62981, 62983, 62987, 62989, + 63029, 63031, 63059, 63067, 63073, 63079, 63097, 63103, 63113, 63127, + 63131, 63149, 63179, 63197, 63199, 63211, 63241, 63247, 63277, 63281, + 63299, 63311, 63313, 63317, 63331, 63337, 63347, 63353, 63361, 63367, + 63377, 63389, 63391, 63397, 63409, 63419, 63421, 63439, 63443, 63463, + 63467, 63473, 63487, 63493, 63499, 63521, 63527, 63533, 63541, 63559, + 63577, 63587, 63589, 63599, 63601, 63607, 63611, 63617, 63629, 63647, + 63649, 63659, 63667, 63671, 63689, 63691, 63697, 63703, 63709, 63719, + 63727, 63737, 63743, 63761, 63773, 63781, 63793, 63799, 63803, 63809, + 63823, 63839, 63841, 63853, 63857, 63863, 63901, 63907, 63913, 63929, + 63949, 63977, 63997, 64007, 64013, 64019, 64033, 64037, 64063, 64067, + 64081, 64091, 64109, 64123, 64151, 64153, 64157, 64171, 64187, 64189, + 64217, 64223, 64231, 64237, 64271, 64279, 64283, 64301, 64303, 64319, + 64327, 64333, 64373, 64381, 64399, 64403, 64433, 64439, 64451, 64453, + 64483, 64489, 64499, 64513, 64553, 64567, 64577, 64579, 64591, 64601, + 64609, 64613, 64621, 64627, 64633, 64661, 64663, 64667, 64679, 64693, + 64709, 64717, 64747, 64763, 64781, 64783, 64793, 64811, 64817, 64849, + 64853, 64871, 64877, 64879, 64891, 64901, 64919, 64921, 64927, 64937, + 64951, 64969, 64997, 65003, 65011, 65027, 65029, 65033, 65053, 65063, + 65071, 65089, 65099, 65101, 65111, 65119, 65123, 65129, 65141, 65147, + 65167, 65171, 65173, 65179, 65183, 65203, 65213, 65239, 65257, 65267, + 65269, 65287, 65293, 65309, 65323, 65327, 65353, 65357, 65371, 65381, + 65393, 65407, 65413, 65419, 65423, 65437, 65447, 65449, 65479, 65497, + 65519, 65521, 65537, 65539, 65543, 65551, 65557, 65563, 65579, 65581, + 65587, 65599, 65609, 65617, 65629, 65633, 65647, 65651, 65657, 65677, + 65687, 65699, 65701, 65707, 65713, 65717, 65719, 65729, 65731, 65761, + 65777, 65789, 65809, 65827, 65831, 65837, 65839, 65843, 65851, 65867, + 65881, 65899, 65921, 65927, 65929, 65951, 65957, 65963, 65981, 65983, + 65993, 66029, 66037, 66041, 66047, 66067, 66071, 66083, 66089, 66103, + 66107, 66109, 66137, 66161, 66169, 66173, 66179, 66191, 66221, 66239, + 66271, 66293, 66301, 66337, 66343, 66347, 66359, 66361, 66373, 66377, + 66383, 66403, 66413, 66431, 66449, 66457, 66463, 66467, 66491, 66499, + 66509, 66523, 66529, 66533, 66541, 66553, 66569, 66571, 66587, 66593, + 66601, 66617, 66629, 66643, 66653, 66683, 66697, 66701, 66713, 66721, + 66733, 66739, 66749, 66751, 66763, 66791, 66797, 66809, 66821, 66841, + 66851, 66853, 66863, 66877, 66883, 66889, 66919, 66923, 66931, 66943, + 66947, 66949, 66959, 66973, 66977, 67003, 67021, 67033, 67043, 67049, + 67057, 67061, 67073, 67079, 67103, 67121, 67129, 67139, 67141, 67153, + 67157, 67169, 67181, 67187, 67189, 67211, 67213, 67217, 67219, 67231, + 67247, 67261, 67271, 67273, 67289, 67307, 67339, 67343, 67349, 67369, + 67391, 67399, 67409, 67411, 67421, 67427, 67429, 67433, 67447, 67453, + 67477, 67481, 67489, 67493, 67499, 67511, 67523, 67531, 67537, 67547, + 67559, 67567, 67577, 67579, 67589, 67601, 67607, 67619, 67631, 67651, + 67679, 67699, 67709, 67723, 67733, 67741, 67751, 67757, 67759, 67763, + 67777, 67783, 67789, 67801, 67807, 67819, 67829, 67843, 67853, 67867, + 67883, 67891, 67901, 67927, 67931, 67933, 67939, 67943, 67957, 67961, + 67967, 67979, 67987, 67993, 68023, 68041, 68053, 68059, 68071, 68087, + 68099, 68111, 68113, 68141, 68147, 68161, 68171, 68207, 68209, 68213, + 68219, 68227, 68239, 68261, 68279, 68281, 68311, 68329, 68351, 68371, + 68389, 68399, 68437, 68443, 68447, 68449, 68473, 68477, 68483, 68489, + 68491, 68501, 68507, 68521, 68531, 68539, 68543, 68567, 68581, 68597, + 68611, 68633, 68639, 68659, 68669, 68683, 68687, 68699, 68711, 68713, + 68729, 68737, 68743, 68749, 68767, 68771, 68777, 68791, 68813, 68819, + 68821, 68863, 68879, 68881, 68891, 68897, 68899, 68903, 68909, 68917, + 68927, 68947, 68963, 68993, 69001, 69011, 69019, 69029, 69031, 69061, + 69067, 69073, 69109, 69119, 69127, 69143, 69149, 69151, 69163, 69191, + 69193, 69197, 69203, 69221, 69233, 69239, 69247, 69257, 69259, 69263, + 69313, 69317, 69337, 69341, 69371, 69379, 69383, 69389, 69401, 69403, + 69427, 69431, 69439, 69457, 69463, 69467, 69473, 69481, 69491, 69493, + 69497, 69499, 69539, 69557, 69593, 69623, 69653, 69661, 69677, 69691, + 69697, 69709, 69737, 69739, 69761, 69763, 69767, 69779, 69809, 69821, + 69827, 69829, 69833, 69847, 69857, 69859, 69877, 69899, 69911, 69929, + 69931, 69941, 69959, 69991, 69997, 70001, 70003, 70009, 70019, 70039, + 70051, 70061, 70067, 70079, 70099, 70111, 70117, 70121, 70123, 70139, + 70141, 70157, 70163, 70177, 70181, 70183, 70199, 70201, 70207, 70223, + 70229, 70237, 70241, 70249, 70271, 70289, 70297, 70309, 70313, 70321, + 70327, 70351, 70373, 70379, 70381, 70393, 70423, 70429, 70439, 70451, + 70457, 70459, 70481, 70487, 70489, 70501, 70507, 70529, 70537, 70549, + 70571, 70573, 70583, 70589, 70607, 70619, 70621, 70627, 70639, 70657, + 70663, 70667, 70687, 70709, 70717, 70729, 70753, 70769, 70783, 70793, + 70823, 70841, 70843, 70849, 70853, 70867, 70877, 70879, 70891, 70901, + 70913, 70919, 70921, 70937, 70949, 70951, 70957, 70969, 70979, 70981, + 70991, 70997, 70999, 71011, 71023, 71039, 71059, 71069, 71081, 71089, + 71119, 71129, 71143, 71147, 71153, 71161, 71167, 71171, 71191, 71209, + 71233, 71237, 71249, 71257, 71261, 71263, 71287, 71293, 71317, 71327, + 71329, 71333, 71339, 71341, 71347, 71353, 71359, 71363, 71387, 71389, + 71399, 71411, 71413, 71419, 71429, 71437, 71443, 71453, 71471, 71473, + 71479, 71483, 71503, 71527, 71537, 71549, 71551, 71563, 71569, 71593, + 71597, 71633, 71647, 71663, 71671, 71693, 71699, 71707, 71711, 71713, + 71719, 71741, 71761, 71777, 71789, 71807, 71809, 71821, 71837, 71843, + 71849, 71861, 71867, 71879, 71881, 71887, 71899, 71909, 71917, 71933, + 71941, 71947, 71963, 71971, 71983, 71987, 71993, 71999, 72019, 72031, + 72043, 72047, 72053, 72073, 72077, 72089, 72091, 72101, 72103, 72109, + 72139, 72161, 72167, 72169, 72173, 72211, 72221, 72223, 72227, 72229, + 72251, 72253, 72269, 72271, 72277, 72287, 72307, 72313, 72337, 72341, + 72353, 72367, 72379, 72383, 72421, 72431, 72461, 72467, 72469, 72481, + 72493, 72497, 72503, 72533, 72547, 72551, 72559, 72577, 72613, 72617, + 72623, 72643, 72647, 72649, 72661, 72671, 72673, 72679, 72689, 72701, + 72707, 72719, 72727, 72733, 72739, 72763, 72767, 72797, 72817, 72823, + 72859, 72869, 72871, 72883, 72889, 72893, 72901, 72907, 72911, 72923, + 72931, 72937, 72949, 72953, 72959, 72973, 72977, 72997, 73009, 73013, + 73019, 73037, 73039, 73043, 73061, 73063, 73079, 73091, 73121, 73127, + 73133, 73141, 73181, 73189, 73237, 73243, 73259, 73277, 73291, 73303, + 73309, 73327, 73331, 73351, 73361, 73363, 73369, 73379, 73387, 73417, + 73421, 73433, 73453, 73459, 73471, 73477, 73483, 73517, 73523, 73529, + 73547, 73553, 73561, 73571, 73583, 73589, 73597, 73607, 73609, 73613, + 73637, 73643, 73651, 73673, 73679, 73681, 73693, 73699, 73709, 73721, + 73727, 73751, 73757, 73771, 73783, 73819, 73823, 73847, 73849, 73859, + 73867, 73877, 73883, 73897, 73907, 73939, 73943, 73951, 73961, 73973, + 73999, 74017, 74021, 74027, 74047, 74051, 74071, 74077, 74093, 74099, + 74101, 74131, 74143, 74149, 74159, 74161, 74167, 74177, 74189, 74197, + 74201, 74203, 74209, 74219, 74231, 74257, 74279, 74287, 74293, 74297, + 74311, 74317, 74323, 74353, 74357, 74363, 74377, 74381, 74383, 74411, + 74413, 74419, 74441, 74449, 74453, 74471, 74489, 74507, 74509, 74521, + 74527, 74531, 74551, 74561, 74567, 74573, 74587, 74597, 74609, 74611, + 74623, 74653, 74687, 74699, 74707, 74713, 74717, 74719, 74729, 74731, + 74747, 74759, 74761, 74771, 74779, 74797, 74821, 74827, 74831, 74843, + 74857, 74861, 74869, 74873, 74887, 74891, 74897, 74903, 74923, 74929, + 74933, 74941, 74959, 75011, 75013, 75017, 75029, 75037, 75041, 75079, + 75083, 75109, 75133, 75149, 75161, 75167, 75169, 75181, 75193, 75209, + 75211, 75217, 75223, 75227, 75239, 75253, 75269, 75277, 75289, 75307, + 75323, 75329, 75337, 75347, 75353, 75367, 75377, 75389, 75391, 75401, + 75403, 75407, 75431, 75437, 75479, 75503, 75511, 75521, 75527, 75533, + 75539, 75541, 75553, 75557, 75571, 75577, 75583, 75611, 75617, 75619, + 75629, 75641, 75653, 75659, 75679, 75683, 75689, 75703, 75707, 75709, + 75721, 75731, 75743, 75767, 75773, 75781, 75787, 75793, 75797, 75821, + 75833, 75853, 75869, 75883, 75913, 75931, 75937, 75941, 75967, 75979, + 75983, 75989, 75991, 75997, 76001, 76003, 76031, 76039, 76079, 76081, + 76091, 76099, 76103, 76123, 76129, 76147, 76157, 76159, 76163, 76207, + 76213, 76231, 76243, 76249, 76253, 76259, 76261, 76283, 76289, 76303, + 76333, 76343, 76367, 76369, 76379, 76387, 76403, 76421, 76423, 76441, + 76463, 76471, 76481, 76487, 76493, 76507, 76511, 76519, 76537, 76541, + 76543, 76561, 76579, 76597, 76603, 76607, 76631, 76649, 76651, 76667, + 76673, 76679, 76697, 76717, 76733, 76753, 76757, 76771, 76777, 76781, + 76801, 76819, 76829, 76831, 76837, 76847, 76871, 76873, 76883, 76907, + 76913, 76919, 76943, 76949, 76961, 76963, 76991, 77003, 77017, 77023, + 77029, 77041, 77047, 77069, 77081, 77093, 77101, 77137, 77141, 77153, + 77167, 77171, 77191, 77201, 77213, 77237, 77239, 77243, 77249, 77261, + 77263, 77267, 77269, 77279, 77291, 77317, 77323, 77339, 77347, 77351, + 77359, 77369, 77377, 77383, 77417, 77419, 77431, 77447, 77471, 77477, + 77479, 77489, 77491, 77509, 77513, 77521, 77527, 77543, 77549, 77551, + 77557, 77563, 77569, 77573, 77587, 77591, 77611, 77617, 77621, 77641, + 77647, 77659, 77681, 77687, 77689, 77699, 77711, 77713, 77719, 77723, + 77731, 77743, 77747, 77761, 77773, 77783, 77797, 77801, 77813, 77839, + 77849, 77863, 77867, 77893, 77899, 77929, 77933, 77951, 77969, 77977, + 77983, 77999, 78007, 78017, 78031, 78041, 78049, 78059, 78079, 78101, + 78121, 78137, 78139, 78157, 78163, 78167, 78173, 78179, 78191, 78193, + 78203, 78229, 78233, 78241, 78259, 78277, 78283, 78301, 78307, 78311, + 78317, 78341, 78347, 78367, 78401, 78427, 78437, 78439, 78467, 78479, + 78487, 78497, 78509, 78511, 78517, 78539, 78541, 78553, 78569, 78571, + 78577, 78583, 78593, 78607, 78623, 78643, 78649, 78653, 78691, 78697, + 78707, 78713, 78721, 78737, 78779, 78781, 78787, 78791, 78797, 78803, + 78809, 78823, 78839, 78853, 78857, 78877, 78887, 78889, 78893, 78901, + 78919, 78929, 78941, 78977, 78979, 78989, 79031, 79039, 79043, 79063, + 79087, 79103, 79111, 79133, 79139, 79147, 79151, 79153, 79159, 79181, + 79187, 79193, 79201, 79229, 79231, 79241, 79259, 79273, 79279, 79283, + 79301, 79309, 79319, 79333, 79337, 79349, 79357, 79367, 79379, 79393, + 79397, 79399, 79411, 79423, 79427, 79433, 79451, 79481, 79493, 79531, + 79537, 79549, 79559, 79561, 79579, 79589, 79601, 79609, 79613, 79621, + 79627, 79631, 79633, 79657, 79669, 79687, 79691, 79693, 79697, 79699, + 79757, 79769, 79777, 79801, 79811, 79813, 79817, 79823, 79829, 79841, + 79843, 79847, 79861, 79867, 79873, 79889, 79901, 79903, 79907, 79939, + 79943, 79967, 79973, 79979, 79987, 79997, 79999, 80021, 80039, 80051, + 80071, 80077, 80107, 80111, 80141, 80147, 80149, 80153, 80167, 80173, + 80177, 80191, 80207, 80209, 80221, 80231, 80233, 80239, 80251, 80263, + 80273, 80279, 80287, 80309, 80317, 80329, 80341, 80347, 80363, 80369, + 80387, 80407, 80429, 80447, 80449, 80471, 80473, 80489, 80491, 80513, + 80527, 80537, 80557, 80567, 80599, 80603, 80611, 80621, 80627, 80629, + 80651, 80657, 80669, 80671, 80677, 80681, 80683, 80687, 80701, 80713, + 80737, 80747, 80749, 80761, 80777, 80779, 80783, 80789, 80803, 80809, + 80819, 80831, 80833, 80849, 80863, 80897, 80909, 80911, 80917, 80923, + 80929, 80933, 80953, 80963, 80989, 81001, 81013, 81017, 81019, 81023, + 81031, 81041, 81043, 81047, 81049, 81071, 81077, 81083, 81097, 81101, + 81119, 81131, 81157, 81163, 81173, 81181, 81197, 81199, 81203, 81223, + 81233, 81239, 81281, 81283, 81293, 81299, 81307, 81331, 81343, 81349, + 81353, 81359, 81371, 81373, 81401, 81409, 81421, 81439, 81457, 81463, + 81509, 81517, 81527, 81533, 81547, 81551, 81553, 81559, 81563, 81569, + 81611, 81619, 81629, 81637, 81647, 81649, 81667, 81671, 81677, 81689, + 81701, 81703, 81707, 81727, 81737, 81749, 81761, 81769, 81773, 81799, + 81817, 81839, 81847, 81853, 81869, 81883, 81899, 81901, 81919, 81929, + 81931, 81937, 81943, 81953, 81967, 81971, 81973, 82003, 82007, 82009, + 82013, 82021, 82031, 82037, 82039, 82051, 82067, 82073, 82129, 82139, + 82141, 82153, 82163, 82171, 82183, 82189, 82193, 82207, 82217, 82219, + 82223, 82231, 82237, 82241, 82261, 82267, 82279, 82301, 82307, 82339, + 82349, 82351, 82361, 82373, 82387, 82393, 82421, 82457, 82463, 82469, + 82471, 82483, 82487, 82493, 82499, 82507, 82529, 82531, 82549, 82559, + 82561, 82567, 82571, 82591, 82601, 82609, 82613, 82619, 82633, 82651, + 82657, 82699, 82721, 82723, 82727, 82729, 82757, 82759, 82763, 82781, + 82787, 82793, 82799, 82811, 82813, 82837, 82847, 82883, 82889, 82891, + 82903, 82913, 82939, 82963, 82981, 82997, 83003, 83009, 83023, 83047, + 83059, 83063, 83071, 83077, 83089, 83093, 83101, 83117, 83137, 83177, + 83203, 83207, 83219, 83221, 83227, 83231, 83233, 83243, 83257, 83267, + 83269, 83273, 83299, 83311, 83339, 83341, 83357, 83383, 83389, 83399, + 83401, 83407, 83417, 83423, 83431, 83437, 83443, 83449, 83459, 83471, + 83477, 83497, 83537, 83557, 83561, 83563, 83579, 83591, 83597, 83609, + 83617, 83621, 83639, 83641, 83653, 83663, 83689, 83701, 83717, 83719, + 83737, 83761, 83773, 83777, 83791, 83813, 83833, 83843, 83857, 83869, + 83873, 83891, 83903, 83911, 83921, 83933, 83939, 83969, 83983, 83987, + 84011, 84017, 84047, 84053, 84059, 84061, 84067, 84089, 84121, 84127, + 84131, 84137, 84143, 84163, 84179, 84181, 84191, 84199, 84211, 84221, + 84223, 84229, 84239, 84247, 84263, 84299, 84307, 84313, 84317, 84319, + 84347, 84349, 84377, 84389, 84391, 84401, 84407, 84421, 84431, 84437, + 84443, 84449, 84457, 84463, 84467, 84481, 84499, 84503, 84509, 84521, + 84523, 84533, 84551, 84559, 84589, 84629, 84631, 84649, 84653, 84659, + 84673, 84691, 84697, 84701, 84713, 84719, 84731, 84737, 84751, 84761, + 84787, 84793, 84809, 84811, 84827, 84857, 84859, 84869, 84871, 84913, + 84919, 84947, 84961, 84967, 84977, 84979, 84991, 85009, 85021, 85027, + 85037, 85049, 85061, 85081, 85087, 85091, 85093, 85103, 85109, 85121, + 85133, 85147, 85159, 85193, 85199, 85201, 85213, 85223, 85229, 85237, + 85243, 85247, 85259, 85297, 85303, 85313, 85331, 85333, 85361, 85363, + 85369, 85381, 85411, 85427, 85429, 85439, 85447, 85451, 85453, 85469, + 85487, 85513, 85517, 85523, 85531, 85549, 85571, 85577, 85597, 85601, + 85607, 85619, 85621, 85627, 85639, 85643, 85661, 85667, 85669, 85691, + 85703, 85711, 85717, 85733, 85751, 85781, 85793, 85817, 85819, 85829, + 85831, 85837, 85843, 85847, 85853, 85889, 85903, 85909, 85931, 85933, + 85991, 85999, 86011, 86017, 86027, 86029, 86069, 86077, 86083, 86111, + 86113, 86117, 86131, 86137, 86143, 86161, 86171, 86179, 86183, 86197, + 86201, 86209, 86239, 86243, 86249, 86257, 86263, 86269, 86287, 86291, + 86293, 86297, 86311, 86323, 86341, 86351, 86353, 86357, 86369, 86371, + 86381, 86389, 86399, 86413, 86423, 86441, 86453, 86461, 86467, 86477, + 86491, 86501, 86509, 86531, 86533, 86539, 86561, 86573, 86579, 86587, + 86599, 86627, 86629, 86677, 86689, 86693, 86711, 86719, 86729, 86743, + 86753, 86767, 86771, 86783, 86813, 86837, 86843, 86851, 86857, 86861, + 86869, 86923, 86927, 86929, 86939, 86951, 86959, 86969, 86981, 86993, + 87011, 87013, 87037, 87041, 87049, 87071, 87083, 87103, 87107, 87119, + 87121, 87133, 87149, 87151, 87179, 87181, 87187, 87211, 87221, 87223, + 87251, 87253, 87257, 87277, 87281, 87293, 87299, 87313, 87317, 87323, + 87337, 87359, 87383, 87403, 87407, 87421, 87427, 87433, 87443, 87473, + 87481, 87491, 87509, 87511, 87517, 87523, 87539, 87541, 87547, 87553, + 87557, 87559, 87583, 87587, 87589, 87613, 87623, 87629, 87631, 87641, + 87643, 87649, 87671, 87679, 87683, 87691, 87697, 87701, 87719, 87721, + 87739, 87743, 87751, 87767, 87793, 87797, 87803, 87811, 87833, 87853, + 87869, 87877, 87881, 87887, 87911, 87917, 87931, 87943, 87959, 87961, + 87973, 87977, 87991, 88001, 88003, 88007, 88019, 88037, 88069, 88079, + 88093, 88117, 88129, 88169, 88177, 88211, 88223, 88237, 88241, 88259, + 88261, 88289, 88301, 88321, 88327, 88337, 88339, 88379, 88397, 88411, + 88423, 88427, 88463, 88469, 88471, 88493, 88499, 88513, 88523, 88547, + 88589, 88591, 88607, 88609, 88643, 88651, 88657, 88661, 88663, 88667, + 88681, 88721, 88729, 88741, 88747, 88771, 88789, 88793, 88799, 88801, + 88807, 88811, 88813, 88817, 88819, 88843, 88853, 88861, 88867, 88873, + 88883, 88897, 88903, 88919, 88937, 88951, 88969, 88993, 88997, 89003, + 89009, 89017, 89021, 89041, 89051, 89057, 89069, 89071, 89083, 89087, + 89101, 89107, 89113, 89119, 89123, 89137, 89153, 89189, 89203, 89209, + 89213, 89227, 89231, 89237, 89261, 89269, 89273, 89293, 89303, 89317, + 89329, 89363, 89371, 89381, 89387, 89393, 89399, 89413, 89417, 89431, + 89443, 89449, 89459, 89477, 89491, 89501, 89513, 89519, 89521, 89527, + 89533, 89561, 89563, 89567, 89591, 89597, 89599, 89603, 89611, 89627, + 89633, 89653, 89657, 89659, 89669, 89671, 89681, 89689, 89753, 89759, + 89767, 89779, 89783, 89797, 89809, 89819, 89821, 89833, 89839, 89849, + 89867, 89891, 89897, 89899, 89909, 89917, 89923, 89939, 89959, 89963, + 89977, 89983, 89989, 90001, 90007, 90011, 90017, 90019, 90023, 90031, + 90053, 90059, 90067, 90071, 90073, 90089, 90107, 90121, 90127, 90149, + 90163, 90173, 90187, 90191, 90197, 90199, 90203, 90217, 90227, 90239, + 90247, 90263, 90271, 90281, 90289, 90313, 90353, 90359, 90371, 90373, + 90379, 90397, 90401, 90403, 90407, 90437, 90439, 90469, 90473, 90481, + 90499, 90511, 90523, 90527, 90529, 90533, 90547, 90583, 90599, 90617, + 90619, 90631, 90641, 90647, 90659, 90677, 90679, 90697, 90703, 90709, + 90731, 90749, 90787, 90793, 90803, 90821, 90823, 90833, 90841, 90847, + 90863, 90887, 90901, 90907, 90911, 90917, 90931, 90947, 90971, 90977, + 90989, 90997, 91009, 91019, 91033, 91079, 91081, 91097, 91099, 91121, + 91127, 91129, 91139, 91141, 91151, 91153, 91159, 91163, 91183, 91193, + 91199, 91229, 91237, 91243, 91249, 91253, 91283, 91291, 91297, 91303, + 91309, 91331, 91367, 91369, 91373, 91381, 91387, 91393, 91397, 91411, + 91423, 91433, 91453, 91457, 91459, 91463, 91493, 91499, 91513, 91529, + 91541, 91571, 91573, 91577, 91583, 91591, 91621, 91631, 91639, 91673, + 91691, 91703, 91711, 91733, 91753, 91757, 91771, 91781, 91801, 91807, + 91811, 91813, 91823, 91837, 91841, 91867, 91873, 91909, 91921, 91939, + 91943, 91951, 91957, 91961, 91967, 91969, 91997, 92003, 92009, 92033, + 92041, 92051, 92077, 92083, 92107, 92111, 92119, 92143, 92153, 92173, + 92177, 92179, 92189, 92203, 92219, 92221, 92227, 92233, 92237, 92243, + 92251, 92269, 92297, 92311, 92317, 92333, 92347, 92353, 92357, 92363, + 92369, 92377, 92381, 92383, 92387, 92399, 92401, 92413, 92419, 92431, + 92459, 92461, 92467, 92479, 92489, 92503, 92507, 92551, 92557, 92567, + 92569, 92581, 92593, 92623, 92627, 92639, 92641, 92647, 92657, 92669, + 92671, 92681, 92683, 92693, 92699, 92707, 92717, 92723, 92737, 92753, + 92761, 92767, 92779, 92789, 92791, 92801, 92809, 92821, 92831, 92849, + 92857, 92861, 92863, 92867, 92893, 92899, 92921, 92927, 92941, 92951, + 92957, 92959, 92987, 92993, 93001, 93047, 93053, 93059, 93077, 93083, + 93089, 93097, 93103, 93113, 93131, 93133, 93139, 93151, 93169, 93179, + 93187, 93199, 93229, 93239, 93241, 93251, 93253, 93257, 93263, 93281, + 93283, 93287, 93307, 93319, 93323, 93329, 93337, 93371, 93377, 93383, + 93407, 93419, 93427, 93463, 93479, 93481, 93487, 93491, 93493, 93497, + 93503, 93523, 93529, 93553, 93557, 93559, 93563, 93581, 93601, 93607, + 93629, 93637, 93683, 93701, 93703, 93719, 93739, 93761, 93763, 93787, + 93809, 93811, 93827, 93851, 93871, 93887, 93889, 93893, 93901, 93911, + 93913, 93923, 93937, 93941, 93949, 93967, 93971, 93979, 93983, 93997, + 94007, 94009, 94033, 94049, 94057, 94063, 94079, 94099, 94109, 94111, + 94117, 94121, 94151, 94153, 94169, 94201, 94207, 94219, 94229, 94253, + 94261, 94273, 94291, 94307, 94309, 94321, 94327, 94331, 94343, 94349, + 94351, 94379, 94397, 94399, 94421, 94427, 94433, 94439, 94441, 94447, + 94463, 94477, 94483, 94513, 94529, 94531, 94541, 94543, 94547, 94559, + 94561, 94573, 94583, 94597, 94603, 94613, 94621, 94649, 94651, 94687, + 94693, 94709, 94723, 94727, 94747, 94771, 94777, 94781, 94789, 94793, + 94811, 94819, 94823, 94837, 94841, 94847, 94849, 94873, 94889, 94903, + 94907, 94933, 94949, 94951, 94961, 94993, 94999, 95003, 95009, 95021, + 95027, 95063, 95071, 95083, 95087, 95089, 95093, 95101, 95107, 95111, + 95131, 95143, 95153, 95177, 95189, 95191, 95203, 95213, 95219, 95231, + 95233, 95239, 95257, 95261, 95267, 95273, 95279, 95287, 95311, 95317, + 95327, 95339, 95369, 95383, 95393, 95401, 95413, 95419, 95429, 95441, + 95443, 95461, 95467, 95471, 95479, 95483, 95507, 95527, 95531, 95539, + 95549, 95561, 95569, 95581, 95597, 95603, 95617, 95621, 95629, 95633, + 95651, 95701, 95707, 95713, 95717, 95723, 95731, 95737, 95747, 95773, + 95783, 95789, 95791, 95801, 95803, 95813, 95819, 95857, 95869, 95873, + 95881, 95891, 95911, 95917, 95923, 95929, 95947, 95957, 95959, 95971, + 95987, 95989, 96001, 96013, 96017, 96043, 96053, 96059, 96079, 96097, + 96137, 96149, 96157, 96167, 96179, 96181, 96199, 96211, 96221, 96223, + 96233, 96259, 96263, 96269, 96281, 96289, 96293, 96323, 96329, 96331, + 96337, 96353, 96377, 96401, 96419, 96431, 96443, 96451, 96457, 96461, + 96469, 96479, 96487, 96493, 96497, 96517, 96527, 96553, 96557, 96581, + 96587, 96589, 96601, 96643, 96661, 96667, 96671, 96697, 96703, 96731, + 96737, 96739, 96749, 96757, 96763, 96769, 96779, 96787, 96797, 96799, + 96821, 96823, 96827, 96847, 96851, 96857, 96893, 96907, 96911, 96931, + 96953, 96959, 96973, 96979, 96989, 96997, 97001, 97003, 97007, 97021, + 97039, 97073, 97081, 97103, 97117, 97127, 97151, 97157, 97159, 97169, + 97171, 97177, 97187, 97213, 97231, 97241, 97259, 97283, 97301, 97303, + 97327, 97367, 97369, 97373, 97379, 97381, 97387, 97397, 97423, 97429, + 97441, 97453, 97459, 97463, 97499, 97501, 97511, 97523, 97547, 97549, + 97553, 97561, 97571, 97577, 97579, 97583, 97607, 97609, 97613, 97649, + 97651, 97673, 97687, 97711, 97729, 97771, 97777, 97787, 97789, 97813, + 97829, 97841, 97843, 97847, 97849, 97859, 97861, 97871, 97879, 97883, + 97919, 97927, 97931, 97943, 97961, 97967, 97973, 97987, 98009, 98011, + 98017, 98041, 98047, 98057, 98081, 98101, 98123, 98129, 98143, 98179, + 98207, 98213, 98221, 98227, 98251, 98257, 98269, 98297, 98299, 98317, + 98321, 98323, 98327, 98347, 98369, 98377, 98387, 98389, 98407, 98411, + 98419, 98429, 98443, 98453, 98459, 98467, 98473, 98479, 98491, 98507, + 98519, 98533, 98543, 98561, 98563, 98573, 98597, 98621, 98627, 98639, + 98641, 98663, 98669, 98689, 98711, 98713, 98717, 98729, 98731, 98737, + 98773, 98779, 98801, 98807, 98809, 98837, 98849, 98867, 98869, 98873, + 98887, 98893, 98897, 98899, 98909, 98911, 98927, 98929, 98939, 98947, + 98953, 98963, 98981, 98993, 98999, 99013, 99017, 99023, 99041, 99053, + 99079, 99083, 99089, 99103, 99109, 99119, 99131, 99133, 99137, 99139, + 99149, 99173, 99181, 99191, 99223, 99233, 99241, 99251, 99257, 99259, + 99277, 99289, 99317, 99347, 99349, 99367, 99371, 99377, 99391, 99397, + 99401, 99409, 99431, 99439, 99469, 99487, 99497, 99523, 99527, 99529, + 99551, 99559, 99563, 99571, 99577, 99581, 99607, 99611, 99623, 99643, + 99661, 99667, 99679, 99689, 99707, 99709, 99713, 99719, 99721, 99733, + 99761, 99767, 99787, 99793, 99809, 99817, 99823, 99829, 99833, 99839, + 99859, 99871, 99877, 99881, 99901, 99907, 99923, 99929, 99961, 99971, + 99989, 99991, 100003, 100019, 100043, 100049, 100057, 100069, 100103, 100109, + 100129, 100151, 100153, 100169, 100183, 100189, 100193, 100207, 100213, 100237, + 100267, 100271, 100279, 100291, 100297, 100313, 100333, 100343, 100357, 100361, + 100363, 100379, 100391, 100393, 100403, 100411, 100417, 100447, 100459, 100469, + 100483, 100493, 100501, 100511, 100517, 100519, 100523, 100537, 100547, 100549, + 100559, 100591, 100609, 100613, 100621, 100649, 100669, 100673, 100693, 100699, + 100703, 100733, 100741, 100747, 100769, 100787, 100799, 100801, 100811, 100823, + 100829, 100847, 100853, 100907, 100913, 100927, 100931, 100937, 100943, 100957, + 100981, 100987, 100999, 101009, 101021, 101027, 101051, 101063, 101081, 101089, + 101107, 101111, 101113, 101117, 101119, 101141, 101149, 101159, 101161, 101173, + 101183, 101197, 101203, 101207, 101209, 101221, 101267, 101273, 101279, 101281, + 101287, 101293, 101323, 101333, 101341, 101347, 101359, 101363, 101377, 101383, + 101399, 101411, 101419, 101429, 101449, 101467, 101477, 101483, 101489, 101501, + 101503, 101513, 101527, 101531, 101533, 101537, 101561, 101573, 101581, 101599, + 101603, 101611, 101627, 101641, 101653, 101663, 101681, 101693, 101701, 101719, + 101723, 101737, 101741, 101747, 101749, 101771, 101789, 101797, 101807, 101833, + 101837, 101839, 101863, 101869, 101873, 101879, 101891, 101917, 101921, 101929, + 101939, 101957, 101963, 101977, 101987, 101999, 102001, 102013, 102019, 102023, + 102031, 102043, 102059, 102061, 102071, 102077, 102079, 102101, 102103, 102107, + 102121, 102139, 102149, 102161, 102181, 102191, 102197, 102199, 102203, 102217, + 102229, 102233, 102241, 102251, 102253, 102259, 102293, 102299, 102301, 102317, + 102329, 102337, 102359, 102367, 102397, 102407, 102409, 102433, 102437, 102451, + 102461, 102481, 102497, 102499, 102503, 102523, 102533, 102539, 102547, 102551, + 102559, 102563, 102587, 102593, 102607, 102611, 102643, 102647, 102653, 102667, + 102673, 102677, 102679, 102701, 102761, 102763, 102769, 102793, 102797, 102811, + 102829, 102841, 102859, 102871, 102877, 102881, 102911, 102913, 102929, 102931, + 102953, 102967, 102983, 103001, 103007, 103043, 103049, 103067, 103069, 103079, + 103087, 103091, 103093, 103099, 103123, 103141, 103171, 103177, 103183, 103217, + 103231, 103237, 103289, 103291, 103307, 103319, 103333, 103349, 103357, 103387, + 103391, 103393, 103399, 103409, 103421, 103423, 103451, 103457, 103471, 103483, + 103511, 103529, 103549, 103553, 103561, 103567, 103573, 103577, 103583, 103591, + 103613, 103619, 103643, 103651, 103657, 103669, 103681, 103687, 103699, 103703, + 103723, 103769, 103787, 103801, 103811, 103813, 103837, 103841, 103843, 103867, + 103889, 103903, 103913, 103919, 103951, 103963, 103967, 103969, 103979, 103981, + 103991, 103993, 103997, 104003, 104009, 104021, 104033, 104047, 104053, 104059, + 104087, 104089, 104107, 104113, 104119, 104123, 104147, 104149, 104161, 104173, + 104179, 104183, 104207, 104231, 104233, 104239, 104243, 104281, 104287, 104297, + 104309, 104311, 104323, 104327, 104347, 104369, 104381, 104383, 104393, 104399, + 104417, 104459, 104471, 104473, 104479, 104491, 104513, 104527, 104537, 104543, + 104549, 104551, 104561, 104579, 104593, 104597, 104623, 104639, 104651, 104659, + 104677, 104681, 104683, 104693, 104701, 104707, 104711, 104717, 104723, 104729, +}; + +// return 1 if p is divisable by sp, 0 otherwise +static int +divides(mpint *dividend, ulong divisor) +{ + mpdigit d[2], q; + int i; + + d[1] = 0; + for(i = dividend->top-1; i >= 0; i--){ + d[0] = dividend->p[i]; + mpdigdiv(d, divisor, &q); + d[1] = d[0] - divisor*q; + } + return d[1] == 0; +} + +// return -1 if p is divisable by one of the small primes, 0 otherwise +int +smallprimetest(mpint *p) +{ + int i; + ulong sp; + + for(i = 0; i < nelem(smallprimes); i++){ + sp = smallprimes[i]; + if(p->top == 1 && p->p[0] <= sp) + break; + if(divides(p, sp)) + return -1; + } + return 0; +} diff --git a/libsec/thumb.c b/libsec/thumb.c new file mode 100644 index 0000000..cac95a4 --- /dev/null +++ b/libsec/thumb.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include +#include + +enum{ ThumbTab = 1<<10 }; + +static void * +emalloc(int n) +{ + void *p; + if(n==0) + n=1; + p = malloc(n); + if(p == nil){ + exits("out of memory"); + } + memset(p, 0, n); + return p; +} + +void +freeThumbprints(Thumbprint *table) +{ + Thumbprint *hd, *p, *q; + for(hd = table; hd < table+ThumbTab; hd++){ + for(p = hd->next; p; p = q){ + q = p->next; + free(p); + } + } + free(table); +} + +int +okThumbprint(uchar *sum, Thumbprint *table) +{ + Thumbprint *p; + int i = ((sum[0]<<8) + sum[1]) & (ThumbTab-1); + + for(p = table[i].next; p; p = p->next) + if(memcmp(sum, p->sha1, SHA1dlen) == 0) + return 1; + return 0; +} + +static void +loadThumbprints(char *file, Thumbprint *table, Thumbprint *crltab) +{ + Thumbprint *entry; + Biobuf *bin; + char *line, *field[50]; + uchar sum[SHA1dlen]; + int i; + + bin = Bopen(file, OREAD); + if(bin == nil) + return; + for(; (line = Brdstr(bin, '\n', 1)) != 0; free(line)){ + if(tokenize(line, field, nelem(field)) < 2) + continue; + if(strcmp(field[0], "#include") == 0){ + loadThumbprints(field[1], table, crltab); + continue; + } + if(strcmp(field[0], "x509") != 0 || strncmp(field[1], "sha1=", strlen("sha1=")) != 0) + continue; + field[1] += strlen("sha1="); + dec16(sum, sizeof(sum), field[1], strlen(field[1])); + if(crltab && okThumbprint(sum, crltab)) + continue; + entry = (Thumbprint*)emalloc(sizeof(*entry)); + memcpy(entry->sha1, sum, SHA1dlen); + i = ((sum[0]<<8) + sum[1]) & (ThumbTab-1); + entry->next = table[i].next; + table[i].next = entry; + } + Bterm(bin); +} + +Thumbprint * +initThumbprints(char *ok, char *crl) +{ + Thumbprint *table, *crltab = nil; + + if(crl){ + crltab = emalloc(ThumbTab * sizeof(*table)); + loadThumbprints(crl, crltab, nil); + } + table = emalloc(ThumbTab * sizeof(*table)); + loadThumbprints(ok, table, crltab); + free(crltab); + return table; +} + diff --git a/libsec/tlshand.c b/libsec/tlshand.c new file mode 100644 index 0000000..9777de1 --- /dev/null +++ b/libsec/tlshand.c @@ -0,0 +1,2291 @@ +#include +#include +#include +#include +#include +#include + +// The main groups of functions are: +// client/server - main handshake protocol definition +// message functions - formating handshake messages +// cipher choices - catalog of digest and encrypt algorithms +// security functions - PKCS#1, sslHMAC, session keygen +// general utility functions - malloc, serialization +// The handshake protocol builds on the TLS/SSL3 record layer protocol, +// which is implemented in kernel device #a. See also /lib/rfc/rfc2246. + +enum { + TLSFinishedLen = 12, + SSL3FinishedLen = MD5dlen+SHA1dlen, + MaxKeyData = 104, // amount of secret we may need + MaxChunk = 1<<14, + RandomSize = 32, + SidSize = 32, + MasterSecretSize = 48, + AQueue = 0, + AFlush = 1, +}; + +typedef struct TlsSec TlsSec; + +typedef struct Bytes{ + int len; + uchar data[1]; // [len] +} Bytes; + +typedef struct Ints{ + int len; + int data[1]; // [len] +} Ints; + +typedef struct Algs{ + char *enc; + char *digest; + int nsecret; + int tlsid; + int ok; +} Algs; + +typedef struct Finished{ + uchar verify[SSL3FinishedLen]; + int n; +} Finished; + +typedef struct TlsConnection{ + TlsSec *sec; // security management goo + int hand, ctl; // record layer file descriptors + int erred; // set when tlsError called + int (*trace)(char*fmt, ...); // for debugging + int version; // protocol we are speaking + int verset; // version has been set + int ver2hi; // server got a version 2 hello + int isClient; // is this the client or server? + Bytes *sid; // SessionID + Bytes *cert; // only last - no chain + + Lock statelk; + int state; // must be set using setstate + + // input buffer for handshake messages + uchar buf[MaxChunk+2048]; + uchar *rp, *ep; + + uchar crandom[RandomSize]; // client random + uchar srandom[RandomSize]; // server random + int clientVersion; // version in ClientHello + char *digest; // name of digest algorithm to use + char *enc; // name of encryption algorithm to use + int nsecret; // amount of secret data to init keys + + // for finished messages + MD5state hsmd5; // handshake hash + SHAstate hssha1; // handshake hash + Finished finished; +} TlsConnection; + +typedef struct Msg{ + int tag; + union { + struct { + int version; + uchar random[RandomSize]; + Bytes* sid; + Ints* ciphers; + Bytes* compressors; + } clientHello; + struct { + int version; + uchar random[RandomSize]; + Bytes* sid; + int cipher; + int compressor; + } serverHello; + struct { + int ncert; + Bytes **certs; + } certificate; + struct { + Bytes *types; + int nca; + Bytes **cas; + } certificateRequest; + struct { + Bytes *key; + } clientKeyExchange; + Finished finished; + } u; +} Msg; + +typedef struct TlsSec{ + char *server; // name of remote; nil for server + int ok; // <0 killed; ==0 in progress; >0 reusable + RSApub *rsapub; + AuthRpc *rpc; // factotum for rsa private key + uchar sec[MasterSecretSize]; // master secret + uchar crandom[RandomSize]; // client random + uchar srandom[RandomSize]; // server random + int clientVers; // version in ClientHello + int vers; // final version + // byte generation and handshake checksum + void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int, uchar*, int); + void (*setFinished)(TlsSec*, MD5state, SHAstate, uchar*, int); + int nfin; +} TlsSec; + + +enum { + TLSVersion = 0x0301, + SSL3Version = 0x0300, + ProtocolVersion = 0x0301, // maximum version we speak + MinProtoVersion = 0x0300, // limits on version we accept + MaxProtoVersion = 0x03ff, +}; + +// handshake type +enum { + HHelloRequest, + HClientHello, + HServerHello, + HSSL2ClientHello = 9, /* local convention; see devtls.c */ + HCertificate = 11, + HServerKeyExchange, + HCertificateRequest, + HServerHelloDone, + HCertificateVerify, + HClientKeyExchange, + HFinished = 20, + HMax +}; + +// alerts +enum { + ECloseNotify = 0, + EUnexpectedMessage = 10, + EBadRecordMac = 20, + EDecryptionFailed = 21, + ERecordOverflow = 22, + EDecompressionFailure = 30, + EHandshakeFailure = 40, + ENoCertificate = 41, + EBadCertificate = 42, + EUnsupportedCertificate = 43, + ECertificateRevoked = 44, + ECertificateExpired = 45, + ECertificateUnknown = 46, + EIllegalParameter = 47, + EUnknownCa = 48, + EAccessDenied = 49, + EDecodeError = 50, + EDecryptError = 51, + EExportRestriction = 60, + EProtocolVersion = 70, + EInsufficientSecurity = 71, + EInternalError = 80, + EUserCanceled = 90, + ENoRenegotiation = 100, + EMax = 256 +}; + +// cipher suites +enum { + TLS_NULL_WITH_NULL_NULL = 0x0000, + TLS_RSA_WITH_NULL_MD5 = 0x0001, + TLS_RSA_WITH_NULL_SHA = 0x0002, + TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003, + TLS_RSA_WITH_RC4_128_MD5 = 0x0004, + TLS_RSA_WITH_RC4_128_SHA = 0x0005, + TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0X0006, + TLS_RSA_WITH_IDEA_CBC_SHA = 0X0007, + TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X0008, + TLS_RSA_WITH_DES_CBC_SHA = 0X0009, + TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0X000A, + TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0X000B, + TLS_DH_DSS_WITH_DES_CBC_SHA = 0X000C, + TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0X000D, + TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X000E, + TLS_DH_RSA_WITH_DES_CBC_SHA = 0X000F, + TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0X0010, + TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0X0011, + TLS_DHE_DSS_WITH_DES_CBC_SHA = 0X0012, + TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0X0013, // ZZZ must be implemented for tls1.0 compliance + TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0X0014, + TLS_DHE_RSA_WITH_DES_CBC_SHA = 0X0015, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0X0016, + TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017, + TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018, + TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0X0019, + TLS_DH_anon_WITH_DES_CBC_SHA = 0X001A, + TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0X001B, + + TLS_RSA_WITH_AES_128_CBC_SHA = 0X002f, // aes, aka rijndael with 128 bit blocks + TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0X0030, + TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0X0031, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0X0032, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0X0033, + TLS_DH_anon_WITH_AES_128_CBC_SHA = 0X0034, + TLS_RSA_WITH_AES_256_CBC_SHA = 0X0035, + TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0X0036, + TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0X0037, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0X0038, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0X0039, + TLS_DH_anon_WITH_AES_256_CBC_SHA = 0X003A, + CipherMax +}; + +// compression methods +enum { + CompressionNull = 0, + CompressionMax +}; + +static Algs cipherAlgs[] = { + {"rc4_128", "md5", 2 * (16 + MD5dlen), TLS_RSA_WITH_RC4_128_MD5}, + {"rc4_128", "sha1", 2 * (16 + SHA1dlen), TLS_RSA_WITH_RC4_128_SHA}, + {"3des_ede_cbc","sha1",2*(4*8+SHA1dlen), TLS_RSA_WITH_3DES_EDE_CBC_SHA}, +}; + +static uchar compressors[] = { + CompressionNull, +}; + +static TlsConnection *tlsServer2(int ctl, int hand, uchar *cert, int ncert, int (*trace)(char*fmt, ...)); +static TlsConnection *tlsClient2(int ctl, int hand, uchar *csid, int ncsid, int (*trace)(char*fmt, ...)); + +static void msgClear(Msg *m); +static char* msgPrint(char *buf, int n, Msg *m); +static int msgRecv(TlsConnection *c, Msg *m); +static int msgSend(TlsConnection *c, Msg *m, int act); +static void tlsError(TlsConnection *c, int err, char *msg, ...); +#pragma varargck argpos tlsError 3 +static int setVersion(TlsConnection *c, int version); +static int finishedMatch(TlsConnection *c, Finished *f); +static void tlsConnectionFree(TlsConnection *c); + +static int setAlgs(TlsConnection *c, int a); +static int okCipher(Ints *cv); +static int okCompression(Bytes *cv); +static int initCiphers(void); +static Ints* makeciphers(void); + +static TlsSec* tlsSecInits(int cvers, uchar *csid, int ncsid, uchar *crandom, uchar *ssid, int *nssid, uchar *srandom); +static int tlsSecSecrets(TlsSec *sec, int vers, uchar *epm, int nepm, uchar *kd, int nkd); +static TlsSec* tlsSecInitc(int cvers, uchar *crandom); +static int tlsSecSecretc(TlsSec *sec, uchar *sid, int nsid, uchar *srandom, uchar *cert, int ncert, int vers, uchar **epm, int *nepm, uchar *kd, int nkd); +static int tlsSecFinished(TlsSec *sec, MD5state md5, SHAstate sha1, uchar *fin, int nfin, int isclient); +static void tlsSecOk(TlsSec *sec); +static void tlsSecKill(TlsSec *sec); +static void tlsSecClose(TlsSec *sec); +static void setMasterSecret(TlsSec *sec, Bytes *pm); +static void serverMasterSecret(TlsSec *sec, uchar *epm, int nepm); +static void setSecrets(TlsSec *sec, uchar *kd, int nkd); +static int clientMasterSecret(TlsSec *sec, RSApub *pub, uchar **epm, int *nepm); +static Bytes *pkcs1_encrypt(Bytes* data, RSApub* key, int blocktype); +static Bytes *pkcs1_decrypt(TlsSec *sec, uchar *epm, int nepm); +static void tlsSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient); +static void sslSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient); +static void sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, + uchar *seed0, int nseed0, uchar *seed1, int nseed1); +static int setVers(TlsSec *sec, int version); + +static AuthRpc* factotum_rsa_open(uchar *cert, int certlen); +static mpint* factotum_rsa_decrypt(AuthRpc *rpc, mpint *cipher); +static void factotum_rsa_close(AuthRpc*rpc); + +static void* emalloc(int); +static void* erealloc(void*, int); +static void put32(uchar *p, u32int); +static void put24(uchar *p, int); +static void put16(uchar *p, int); +static u32int get32(uchar *p); +static int get24(uchar *p); +static int get16(uchar *p); +static Bytes* newbytes(int len); +static Bytes* makebytes(uchar* buf, int len); +static void freebytes(Bytes* b); +static Ints* newints(int len); +static Ints* makeints(int* buf, int len); +static void freeints(Ints* b); + +//================= client/server ======================== + +// push TLS onto fd, returning new (application) file descriptor +// or -1 if error. +int +tlsServer(int fd, TLSconn *conn) +{ + char buf[8]; + char dname[64]; + int n, data, ctl, hand; + TlsConnection *tls; + + if(conn == nil) + return -1; + ctl = open("#a/tls/clone", ORDWR); + if(ctl < 0) + return -1; + n = read(ctl, buf, sizeof(buf)-1); + if(n < 0){ + close(ctl); + return -1; + } + buf[n] = 0; + sprint(conn->dir, "#a/tls/%s", buf); + sprint(dname, "#a/tls/%s/hand", buf); + hand = open(dname, ORDWR); + if(hand < 0){ + close(ctl); + return -1; + } + fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion); + tls = tlsServer2(ctl, hand, conn->cert, conn->certlen, conn->trace); + sprint(dname, "#a/tls/%s/data", buf); + data = open(dname, ORDWR); + close(fd); + close(hand); + close(ctl); + if(data < 0){ + return -1; + } + if(tls == nil){ + close(data); + return -1; + } + if(conn->cert) + free(conn->cert); + conn->cert = 0; // client certificates are not yet implemented + conn->certlen = 0; + conn->sessionIDlen = tls->sid->len; + conn->sessionID = emalloc(conn->sessionIDlen); + memcpy(conn->sessionID, tls->sid->data, conn->sessionIDlen); + tlsConnectionFree(tls); + return data; +} + +// push TLS onto fd, returning new (application) file descriptor +// or -1 if error. +int +tlsClient(int fd, TLSconn *conn) +{ + char buf[8]; + char dname[64]; + int n, data, ctl, hand; + TlsConnection *tls; + + if(!conn) + return -1; + ctl = open("#a/tls/clone", ORDWR); + if(ctl < 0) + return -1; + n = read(ctl, buf, sizeof(buf)-1); + if(n < 0){ + close(ctl); + return -1; + } + buf[n] = 0; + sprint(conn->dir, "#a/tls/%s", buf); + sprint(dname, "#a/tls/%s/hand", buf); + hand = open(dname, ORDWR); + if(hand < 0){ + close(ctl); + return -1; + } + sprint(dname, "#a/tls/%s/data", buf); + data = open(dname, ORDWR); + if(data < 0) + return -1; + fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion); + tls = tlsClient2(ctl, hand, conn->sessionID, conn->sessionIDlen, conn->trace); + close(fd); + close(hand); + close(ctl); + if(tls == nil){ + close(data); + return -1; + } + conn->certlen = tls->cert->len; + conn->cert = emalloc(conn->certlen); + memcpy(conn->cert, tls->cert->data, conn->certlen); + conn->sessionIDlen = tls->sid->len; + conn->sessionID = emalloc(conn->sessionIDlen); + memcpy(conn->sessionID, tls->sid->data, conn->sessionIDlen); + tlsConnectionFree(tls); + return data; +} + +static TlsConnection * +tlsServer2(int ctl, int hand, uchar *cert, int ncert, int (*trace)(char*fmt, ...)) +{ + TlsConnection *c; + Msg m; + Bytes *csid; + uchar sid[SidSize], kd[MaxKeyData]; + char *secrets; + int cipher, compressor, nsid, rv; + + if(trace) + trace("tlsServer2\n"); + if(!initCiphers()) + return nil; + c = emalloc(sizeof(TlsConnection)); + c->ctl = ctl; + c->hand = hand; + c->trace = trace; + c->version = ProtocolVersion; + + memset(&m, 0, sizeof(m)); + if(!msgRecv(c, &m)){ + if(trace) + trace("initial msgRecv failed\n"); + goto Err; + } + if(m.tag != HClientHello) { + tlsError(c, EUnexpectedMessage, "expected a client hello"); + goto Err; + } + c->clientVersion = m.u.clientHello.version; + if(trace) + trace("ClientHello version %x\n", c->clientVersion); + if(setVersion(c, m.u.clientHello.version) < 0) { + tlsError(c, EIllegalParameter, "incompatible version"); + goto Err; + } + + memmove(c->crandom, m.u.clientHello.random, RandomSize); + cipher = okCipher(m.u.clientHello.ciphers); + if(cipher < 0) { + // reply with EInsufficientSecurity if we know that's the case + if(cipher == -2) + tlsError(c, EInsufficientSecurity, "cipher suites too weak"); + else + tlsError(c, EHandshakeFailure, "no matching cipher suite"); + goto Err; + } + if(!setAlgs(c, cipher)){ + tlsError(c, EHandshakeFailure, "no matching cipher suite"); + goto Err; + } + compressor = okCompression(m.u.clientHello.compressors); + if(compressor < 0) { + tlsError(c, EHandshakeFailure, "no matching compressor"); + goto Err; + } + + csid = m.u.clientHello.sid; + if(trace) + trace(" cipher %d, compressor %d, csidlen %d\n", cipher, compressor, csid->len); + c->sec = tlsSecInits(c->clientVersion, csid->data, csid->len, c->crandom, sid, &nsid, c->srandom); + if(c->sec == nil){ + tlsError(c, EHandshakeFailure, "can't initialize security: %r"); + goto Err; + } + c->sec->rpc = factotum_rsa_open(cert, ncert); + if(c->sec->rpc == nil){ + tlsError(c, EHandshakeFailure, "factotum_rsa_open: %r"); + goto Err; + } + c->sec->rsapub = X509toRSApub(cert, ncert, nil, 0); + msgClear(&m); + + m.tag = HServerHello; + m.u.serverHello.version = c->version; + memmove(m.u.serverHello.random, c->srandom, RandomSize); + m.u.serverHello.cipher = cipher; + m.u.serverHello.compressor = compressor; + c->sid = makebytes(sid, nsid); + m.u.serverHello.sid = makebytes(c->sid->data, c->sid->len); + if(!msgSend(c, &m, AQueue)) + goto Err; + msgClear(&m); + + m.tag = HCertificate; + m.u.certificate.ncert = 1; + m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes)); + m.u.certificate.certs[0] = makebytes(cert, ncert); + if(!msgSend(c, &m, AQueue)) + goto Err; + msgClear(&m); + + m.tag = HServerHelloDone; + if(!msgSend(c, &m, AFlush)) + goto Err; + msgClear(&m); + + if(!msgRecv(c, &m)) + goto Err; + if(m.tag != HClientKeyExchange) { + tlsError(c, EUnexpectedMessage, "expected a client key exchange"); + goto Err; + } + if(tlsSecSecrets(c->sec, c->version, m.u.clientKeyExchange.key->data, m.u.clientKeyExchange.key->len, kd, c->nsecret) < 0){ + tlsError(c, EHandshakeFailure, "couldn't set secrets: %r"); + goto Err; + } + if(trace) + trace("tls secrets\n"); + secrets = (char*)emalloc(2*c->nsecret); + enc64(secrets, 2*c->nsecret, kd, c->nsecret); + rv = fprint(c->ctl, "secret %s %s 0 %s", c->digest, c->enc, secrets); + memset(secrets, 0, 2*c->nsecret); + free(secrets); + memset(kd, 0, c->nsecret); + if(rv < 0){ + tlsError(c, EHandshakeFailure, "can't set keys: %r"); + goto Err; + } + msgClear(&m); + + /* no CertificateVerify; skip to Finished */ + if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 1) < 0){ + tlsError(c, EInternalError, "can't set finished: %r"); + goto Err; + } + if(!msgRecv(c, &m)) + goto Err; + if(m.tag != HFinished) { + tlsError(c, EUnexpectedMessage, "expected a finished"); + goto Err; + } + if(!finishedMatch(c, &m.u.finished)) { + tlsError(c, EHandshakeFailure, "finished verification failed"); + goto Err; + } + msgClear(&m); + + /* change cipher spec */ + if(fprint(c->ctl, "changecipher") < 0){ + tlsError(c, EInternalError, "can't enable cipher: %r"); + goto Err; + } + + if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 0) < 0){ + tlsError(c, EInternalError, "can't set finished: %r"); + goto Err; + } + m.tag = HFinished; + m.u.finished = c->finished; + if(!msgSend(c, &m, AFlush)) + goto Err; + msgClear(&m); + if(trace) + trace("tls finished\n"); + + if(fprint(c->ctl, "opened") < 0) + goto Err; + tlsSecOk(c->sec); + return c; + +Err: + msgClear(&m); + tlsConnectionFree(c); + return 0; +} + +static TlsConnection * +tlsClient2(int ctl, int hand, uchar *csid, int ncsid, int (*trace)(char*fmt, ...)) +{ + TlsConnection *c; + Msg m; + uchar kd[MaxKeyData], *epm; + char *secrets; + int creq, nepm, rv; + + if(!initCiphers()) + return nil; + epm = nil; + c = emalloc(sizeof(TlsConnection)); + c->version = ProtocolVersion; + c->ctl = ctl; + c->hand = hand; + c->trace = trace; + c->isClient = 1; + c->clientVersion = c->version; + + c->sec = tlsSecInitc(c->clientVersion, c->crandom); + if(c->sec == nil) + goto Err; + + /* client hello */ + memset(&m, 0, sizeof(m)); + m.tag = HClientHello; + m.u.clientHello.version = c->clientVersion; + memmove(m.u.clientHello.random, c->crandom, RandomSize); + m.u.clientHello.sid = makebytes(csid, ncsid); + m.u.clientHello.ciphers = makeciphers(); + m.u.clientHello.compressors = makebytes(compressors,sizeof(compressors)); + if(!msgSend(c, &m, AFlush)) + goto Err; + msgClear(&m); + + /* server hello */ + if(!msgRecv(c, &m)) + goto Err; + if(m.tag != HServerHello) { + tlsError(c, EUnexpectedMessage, "expected a server hello"); + goto Err; + } + if(setVersion(c, m.u.serverHello.version) < 0) { + tlsError(c, EIllegalParameter, "incompatible version %r"); + goto Err; + } + memmove(c->srandom, m.u.serverHello.random, RandomSize); + c->sid = makebytes(m.u.serverHello.sid->data, m.u.serverHello.sid->len); + if(c->sid->len != 0 && c->sid->len != SidSize) { + tlsError(c, EIllegalParameter, "invalid server session identifier"); + goto Err; + } + if(!setAlgs(c, m.u.serverHello.cipher)) { + tlsError(c, EIllegalParameter, "invalid cipher suite"); + goto Err; + } + if(m.u.serverHello.compressor != CompressionNull) { + tlsError(c, EIllegalParameter, "invalid compression"); + goto Err; + } + msgClear(&m); + + /* certificate */ + if(!msgRecv(c, &m) || m.tag != HCertificate) { + tlsError(c, EUnexpectedMessage, "expected a certificate"); + goto Err; + } + if(m.u.certificate.ncert < 1) { + tlsError(c, EIllegalParameter, "runt certificate"); + goto Err; + } + c->cert = makebytes(m.u.certificate.certs[0]->data, m.u.certificate.certs[0]->len); + msgClear(&m); + + /* server key exchange (optional) */ + if(!msgRecv(c, &m)) + goto Err; + if(m.tag == HServerKeyExchange) { + tlsError(c, EUnexpectedMessage, "got an server key exchange"); + goto Err; + // If implementing this later, watch out for rollback attack + // described in Wagner Schneier 1996, section 4.4. + } + + /* certificate request (optional) */ + creq = 0; + if(m.tag == HCertificateRequest) { + creq = 1; + msgClear(&m); + if(!msgRecv(c, &m)) + goto Err; + } + + if(m.tag != HServerHelloDone) { + tlsError(c, EUnexpectedMessage, "expected a server hello done"); + goto Err; + } + msgClear(&m); + + if(tlsSecSecretc(c->sec, c->sid->data, c->sid->len, c->srandom, + c->cert->data, c->cert->len, c->version, &epm, &nepm, + kd, c->nsecret) < 0){ + tlsError(c, EBadCertificate, "invalid x509/rsa certificate"); + goto Err; + } + secrets = (char*)emalloc(2*c->nsecret); + enc64(secrets, 2*c->nsecret, kd, c->nsecret); + rv = fprint(c->ctl, "secret %s %s 1 %s", c->digest, c->enc, secrets); + memset(secrets, 0, 2*c->nsecret); + free(secrets); + memset(kd, 0, c->nsecret); + if(rv < 0){ + tlsError(c, EHandshakeFailure, "can't set keys: %r"); + goto Err; + } + + if(creq) { + /* send a zero length certificate */ + m.tag = HCertificate; + if(!msgSend(c, &m, AFlush)) + goto Err; + msgClear(&m); + } + + /* client key exchange */ + m.tag = HClientKeyExchange; + m.u.clientKeyExchange.key = makebytes(epm, nepm); + free(epm); + epm = nil; + if(m.u.clientKeyExchange.key == nil) { + tlsError(c, EHandshakeFailure, "can't set secret: %r"); + goto Err; + } + if(!msgSend(c, &m, AFlush)) + goto Err; + msgClear(&m); + + /* change cipher spec */ + if(fprint(c->ctl, "changecipher") < 0){ + tlsError(c, EInternalError, "can't enable cipher: %r"); + goto Err; + } + + // Cipherchange must occur immediately before Finished to avoid + // potential hole; see section 4.3 of Wagner Schneier 1996. + if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 1) < 0){ + tlsError(c, EInternalError, "can't set finished 1: %r"); + goto Err; + } + m.tag = HFinished; + m.u.finished = c->finished; + + if(!msgSend(c, &m, AFlush)) { + fprint(2, "tlsClient nepm=%d\n", nepm); + tlsError(c, EInternalError, "can't flush after client Finished: %r"); + goto Err; + } + msgClear(&m); + + if(tlsSecFinished(c->sec, c->hsmd5, c->hssha1, c->finished.verify, c->finished.n, 0) < 0){ + fprint(2, "tlsClient nepm=%d\n", nepm); + tlsError(c, EInternalError, "can't set finished 0: %r"); + goto Err; + } + if(!msgRecv(c, &m)) { + fprint(2, "tlsClient nepm=%d\n", nepm); + tlsError(c, EInternalError, "can't read server Finished: %r"); + goto Err; + } + if(m.tag != HFinished) { + fprint(2, "tlsClient nepm=%d\n", nepm); + tlsError(c, EUnexpectedMessage, "expected a Finished msg from server"); + goto Err; + } + + if(!finishedMatch(c, &m.u.finished)) { + tlsError(c, EHandshakeFailure, "finished verification failed"); + goto Err; + } + msgClear(&m); + + if(fprint(c->ctl, "opened") < 0){ + if(trace) + trace("unable to do final open: %r\n"); + goto Err; + } + tlsSecOk(c->sec); + return c; + +Err: + free(epm); + msgClear(&m); + tlsConnectionFree(c); + return 0; +} + + +//================= message functions ======================== + +static uchar sendbuf[9000], *sendp; + +static int +msgSend(TlsConnection *c, Msg *m, int act) +{ + uchar *p; // sendp = start of new message; p = write pointer + int nn, n, i; + + if(sendp == nil) + sendp = sendbuf; + p = sendp; + if(c->trace) + c->trace("send %s", msgPrint((char*)p, (sizeof sendbuf) - (p-sendbuf), m)); + + p[0] = m->tag; // header - fill in size later + p += 4; + + switch(m->tag) { + default: + tlsError(c, EInternalError, "can't encode a %d", m->tag); + goto Err; + case HClientHello: + // version + put16(p, m->u.clientHello.version); + p += 2; + + // random + memmove(p, m->u.clientHello.random, RandomSize); + p += RandomSize; + + // sid + n = m->u.clientHello.sid->len; + assert(n < 256); + p[0] = n; + memmove(p+1, m->u.clientHello.sid->data, n); + p += n+1; + + n = m->u.clientHello.ciphers->len; + assert(n > 0 && n < 200); + put16(p, n*2); + p += 2; + for(i=0; iu.clientHello.ciphers->data[i]); + p += 2; + } + + n = m->u.clientHello.compressors->len; + assert(n > 0); + p[0] = n; + memmove(p+1, m->u.clientHello.compressors->data, n); + p += n+1; + break; + case HServerHello: + put16(p, m->u.serverHello.version); + p += 2; + + // random + memmove(p, m->u.serverHello.random, RandomSize); + p += RandomSize; + + // sid + n = m->u.serverHello.sid->len; + assert(n < 256); + p[0] = n; + memmove(p+1, m->u.serverHello.sid->data, n); + p += n+1; + + put16(p, m->u.serverHello.cipher); + p += 2; + p[0] = m->u.serverHello.compressor; + p += 1; + break; + case HServerHelloDone: + break; + case HCertificate: + nn = 0; + for(i = 0; i < m->u.certificate.ncert; i++) + nn += 3 + m->u.certificate.certs[i]->len; + if(p + 3 + nn - sendbuf > sizeof(sendbuf)) { + tlsError(c, EInternalError, "output buffer too small for certificate"); + goto Err; + } + put24(p, nn); + p += 3; + for(i = 0; i < m->u.certificate.ncert; i++){ + put24(p, m->u.certificate.certs[i]->len); + p += 3; + memmove(p, m->u.certificate.certs[i]->data, m->u.certificate.certs[i]->len); + p += m->u.certificate.certs[i]->len; + } + break; + case HClientKeyExchange: + n = m->u.clientKeyExchange.key->len; + if(c->version != SSL3Version){ + put16(p, n); + p += 2; + } + memmove(p, m->u.clientKeyExchange.key->data, n); + p += n; + break; + case HFinished: + memmove(p, m->u.finished.verify, m->u.finished.n); + p += m->u.finished.n; + break; + } + + // go back and fill in size + n = p-sendp; + assert(p <= sendbuf+sizeof(sendbuf)); + put24(sendp+1, n-4); + + // remember hash of Handshake messages + if(m->tag != HHelloRequest) { + md5(sendp, n, 0, &c->hsmd5); + sha1(sendp, n, 0, &c->hssha1); + } + + sendp = p; + if(act == AFlush){ + sendp = sendbuf; + if(write(c->hand, sendbuf, p-sendbuf) < 0){ + fprint(2, "write error: %r\n"); + goto Err; + } + } + msgClear(m); + return 1; +Err: + msgClear(m); + return 0; +} + +static uchar* +tlsReadN(TlsConnection *c, int n) +{ + uchar *p; + int nn, nr; + + nn = c->ep - c->rp; + if(nn < n){ + if(c->rp != c->buf){ + memmove(c->buf, c->rp, nn); + c->rp = c->buf; + c->ep = &c->buf[nn]; + } + for(; nn < n; nn += nr) { + nr = read(c->hand, &c->rp[nn], n - nn); + if(nr <= 0) + return nil; + c->ep += nr; + } + } + p = c->rp; + c->rp += n; + return p; +} + +static int +msgRecv(TlsConnection *c, Msg *m) +{ + uchar *p; + int type, n, nn, i, nsid, nrandom, nciph; + + for(;;) { + p = tlsReadN(c, 4); + if(p == nil) + return 0; + type = p[0]; + n = get24(p+1); + + if(type != HHelloRequest) + break; + if(n != 0) { + tlsError(c, EDecodeError, "invalid hello request during handshake"); + return 0; + } + } + + if(n > sizeof(c->buf)) { + tlsError(c, EDecodeError, "handshake message too long %d %d", n, sizeof(c->buf)); + return 0; + } + + if(type == HSSL2ClientHello){ + /* Cope with an SSL3 ClientHello expressed in SSL2 record format. + This is sent by some clients that we must interoperate + with, such as Java's JSSE and Microsoft's Internet Explorer. */ + p = tlsReadN(c, n); + if(p == nil) + return 0; + md5(p, n, 0, &c->hsmd5); + sha1(p, n, 0, &c->hssha1); + m->tag = HClientHello; + if(n < 22) + goto Short; + m->u.clientHello.version = get16(p+1); + p += 3; + n -= 3; + nn = get16(p); /* cipher_spec_len */ + nsid = get16(p + 2); + nrandom = get16(p + 4); + p += 6; + n -= 6; + if(nsid != 0 /* no sid's, since shouldn't restart using ssl2 header */ + || nrandom < 16 || nn % 3) + goto Err; + if(c->trace && (n - nrandom != nn)) + c->trace("n-nrandom!=nn: n=%d nrandom=%d nn=%d\n", n, nrandom, nn); + /* ignore ssl2 ciphers and look for {0x00, ssl3 cipher} */ + nciph = 0; + for(i = 0; i < nn; i += 3) + if(p[i] == 0) + nciph++; + m->u.clientHello.ciphers = newints(nciph); + nciph = 0; + for(i = 0; i < nn; i += 3) + if(p[i] == 0) + m->u.clientHello.ciphers->data[nciph++] = get16(&p[i + 1]); + p += nn; + m->u.clientHello.sid = makebytes(nil, 0); + if(nrandom > RandomSize) + nrandom = RandomSize; + memset(m->u.clientHello.random, 0, RandomSize - nrandom); + memmove(&m->u.clientHello.random[RandomSize - nrandom], p, nrandom); + m->u.clientHello.compressors = newbytes(1); + m->u.clientHello.compressors->data[0] = CompressionNull; + goto Ok; + } + + md5(p, 4, 0, &c->hsmd5); + sha1(p, 4, 0, &c->hssha1); + + p = tlsReadN(c, n); + if(p == nil) + return 0; + + md5(p, n, 0, &c->hsmd5); + sha1(p, n, 0, &c->hssha1); + + m->tag = type; + + switch(type) { + default: + tlsError(c, EUnexpectedMessage, "can't decode a %d", type); + goto Err; + case HClientHello: + if(n < 2) + goto Short; + m->u.clientHello.version = get16(p); + p += 2; + n -= 2; + + if(n < RandomSize) + goto Short; + memmove(m->u.clientHello.random, p, RandomSize); + p += RandomSize; + n -= RandomSize; + if(n < 1 || n < p[0]+1) + goto Short; + m->u.clientHello.sid = makebytes(p+1, p[0]); + p += m->u.clientHello.sid->len+1; + n -= m->u.clientHello.sid->len+1; + + if(n < 2) + goto Short; + nn = get16(p); + p += 2; + n -= 2; + + if((nn & 1) || n < nn || nn < 2) + goto Short; + m->u.clientHello.ciphers = newints(nn >> 1); + for(i = 0; i < nn; i += 2) + m->u.clientHello.ciphers->data[i >> 1] = get16(&p[i]); + p += nn; + n -= nn; + + if(n < 1 || n < p[0]+1 || p[0] == 0) + goto Short; + nn = p[0]; + m->u.clientHello.compressors = newbytes(nn); + memmove(m->u.clientHello.compressors->data, p+1, nn); + n -= nn + 1; + break; + case HServerHello: + if(n < 2) + goto Short; + m->u.serverHello.version = get16(p); + p += 2; + n -= 2; + + if(n < RandomSize) + goto Short; + memmove(m->u.serverHello.random, p, RandomSize); + p += RandomSize; + n -= RandomSize; + + if(n < 1 || n < p[0]+1) + goto Short; + m->u.serverHello.sid = makebytes(p+1, p[0]); + p += m->u.serverHello.sid->len+1; + n -= m->u.serverHello.sid->len+1; + + if(n < 3) + goto Short; + m->u.serverHello.cipher = get16(p); + m->u.serverHello.compressor = p[2]; + n -= 3; + break; + case HCertificate: + if(n < 3) + goto Short; + nn = get24(p); + p += 3; + n -= 3; + if(n != nn) + goto Short; + /* certs */ + i = 0; + while(n > 0) { + if(n < 3) + goto Short; + nn = get24(p); + p += 3; + n -= 3; + if(nn > n) + goto Short; + m->u.certificate.ncert = i+1; + m->u.certificate.certs = erealloc(m->u.certificate.certs, (i+1)*sizeof(Bytes)); + m->u.certificate.certs[i] = makebytes(p, nn); + p += nn; + n -= nn; + i++; + } + break; + case HCertificateRequest: + if(n < 2) + goto Short; + nn = get16(p); + p += 2; + n -= 2; + if(nn < 1 || nn > n) + goto Short; + m->u.certificateRequest.types = makebytes(p, nn); + nn = get24(p); + p += 3; + n -= 3; + if(nn == 0 || n != nn) + goto Short; + /* cas */ + i = 0; + while(n > 0) { + if(n < 2) + goto Short; + nn = get16(p); + p += 2; + n -= 2; + if(nn < 1 || nn > n) + goto Short; + m->u.certificateRequest.nca = i+1; + m->u.certificateRequest.cas = erealloc(m->u.certificateRequest.cas, (i+1)*sizeof(Bytes)); + m->u.certificateRequest.cas[i] = makebytes(p, nn); + p += nn; + n -= nn; + i++; + } + break; + case HServerHelloDone: + break; + case HClientKeyExchange: + /* + * this message depends upon the encryption selected + * assume rsa. + */ + if(c->version == SSL3Version) + nn = n; + else{ + if(n < 2) + goto Short; + nn = get16(p); + p += 2; + n -= 2; + } + if(n < nn) + goto Short; + m->u.clientKeyExchange.key = makebytes(p, nn); + n -= nn; + break; + case HFinished: + m->u.finished.n = c->finished.n; + if(n < m->u.finished.n) + goto Short; + memmove(m->u.finished.verify, p, m->u.finished.n); + n -= m->u.finished.n; + break; + } + + if(type != HClientHello && n != 0) + goto Short; +Ok: + if(c->trace){ + char buf[8000]; + c->trace("recv %s", msgPrint(buf, sizeof buf, m)); + } + return 1; +Short: + tlsError(c, EDecodeError, "handshake message has invalid length"); +Err: + msgClear(m); + return 0; +} + +static void +msgClear(Msg *m) +{ + int i; + + switch(m->tag) { + default: + sysfatal("msgClear: unknown message type: %d\n", m->tag); + case HHelloRequest: + break; + case HClientHello: + freebytes(m->u.clientHello.sid); + freeints(m->u.clientHello.ciphers); + freebytes(m->u.clientHello.compressors); + break; + case HServerHello: + freebytes(m->u.clientHello.sid); + break; + case HCertificate: + for(i=0; iu.certificate.ncert; i++) + freebytes(m->u.certificate.certs[i]); + free(m->u.certificate.certs); + break; + case HCertificateRequest: + freebytes(m->u.certificateRequest.types); + for(i=0; iu.certificateRequest.nca; i++) + freebytes(m->u.certificateRequest.cas[i]); + free(m->u.certificateRequest.cas); + break; + case HServerHelloDone: + break; + case HClientKeyExchange: + freebytes(m->u.clientKeyExchange.key); + break; + case HFinished: + break; + } + memset(m, 0, sizeof(Msg)); +} + +static char * +bytesPrint(char *bs, char *be, char *s0, Bytes *b, char *s1) +{ + int i; + + if(s0) + bs = seprint(bs, be, "%s", s0); + bs = seprint(bs, be, "["); + if(b == nil) + bs = seprint(bs, be, "nil"); + else + for(i=0; ilen; i++) + bs = seprint(bs, be, "%.2x ", b->data[i]); + bs = seprint(bs, be, "]"); + if(s1) + bs = seprint(bs, be, "%s", s1); + return bs; +} + +static char * +intsPrint(char *bs, char *be, char *s0, Ints *b, char *s1) +{ + int i; + + if(s0) + bs = seprint(bs, be, "%s", s0); + bs = seprint(bs, be, "["); + if(b == nil) + bs = seprint(bs, be, "nil"); + else + for(i=0; ilen; i++) + bs = seprint(bs, be, "%x ", b->data[i]); + bs = seprint(bs, be, "]"); + if(s1) + bs = seprint(bs, be, "%s", s1); + return bs; +} + +static char* +msgPrint(char *buf, int n, Msg *m) +{ + int i; + char *bs = buf, *be = buf+n; + + switch(m->tag) { + default: + bs = seprint(bs, be, "unknown %d\n", m->tag); + break; + case HClientHello: + bs = seprint(bs, be, "ClientHello\n"); + bs = seprint(bs, be, "\tversion: %.4x\n", m->u.clientHello.version); + bs = seprint(bs, be, "\trandom: "); + for(i=0; iu.clientHello.random[i]); + bs = seprint(bs, be, "\n"); + bs = bytesPrint(bs, be, "\tsid: ", m->u.clientHello.sid, "\n"); + bs = intsPrint(bs, be, "\tciphers: ", m->u.clientHello.ciphers, "\n"); + bs = bytesPrint(bs, be, "\tcompressors: ", m->u.clientHello.compressors, "\n"); + break; + case HServerHello: + bs = seprint(bs, be, "ServerHello\n"); + bs = seprint(bs, be, "\tversion: %.4x\n", m->u.serverHello.version); + bs = seprint(bs, be, "\trandom: "); + for(i=0; iu.serverHello.random[i]); + bs = seprint(bs, be, "\n"); + bs = bytesPrint(bs, be, "\tsid: ", m->u.serverHello.sid, "\n"); + bs = seprint(bs, be, "\tcipher: %.4x\n", m->u.serverHello.cipher); + bs = seprint(bs, be, "\tcompressor: %.2x\n", m->u.serverHello.compressor); + break; + case HCertificate: + bs = seprint(bs, be, "Certificate\n"); + for(i=0; iu.certificate.ncert; i++) + bs = bytesPrint(bs, be, "\t", m->u.certificate.certs[i], "\n"); + break; + case HCertificateRequest: + bs = seprint(bs, be, "CertificateRequest\n"); + bs = bytesPrint(bs, be, "\ttypes: ", m->u.certificateRequest.types, "\n"); + bs = seprint(bs, be, "\tcertificateauthorities\n"); + for(i=0; iu.certificateRequest.nca; i++) + bs = bytesPrint(bs, be, "\t\t", m->u.certificateRequest.cas[i], "\n"); + break; + case HServerHelloDone: + bs = seprint(bs, be, "ServerHelloDone\n"); + break; + case HClientKeyExchange: + bs = seprint(bs, be, "HClientKeyExchange\n"); + bs = bytesPrint(bs, be, "\tkey: ", m->u.clientKeyExchange.key, "\n"); + break; + case HFinished: + bs = seprint(bs, be, "HFinished\n"); + for(i=0; iu.finished.n; i++) + bs = seprint(bs, be, "%.2x", m->u.finished.verify[i]); + bs = seprint(bs, be, "\n"); + break; + } + USED(bs); + return buf; +} + +static void +tlsError(TlsConnection *c, int err, char *fmt, ...) +{ + char msg[512]; + va_list arg; + + va_start(arg, fmt); + vseprint(msg, msg+sizeof(msg), fmt, arg); + va_end(arg); + if(c->trace) + c->trace("tlsError: %s\n", msg); + else if(c->erred) + fprint(2, "double error: %r, %s", msg); + else + werrstr("tls: local %s", msg); + c->erred = 1; + fprint(c->ctl, "alert %d", err); +} + +// commit to specific version number +static int +setVersion(TlsConnection *c, int version) +{ + if(c->verset || version > MaxProtoVersion || version < MinProtoVersion) + return -1; + if(version > c->version) + version = c->version; + if(version == SSL3Version) { + c->version = version; + c->finished.n = SSL3FinishedLen; + }else if(version == TLSVersion){ + c->version = version; + c->finished.n = TLSFinishedLen; + }else + return -1; + c->verset = 1; + return fprint(c->ctl, "version 0x%x", version); +} + +// confirm that received Finished message matches the expected value +static int +finishedMatch(TlsConnection *c, Finished *f) +{ + return memcmp(f->verify, c->finished.verify, f->n) == 0; +} + +// free memory associated with TlsConnection struct +// (but don't close the TLS channel itself) +static void +tlsConnectionFree(TlsConnection *c) +{ + tlsSecClose(c->sec); + freebytes(c->sid); + freebytes(c->cert); + memset(c, 0, sizeof(c)); + free(c); +} + + +//================= cipher choices ======================== + +static int weakCipher[CipherMax] = +{ + 1, /* TLS_NULL_WITH_NULL_NULL */ + 1, /* TLS_RSA_WITH_NULL_MD5 */ + 1, /* TLS_RSA_WITH_NULL_SHA */ + 1, /* TLS_RSA_EXPORT_WITH_RC4_40_MD5 */ + 0, /* TLS_RSA_WITH_RC4_128_MD5 */ + 0, /* TLS_RSA_WITH_RC4_128_SHA */ + 1, /* TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 */ + 0, /* TLS_RSA_WITH_IDEA_CBC_SHA */ + 1, /* TLS_RSA_EXPORT_WITH_DES40_CBC_SHA */ + 0, /* TLS_RSA_WITH_DES_CBC_SHA */ + 0, /* TLS_RSA_WITH_3DES_EDE_CBC_SHA */ + 1, /* TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA */ + 0, /* TLS_DH_DSS_WITH_DES_CBC_SHA */ + 0, /* TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA */ + 1, /* TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA */ + 0, /* TLS_DH_RSA_WITH_DES_CBC_SHA */ + 0, /* TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA */ + 1, /* TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA */ + 0, /* TLS_DHE_DSS_WITH_DES_CBC_SHA */ + 0, /* TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA */ + 1, /* TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA */ + 0, /* TLS_DHE_RSA_WITH_DES_CBC_SHA */ + 0, /* TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA */ + 1, /* TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 */ + 1, /* TLS_DH_anon_WITH_RC4_128_MD5 */ + 1, /* TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA */ + 1, /* TLS_DH_anon_WITH_DES_CBC_SHA */ + 1, /* TLS_DH_anon_WITH_3DES_EDE_CBC_SHA */ +}; + +static int +setAlgs(TlsConnection *c, int a) +{ + int i; + + for(i = 0; i < nelem(cipherAlgs); i++){ + if(cipherAlgs[i].tlsid == a){ + c->enc = cipherAlgs[i].enc; + c->digest = cipherAlgs[i].digest; + c->nsecret = cipherAlgs[i].nsecret; + if(c->nsecret > MaxKeyData) + return 0; + return 1; + } + } + return 0; +} + +static int +okCipher(Ints *cv) +{ + int weak, i, j, c; + + weak = 1; + for(i = 0; i < cv->len; i++) { + c = cv->data[i]; + if(c >= CipherMax) + weak = 0; + else + weak &= weakCipher[c]; + for(j = 0; j < nelem(cipherAlgs); j++) + if(cipherAlgs[j].ok && cipherAlgs[j].tlsid == c) + return c; + } + if(weak) + return -2; + return -1; +} + +static int +okCompression(Bytes *cv) +{ + int i, j, c; + + for(i = 0; i < cv->len; i++) { + c = cv->data[i]; + for(j = 0; j < nelem(compressors); j++) { + if(compressors[j] == c) + return c; + } + } + return -1; +} + +static Lock ciphLock; +static int nciphers; + +static int +initCiphers(void) +{ + enum {MaxAlgF = 1024, MaxAlgs = 10}; + char s[MaxAlgF], *flds[MaxAlgs]; + int i, j, n, ok; + + lock(&ciphLock); + if(nciphers){ + unlock(&ciphLock); + return nciphers; + } + j = open("#a/tls/encalgs", OREAD); + if(j < 0){ + werrstr("can't open #a/tls/encalgs: %r"); + return 0; + } + n = read(j, s, MaxAlgF-1); + close(j); + if(n <= 0){ + werrstr("nothing in #a/tls/encalgs: %r"); + return 0; + } + s[n] = 0; + n = getfields(s, flds, MaxAlgs, 1, " \t\r\n"); + for(i = 0; i < nelem(cipherAlgs); i++){ + ok = 0; + for(j = 0; j < n; j++){ + if(strcmp(cipherAlgs[i].enc, flds[j]) == 0){ + ok = 1; + break; + } + } + cipherAlgs[i].ok = ok; + } + + j = open("#a/tls/hashalgs", OREAD); + if(j < 0){ + werrstr("can't open #a/tls/hashalgs: %r"); + return 0; + } + n = read(j, s, MaxAlgF-1); + close(j); + if(n <= 0){ + werrstr("nothing in #a/tls/hashalgs: %r"); + return 0; + } + s[n] = 0; + n = getfields(s, flds, MaxAlgs, 1, " \t\r\n"); + for(i = 0; i < nelem(cipherAlgs); i++){ + ok = 0; + for(j = 0; j < n; j++){ + if(strcmp(cipherAlgs[i].digest, flds[j]) == 0){ + ok = 1; + break; + } + } + cipherAlgs[i].ok &= ok; + if(cipherAlgs[i].ok) + nciphers++; + } + unlock(&ciphLock); + return nciphers; +} + +static Ints* +makeciphers(void) +{ + Ints *is; + int i, j; + + is = newints(nciphers); + j = 0; + for(i = 0; i < nelem(cipherAlgs); i++){ + if(cipherAlgs[i].ok) + is->data[j++] = cipherAlgs[i].tlsid; + } + return is; +} + + + +//================= security functions ======================== + +// given X.509 certificate, set up connection to factotum +// for using corresponding private key +static AuthRpc* +factotum_rsa_open(uchar *cert, int certlen) +{ + int afd; + char *s; + mpint *pub = nil; + RSApub *rsapub; + AuthRpc *rpc; + + // start talking to factotum + if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0) + return nil; + if((rpc = auth_allocrpc(afd)) == nil){ + close(afd); + return nil; + } + s = "proto=rsa service=tls role=client"; + if(auth_rpc(rpc, "start", s, strlen(s)) != ARok){ + factotum_rsa_close(rpc); + return nil; + } + + // roll factotum keyring around to match certificate + rsapub = X509toRSApub(cert, certlen, nil, 0); + while(1){ + if(auth_rpc(rpc, "read", nil, 0) != ARok){ + factotum_rsa_close(rpc); + rpc = nil; + goto done; + } + pub = strtomp(rpc->arg, nil, 16, nil); + assert(pub != nil); + if(mpcmp(pub,rsapub->n) == 0) + break; + } +done: + mpfree(pub); + rsapubfree(rsapub); + return rpc; +} + +static mpint* +factotum_rsa_decrypt(AuthRpc *rpc, mpint *cipher) +{ + char *p; + int rv; + + if((p = mptoa(cipher, 16, nil, 0)) == nil) + return nil; + rv = auth_rpc(rpc, "write", p, strlen(p)); + free(p); + if(rv != ARok || auth_rpc(rpc, "read", nil, 0) != ARok) + return nil; + mpfree(cipher); + return strtomp(rpc->arg, nil, 16, nil); +} + +static void +factotum_rsa_close(AuthRpc*rpc) +{ + if(!rpc) + return; + close(rpc->afd); + auth_freerpc(rpc); +} + +static void +tlsPmd5(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +{ + uchar ai[MD5dlen], tmp[MD5dlen]; + int i, n; + MD5state *s; + + // generate a1 + s = hmac_md5(label, nlabel, key, nkey, nil, nil); + s = hmac_md5(seed0, nseed0, key, nkey, nil, s); + hmac_md5(seed1, nseed1, key, nkey, ai, s); + + while(nbuf > 0) { + s = hmac_md5(ai, MD5dlen, key, nkey, nil, nil); + s = hmac_md5(label, nlabel, key, nkey, nil, s); + s = hmac_md5(seed0, nseed0, key, nkey, nil, s); + hmac_md5(seed1, nseed1, key, nkey, tmp, s); + n = MD5dlen; + if(n > nbuf) + n = nbuf; + for(i = 0; i < n; i++) + buf[i] ^= tmp[i]; + buf += n; + nbuf -= n; + hmac_md5(ai, MD5dlen, key, nkey, tmp, nil); + memmove(ai, tmp, MD5dlen); + } +} + +static void +tlsPsha1(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +{ + uchar ai[SHA1dlen], tmp[SHA1dlen]; + int i, n; + SHAstate *s; + + // generate a1 + s = hmac_sha1(label, nlabel, key, nkey, nil, nil); + s = hmac_sha1(seed0, nseed0, key, nkey, nil, s); + hmac_sha1(seed1, nseed1, key, nkey, ai, s); + + while(nbuf > 0) { + s = hmac_sha1(ai, SHA1dlen, key, nkey, nil, nil); + s = hmac_sha1(label, nlabel, key, nkey, nil, s); + s = hmac_sha1(seed0, nseed0, key, nkey, nil, s); + hmac_sha1(seed1, nseed1, key, nkey, tmp, s); + n = SHA1dlen; + if(n > nbuf) + n = nbuf; + for(i = 0; i < n; i++) + buf[i] ^= tmp[i]; + buf += n; + nbuf -= n; + hmac_sha1(ai, SHA1dlen, key, nkey, tmp, nil); + memmove(ai, tmp, SHA1dlen); + } +} + +// fill buf with md5(args)^sha1(args) +static void +tlsPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +{ + int i; + int nlabel = strlen(label); + int n = (nkey + 1) >> 1; + + for(i = 0; i < nbuf; i++) + buf[i] = 0; + tlsPmd5(buf, nbuf, key, n, (uchar*)label, nlabel, seed0, nseed0, seed1, nseed1); + tlsPsha1(buf, nbuf, key+nkey-n, n, (uchar*)label, nlabel, seed0, nseed0, seed1, nseed1); +} + +/* + * for setting server session id's + */ +static Lock sidLock; +static long maxSid = 1; + +/* the keys are verified to have the same public components + * and to function correctly with pkcs 1 encryption and decryption. */ +static TlsSec* +tlsSecInits(int cvers, uchar *csid, int ncsid, uchar *crandom, uchar *ssid, int *nssid, uchar *srandom) +{ + TlsSec *sec = emalloc(sizeof(*sec)); + + USED(csid); USED(ncsid); // ignore csid for now + + memmove(sec->crandom, crandom, RandomSize); + sec->clientVers = cvers; + + put32(sec->srandom, time(0)); + genrandom(sec->srandom+4, RandomSize-4); + memmove(srandom, sec->srandom, RandomSize); + + /* + * make up a unique sid: use our pid, and and incrementing id + * can signal no sid by setting nssid to 0. + */ + memset(ssid, 0, SidSize); + put32(ssid, getpid()); + lock(&sidLock); + put32(ssid+4, maxSid++); + unlock(&sidLock); + *nssid = SidSize; + return sec; +} + +static int +tlsSecSecrets(TlsSec *sec, int vers, uchar *epm, int nepm, uchar *kd, int nkd) +{ + if(epm != nil){ + if(setVers(sec, vers) < 0) + goto Err; + serverMasterSecret(sec, epm, nepm); + }else if(sec->vers != vers){ + werrstr("mismatched session versions"); + goto Err; + } + setSecrets(sec, kd, nkd); + return 0; +Err: + sec->ok = -1; + return -1; +} + +static TlsSec* +tlsSecInitc(int cvers, uchar *crandom) +{ + TlsSec *sec = emalloc(sizeof(*sec)); + sec->clientVers = cvers; + put32(sec->crandom, time(0)); + genrandom(sec->crandom+4, RandomSize-4); + memmove(crandom, sec->crandom, RandomSize); + return sec; +} + +static int +tlsSecSecretc(TlsSec *sec, uchar *sid, int nsid, uchar *srandom, uchar *cert, int ncert, int vers, uchar **epm, int *nepm, uchar *kd, int nkd) +{ + RSApub *pub; + + pub = nil; + + USED(sid); + USED(nsid); + + memmove(sec->srandom, srandom, RandomSize); + + if(setVers(sec, vers) < 0) + goto Err; + + pub = X509toRSApub(cert, ncert, nil, 0); + if(pub == nil){ + werrstr("invalid x509/rsa certificate"); + goto Err; + } + if(clientMasterSecret(sec, pub, epm, nepm) < 0) + goto Err; + rsapubfree(pub); + setSecrets(sec, kd, nkd); + return 0; + +Err: + if(pub != nil) + rsapubfree(pub); + sec->ok = -1; + return -1; +} + +static int +tlsSecFinished(TlsSec *sec, MD5state md5, SHAstate sha1, uchar *fin, int nfin, int isclient) +{ + if(sec->nfin != nfin){ + sec->ok = -1; + werrstr("invalid finished exchange"); + return -1; + } + md5.malloced = 0; + sha1.malloced = 0; + (*sec->setFinished)(sec, md5, sha1, fin, isclient); + return 1; +} + +static void +tlsSecOk(TlsSec *sec) +{ + if(sec->ok == 0) + sec->ok = 1; +} + +static void +tlsSecKill(TlsSec *sec) +{ + if(!sec) + return; + factotum_rsa_close(sec->rpc); + sec->ok = -1; +} + +static void +tlsSecClose(TlsSec *sec) +{ + if(!sec) + return; + factotum_rsa_close(sec->rpc); + free(sec->server); + free(sec); +} + +static int +setVers(TlsSec *sec, int v) +{ + if(v == SSL3Version){ + sec->setFinished = sslSetFinished; + sec->nfin = SSL3FinishedLen; + sec->prf = sslPRF; + }else if(v == TLSVersion){ + sec->setFinished = tlsSetFinished; + sec->nfin = TLSFinishedLen; + sec->prf = tlsPRF; + }else{ + werrstr("invalid version"); + return -1; + } + sec->vers = v; + return 0; +} + +/* + * generate secret keys from the master secret. + * + * different crypto selections will require different amounts + * of key expansion and use of key expansion data, + * but it's all generated using the same function. + */ +static void +setSecrets(TlsSec *sec, uchar *kd, int nkd) +{ + (*sec->prf)(kd, nkd, sec->sec, MasterSecretSize, "key expansion", + sec->srandom, RandomSize, sec->crandom, RandomSize); +} + +/* + * set the master secret from the pre-master secret. + */ +static void +setMasterSecret(TlsSec *sec, Bytes *pm) +{ + (*sec->prf)(sec->sec, MasterSecretSize, pm->data, MasterSecretSize, "master secret", + sec->crandom, RandomSize, sec->srandom, RandomSize); +} + +static void +serverMasterSecret(TlsSec *sec, uchar *epm, int nepm) +{ + Bytes *pm; + + pm = pkcs1_decrypt(sec, epm, nepm); + + // if the client messed up, just continue as if everything is ok, + // to prevent attacks to check for correctly formatted messages. + // Hence the fprint(2,) can't be replaced by tlsError(), which sends an Alert msg to the client. + if(sec->ok < 0 || pm == nil || get16(pm->data) != sec->clientVers){ + fprint(2, "serverMasterSecret failed ok=%d pm=%p pmvers=%x cvers=%x nepm=%d\n", + sec->ok, pm, pm ? get16(pm->data) : -1, sec->clientVers, nepm); + sec->ok = -1; + if(pm != nil) + freebytes(pm); + pm = newbytes(MasterSecretSize); + genrandom(pm->data, MasterSecretSize); + } + setMasterSecret(sec, pm); + memset(pm->data, 0, pm->len); + freebytes(pm); +} + +static int +clientMasterSecret(TlsSec *sec, RSApub *pub, uchar **epm, int *nepm) +{ + Bytes *pm, *key; + + pm = newbytes(MasterSecretSize); + put16(pm->data, sec->clientVers); + genrandom(pm->data+2, MasterSecretSize - 2); + + setMasterSecret(sec, pm); + + key = pkcs1_encrypt(pm, pub, 2); + memset(pm->data, 0, pm->len); + freebytes(pm); + if(key == nil){ + werrstr("tls pkcs1_encrypt failed"); + return -1; + } + + *nepm = key->len; + *epm = malloc(*nepm); + if(*epm == nil){ + freebytes(key); + werrstr("out of memory"); + return -1; + } + memmove(*epm, key->data, *nepm); + + freebytes(key); + + return 1; +} + +static void +sslSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient) +{ + DigestState *s; + uchar h0[MD5dlen], h1[SHA1dlen], pad[48]; + char *label; + + if(isClient) + label = "CLNT"; + else + label = "SRVR"; + + md5((uchar*)label, 4, nil, &hsmd5); + md5(sec->sec, MasterSecretSize, nil, &hsmd5); + memset(pad, 0x36, 48); + md5(pad, 48, nil, &hsmd5); + md5(nil, 0, h0, &hsmd5); + memset(pad, 0x5C, 48); + s = md5(sec->sec, MasterSecretSize, nil, nil); + s = md5(pad, 48, nil, s); + md5(h0, MD5dlen, finished, s); + + sha1((uchar*)label, 4, nil, &hssha1); + sha1(sec->sec, MasterSecretSize, nil, &hssha1); + memset(pad, 0x36, 40); + sha1(pad, 40, nil, &hssha1); + sha1(nil, 0, h1, &hssha1); + memset(pad, 0x5C, 40); + s = sha1(sec->sec, MasterSecretSize, nil, nil); + s = sha1(pad, 40, nil, s); + sha1(h1, SHA1dlen, finished + MD5dlen, s); +} + +// fill "finished" arg with md5(args)^sha1(args) +static void +tlsSetFinished(TlsSec *sec, MD5state hsmd5, SHAstate hssha1, uchar *finished, int isClient) +{ + uchar h0[MD5dlen], h1[SHA1dlen]; + char *label; + + // get current hash value, but allow further messages to be hashed in + md5(nil, 0, h0, &hsmd5); + sha1(nil, 0, h1, &hssha1); + + if(isClient) + label = "client finished"; + else + label = "server finished"; + tlsPRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h0, MD5dlen, h1, SHA1dlen); +} + +static void +sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed0, int nseed0, uchar *seed1, int nseed1) +{ + DigestState *s; + uchar sha1dig[SHA1dlen], md5dig[MD5dlen], tmp[26]; + int i, n, len; + + USED(label); + len = 1; + while(nbuf > 0){ + if(len > 26) + return; + for(i = 0; i < len; i++) + tmp[i] = 'A' - 1 + len; + s = sha1(tmp, len, nil, nil); + s = sha1(key, nkey, nil, s); + s = sha1(seed0, nseed0, nil, s); + sha1(seed1, nseed1, sha1dig, s); + s = md5(key, nkey, nil, nil); + md5(sha1dig, SHA1dlen, md5dig, s); + n = MD5dlen; + if(n > nbuf) + n = nbuf; + memmove(buf, md5dig, n); + buf += n; + nbuf -= n; + len++; + } +} + +static mpint* +bytestomp(Bytes* bytes) +{ + mpint* ans; + + ans = betomp(bytes->data, bytes->len, nil); + return ans; +} + +/* + * Convert mpint* to Bytes, putting high order byte first. + */ +static Bytes* +mptobytes(mpint* big) +{ + int n, m; + uchar *a; + Bytes* ans; + + n = (mpsignif(big)+7)/8; + m = mptobe(big, nil, n, &a); + ans = makebytes(a, m); + return ans; +} + +// Do RSA computation on block according to key, and pad +// result on left with zeros to make it modlen long. +static Bytes* +rsacomp(Bytes* block, RSApub* key, int modlen) +{ + mpint *x, *y; + Bytes *a, *ybytes; + int ylen; + + x = bytestomp(block); + y = rsaencrypt(key, x, nil); + mpfree(x); + ybytes = mptobytes(y); + ylen = ybytes->len; + + if(ylen < modlen) { + a = newbytes(modlen); + memset(a->data, 0, modlen-ylen); + memmove(a->data+modlen-ylen, ybytes->data, ylen); + freebytes(ybytes); + ybytes = a; + } + else if(ylen > modlen) { + // assume it has leading zeros (mod should make it so) + a = newbytes(modlen); + memmove(a->data, ybytes->data, modlen); + freebytes(ybytes); + ybytes = a; + } + mpfree(y); + return ybytes; +} + +// encrypt data according to PKCS#1, /lib/rfc/rfc2437 9.1.2.1 +static Bytes* +pkcs1_encrypt(Bytes* data, RSApub* key, int blocktype) +{ + Bytes *pad, *eb, *ans; + int i, dlen, padlen, modlen; + + modlen = (mpsignif(key->n)+7)/8; + dlen = data->len; + if(modlen < 12 || dlen > modlen - 11) + return nil; + padlen = modlen - 3 - dlen; + pad = newbytes(padlen); + genrandom(pad->data, padlen); + for(i = 0; i < padlen; i++) { + if(blocktype == 0) + pad->data[i] = 0; + else if(blocktype == 1) + pad->data[i] = 255; + else if(pad->data[i] == 0) + pad->data[i] = 1; + } + eb = newbytes(modlen); + eb->data[0] = 0; + eb->data[1] = blocktype; + memmove(eb->data+2, pad->data, padlen); + eb->data[padlen+2] = 0; + memmove(eb->data+padlen+3, data->data, dlen); + ans = rsacomp(eb, key, modlen); + freebytes(eb); + freebytes(pad); + return ans; +} + +// decrypt data according to PKCS#1, with given key. +// expect a block type of 2. +static Bytes* +pkcs1_decrypt(TlsSec *sec, uchar *epm, int nepm) +{ + Bytes *eb, *ans = nil; + int i, modlen; + mpint *x, *y; + + modlen = (mpsignif(sec->rsapub->n)+7)/8; + if(nepm != modlen) + return nil; + x = betomp(epm, nepm, nil); + y = factotum_rsa_decrypt(sec->rpc, x); + if(y == nil) + return nil; + eb = mptobytes(y); + if(eb->len < modlen){ // pad on left with zeros + ans = newbytes(modlen); + memset(ans->data, 0, modlen-eb->len); + memmove(ans->data+modlen-eb->len, eb->data, eb->len); + freebytes(eb); + eb = ans; + } + if(eb->data[0] == 0 && eb->data[1] == 2) { + for(i = 2; i < modlen; i++) + if(eb->data[i] == 0) + break; + if(i < modlen - 1) + ans = makebytes(eb->data+i+1, modlen-(i+1)); + } + freebytes(eb); + return ans; +} + + +//================= general utility functions ======================== + +static void * +emalloc(int n) +{ + void *p; + if(n==0) + n=1; + p = malloc(n); + if(p == nil){ + exits("out of memory"); + } + memset(p, 0, n); + return p; +} + +static void * +erealloc(void *ReallocP, int ReallocN) +{ + if(ReallocN == 0) + ReallocN = 1; + if(!ReallocP) + ReallocP = emalloc(ReallocN); + else if(!(ReallocP = realloc(ReallocP, ReallocN))){ + exits("out of memory"); + } + return(ReallocP); +} + +static void +put32(uchar *p, u32int x) +{ + p[0] = x>>24; + p[1] = x>>16; + p[2] = x>>8; + p[3] = x; +} + +static void +put24(uchar *p, int x) +{ + p[0] = x>>16; + p[1] = x>>8; + p[2] = x; +} + +static void +put16(uchar *p, int x) +{ + p[0] = x>>8; + p[1] = x; +} + +static u32int +get32(uchar *p) +{ + return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; +} + +static int +get24(uchar *p) +{ + return (p[0]<<16)|(p[1]<<8)|p[2]; +} + +static int +get16(uchar *p) +{ + return (p[0]<<8)|p[1]; +} + +/* ANSI offsetof() */ +#define OFFSET(x, s) ((int)(&(((s*)0)->x))) + +/* + * malloc and return a new Bytes structure capable of + * holding len bytes. (len >= 0) + * Used to use crypt_malloc, which aborts if malloc fails. + */ +static Bytes* +newbytes(int len) +{ + Bytes* ans; + + ans = (Bytes*)malloc(OFFSET(data[0], Bytes) + len); + ans->len = len; + return ans; +} + +/* + * newbytes(len), with data initialized from buf + */ +static Bytes* +makebytes(uchar* buf, int len) +{ + Bytes* ans; + + ans = newbytes(len); + memmove(ans->data, buf, len); + return ans; +} + +static void +freebytes(Bytes* b) +{ + if(b != nil) + free(b); +} + +/* len is number of ints */ +static Ints* +newints(int len) +{ + Ints* ans; + + ans = (Ints*)malloc(OFFSET(data[0], Ints) + len*sizeof(int)); + ans->len = len; + return ans; +} + +static Ints* +makeints(int* buf, int len) +{ + Ints* ans; + + ans = newints(len); + if(len > 0) + memmove(ans->data, buf, len*sizeof(int)); + return ans; +} + +static void +freeints(Ints* b) +{ + if(b != nil) + free(b); +} diff --git a/libsec/x509.c b/libsec/x509.c new file mode 100644 index 0000000..081dd8f --- /dev/null +++ b/libsec/x509.c @@ -0,0 +1,2520 @@ +#include +#include +#include +#include + +typedef DigestState*(*DigestFun)(uchar*,ulong,uchar*,DigestState*); + +/* ANSI offsetof, backwards. */ +#define OFFSETOF(a, b) offsetof(b, a) + +/*=============================================================*/ +/* general ASN1 declarations and parsing + * + * For now, this is used only for extracting the key from an + * X509 certificate, so the entire collection is hidden. But + * someday we should probably make the functions visible and + * give them their own man page. + */ +typedef struct Elem Elem; +typedef struct Tag Tag; +typedef struct Value Value; +typedef struct Bytes Bytes; +typedef struct Ints Ints; +typedef struct Bits Bits; +typedef struct Elist Elist; + +/* tag classes */ +#define Universal 0 +#define Context 0x80 + +/* universal tags */ +#define BOOLEAN 1 +#define INTEGER 2 +#define BIT_STRING 3 +#define OCTET_STRING 4 +#define NULLTAG 5 +#define OBJECT_ID 6 +#define ObjectDescriptor 7 +#define EXTERNAL 8 +#define REAL 9 +#define ENUMERATED 10 +#define EMBEDDED_PDV 11 +#define SEQUENCE 16 /* also SEQUENCE OF */ +#define SETOF 17 /* also SETOF OF */ +#define NumericString 18 +#define PrintableString 19 +#define TeletexString 20 +#define VideotexString 21 +#define IA5String 22 +#define UTCTime 23 +#define GeneralizedTime 24 +#define GraphicString 25 +#define VisibleString 26 +#define GeneralString 27 +#define UniversalString 28 +#define BMPString 30 + +struct Bytes { + int len; + uchar data[1]; +}; + +struct Ints { + int len; + int data[1]; +}; + +struct Bits { + int len; /* number of bytes */ + int unusedbits; /* unused bits in last byte */ + uchar data[1]; /* most-significant bit first */ +}; + +struct Tag { + int class; + int num; +}; + +enum { VBool, VInt, VOctets, VBigInt, VReal, VOther, + VBitString, VNull, VEOC, VObjId, VString, VSeq, VSet }; +struct Value { + int tag; /* VBool, etc. */ + union { + int boolval; + int intval; + Bytes* octetsval; + Bytes* bigintval; + Bytes* realval; /* undecoded; hardly ever used */ + Bytes* otherval; + Bits* bitstringval; + Ints* objidval; + char* stringval; + Elist* seqval; + Elist* setval; + } u; /* (Don't use anonymous unions, for ease of porting) */ +}; + +struct Elem { + Tag tag; + Value val; +}; + +struct Elist { + Elist* tl; + Elem hd; +}; + +/* decoding errors */ +enum { ASN_OK, ASN_ESHORT, ASN_ETOOBIG, ASN_EVALLEN, + ASN_ECONSTR, ASN_EPRIM, ASN_EINVAL, ASN_EUNIMPL }; + + +/* here are the functions to consider making extern someday */ +static Bytes* newbytes(int len); +static Bytes* makebytes(uchar* buf, int len); +static void freebytes(Bytes* b); +static Bytes* catbytes(Bytes* b1, Bytes* b2); +static Ints* newints(int len); +static Ints* makeints(int* buf, int len); +static void freeints(Ints* b); +static Bits* newbits(int len); +static Bits* makebits(uchar* buf, int len, int unusedbits); +static void freebits(Bits* b); +static Elist* mkel(Elem e, Elist* tail); +static void freeelist(Elist* el); +static int elistlen(Elist* el); +static int is_seq(Elem* pe, Elist** pseq); +static int is_set(Elem* pe, Elist** pset); +static int is_int(Elem* pe, int* pint); +static int is_bigint(Elem* pe, Bytes** pbigint); +static int is_bitstring(Elem* pe, Bits** pbits); +static int is_octetstring(Elem* pe, Bytes** poctets); +static int is_oid(Elem* pe, Ints** poid); +static int is_string(Elem* pe, char** pstring); +static int is_time(Elem* pe, char** ptime); +static int decode(uchar* a, int alen, Elem* pelem); +static int decode_seq(uchar* a, int alen, Elist** pelist); +static int decode_value(uchar* a, int alen, int kind, int isconstr, Value* pval); +static int encode(Elem e, Bytes** pbytes); +static int oid_lookup(Ints* o, Ints** tab); +static void freevalfields(Value* v); +static mpint *asn1mpint(Elem *e); + + + +#define TAG_MASK 0x1F +#define CONSTR_MASK 0x20 +#define CLASS_MASK 0xC0 +#define MAXOBJIDLEN 20 + +static int ber_decode(uchar** pp, uchar* pend, Elem* pelem); +static int tag_decode(uchar** pp, uchar* pend, Tag* ptag, int* pisconstr); +static int length_decode(uchar** pp, uchar* pend, int* plength); +static int value_decode(uchar** pp, uchar* pend, int length, int kind, int isconstr, Value* pval); +static int int_decode(uchar** pp, uchar* pend, int count, int unsgned, int* pint); +static int uint7_decode(uchar** pp, uchar* pend, int* pint); +static int octet_decode(uchar** pp, uchar* pend, int length, int isconstr, Bytes** pbytes); +static int seq_decode(uchar** pp, uchar* pend, int length, int isconstr, Elist** pelist); +static int enc(uchar** pp, Elem e, int lenonly); +static int val_enc(uchar** pp, Elem e, int *pconstr, int lenonly); +static void uint7_enc(uchar** pp, int num, int lenonly); +static void int_enc(uchar** pp, int num, int unsgned, int lenonly); + +static void * +emalloc(int n) +{ + void *p; + if(n==0) + n=1; + p = malloc(n); + if(p == nil){ + exits("out of memory"); + } + memset(p, 0, n); + return p; +} + +static char* +estrdup(char *s) +{ + char *d, *d0; + + if(!s) + return 0; + d = d0 = emalloc(strlen(s)+1); + while(*d++ = *s++) + ; + return d0; +} + + +/* + * Decode a[0..len] as a BER encoding of an ASN1 type. + * The return value is one of ASN_OK, etc. + * Depending on the error, the returned elem may or may not + * be nil. + */ +static int +decode(uchar* a, int alen, Elem* pelem) +{ + uchar* p = a; + + return ber_decode(&p, &a[alen], pelem); +} + +/* + * Like decode, but continue decoding after first element + * of array ends. + */ +static int +decode_seq(uchar* a, int alen, Elist** pelist) +{ + uchar* p = a; + + return seq_decode(&p, &a[alen], -1, 1, pelist); +} + +/* + * Decode the whole array as a BER encoding of an ASN1 value, + * (i.e., the part after the tag and length). + * Assume the value is encoded as universal tag "kind". + * The constr arg is 1 if the value is constructed, 0 if primitive. + * If there's an error, the return string will contain the error. + * Depending on the error, the returned value may or may not + * be nil. + */ +static int +decode_value(uchar* a, int alen, int kind, int isconstr, Value* pval) +{ + uchar* p = a; + + return value_decode(&p, &a[alen], alen, kind, isconstr, pval); +} + +/* + * All of the following decoding routines take arguments: + * uchar **pp; + * uchar *pend; + * Where parsing is supposed to start at **pp, and when parsing + * is done, *pp is updated to point at next char to be parsed. + * The pend pointer is just past end of string; an error should + * be returned parsing hasn't finished by then. + * + * The returned int is ASN_OK if all went fine, else ASN_ESHORT, etc. + * The remaining argument(s) are pointers to where parsed entity goes. + */ + +/* Decode an ASN1 'Elem' (tag, length, value) */ +static int +ber_decode(uchar** pp, uchar* pend, Elem* pelem) +{ + int err; + int isconstr; + int length; + Tag tag; + Value val; + + err = tag_decode(pp, pend, &tag, &isconstr); + if(err == ASN_OK) { + err = length_decode(pp, pend, &length); + if(err == ASN_OK) { + if(tag.class == Universal) + err = value_decode(pp, pend, length, tag.num, isconstr, &val); + else + err = value_decode(pp, pend, length, OCTET_STRING, 0, &val); + if(err == ASN_OK) { + pelem->tag = tag; + pelem->val = val; + } + } + } + return err; +} + +/* Decode a tag field */ +static int +tag_decode(uchar** pp, uchar* pend, Tag* ptag, int* pisconstr) +{ + int err; + int v; + uchar* p; + + err = ASN_OK; + p = *pp; + if(pend-p >= 2) { + v = *p++; + ptag->class = v&CLASS_MASK; + if(v&CONSTR_MASK) + *pisconstr = 1; + else + *pisconstr = 0; + v &= TAG_MASK; + if(v == TAG_MASK) + err = uint7_decode(&p, pend, &v); + ptag->num = v; + } + else + err = ASN_ESHORT; + *pp = p; + return err; +} + +/* Decode a length field */ +static int +length_decode(uchar** pp, uchar* pend, int* plength) +{ + int err; + int num; + int v; + uchar* p; + + err = ASN_OK; + num = 0; + p = *pp; + if(p < pend) { + v = *p++; + if(v&0x80) + err = int_decode(&p, pend, v&0x7F, 1, &num); + else if(v == 0x80) + num = -1; + else + num = v; + } + else + err = ASN_ESHORT; + *pp = p; + *plength = num; + return err; +} + +/* Decode a value field */ +static int +value_decode(uchar** pp, uchar* pend, int length, int kind, int isconstr, Value* pval) +{ + int err; + Bytes* va; + int num; + int bitsunused; + int subids[MAXOBJIDLEN]; + int isubid; + Elist* vl; + uchar* p; + uchar* pe; + + err = ASN_OK; + p = *pp; + if(length == -1) { /* "indefinite" length spec */ + if(!isconstr) + err = ASN_EINVAL; + } + else if(p + length > pend) + err = ASN_EVALLEN; + if(err != ASN_OK) + return err; + + switch(kind) { + case 0: + /* marker for end of indefinite constructions */ + if(length == 0) + pval->tag = VNull; + else + err = ASN_EINVAL; + break; + + case BOOLEAN: + if(isconstr) + err = ASN_ECONSTR; + else if(length != 1) + err = ASN_EVALLEN; + else { + pval->tag = VBool; + pval->u.boolval = (*p++ != 0); + } + break; + + case INTEGER: + case ENUMERATED: + if(isconstr) + err = ASN_ECONSTR; + else if(length <= 4) { + err = int_decode(&p, pend, length, 0, &num); + if(err == ASN_OK) { + pval->tag = VInt; + pval->u.intval = num; + } + } + else { + pval->tag = VBigInt; + pval->u.bigintval = makebytes(p, length); + p += length; + } + break; + + case BIT_STRING: + pval->tag = VBitString; + if(isconstr) { + if(length == -1 && p + 2 <= pend && *p == 0 && *(p+1) ==0) { + pval->u.bitstringval = makebits(0, 0, 0); + p += 2; + } + else + /* TODO: recurse and concat results */ + err = ASN_EUNIMPL; + } + else { + if(length < 2) { + if(length == 1 && *p == 0) { + pval->u.bitstringval = makebits(0, 0, 0); + p++; + } + else + err = ASN_EINVAL; + } + else { + bitsunused = *p; + if(bitsunused > 7) + err = ASN_EINVAL; + else if(length > 0x0FFFFFFF) + err = ASN_ETOOBIG; + else { + pval->u.bitstringval = makebits(p+1, length-1, bitsunused); + p += length; + } + } + } + break; + + case OCTET_STRING: + case ObjectDescriptor: + err = octet_decode(&p, pend, length, isconstr, &va); + if(err == ASN_OK) { + pval->tag = VOctets; + pval->u.octetsval = va; + } + break; + + case NULLTAG: + if(isconstr) + err = ASN_ECONSTR; + else if(length != 0) + err = ASN_EVALLEN; + else + pval->tag = VNull; + break; + + case OBJECT_ID: + if(isconstr) + err = ASN_ECONSTR; + else if(length == 0) + err = ASN_EVALLEN; + else { + isubid = 0; + pe = p+length; + while(p < pe && isubid < MAXOBJIDLEN) { + err = uint7_decode(&p, pend, &num); + if(err != ASN_OK) + break; + if(isubid == 0) { + subids[isubid++] = num / 40; + subids[isubid++] = num % 40; + } + else + subids[isubid++] = num; + } + if(err == ASN_OK) { + if(p != pe) + err = ASN_EVALLEN; + else { + pval->tag = VObjId; + pval->u.objidval = makeints(subids, isubid); + } + } + } + break; + + case EXTERNAL: + case EMBEDDED_PDV: + /* TODO: parse this internally */ + if(p+length > pend) + err = ASN_EVALLEN; + else { + pval->tag = VOther; + pval->u.otherval = makebytes(p, length); + p += length; + } + break; + + case REAL: + /* Let the application decode */ + if(isconstr) + err = ASN_ECONSTR; + else if(p+length > pend) + err = ASN_EVALLEN; + else { + pval->tag = VReal; + pval->u.realval = makebytes(p, length); + p += length; + } + break; + + case SEQUENCE: + err = seq_decode(&p, pend, length, isconstr, &vl); + if(err == ASN_OK) { + pval->tag = VSeq ; + pval->u.seqval = vl; + } + break; + + case SETOF: + err = seq_decode(&p, pend, length, isconstr, &vl); + if(err == ASN_OK) { + pval->tag = VSet; + pval->u.setval = vl; + } + break; + + case NumericString: + case PrintableString: + case TeletexString: + case VideotexString: + case IA5String: + case UTCTime: + case GeneralizedTime: + case GraphicString: + case VisibleString: + case GeneralString: + case UniversalString: + case BMPString: + /* TODO: figure out when character set conversion is necessary */ + err = octet_decode(&p, pend, length, isconstr, &va); + if(err == ASN_OK) { + pval->tag = VString; + pval->u.stringval = (char*)emalloc(va->len+1); + memmove(pval->u.stringval, va->data, va->len); + pval->u.stringval[va->len] = 0; + free(va); + } + break; + + default: + if(p+length > pend) + err = ASN_EVALLEN; + else { + pval->tag = VOther; + pval->u.otherval = makebytes(p, length); + p += length; + } + break; + } + *pp = p; + return err; +} + +/* + * Decode an int in format where count bytes are + * concatenated to form value. + * Although ASN1 allows any size integer, we return + * an error if the result doesn't fit in a 32-bit int. + * If unsgned is not set, make sure to propagate sign bit. + */ +static int +int_decode(uchar** pp, uchar* pend, int count, int unsgned, int* pint) +{ + int err; + int num; + uchar* p; + + p = *pp; + err = ASN_OK; + num = 0; + if(p+count <= pend) { + if((count > 4) || (unsgned && count == 4 && (*p&0x80))) + err = ASN_ETOOBIG; + else { + if(!unsgned && count > 0 && count < 4 && (*p&0x80)) + num = -1; // set all bits, initially + while(count--) + num = (num << 8)|(*p++); + } + } + else + err = ASN_ESHORT; + *pint = num; + *pp = p; + return err; +} + +/* + * Decode an unsigned int in format where each + * byte except last has high bit set, and remaining + * seven bits of each byte are concatenated to form value. + * Although ASN1 allows any size integer, we return + * an error if the result doesn't fit in a 32 bit int. + */ +static int +uint7_decode(uchar** pp, uchar* pend, int* pint) +{ + int err; + int num; + int more; + int v; + uchar* p; + + p = *pp; + err = ASN_OK; + num = 0; + more = 1; + while(more && p < pend) { + v = *p++; + if(num&0x7F000000) { + err = ASN_ETOOBIG; + break; + } + num <<= 7; + more = v&0x80; + num |= (v&0x7F); + } + if(p == pend) + err = ASN_ESHORT; + *pint = num; + *pp = p; + return err; +} + +/* + * Decode an octet string, recursively if isconstr. + * We've already checked that length==-1 implies isconstr==1, + * and otherwise that specified length fits within (*pp..pend) + */ +static int +octet_decode(uchar** pp, uchar* pend, int length, int isconstr, Bytes** pbytes) +{ + int err; + uchar* p; + Bytes* ans; + Bytes* newans; + uchar* pstart; + uchar* pold; + Elem elem; + + err = ASN_OK; + p = *pp; + ans = nil; + if(length >= 0 && !isconstr) { + ans = makebytes(p, length); + p += length; + } + else { + /* constructed, either definite or indefinite length */ + pstart = p; + for(;;) { + if(length >= 0 && p >= pstart + length) { + if(p != pstart + length) + err = ASN_EVALLEN; + break; + } + pold = p; + err = ber_decode(&p, pend, &elem); + if(err != ASN_OK) + break; + switch(elem.val.tag) { + case VOctets: + newans = catbytes(ans, elem.val.u.octetsval); + freebytes(ans); + ans = newans; + break; + + case VEOC: + if(length != -1) { + p = pold; + err = ASN_EINVAL; + } + goto cloop_done; + + default: + p = pold; + err = ASN_EINVAL; + goto cloop_done; + } + } +cloop_done: + ; + } + *pp = p; + *pbytes = ans; + return err; +} + +/* + * Decode a sequence or set. + * We've already checked that length==-1 implies isconstr==1, + * and otherwise that specified length fits within (*p..pend) + */ +static int +seq_decode(uchar** pp, uchar* pend, int length, int isconstr, Elist** pelist) +{ + int err; + uchar* p; + uchar* pstart; + uchar* pold; + Elist* ans; + Elem elem; + Elist* lve; + Elist* lveold; + + err = ASN_OK; + ans = nil; + p = *pp; + if(!isconstr) + err = ASN_EPRIM; + else { + /* constructed, either definite or indefinite length */ + lve = nil; + pstart = p; + for(;;) { + if(length >= 0 && p >= pstart + length) { + if(p != pstart + length) + err = ASN_EVALLEN; + break; + } + pold = p; + err = ber_decode(&p, pend, &elem); + if(err != ASN_OK) + break; + if(elem.val.tag == VEOC) { + if(length != -1) { + p = pold; + err = ASN_EINVAL; + } + break; + } + else + lve = mkel(elem, lve); + } + if(err == ASN_OK) { + /* reverse back to original order */ + while(lve != nil) { + lveold = lve; + lve = lve->tl; + lveold->tl = ans; + ans = lveold; + } + } + } + *pp = p; + *pelist = ans; + return err; +} + +/* + * Encode e by BER rules, putting answer in *pbytes. + * This is done by first calling enc with lenonly==1 + * to get the length of the needed buffer, + * then allocating the buffer and using enc again to fill it up. + */ +static int +encode(Elem e, Bytes** pbytes) +{ + uchar* p; + Bytes* ans; + int err; + uchar uc; + + p = &uc; + err = enc(&p, e, 1); + if(err == ASN_OK) { + ans = newbytes(p-&uc); + p = ans->data; + err = enc(&p, e, 0); + *pbytes = ans; + } + return err; +} + +/* + * The various enc functions take a pointer to a pointer + * into a buffer, and encode their entity starting there, + * updating the pointer afterwards. + * If lenonly is 1, only the pointer update is done, + * allowing enc to be called first to calculate the needed + * buffer length. + * If lenonly is 0, it is assumed that the answer will fit. + */ + +static int +enc(uchar** pp, Elem e, int lenonly) +{ + int err; + int vlen; + int constr; + Tag tag; + int v; + int ilen; + uchar* p; + uchar* psave; + + p = *pp; + err = val_enc(&p, e, &constr, 1); + if(err != ASN_OK) + return err; + vlen = p - *pp; + p = *pp; + tag = e.tag; + v = tag.class|constr; + if(tag.num < 31) { + if(!lenonly) + *p = (v|tag.num); + p++; + } + else { + if(!lenonly) + *p = (v|31); + p++; + if(tag.num < 0) + return ASN_EINVAL; + uint7_enc(&p, tag.num, lenonly); + } + if(vlen < 0x80) { + if(!lenonly) + *p = vlen; + p++; + } + else { + psave = p; + int_enc(&p, vlen, 1, 1); + ilen = p-psave; + p = psave; + if(!lenonly) { + *p++ = (0x80 | ilen); + int_enc(&p, vlen, 1, 0); + } + else + p += 1 + ilen; + } + if(!lenonly) + val_enc(&p, e, &constr, 0); + else + p += vlen; + *pp = p; + return err; +} + +static int +val_enc(uchar** pp, Elem e, int *pconstr, int lenonly) +{ + int err; + uchar* p; + int kind; + int cl; + int v; + Bytes* bb = nil; + Bits* bits; + Ints* oid; + int k; + Elist* el; + char* s; + + p = *pp; + err = ASN_OK; + kind = e.tag.num; + cl = e.tag.class; + *pconstr = 0; + if(cl != Universal) { + switch(e.val.tag) { + case VBool: + kind = BOOLEAN; + break; + case VInt: + kind = INTEGER; + break; + case VBigInt: + kind = INTEGER; + break; + case VOctets: + kind = OCTET_STRING; + break; + case VReal: + kind = REAL; + break; + case VOther: + kind = OCTET_STRING; + break; + case VBitString: + kind = BIT_STRING; + break; + case VNull: + kind = NULLTAG; + break; + case VObjId: + kind = OBJECT_ID; + break; + case VString: + kind = UniversalString; + break; + case VSeq: + kind = SEQUENCE; + break; + case VSet: + kind = SETOF; + break; + } + } + switch(kind) { + case BOOLEAN: + if(is_int(&e, &v)) { + if(v != 0) + v = 255; + int_enc(&p, v, 1, lenonly); + } + else + err = ASN_EINVAL; + break; + + case INTEGER: + case ENUMERATED: + if(is_int(&e, &v)) + int_enc(&p, v, 0, lenonly); + else { + if(is_bigint(&e, &bb)) { + if(!lenonly) + memmove(p, bb->data, bb->len); + p += bb->len; + } + else + err = ASN_EINVAL; + } + break; + + case BIT_STRING: + if(is_bitstring(&e, &bits)) { + if(bits->len == 0) { + if(!lenonly) + *p = 0; + p++; + } + else { + v = bits->unusedbits; + if(v < 0 || v > 7) + err = ASN_EINVAL; + else { + if(!lenonly) { + *p = v; + memmove(p+1, bits->data, bits->len); + } + p += 1 + bits->len; + } + } + } + else + err = ASN_EINVAL; + break; + + case OCTET_STRING: + case ObjectDescriptor: + case EXTERNAL: + case REAL: + case EMBEDDED_PDV: + bb = nil; + switch(e.val.tag) { + case VOctets: + bb = e.val.u.octetsval; + break; + case VReal: + bb = e.val.u.realval; + break; + case VOther: + bb = e.val.u.otherval; + break; + } + if(bb != nil) { + if(!lenonly) + memmove(p, bb->data, bb->len); + p += bb->len; + } + else + err = ASN_EINVAL; + break; + + case NULLTAG: + break; + + case OBJECT_ID: + if(is_oid(&e, &oid)) { + for(k = 0; k < oid->len; k++) { + v = oid->data[k]; + if(k == 0) { + v *= 40; + if(oid->len > 1) + v += oid->data[++k]; + } + uint7_enc(&p, v, lenonly); + } + } + else + err = ASN_EINVAL; + break; + + case SEQUENCE: + case SETOF: + el = nil; + if(e.val.tag == VSeq) + el = e.val.u.seqval; + else if(e.val.tag == VSet) + el = e.val.u.setval; + else + err = ASN_EINVAL; + if(el != nil) { + *pconstr = CONSTR_MASK; + for(; el != nil; el = el->tl) { + err = enc(&p, el->hd, lenonly); + if(err != ASN_OK) + break; + } + } + break; + + case NumericString: + case PrintableString: + case TeletexString: + case VideotexString: + case IA5String: + case UTCTime: + case GeneralizedTime: + case GraphicString: + case VisibleString: + case GeneralString: + case UniversalString: + case BMPString: + if(e.val.tag == VString) { + s = e.val.u.stringval; + if(s != nil) { + v = strlen(s); + if(!lenonly) + memmove(p, s, v); + p += v; + } + } + else + err = ASN_EINVAL; + break; + + default: + err = ASN_EINVAL; + } + *pp = p; + return err; +} + +/* + * Encode num as unsigned 7 bit values with top bit 1 on all bytes + * except last, only putting in bytes if !lenonly. + */ +static void +uint7_enc(uchar** pp, int num, int lenonly) +{ + int n; + int v; + int k; + uchar* p; + + p = *pp; + n = 1; + v = num >> 7; + while(v > 0) { + v >>= 7; + n++; + } + if(lenonly) + p += n; + else { + for(k = (n - 1)*7; k > 0; k -= 7) + *p++= ((num >> k)|0x80); + *p++ = (num&0x7F); + } + *pp = p; +} + +/* + * Encode num as unsigned or signed integer, + * only putting in bytes if !lenonly. + * Encoding is length followed by bytes to concatenate. + */ +static void +int_enc(uchar** pp, int num, int unsgned, int lenonly) +{ + int v; + int n; + int prevv; + int k; + uchar* p; + + p = *pp; + v = num; + if(v < 0) + v = -(v + 1); + n = 1; + prevv = v; + v >>= 8; + while(v > 0) { + prevv = v; + v >>= 8; + n++; + } + if(!unsgned && (prevv&0x80)) + n++; + if(lenonly) + p += n; + else { + for(k = (n - 1)*8; k >= 0; k -= 8) + *p++ = (num >> k); + } + *pp = p; +} + +static int +ints_eq(Ints* a, Ints* b) +{ + int alen; + int i; + + alen = a->len; + if(alen != b->len) + return 0; + for(i = 0; i < alen; i++) + if(a->data[i] != b->data[i]) + return 0; + return 1; +} + +/* + * Look up o in tab (which must have nil entry to terminate). + * Return index of matching entry, or -1 if none. + */ +static int +oid_lookup(Ints* o, Ints** tab) +{ + int i; + + for(i = 0; tab[i] != nil; i++) + if(ints_eq(o, tab[i])) + return i; + return -1; +} + +/* + * Return true if *pe is a SEQUENCE, and set *pseq to + * the value of the sequence if so. + */ +static int +is_seq(Elem* pe, Elist** pseq) +{ + if(pe->tag.class == Universal && pe->tag.num == SEQUENCE && pe->val.tag == VSeq) { + *pseq = pe->val.u.seqval; + return 1; + } + return 0; +} + +static int +is_set(Elem* pe, Elist** pset) +{ + if(pe->tag.class == Universal && pe->tag.num == SETOF && pe->val.tag == VSet) { + *pset = pe->val.u.setval; + return 1; + } + return 0; +} + +static int +is_int(Elem* pe, int* pint) +{ + if(pe->tag.class == Universal) { + if(pe->tag.num == INTEGER && pe->val.tag == VInt) { + *pint = pe->val.u.intval; + return 1; + } + else if(pe->tag.num == BOOLEAN && pe->val.tag == VBool) { + *pint = pe->val.u.boolval; + return 1; + } + } + return 0; +} + +/* + * for convience, all VInt's are readable via this routine, + * as well as all VBigInt's + */ +static int +is_bigint(Elem* pe, Bytes** pbigint) +{ + int v, n, i; + + if(pe->tag.class == Universal && pe->tag.num == INTEGER) { + if(pe->val.tag == VBigInt) + *pbigint = pe->val.u.bigintval; + else if(pe->val.tag == VInt){ + v = pe->val.u.intval; + for(n = 1; n < 4; n++) + if((1 << (8 * n)) > v) + break; + *pbigint = newbytes(n); + for(i = 0; i < n; i++) + (*pbigint)->data[i] = (v >> ((n - 1 - i) * 8)); + }else + return 0; + return 1; + } + return 0; +} + +static int +is_bitstring(Elem* pe, Bits** pbits) +{ + if(pe->tag.class == Universal && pe->tag.num == BIT_STRING && pe->val.tag == VBitString) { + *pbits = pe->val.u.bitstringval; + return 1; + } + return 0; +} + +static int +is_octetstring(Elem* pe, Bytes** poctets) +{ + if(pe->tag.class == Universal && pe->tag.num == OCTET_STRING && pe->val.tag == VOctets) { + *poctets = pe->val.u.octetsval; + return 1; + } + return 0; +} + +static int +is_oid(Elem* pe, Ints** poid) +{ + if(pe->tag.class == Universal && pe->tag.num == OBJECT_ID && pe->val.tag == VObjId) { + *poid = pe->val.u.objidval; + return 1; + } + return 0; +} + +static int +is_string(Elem* pe, char** pstring) +{ + if(pe->tag.class == Universal) { + switch(pe->tag.num) { + case NumericString: + case PrintableString: + case TeletexString: + case VideotexString: + case IA5String: + case GraphicString: + case VisibleString: + case GeneralString: + case UniversalString: + case BMPString: + if(pe->val.tag == VString) { + *pstring = pe->val.u.stringval; + return 1; + } + } + } + return 0; +} + +static int +is_time(Elem* pe, char** ptime) +{ + if(pe->tag.class == Universal + && (pe->tag.num == UTCTime || pe->tag.num == GeneralizedTime) + && pe->val.tag == VString) { + *ptime = pe->val.u.stringval; + return 1; + } + return 0; +} + + +/* + * malloc and return a new Bytes structure capable of + * holding len bytes. (len >= 0) + */ +static Bytes* +newbytes(int len) +{ + Bytes* ans; + + ans = (Bytes*)emalloc(OFFSETOF(data[0], Bytes) + len); + ans->len = len; + return ans; +} + +/* + * newbytes(len), with data initialized from buf + */ +static Bytes* +makebytes(uchar* buf, int len) +{ + Bytes* ans; + + ans = newbytes(len); + memmove(ans->data, buf, len); + return ans; +} + +static void +freebytes(Bytes* b) +{ + if(b != nil) + free(b); +} + +/* + * Make a new Bytes, containing bytes of b1 followed by those of b2. + * Either b1 or b2 or both can be nil. + */ +static Bytes* +catbytes(Bytes* b1, Bytes* b2) +{ + Bytes* ans; + int n; + + if(b1 == nil) { + if(b2 == nil) + ans = newbytes(0); + else + ans = makebytes(b2->data, b2->len); + } + else if(b2 == nil) { + ans = makebytes(b1->data, b1->len); + } + else { + n = b1->len + b2->len; + ans = newbytes(n); + ans->len = n; + memmove(ans->data, b1->data, b1->len); + memmove(ans->data+b1->len, b2->data, b2->len); + } + return ans; +} + +/* len is number of ints */ +static Ints* +newints(int len) +{ + Ints* ans; + + ans = (Ints*)emalloc(OFFSETOF(data[0], Ints) + len*sizeof(int)); + ans->len = len; + return ans; +} + +static Ints* +makeints(int* buf, int len) +{ + Ints* ans; + + ans = newints(len); + if(len > 0) + memmove(ans->data, buf, len*sizeof(int)); + return ans; +} + +static void +freeints(Ints* b) +{ + if(b != nil) + free(b); +} + +/* len is number of bytes */ +static Bits* +newbits(int len) +{ + Bits* ans; + + ans = (Bits*)emalloc(OFFSETOF(data[0], Bits) + len); + ans->len = len; + ans->unusedbits = 0; + return ans; +} + +static Bits* +makebits(uchar* buf, int len, int unusedbits) +{ + Bits* ans; + + ans = newbits(len); + memmove(ans->data, buf, len); + ans->unusedbits = unusedbits; + return ans; +} + +static void +freebits(Bits* b) +{ + if(b != nil) + free(b); +} + +static Elist* +mkel(Elem e, Elist* tail) +{ + Elist* el; + + el = (Elist*)emalloc(sizeof(Elist)); + el->hd = e; + el->tl = tail; + return el; +} + +static int +elistlen(Elist* el) +{ + int ans = 0; + while(el != nil) { + ans++; + el = el->tl; + } + return ans; +} + +/* Frees elist, but not fields inside values of constituent elems */ +static void +freeelist(Elist* el) +{ + Elist* next; + + while(el != nil) { + next = el->tl; + free(el); + el = next; + } +} + +/* free any allocated structures inside v (recursively freeing Elists) */ +static void +freevalfields(Value* v) +{ + Elist* el; + Elist* l; + if(v == nil) + return; + switch(v->tag) { + case VOctets: + freebytes(v->u.octetsval); + break; + case VBigInt: + freebytes(v->u.bigintval); + break; + case VReal: + freebytes(v->u.realval); + break; + case VOther: + freebytes(v->u.otherval); + break; + case VBitString: + freebits(v->u.bitstringval); + break; + case VObjId: + freeints(v->u.objidval); + break; + case VString: + if (v->u.stringval) + free(v->u.stringval); + break; + case VSeq: + el = v->u.seqval; + for(l = el; l != nil; l = l->tl) + freevalfields(&l->hd.val); + if (el) + freeelist(el); + break; + case VSet: + el = v->u.setval; + for(l = el; l != nil; l = l->tl) + freevalfields(&l->hd.val); + if (el) + freeelist(el); + break; + } +} + +/* end of general ASN1 functions */ + + + + + +/*=============================================================*/ +/* + * Decode and parse an X.509 Certificate, defined by this ASN1: + * Certificate ::= SEQUENCE { + * certificateInfo CertificateInfo, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING } + * + * CertificateInfo ::= SEQUENCE { + * version [0] INTEGER DEFAULT v1 (0), + * serialNumber INTEGER, + * signature AlgorithmIdentifier, + * issuer Name, + * validity Validity, + * subject Name, + * subjectPublicKeyInfo SubjectPublicKeyInfo } + * (version v2 has two more fields, optional unique identifiers for + * issuer and subject; since we ignore these anyway, we won't parse them) + * + * Validity ::= SEQUENCE { + * notBefore UTCTime, + * notAfter UTCTime } + * + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFER, + * parameters ANY DEFINED BY ALGORITHM OPTIONAL } + * + * Name ::= SEQUENCE OF RelativeDistinguishedName + * + * RelativeDistinguishedName ::= SETOF SIZE(1..MAX) OF AttributeTypeAndValue + * + * AttributeTypeAndValue ::= SEQUENCE { + * type OBJECT IDENTIFER, + * value DirectoryString } + * (selected attributes have these Object Ids: + * commonName {2 5 4 3} + * countryName {2 5 4 6} + * localityName {2 5 4 7} + * stateOrProvinceName {2 5 4 8} + * organizationName {2 5 4 10} + * organizationalUnitName {2 5 4 11} + * ) + * + * DirectoryString ::= CHOICE { + * teletexString TeletexString, + * printableString PrintableString, + * universalString UniversalString } + * + * See rfc1423, rfc2437 for AlgorithmIdentifier, subjectPublicKeyInfo, signature. + * + * Not yet implemented: + * CertificateRevocationList ::= SIGNED SEQUENCE{ + * signature AlgorithmIdentifier, + * issuer Name, + * lastUpdate UTCTime, + * nextUpdate UTCTime, + * revokedCertificates + * SEQUENCE OF CRLEntry OPTIONAL} + * CRLEntry ::= SEQUENCE{ + * userCertificate SerialNumber, + * revocationDate UTCTime} + */ + +typedef struct CertX509 { + int serial; + char* issuer; + char* validity_start; + char* validity_end; + char* subject; + int publickey_alg; + Bytes* publickey; + int signature_alg; + Bytes* signature; +} CertX509; + +/* Algorithm object-ids */ +enum { + ALG_rsaEncryption, + ALG_md2WithRSAEncryption, + ALG_md4WithRSAEncryption, + ALG_md5WithRSAEncryption, + ALG_sha1WithRSAEncryption, + ALG_md5, + NUMALGS +}; +typedef struct Ints7 { + int len; + int data[7]; +} Ints7; +static Ints7 oid_rsaEncryption = {7, 1, 2, 840, 113549, 1, 1, 1 }; +static Ints7 oid_md2WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 2 }; +static Ints7 oid_md4WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 3 }; +static Ints7 oid_md5WithRSAEncryption = {7, 1, 2, 840, 113549, 1, 1, 4 }; +static Ints7 oid_sha1WithRSAEncryption ={7, 1, 2, 840, 113549, 1, 1, 5 }; +static Ints7 oid_md5 ={6, 1, 2, 840, 113549, 2, 5, 0 }; +static Ints *alg_oid_tab[NUMALGS+1] = { + (Ints*)&oid_rsaEncryption, + (Ints*)&oid_md2WithRSAEncryption, + (Ints*)&oid_md4WithRSAEncryption, + (Ints*)&oid_md5WithRSAEncryption, + (Ints*)&oid_sha1WithRSAEncryption, + (Ints*)&oid_md5, + nil +}; +static DigestFun digestalg[NUMALGS+1] = { md5, md5, md5, md5, sha1, md5, nil }; + +static void +freecert(CertX509* c) +{ + if (!c) return; + if(c->issuer != nil) + free(c->issuer); + if(c->validity_start != nil) + free(c->validity_start); + if(c->validity_end != nil) + free(c->validity_end); + if(c->subject != nil) + free(c->subject); + freebytes(c->publickey); + freebytes(c->signature); +} + +/* + * Parse the Name ASN1 type. + * The sequence of RelativeDistinguishedName's gives a sort of pathname, + * from most general to most specific. Each element of the path can be + * one or more (but usually just one) attribute-value pair, such as + * countryName="US". + * We'll just form a "postal-style" address string by concatenating the elements + * from most specific to least specific, separated by commas. + * Return name-as-string (which must be freed by caller). + */ +static char* +parse_name(Elem* e) +{ + Elist* el; + Elem* es; + Elist* esetl; + Elem* eat; + Elist* eatl; + char* s; + enum { MAXPARTS = 100 }; + char* parts[MAXPARTS]; + int i; + int plen; + char* ans = nil; + + if(!is_seq(e, &el)) + goto errret; + i = 0; + plen = 0; + while(el != nil) { + es = &el->hd; + if(!is_set(es, &esetl)) + goto errret; + while(esetl != nil) { + eat = &esetl->hd; + if(!is_seq(eat, &eatl) || elistlen(eatl) != 2) + goto errret; + if(!is_string(&eatl->tl->hd, &s) || i>=MAXPARTS) + goto errret; + parts[i++] = s; + plen += strlen(s) + 2; /* room for ", " after */ + esetl = esetl->tl; + } + el = el->tl; + } + if(i > 0) { + ans = (char*)emalloc(plen); + *ans = '\0'; + while(--i >= 0) { + s = parts[i]; + strcat(ans, s); + if(i > 0) + strcat(ans, ", "); + } + } + +errret: + return ans; +} + +/* + * Parse an AlgorithmIdentifer ASN1 type. + * Look up the oid in oid_tab and return one of OID_rsaEncryption, etc.., + * or -1 if not found. + * For now, ignore parameters, since none of our algorithms need them. + */ +static int +parse_alg(Elem* e) +{ + Elist* el; + Ints* oid; + + if(!is_seq(e, &el) || el == nil || !is_oid(&el->hd, &oid)) + return -1; + return oid_lookup(oid, alg_oid_tab); +} + +static CertX509* +decode_cert(Bytes* a) +{ + int ok = 0; + int n; + CertX509* c = nil; + Elem ecert; + Elem* ecertinfo; + Elem* esigalg; + Elem* esig; + Elem* eserial; + Elem* eissuer; + Elem* evalidity; + Elem* esubj; + Elem* epubkey; + Elist* el; + Elist* elcert = nil; + Elist* elcertinfo = nil; + Elist* elvalidity = nil; + Elist* elpubkey = nil; + Bits* bits = nil; + Bytes* b; + Elem* e; + + if(decode(a->data, a->len, &ecert) != ASN_OK) + goto errret; + + c = (CertX509*)emalloc(sizeof(CertX509)); + c->serial = -1; + c->issuer = nil; + c->validity_start = nil; + c->validity_end = nil; + c->subject = nil; + c->publickey_alg = -1; + c->publickey = nil; + c->signature_alg = -1; + c->signature = nil; + + /* Certificate */ + if(!is_seq(&ecert, &elcert) || elistlen(elcert) !=3) + goto errret; + ecertinfo = &elcert->hd; + el = elcert->tl; + esigalg = &el->hd; + c->signature_alg = parse_alg(esigalg); + el = el->tl; + esig = &el->hd; + + /* Certificate Info */ + if(!is_seq(ecertinfo, &elcertinfo)) + goto errret; + n = elistlen(elcertinfo); + if(n < 6) + goto errret; + eserial =&elcertinfo->hd; + el = elcertinfo->tl; + /* check for optional version, marked by explicit context tag 0 */ + if(eserial->tag.class == Context && eserial->tag.num == 0) { + eserial = &el->hd; + if(n < 7) + goto errret; + el = el->tl; + } + + if(parse_alg(&el->hd) != c->signature_alg) + goto errret; + el = el->tl; + eissuer = &el->hd; + el = el->tl; + evalidity = &el->hd; + el = el->tl; + esubj = &el->hd; + el = el->tl; + epubkey = &el->hd; + if(!is_int(eserial, &c->serial)) { + if(!is_bigint(eserial, &b)) + goto errret; + c->serial = -1; /* else we have to change cert struct */ + } + c->issuer = parse_name(eissuer); + if(c->issuer == nil) + goto errret; + /* Validity */ + if(!is_seq(evalidity, &elvalidity)) + goto errret; + if(elistlen(elvalidity) != 2) + goto errret; + e = &elvalidity->hd; + if(!is_time(e, &c->validity_start)) + goto errret; + e->val.u.stringval = nil; /* string ownership transfer */ + e = &elvalidity->tl->hd; + if(!is_time(e, &c->validity_end)) + goto errret; + e->val.u.stringval = nil; /* string ownership transfer */ + + /* resume CertificateInfo */ + c->subject = parse_name(esubj); + if(c->subject == nil) + goto errret; + + /* SubjectPublicKeyInfo */ + if(!is_seq(epubkey, &elpubkey)) + goto errret; + if(elistlen(elpubkey) != 2) + goto errret; + + c->publickey_alg = parse_alg(&elpubkey->hd); + if(c->publickey_alg < 0) + goto errret; + if(!is_bitstring(&elpubkey->tl->hd, &bits)) + goto errret; + if(bits->unusedbits != 0) + goto errret; + c->publickey = makebytes(bits->data, bits->len); + + /*resume Certificate */ + if(c->signature_alg < 0) + goto errret; + if(!is_bitstring(esig, &bits)) + goto errret; + c->signature = makebytes(bits->data, bits->len); + ok = 1; + +errret: + freevalfields(&ecert.val); /* recurses through lists, too */ + if(!ok){ + freecert(c); + c = nil; + } + return c; +} + +/* + * RSAPublickKey :: SEQUENCE { + * modulus INTEGER, + * publicExponent INTEGER + * } + */ +static RSApub* +decode_rsapubkey(Bytes* a) +{ + Elem e; + Elist *el; + mpint *mp; + RSApub* key; + + key = rsapuballoc(); + if(decode(a->data, a->len, &e) != ASN_OK) + goto errret; + if(!is_seq(&e, &el) || elistlen(el) != 2) + goto errret; + + key->n = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + el = el->tl; + key->ek = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + return key; +errret: + rsapubfree(key); + return nil; +} + +/* + * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER -- (inverse of q) mod p } + */ +static RSApriv* +decode_rsaprivkey(Bytes* a) +{ + int version; + Elem e; + Elist *el; + mpint *mp; + RSApriv* key; + + key = rsaprivalloc(); + if(decode(a->data, a->len, &e) != ASN_OK) + goto errret; + if(!is_seq(&e, &el) || elistlen(el) != 9) + goto errret; + if(!is_int(&el->hd, &version) || version != 0) + goto errret; + + el = el->tl; + key->pub.n = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + el = el->tl; + key->pub.ek = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + el = el->tl; + key->dk = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + el = el->tl; + key->q = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + el = el->tl; + key->p = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + el = el->tl; + key->kq = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + el = el->tl; + key->kp = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + el = el->tl; + key->c2 = mp = asn1mpint(&el->hd); + if(mp == nil) + goto errret; + + return key; +errret: + rsaprivfree(key); + return nil; +} + +static mpint* +asn1mpint(Elem *e) +{ + Bytes *b; + mpint *mp; + int v; + + if(is_int(e, &v)) + return itomp(v, nil); + if(is_bigint(e, &b)) { + mp = betomp(b->data, b->len, nil); + freebytes(b); + return mp; + } + return nil; +} + +static mpint* +pkcs1pad(Bytes *b, mpint *modulus) +{ + int n = (mpsignif(modulus)+7)/8; + int pm1, i; + uchar *p; + mpint *mp; + + pm1 = n - 1 - b->len; + p = (uchar*)emalloc(n); + p[0] = 0; + p[1] = 1; + for(i = 2; i < pm1; i++) + p[i] = 0xFF; + p[pm1] = 0; + memcpy(&p[pm1+1], b->data, b->len); + mp = betomp(p, n, nil); + free(p); + return mp; +} + +RSApriv* +asn1toRSApriv(uchar *kd, int kn) +{ + Bytes *b; + RSApriv *key; + + b = makebytes(kd, kn); + key = decode_rsaprivkey(b); + freebytes(b); + return key; +} + +/* + * digest(CertificateInfo) + * Our ASN.1 library doesn't return pointers into the original + * data array, so we need to do a little hand decoding. + */ +static void +digest_certinfo(Bytes *cert, DigestFun digestfun, uchar *digest) +{ + uchar *info, *p, *pend; + ulong infolen; + int isconstr, length; + Tag tag; + Elem elem; + + p = cert->data; + pend = cert->data + cert->len; + if(tag_decode(&p, pend, &tag, &isconstr) != ASN_OK || + tag.class != Universal || tag.num != SEQUENCE || + length_decode(&p, pend, &length) != ASN_OK || + p+length > pend) + return; + info = p; + if(ber_decode(&p, pend, &elem) != ASN_OK || elem.tag.num != SEQUENCE) + return; + infolen = p - info; + (*digestfun)(info, infolen, digest, nil); +} + +static char* +verify_signature(Bytes* signature, RSApub *pk, uchar *edigest, Elem **psigalg) +{ + Elem e; + Elist *el; + Bytes *digest; + uchar *pkcs1buf, *buf; + int buflen; + mpint *pkcs1; + + /* see 9.2.1 of rfc2437 */ + pkcs1 = betomp(signature->data, signature->len, nil); + mpexp(pkcs1, pk->ek, pk->n, pkcs1); + buflen = mptobe(pkcs1, nil, 0, &pkcs1buf); + buf = pkcs1buf; + if(buflen < 4 || buf[0] != 1) + return "expected 1"; + buf++; + while(buf[0] == 0xff) + buf++; + if(buf[0] != 0) + return "expected 0"; + buf++; + buflen -= buf-pkcs1buf; + if(decode(buf, buflen, &e) != ASN_OK || !is_seq(&e, &el) || elistlen(el) != 2 || + !is_octetstring(&el->tl->hd, &digest)) + return "signature parse error"; + *psigalg = &el->hd; + if(memcmp(digest->data, edigest, digest->len) == 0) + return nil; + return "digests did not match"; +} + +RSApub* +X509toRSApub(uchar *cert, int ncert, char *name, int nname) +{ + char *e; + Bytes *b; + CertX509 *c; + RSApub *pk; + + b = makebytes(cert, ncert); + c = decode_cert(b); + freebytes(b); + if(c == nil) + return nil; + if(name != nil && c->subject != nil){ + e = strchr(c->subject, ','); + if(e != nil) + *e = 0; // take just CN part of Distinguished Name + strncpy(name, c->subject, nname); + } + pk = decode_rsapubkey(c->publickey); + freecert(c); + return pk; +} + +char* +X509verify(uchar *cert, int ncert, RSApub *pk) +{ + char *e; + Bytes *b; + CertX509 *c; + uchar digest[SHA1dlen]; + Elem *sigalg; + + b = makebytes(cert, ncert); + c = decode_cert(b); + if(c != nil) + digest_certinfo(b, digestalg[c->signature_alg], digest); + freebytes(b); + if(c == nil) + return "cannot decode cert"; + e = verify_signature(c->signature, pk, digest, &sigalg); + freecert(c); + return e; +} + +/* ------- Elem constructors ---------- */ +static Elem +Null(void) +{ + Elem e; + + e.tag.class = Universal; + e.tag.num = NULLTAG; + e.val.tag = VNull; + return e; +} + +static Elem +mkint(int j) +{ + Elem e; + + e.tag.class = Universal; + e.tag.num = INTEGER; + e.val.tag = VInt; + e.val.u.intval = j; + return e; +} + +static Elem +mkbigint(mpint *p) +{ + Elem e; + uchar *buf; + int buflen; + + e.tag.class = Universal; + e.tag.num = INTEGER; + e.val.tag = VBigInt; + buflen = mptobe(p, nil, 0, &buf); + e.val.u.bigintval = makebytes(buf, buflen); + free(buf); + return e; +} + +static Elem +mkstring(char *s) +{ + Elem e; + + e.tag.class = Universal; + e.tag.num = IA5String; + e.val.tag = VString; + e.val.u.stringval = estrdup(s); + return e; +} + +static Elem +mkoctet(uchar *buf, int buflen) +{ + Elem e; + + e.tag.class = Universal; + e.tag.num = OCTET_STRING; + e.val.tag = VOctets; + e.val.u.octetsval = makebytes(buf, buflen); + return e; +} + +static Elem +mkbits(uchar *buf, int buflen) +{ + Elem e; + + e.tag.class = Universal; + e.tag.num = BIT_STRING; + e.val.tag = VBitString; + e.val.u.bitstringval = makebits(buf, buflen, 0); + return e; +} + +static Elem +mkutc(long t) +{ + Elem e; + char utc[50]; + Tm *tm = gmtime(t); + + e.tag.class = Universal; + e.tag.num = UTCTime; + e.val.tag = VString; + snprint(utc, 50, "%.2d%.2d%.2d%.2d%.2d%.2dZ", + tm->year % 100, tm->mon+1, tm->mday, tm->hour, tm->min, tm->sec); + e.val.u.stringval = estrdup(utc); + return e; +} + +static Elem +mkoid(Ints *oid) +{ + Elem e; + + e.tag.class = Universal; + e.tag.num = OBJECT_ID; + e.val.tag = VObjId; + e.val.u.objidval = makeints(oid->data, oid->len); + return e; +} + +static Elem +mkseq(Elist *el) +{ + Elem e; + + e.tag.class = Universal; + e.tag.num = SEQUENCE; + e.val.tag = VSeq; + e.val.u.seqval = el; + return e; +} + +static Elem +mkset(Elist *el) +{ + Elem e; + + e.tag.class = Universal; + e.tag.num = SETOF; + e.val.tag = VSet; + e.val.u.setval = el; + return e; +} + +static Elem +mkalg(int alg) +{ + return mkseq(mkel(mkoid(alg_oid_tab[alg]), mkel(Null(), nil))); +} + +typedef struct Ints7pref { + int len; + int data[7]; + char prefix[4]; +} Ints7pref; +Ints7pref DN_oid[] = { + {4, 2, 5, 4, 6, 0, 0, 0, "C="}, + {4, 2, 5, 4, 8, 0, 0, 0, "ST="}, + {4, 2, 5, 4, 7, 0, 0, 0, "L="}, + {4, 2, 5, 4, 10, 0, 0, 0, "O="}, + {4, 2, 5, 4, 11, 0, 0, 0, "OU="}, + {4, 2, 5, 4, 3, 0, 0, 0, "CN="}, + {7, 1,2,840,113549,1,9,1, "E="}, +}; + +static Elem +mkname(Ints7pref *oid, char *subj) +{ + return mkset(mkel(mkseq(mkel(mkoid((Ints*)oid), mkel(mkstring(subj), nil))), nil)); +} + +static Elem +mkDN(char *dn) +{ + int i, j, nf; + char *f[20], *prefix, *d2 = estrdup(dn); + Elist* el = nil; + + nf = tokenize(d2, f, nelem(f)); + for(i=nf-1; i>=0; i--){ + for(j=0; jn),mkel(mkint(mptoi(pk->ek)),nil))); + if(encode(pubkey, &pkbytes) != ASN_OK) + goto errret; + freevalfields(&pubkey.val); + pubkey = mkseq( + mkel(mkalg(ALG_rsaEncryption), + mkel(mkbits(pkbytes->data, pkbytes->len), + nil))); + freebytes(pkbytes); + validity = mkseq( + mkel(mkutc(valid[0]), + mkel(mkutc(valid[1]), + nil))); + certinfo = mkseq( + mkel(mkint(serial), + mkel(mkalg(ALG_md5WithRSAEncryption), + mkel(issuer, + mkel(validity, + mkel(subject, + mkel(pubkey, + nil))))))); + if(encode(certinfo, &certinfobytes) != ASN_OK) + goto errret; + md5(certinfobytes->data, certinfobytes->len, digest, 0); + freebytes(certinfobytes); + sig = mkseq( + mkel(mkalg(ALG_md5), + mkel(mkoctet(digest, MD5dlen), + nil))); + if(encode(sig, &sigbytes) != ASN_OK) + goto errret; + pkcs1 = pkcs1pad(sigbytes, pk->n); + freebytes(sigbytes); + rsadecrypt(priv, pkcs1, pkcs1); + buflen = mptobe(pkcs1, nil, 0, &buf); + mpfree(pkcs1); + e = mkseq( + mkel(certinfo, + mkel(mkalg(ALG_md5WithRSAEncryption), + mkel(mkbits(buf, buflen), + nil)))); + free(buf); + if(encode(e, &certbytes) != ASN_OK) + goto errret; + if(certlen) + *certlen = certbytes->len; + cert = certbytes->data; +errret: + freevalfields(&e.val); + return cert; +} + +uchar* +X509req(RSApriv *priv, char *subj, int *certlen) +{ + /* RFC 2314, PKCS #10 Certification Request Syntax */ + int version = 0; + uchar *cert = nil; + RSApub *pk = rsaprivtopub(priv); + Bytes *certbytes, *pkbytes, *certinfobytes, *sigbytes; + Elem e, certinfo, subject, pubkey, sig; + uchar digest[MD5dlen], *buf; + int buflen; + mpint *pkcs1; + + e.val.tag = VInt; /* so freevalfields at errret is no-op */ + subject = mkDN(subj); + pubkey = mkseq(mkel(mkbigint(pk->n),mkel(mkint(mptoi(pk->ek)),nil))); + if(encode(pubkey, &pkbytes) != ASN_OK) + goto errret; + freevalfields(&pubkey.val); + pubkey = mkseq( + mkel(mkalg(ALG_rsaEncryption), + mkel(mkbits(pkbytes->data, pkbytes->len), + nil))); + freebytes(pkbytes); + certinfo = mkseq( + mkel(mkint(version), + mkel(subject, + mkel(pubkey, + nil)))); + if(encode(certinfo, &certinfobytes) != ASN_OK) + goto errret; + md5(certinfobytes->data, certinfobytes->len, digest, 0); + freebytes(certinfobytes); + sig = mkseq( + mkel(mkalg(ALG_md5), + mkel(mkoctet(digest, MD5dlen), + nil))); + if(encode(sig, &sigbytes) != ASN_OK) + goto errret; + pkcs1 = pkcs1pad(sigbytes, pk->n); + freebytes(sigbytes); + rsadecrypt(priv, pkcs1, pkcs1); + buflen = mptobe(pkcs1, nil, 0, &buf); + mpfree(pkcs1); + e = mkseq( + mkel(certinfo, + mkel(mkalg(ALG_md5), + mkel(mkbits(buf, buflen), + nil)))); + free(buf); + if(encode(e, &certbytes) != ASN_OK) + goto errret; + if(certlen) + *certlen = certbytes->len; + cert = certbytes->data; +errret: + freevalfields(&e.val); + return cert; +} + +static char* +tagdump(Tag tag) +{ + if(tag.class != Universal) + return smprint("class%d,num%d", tag.class, tag.num); + switch(tag.num){ + case BOOLEAN: return "BOOLEAN"; break; + case INTEGER: return "INTEGER"; break; + case BIT_STRING: return "BIT STRING"; break; + case OCTET_STRING: return "OCTET STRING"; break; + case NULLTAG: return "NULLTAG"; break; + case OBJECT_ID: return "OID"; break; + case ObjectDescriptor: return "OBJECT_DES"; break; + case EXTERNAL: return "EXTERNAL"; break; + case REAL: return "REAL"; break; + case ENUMERATED: return "ENUMERATED"; break; + case EMBEDDED_PDV: return "EMBEDDED PDV"; break; + case SEQUENCE: return "SEQUENCE"; break; + case SETOF: return "SETOF"; break; + case NumericString: return "NumericString"; break; + case PrintableString: return "PrintableString"; break; + case TeletexString: return "TeletexString"; break; + case VideotexString: return "VideotexString"; break; + case IA5String: return "IA5String"; break; + case UTCTime: return "UTCTime"; break; + case GeneralizedTime: return "GeneralizedTime"; break; + case GraphicString: return "GraphicString"; break; + case VisibleString: return "VisibleString"; break; + case GeneralString: return "GeneralString"; break; + case UniversalString: return "UniversalString"; break; + case BMPString: return "BMPString"; break; + default: + return smprint("Universal,num%d", tag.num); + } +} + +static void +edump(Elem e) +{ + Value v; + Elist *el; + int i; + + print("%s{", tagdump(e.tag)); + v = e.val; + switch(v.tag){ + case VBool: print("Bool %d",v.u.boolval); break; + case VInt: print("Int %d",v.u.intval); break; + case VOctets: print("Octets[%d] %.2x%.2x...",v.u.octetsval->len,v.u.octetsval->data[0],v.u.octetsval->data[1]); break; + case VBigInt: print("BigInt[%d] %.2x%.2x...",v.u.bigintval->len,v.u.bigintval->data[0],v.u.bigintval->data[1]); break; + case VReal: print("Real..."); break; + case VOther: print("Other..."); break; + case VBitString: print("BitString..."); break; + case VNull: print("Null"); break; + case VEOC: print("EOC..."); break; + case VObjId: print("ObjId"); + for(i = 0; ilen; i++) + print(" %d", v.u.objidval->data[i]); + break; + case VString: print("String \"%s\"",v.u.stringval); break; + case VSeq: print("Seq\n"); + for(el = v.u.seqval; el!=nil; el = el->tl) + edump(el->hd); + break; + case VSet: print("Set\n"); + for(el = v.u.setval; el!=nil; el = el->tl) + edump(el->hd); + break; + } + print("}\n"); +} + +void +asn1dump(uchar *der, int len) +{ + Elem e; + + if(decode(der, len, &e) != ASN_OK){ + print("didn't parse\n"); + exits("didn't parse"); + } + edump(e); +} + +void +X509dump(uchar *cert, int ncert) +{ + char *e; + Bytes *b; + CertX509 *c; + RSApub *pk; + uchar digest[SHA1dlen]; + Elem *sigalg; + + print("begin X509dump\n"); + b = makebytes(cert, ncert); + c = decode_cert(b); + if(c != nil) + digest_certinfo(b, digestalg[c->signature_alg], digest); + freebytes(b); + if(c == nil){ + print("cannot decode cert"); + return; + } + + print("serial %d\n", c->serial); + print("issuer %s\n", c->issuer); + print("validity %s %s\n", c->validity_start, c->validity_end); + print("subject %s\n", c->subject); + pk = decode_rsapubkey(c->publickey); + print("pubkey e=%B n(%d)=%B\n", pk->ek, mpsignif(pk->n), pk->n); + + print("sigalg=%d digest=%.*H\n", c->signature_alg, MD5dlen, digest); + e = verify_signature(c->signature, pk, digest, &sigalg); + if(e==nil){ + e = "nil (meaning ok)"; + print("sigalg=\n"); + if(sigalg) + edump(*sigalg); + } + print("self-signed verify_signature returns: %s\n", e); + + rsapubfree(pk); + freecert(c); + print("end X509dump\n"); +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..5ccc081 --- /dev/null +++ b/main.c @@ -0,0 +1,126 @@ +#include "u.h" +#include "libc.h" +#include "kern/dat.h" +#include "kern/fns.h" + +#include "drawterm.h" + +char *authaddr = "auth"; +char *cpuaddr = "cpu"; +char *argv0; +char *user; + +extern int errfmt(Fmt*); +void +sizebug(void) +{ + /* + * Needed by various parts of the code. + * This is a huge bug. + */ + assert(sizeof(char)==1); + assert(sizeof(short)==2); + assert(sizeof(ushort)==2); + assert(sizeof(int)==4); + assert(sizeof(uint)==4); + assert(sizeof(long)==4); + assert(sizeof(ulong)==4); + assert(sizeof(vlong)==8); + assert(sizeof(uvlong)==8); +} + +int +main(int argc, char **argv) +{ + int fd; + char buf[1024], *s; + int n; + + sizebug(); + fmtinstall('r', errfmt); + + osinit(); + procinit0(); + printinit(); + screeninit(); + + chandevreset(); + chandevinit(); + quotefmtinstall(); + + if(bind("#c", "/dev", MBEFORE) < 0) + panic("bind #c: %r"); + if(bind("#m", "/dev", MBEFORE) < 0) + panic("bind #m: %r"); + if(bind("#i", "/dev", MBEFORE) < 0) + panic("bind #i: %r"); + if(bind("#I", "/net", MBEFORE) < 0) + panic("bind #I: %r"); + if(bind("#U", "/", MAFTER) < 0) + panic("bind #U: %r"); + + if(open("/dev/cons", OREAD) != 0) + panic("open0: %r"); + if(open("/dev/cons", OWRITE) != 1) + panic("open1: %r"); + if(open("/dev/cons", OWRITE) != 2) + panic("open2: %r"); + + cpumain(argc, argv); + return 0; +} + +char* +getkey(char *user, char *dom) +{ + char buf[1024]; + + snprint(buf, sizeof buf, "%s@%s password", user, dom); + return readcons(buf, nil, 1); +} + +char* +findkey(char **puser, char *dom) +{ + char buf[1024], *f[50], *p, *ep, *nextp, *pass, *user; + int nf, haveproto, havedom, i; + + for(p=secstorebuf; *p; p=nextp){ + nextp = strchr(p, '\n'); + if(nextp == nil){ + ep = p+strlen(p); + nextp = ""; + }else{ + ep = nextp++; + } + if(ep-p >= sizeof buf){ + print("warning: skipping long line in secstore factotum file\n"); + continue; + } + memmove(buf, p, ep-p); + buf[ep-p] = 0; + nf = tokenize(buf, f, nelem(f)); + if(nf == 0 || strcmp(f[0], "key") != 0) + continue; + pass = nil; + haveproto = havedom = 0; + for(i=1; i$target diff --git a/posix-386/Makefile b/posix-386/Makefile new file mode 100644 index 0000000..5985231 --- /dev/null +++ b/posix-386/Makefile @@ -0,0 +1,16 @@ +LIB=../libmachdep.a +CC=gcc +CFLAGS=-I../include -I. -c -ggdb -D_THREAD_SAFE -pthread +O=o + +OFILES=\ + getcallerpc.$O\ + tas.$O + +$(LIB): $(OFILES) + ar r $(LIB) $(OFILES) + ranlib $(LIB) + +%.$O: %.c + $(CC) $(CFLAGS) $*.c + diff --git a/posix-386/getcallerpc.c b/posix-386/getcallerpc.c new file mode 100644 index 0000000..553c750 --- /dev/null +++ b/posix-386/getcallerpc.c @@ -0,0 +1,8 @@ +#include "u.h" +#include "libc.h" + +ulong +getcallerpc(void *a) +{ + return ((ulong*)a)[-1]; +} diff --git a/posix-386/md5block.s b/posix-386/md5block.s new file mode 100644 index 0000000..602c970 --- /dev/null +++ b/posix-386/md5block.s @@ -0,0 +1,241 @@ +/* + * rfc1321 requires that I include this. The code is new. The constants + * all come from the rfc (hence the copyright). We trade a table for the + * macros in rfc. The total size is a lot less. -- presotto + * + * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + * rights reserved. + * + * License to copy and use this software is granted provided that it + * is identified as the "RSA Data Security, Inc. MD5 Message-Digest + * Algorithm" in all material mentioning or referencing this software + * or this function. + * + * License is also granted to make and use derivative works provided + * that such works are identified as "derived from the RSA Data + * Security, Inc. MD5 Message-Digest Algorithm" in all material + * mentioning or referencing the derived work. + * + * RSA Data Security, Inc. makes no representations concerning either + * the merchantability of this software or the suitability of this + * software forany particular purpose. It is provided "as is" + * without express or implied warranty of any kind. + * These notices must be retained in any copies of any part of this + * documentation and/or software. + */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +#define PAYME(x) $##x + +/* + * SI is data + * a += FN(B,C,D); + * a += x[sh] + t[sh]; + * a = (a << S11) | (a >> (32 - S11)); + * a += b; + */ + +#define BODY1(off,V,FN,SH,A,B,C,D)\ + FN(B,C,D)\ + leal V(A, %edi, 1), A;\ + addl off(%ebp), A;\ + roll PAYME(SH), A;\ + addl B, A;\ + +#define BODY(off,V,FN,SH,A,B,C,D)\ + FN(B,C,D)\ + leal V(A, %edi, 1), A;\ + addl (off)(%ebp), A;\ + roll PAYME(SH), A;\ + addl B,A;\ + +/* + * fn1 = ((c ^ d) & b) ^ d + */ +#define FN1(B,C,D)\ + movl C, %edi;\ + xorl D, %edi;\ + andl B, %edi;\ + xorl D, %edi;\ + +/* + * fn2 = ((b ^ c) & d) ^ c; + */ +#define FN2(B,C,D)\ + movl B, %edi;\ + xorl C, %edi;\ + andl D, %edi;\ + xorl C, %edi;\ + +/* + * fn3 = b ^ c ^ d; + */ +#define FN3(B,C,D)\ + movl B, %edi;\ + xorl C, %edi;\ + xorl D, %edi;\ + +/* + * fn4 = c ^ (b | ~d); + */ +#define FN4(B,C,D)\ + movl D, %edi;\ + xorl $-1, %edi;\ + orl B, %edi;\ + xorl C, %edi;\ + +#define DATA 8 +#define LEN 12 +#define STATE 16 + +#define EDATA (-4) +#define OLDEBX (-8) +#define OLDESI (-12) +#define OLDEDI (-16) + + .text + + .p2align 2,0x90 + .globl _md5block + .type _md5block, @function + _md5block: + + /* Prelude */ + pushl %ebp + movl %ebx, OLDEBX(%esp) + movl %esi, OLDESI(%esp) + movl %edi, OLDEDI(%esp) + + movl DATA(%esp), %eax + addl LEN(%esp), %eax + movl %eax, EDATA(%esp) + + movl DATA(%esp), %ebp + +mainloop: + movl STATE(%esp), %esi + movl (%esi), %eax + movl 4(%esi), %ebx + movl 8(%esi), %ecx + movl 12(%esi), %edx + + BODY1( 0*4,0xd76aa478,FN1,S11,%eax,%ebx,%ecx,%edx) + BODY1( 1*4,0xe8c7b756,FN1,S12,%edx,%eax,%ebx,%ecx) + BODY1( 2*4,0x242070db,FN1,S13,%ecx,%edx,%eax,%ebx) + BODY1( 3*4,0xc1bdceee,FN1,S14,%ebx,%ecx,%edx,%eax) + + BODY1( 4*4,0xf57c0faf,FN1,S11,%eax,%ebx,%ecx,%edx) + BODY1( 5*4,0x4787c62a,FN1,S12,%edx,%eax,%ebx,%ecx) + BODY1( 6*4,0xa8304613,FN1,S13,%ecx,%edx,%eax,%ebx) + BODY1( 7*4,0xfd469501,FN1,S14,%ebx,%ecx,%edx,%eax) + + BODY1( 8*4,0x698098d8,FN1,S11,%eax,%ebx,%ecx,%edx) + BODY1( 9*4,0x8b44f7af,FN1,S12,%edx,%eax,%ebx,%ecx) + BODY1(10*4,0xffff5bb1,FN1,S13,%ecx,%edx,%eax,%ebx) + BODY1(11*4,0x895cd7be,FN1,S14,%ebx,%ecx,%edx,%eax) + + BODY1(12*4,0x6b901122,FN1,S11,%eax,%ebx,%ecx,%edx) + BODY1(13*4,0xfd987193,FN1,S12,%edx,%eax,%ebx,%ecx) + BODY1(14*4,0xa679438e,FN1,S13,%ecx,%edx,%eax,%ebx) + BODY1(15*4,0x49b40821,FN1,S14,%ebx,%ecx,%edx,%eax) + + + BODY( 1*4,0xf61e2562,FN2,S21,%eax,%ebx,%ecx,%edx) + BODY( 6*4,0xc040b340,FN2,S22,%edx,%eax,%ebx,%ecx) + BODY(11*4,0x265e5a51,FN2,S23,%ecx,%edx,%eax,%ebx) + BODY( 0*4,0xe9b6c7aa,FN2,S24,%ebx,%ecx,%edx,%eax) + + BODY( 5*4,0xd62f105d,FN2,S21,%eax,%ebx,%ecx,%edx) + BODY(10*4,0x02441453,FN2,S22,%edx,%eax,%ebx,%ecx) + BODY(15*4,0xd8a1e681,FN2,S23,%ecx,%edx,%eax,%ebx) + BODY( 4*4,0xe7d3fbc8,FN2,S24,%ebx,%ecx,%edx,%eax) + + BODY( 9*4,0x21e1cde6,FN2,S21,%eax,%ebx,%ecx,%edx) + BODY(14*4,0xc33707d6,FN2,S22,%edx,%eax,%ebx,%ecx) + BODY( 3*4,0xf4d50d87,FN2,S23,%ecx,%edx,%eax,%ebx) + BODY( 8*4,0x455a14ed,FN2,S24,%ebx,%ecx,%edx,%eax) + + BODY(13*4,0xa9e3e905,FN2,S21,%eax,%ebx,%ecx,%edx) + BODY( 2*4,0xfcefa3f8,FN2,S22,%edx,%eax,%ebx,%ecx) + BODY( 7*4,0x676f02d9,FN2,S23,%ecx,%edx,%eax,%ebx) + BODY(12*4,0x8d2a4c8a,FN2,S24,%ebx,%ecx,%edx,%eax) + + + BODY( 5*4,0xfffa3942,FN3,S31,%eax,%ebx,%ecx,%edx) + BODY( 8*4,0x8771f681,FN3,S32,%edx,%eax,%ebx,%ecx) + BODY(11*4,0x6d9d6122,FN3,S33,%ecx,%edx,%eax,%ebx) + BODY(14*4,0xfde5380c,FN3,S34,%ebx,%ecx,%edx,%eax) + + BODY( 1*4,0xa4beea44,FN3,S31,%eax,%ebx,%ecx,%edx) + BODY( 4*4,0x4bdecfa9,FN3,S32,%edx,%eax,%ebx,%ecx) + BODY( 7*4,0xf6bb4b60,FN3,S33,%ecx,%edx,%eax,%ebx) + BODY(10*4,0xbebfbc70,FN3,S34,%ebx,%ecx,%edx,%eax) + + BODY(13*4,0x289b7ec6,FN3,S31,%eax,%ebx,%ecx,%edx) + BODY( 0*4,0xeaa127fa,FN3,S32,%edx,%eax,%ebx,%ecx) + BODY( 3*4,0xd4ef3085,FN3,S33,%ecx,%edx,%eax,%ebx) + BODY( 6*4,0x04881d05,FN3,S34,%ebx,%ecx,%edx,%eax) + + BODY( 9*4,0xd9d4d039,FN3,S31,%eax,%ebx,%ecx,%edx) + BODY(12*4,0xe6db99e5,FN3,S32,%edx,%eax,%ebx,%ecx) + BODY(15*4,0x1fa27cf8,FN3,S33,%ecx,%edx,%eax,%ebx) + BODY( 2*4,0xc4ac5665,FN3,S34,%ebx,%ecx,%edx,%eax) + + + BODY( 0*4,0xf4292244,FN4,S41,%eax,%ebx,%ecx,%edx) + BODY( 7*4,0x432aff97,FN4,S42,%edx,%eax,%ebx,%ecx) + BODY(14*4,0xab9423a7,FN4,S43,%ecx,%edx,%eax,%ebx) + BODY( 5*4,0xfc93a039,FN4,S44,%ebx,%ecx,%edx,%eax) + + BODY(12*4,0x655b59c3,FN4,S41,%eax,%ebx,%ecx,%edx) + BODY( 3*4,0x8f0ccc92,FN4,S42,%edx,%eax,%ebx,%ecx) + BODY(10*4,0xffeff47d,FN4,S43,%ecx,%edx,%eax,%ebx) + BODY( 1*4,0x85845dd1,FN4,S44,%ebx,%ecx,%edx,%eax) + + BODY( 8*4,0x6fa87e4f,FN4,S41,%eax,%ebx,%ecx,%edx) + BODY(15*4,0xfe2ce6e0,FN4,S42,%edx,%eax,%ebx,%ecx) + BODY( 6*4,0xa3014314,FN4,S43,%ecx,%edx,%eax,%ebx) + BODY(13*4,0x4e0811a1,FN4,S44,%ebx,%ecx,%edx,%eax) + + BODY( 4*4,0xf7537e82,FN4,S41,%eax,%ebx,%ecx,%edx) + BODY(11*4,0xbd3af235,FN4,S42,%edx,%eax,%ebx,%ecx) + BODY( 2*4,0x2ad7d2bb,FN4,S43,%ecx,%edx,%eax,%ebx) + BODY( 9*4,0xeb86d391,FN4,S44,%ebx,%ecx,%edx,%eax) + + addl $(16*4), %ebp + movl STATE(%esp), %edi + addl %eax,0(%edi) + addl %ebx,4(%edi) + addl %ecx,8(%edi) + addl %edx,12(%edi) + + movl EDATA(%esp), %edi + cmpl %edi, %ebp + jb mainloop + + /* Postlude */ + movl OLDEBX(%esp), %ebx + movl OLDESI(%esp), %esi + movl OLDEDI(%esp), %edi + movl %esp, %ebp + leave + ret + diff --git a/posix-386/sha1block.s b/posix-386/sha1block.s new file mode 100644 index 0000000..7d0696d --- /dev/null +++ b/posix-386/sha1block.s @@ -0,0 +1,214 @@ +.text + +.p2align 2,0x90 +.globl _sha1block + .type _sha1block, @function +_sha1block: + +/* x = (wp[off-f] ^ wp[off-8] ^ wp[off-14] ^ wp[off-16]) <<< 1; + * wp[off] = x; + * x += A <<< 5; + * E += 0xca62c1d6 + x; + * x = FN(B,C,D); + * E += x; + * B >>> 2 + */ +#define BSWAPDI BYTE $0x0f; BYTE $0xcf; + +#define BODY(off,FN,V,A,B,C,D,E)\ + movl (off-64)(%ebp), %edi;\ + xorl (off-56)(%ebp), %edi;\ + xorl (off-32)(%ebp), %edi;\ + xorl (off-12)(%ebp), %edi;\ + roll $1, %edi;\ + movl %edi, off(%ebp);\ + leal V(%edi, E, 1), E;\ + movl A, %edi;\ + roll $5, %edi;\ + addl %edi, E;\ + FN(B,C,D)\ + addl %edi, E;\ + rorl $2, B;\ + +#define BODY0(off,FN,V,A,B,C,D,E)\ + movl off(%ebx), %edi;\ + bswap %edi;\ + movl %edi, off(%ebp);\ + leal V(%edi,E,1), E;\ + movl A, %edi;\ + roll $5,%edi;\ + addl %edi,E;\ + FN(B,C,D)\ + addl %edi,E;\ + rorl $2,B;\ + +/* + * fn1 = (((C^D)&B)^D); + */ +#define FN1(B,C,D)\ + movl C, %edi;\ + xorl D, %edi;\ + andl B, %edi;\ + xorl D, %edi;\ + +/* + * fn24 = B ^ C ^ D + */ +#define FN24(B,C,D)\ + movl B, %edi;\ + xorl C, %edi;\ + xorl D, %edi;\ + +/* + * fn3 = ((B ^ C) & (D ^= B)) ^ B + * D ^= B to restore D + */ +#define FN3(B,C,D)\ + movl B, %edi;\ + xorl C, %edi;\ + xorl B, D;\ + andl D, %edi;\ + xorl B, %edi;\ + xorl B, D;\ + +/* + * stack offsets + * void sha1block(uchar *DATA, int LEN, ulong *STATE) + */ +#define DATA 8 +#define LEN 12 +#define STATE 16 + +/* + * stack offsets for locals + * ulong w[80]; + * uchar *edata; + * ulong *w15, *w40, *w60, *w80; + * register local + * ulong *wp = %ebp + * ulong a = eax, b = ebx, c = ecx, d = edx, e = esi + * ulong tmp = edi + */ +#define WARRAY (-4-(80*4)) +#define TMP1 (-8-(80*4)) +#define TMP2 (-12-(80*4)) +#define W15 (-16-(80*4)) +#define W40 (-20-(80*4)) +#define W60 (-24-(80*4)) +#define W80 (-28-(80*4)) +#define EDATA (-32-(80*4)) +#define OLDEBX (-36-(80*4)) +#define OLDESI (-40-(80*4)) +#define OLDEDI (-44-(80*4)) + + /* Prelude */ + pushl %ebp + mov %ebx, OLDEBX(%esp) + mov %esi, OLDESI(%esp) + mov %edi, OLDEDI(%esp) + + movl DATA(%esp), %eax + addl LEN(%esp), %eax + movl %eax, EDATA(%esp) + + leal (WARRAY+15*4)(%esp), %edi /* aw15 */ + movl %edi, W15(%esp) + leal (WARRAY+40*4)(%esp), %edx /* aw40 */ + movl %edx, W40(%esp) + leal (WARRAY+60*4)(%esp), %ecx /* aw60 */ + movl %ecx, W60(%esp) + leal (WARRAY+80*4)(%esp), %edi /* aw80 */ + movl %edi, W80(%esp) + +mainloop: + leal WARRAY(%esp), %ebp /* warray */ + + movl STATE(%esp), %edi /* state */ + movl (%edi),%eax + movl 4(%edi),%ebx + movl %ebx, TMP1(%esp) /* tmp1 */ + movl 8(%edi), %ecx + movl 12(%edi), %edx + movl 16(%edi), %esi + + movl DATA(%esp), %ebx /* data */ + +loop1: + BODY0(0,FN1,0x5a827999,%eax,TMP1(%esp),%ecx,%edx,%esi) + movl %esi,TMP2(%esp) + BODY0(4,FN1,0x5a827999,%esi,%eax,TMP1(%esp),%ecx,%edx) + movl TMP1(%esp),%esi + BODY0(8,FN1,0x5a827999,%edx,TMP2(%esp),%eax,%esi,%ecx) + BODY0(12,FN1,0x5a827999,%ecx,%edx,TMP2(%esp),%eax,%esi) + movl %esi,TMP1(%esp) + BODY0(16,FN1,0x5a827999,%esi,%ecx,%edx,TMP2(%esp),%eax) + movl TMP2(%esp),%esi + + addl $20, %ebx + addl $20, %ebp + cmpl W15(%esp), %ebp /* w15 */ + jb loop1 + + BODY0(0,FN1,0x5a827999,%eax,TMP1(%esp),%ecx,%edx,%esi) + addl $4, %ebx + MOVL %ebx, DATA(%esp) /* data */ + MOVL TMP1(%esp),%ebx + + BODY(4,FN1,0x5a827999,%esi,%eax,%ebx,%ecx,%edx) + BODY(8,FN1,0x5a827999,%edx,%esi,%eax,%ebx,%ecx) + BODY(12,FN1,0x5a827999,%ecx,%edx,%esi,%eax,%ebx) + BODY(16,FN1,0x5a827999,%ebx,%ecx,%edx,%esi,%eax) + + addl $20, %ebp + +loop2: + BODY(0,FN24,0x6ed9eba1,%eax,%ebx,%ecx,%edx,%esi) + BODY(4,FN24,0x6ed9eba1,%esi,%eax,%ebx,%ecx,%edx) + BODY(8,FN24,0x6ed9eba1,%edx,%esi,%eax,%ebx,%ecx) + BODY(12,FN24,0x6ed9eba1,%ecx,%edx,%esi,%eax,%ebx) + BODY(16,FN24,0x6ed9eba1,%ebx,%ecx,%edx,%esi,%eax) + + addl $20,%ebp + cmpl W40(%esp), %ebp + jb loop2 + +loop3: + BODY(0,FN3,0x8f1bbcdc,%eax,%ebx,%ecx,%edx,%esi) + BODY(4,FN3,0x8f1bbcdc,%esi,%eax,%ebx,%ecx,%edx) + BODY(8,FN3,0x8f1bbcdc,%edx,%esi,%eax,%ebx,%ecx) + BODY(12,FN3,0x8f1bbcdc,%ecx,%edx,%esi,%eax,%ebx) + BODY(16,FN3,0x8f1bbcdc,%ebx,%ecx,%edx,%esi,%eax) + + addl $20, %ebp + cmpl W60(%esp), %ebp /* w60 */ + jb loop3 + +loop4: + BODY(0,FN24,0xca62c1d6,%eax,%ebx,%ecx,%edx,%esi) + BODY(4,FN24,0xca62c1d6,%esi,%eax,%ebx,%ecx,%edx) + BODY(8,FN24,0xca62c1d6,%edx,%esi,%eax,%ebx,%ecx) + BODY(12,FN24,0xca62c1d6,%ecx,%edx,%esi,%eax,%ebx) + BODY(16,FN24,0xca62c1d6,%ebx,%ecx,%edx,%esi,%eax) + + addl $20, %ebp + cmpl W80(%esp), %ebp /* w80 */ + jb loop4 + + movl STATE(%esp), %edi /* state */ + addl %eax, 0(%edi) + addl %ebx, 4(%edi) + addl %ecx, 8(%edi) + addl %edx, 12(%edi) + addl %esi, 16(%edi) + + movl EDATA(%esp), %edi /* edata */ + cmpl %edi, DATA(%esp) /* data */ + jb mainloop + + /* Postlude */ + mov OLDEBX(%esp), %ebx + mov OLDESI(%esp), %esi + mov OLDEDI(%esp), %edi + movl %esp, %ebp + leave + ret diff --git a/posix-386/tas.c b/posix-386/tas.c new file mode 100644 index 0000000..8a907d8 --- /dev/null +++ b/posix-386/tas.c @@ -0,0 +1,23 @@ +#include "u.h" +#include "libc.h" + +int +tas(long *x) +{ + int v; + + __asm__( "movl $1, %%eax\n\t" + "xchgl %%eax,(%%ecx)" + : "=a" (v) + : "c" (x) + ); + switch(v) { + case 0: + case 1: + return v; + default: + print("canlock: corrupted 0x%lux\n", v); + return 1; + } +} + diff --git a/posix-power/Makefile b/posix-power/Makefile new file mode 100644 index 0000000..5985231 --- /dev/null +++ b/posix-power/Makefile @@ -0,0 +1,16 @@ +LIB=../libmachdep.a +CC=gcc +CFLAGS=-I../include -I. -c -ggdb -D_THREAD_SAFE -pthread +O=o + +OFILES=\ + getcallerpc.$O\ + tas.$O + +$(LIB): $(OFILES) + ar r $(LIB) $(OFILES) + ranlib $(LIB) + +%.$O: %.c + $(CC) $(CFLAGS) $*.c + diff --git a/posix-power/getcallerpc.c b/posix-power/getcallerpc.c new file mode 100644 index 0000000..553c750 --- /dev/null +++ b/posix-power/getcallerpc.c @@ -0,0 +1,8 @@ +#include "u.h" +#include "libc.h" + +ulong +getcallerpc(void *a) +{ + return ((ulong*)a)[-1]; +} diff --git a/posix-power/tas.c b/posix-power/tas.c new file mode 100644 index 0000000..508aa6d --- /dev/null +++ b/posix-power/tas.c @@ -0,0 +1,42 @@ +#include "u.h" +#include "libc.h" + +/* + * first argument (l) is in r3 at entry. + * r3 contains return value upon return. + */ +int +tas(long *x) +{ + int v; + /* + * this __asm__ works with gcc 2.95.2 (mac os x 10.1). + * this assembly language destroys r0 (0), some other register (v), + * r4 (x) and r5 (temp). + */ + __asm__("\n sync\n" + " li r0,0\n" + " mr r4,%1 /* &l->val */\n" + " lis r5,0xdead /* assemble constant 0xdeaddead */\n" + " ori r5,r5,0xdead /* \" */\n" + "tas1:\n" + " dcbf r4,r0 /* cache flush; \"fix for 603x bug\" */\n" + " lwarx %0,r4,r0 /* v = l->val with reservation */\n" + " cmp cr0,0,%0,r0 /* v == 0 */\n" + " bne tas0\n" + " stwcx. r5,r4,r0 /* if (l->val same) l->val = 0xdeaddead */\n" + " bne tas1\n" + "tas0:\n" + " sync\n" + " isync\n" + : "=r" (v) + : "r" (x) + : "cc", "memory", "r0", "r4", "r5" + ); + switch(v) { + case 0: return 0; + case 0xdeaddead: return 1; + default: print("tas: corrupted 0x%lux\n", v); + } + return 0; +} diff --git a/readcons.c b/readcons.c new file mode 100644 index 0000000..844cff8 --- /dev/null +++ b/readcons.c @@ -0,0 +1,110 @@ +#include +#include +#include "drawterm.h" + +void* +erealloc(void *v, ulong n) +{ + v = realloc(v, n); + if(v == nil) + sysfatal("out of memory"); + return v; +} + +char* +estrdup(char *s) +{ + s = strdup(s); + if(s == nil) + sysfatal("out of memory"); + return s; +} + +char* +estrappend(char *s, char *fmt, ...) +{ + char *t; + va_list arg; + + va_start(arg, fmt); + t = vsmprint(fmt, arg); + if(t == nil) + sysfatal("out of memory"); + va_end(arg); + s = erealloc(s, strlen(s)+strlen(t)+1); + strcat(s, t); + free(t); + return s; +} + +/* + * prompt for a string with a possible default response + */ +char* +readcons(char *prompt, char *def, int raw) +{ + int fdin, fdout, ctl, n; + char line[10]; + char *s; + + fdin = open("/dev/cons", OREAD); + if(fdin < 0) + fdin = 0; + fdout = open("/dev/cons", OWRITE); + if(fdout < 0) + fdout = 1; + if(def != nil) + fprint(fdout, "%s[%s]: ", prompt, def); + else + fprint(fdout, "%s: ", prompt); + if(raw){ + ctl = open("/dev/consctl", OWRITE); + if(ctl >= 0) + write(ctl, "rawon", 5); + } else + ctl = -1; + s = estrdup(""); + for(;;){ + n = read(fdin, line, 1); + if(n == 0){ + Error: + close(fdin); + close(fdout); + if(ctl >= 0) + close(ctl); + free(s); + return nil; + } + if(n < 0) + goto Error; + if(line[0] == 0x7f) + goto Error; + if(n == 0 || line[0] == '\n' || line[0] == '\r'){ + if(raw){ + write(ctl, "rawoff", 6); + write(fdout, "\n", 1); + } + close(ctl); + close(fdin); + close(fdout); + if(*s == 0 && def != nil) + s = estrappend(s, "%s", 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(fdout, "\n%s[%s]: ", prompt, def); + else + fprint(fdout, "\n%s: ", prompt); + + s[0] = 0; + } else { + s = estrappend(s, "%c", line[0]); + } + } + return nil; /* not reached */ +} + diff --git a/resource.h b/resource.h new file mode 100644 index 0000000..f419438 --- /dev/null +++ b/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by drawterm.rc +// +#define IDI_ICON1 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/secstore.c b/secstore.c new file mode 100644 index 0000000..368172a --- /dev/null +++ b/secstore.c @@ -0,0 +1,667 @@ +/* + * Various files from /sys/src/cmd/auth/secstore, just enough + * to download a file at boot time. + */ + +#include +#include +#include +#include +#include "drawterm.h" + +static void* +emalloc(ulong n) +{ + return mallocz(n, 1); +} + +enum{ CHK = 16}; +enum{ MAXFILESIZE = 10*1024*1024 }; + +enum{// PW status bits + Enabled = (1<<0), + STA = (1<<1), // extra SecurID step +}; + +static char testmess[] = "__secstore\tPAK\nC=%s\nm=0\n"; + +int +secdial(char *secstore) +{ + char *p, buf[80], *f[3]; + int fd, nf; + + p = secstore; /* take it from writehostowner, if set there */ + if(*p == 0) /* else use the authserver */ + p = "$auth"; + + /* translate $auth ourselves. + * authaddr is something like il!host!566 or tcp!host!567. + * extract host, accounting for a change of format to something + * like il!host or tcp!host or host. + */ + if(strcmp(p, "$auth")==0){ + if(authaddr == nil) + return -1; + strecpy(buf, buf+sizeof buf, authaddr); + nf = getfields(buf, f, nelem(f), 0, "!"); + switch(nf){ + default: + return -1; + case 1: + p = f[0]; + break; + case 2: + case 3: + p = f[1]; + break; + } + } + fd = dial(netmkaddr(p, "tcp", "5356"), 0, 0, 0); + if(fd >= 0) + return fd; + return -1; +} + +int +havesecstore(char *addr, char *owner) +{ + int m, n, fd; + uchar buf[500]; + + n = snprint((char*)buf, sizeof buf, testmess, owner); + hnputs(buf, 0x8000+n-2); + + fd = secdial(addr); + if(fd < 0) + return 0; + if(write(fd, buf, n) != n || readn(fd, buf, 2) != 2){ + close(fd); + return 0; + } + n = ((buf[0]&0x7f)<<8) + buf[1]; + if(n+1 > sizeof buf){ + werrstr("implausibly large count %d", n); + close(fd); + return 0; + } + m = readn(fd, buf, n); + close(fd); + if(m != n){ + if(m >= 0) + werrstr("short read from secstore"); + return 0; + } + buf[n] = 0; + if(strcmp((char*)buf, "!account expired") == 0){ + werrstr("account expired"); + return 0; + } + return strcmp((char*)buf, "!account exists") == 0; +} + +// delimited, authenticated, encrypted connection +enum{ Maxmsg=4096 }; // messages > Maxmsg bytes are truncated +typedef struct SConn SConn; + +extern SConn* newSConn(int); // arg is open file descriptor +struct SConn{ + void *chan; + int secretlen; + int (*secret)(SConn*, uchar*, int);// + int (*read)(SConn*, uchar*, int); // <0 if error; errmess in buffer + int (*write)(SConn*, uchar*, int); + void (*free)(SConn*); // also closes file descriptor +}; +// secret(s,b,dir) sets secret for digest, encrypt, using the secretlen +// bytes in b to form keys for the two directions; +// set dir=0 in client, dir=1 in server + +// error convention: write !message in-band +#define readstr secstore_readstr +static void writerr(SConn*, char*); +static int readstr(SConn*, char*); // call with buf of size Maxmsg+1 + // returns -1 upon error, with error message in buf + +typedef struct ConnState { + uchar secret[SHA1dlen]; + ulong seqno; + RC4state rc4; +} ConnState; + +typedef struct SS{ + int fd; // file descriptor for read/write of encrypted data + int alg; // if nonzero, "alg sha rc4_128" + ConnState in, out; +} SS; + +static int +SC_secret(SConn *conn, uchar *sigma, int direction) +{ + SS *ss = (SS*)(conn->chan); + int nsigma = conn->secretlen; + + if(direction != 0){ + hmac_sha1(sigma, nsigma, (uchar*)"one", 3, ss->out.secret, nil); + hmac_sha1(sigma, nsigma, (uchar*)"two", 3, ss->in.secret, nil); + }else{ + hmac_sha1(sigma, nsigma, (uchar*)"two", 3, ss->out.secret, nil); + hmac_sha1(sigma, nsigma, (uchar*)"one", 3, ss->in.secret, nil); + } + setupRC4state(&ss->in.rc4, ss->in.secret, 16); // restrict to 128 bits + setupRC4state(&ss->out.rc4, ss->out.secret, 16); + ss->alg = 1; + return 0; +} + +static void +hash(uchar secret[SHA1dlen], uchar *data, int len, int seqno, uchar d[SHA1dlen]) +{ + DigestState sha; + uchar seq[4]; + + seq[0] = seqno>>24; + seq[1] = seqno>>16; + seq[2] = seqno>>8; + seq[3] = seqno; + memset(&sha, 0, sizeof sha); + sha1(secret, SHA1dlen, nil, &sha); + sha1(data, len, nil, &sha); + sha1(seq, 4, d, &sha); +} + +static int +verify(uchar secret[SHA1dlen], uchar *data, int len, int seqno, uchar d[SHA1dlen]) +{ + DigestState sha; + uchar seq[4]; + uchar digest[SHA1dlen]; + + seq[0] = seqno>>24; + seq[1] = seqno>>16; + seq[2] = seqno>>8; + seq[3] = seqno; + memset(&sha, 0, sizeof sha); + sha1(secret, SHA1dlen, nil, &sha); + sha1(data, len, nil, &sha); + sha1(seq, 4, digest, &sha); + return memcmp(d, digest, SHA1dlen); +} + +static int +SC_read(SConn *conn, uchar *buf, int n) +{ + SS *ss = (SS*)(conn->chan); + uchar count[2], digest[SHA1dlen]; + int len, nr; + + if(read(ss->fd, count, 2) != 2 || count[0]&0x80 == 0){ + werrstr("!SC_read invalid count"); + return -1; + } + len = (count[0]&0x7f)<<8 | count[1]; // SSL-style count; no pad + if(ss->alg){ + len -= SHA1dlen; + if(len <= 0 || readn(ss->fd, digest, SHA1dlen) != SHA1dlen){ + werrstr("!SC_read missing sha1"); + return -1; + } + if(len > n || readn(ss->fd, buf, len) != len){ + werrstr("!SC_read missing data"); + return -1; + } + rc4(&ss->in.rc4, digest, SHA1dlen); + rc4(&ss->in.rc4, buf, len); + if(verify(ss->in.secret, buf, len, ss->in.seqno, digest) != 0){ + werrstr("!SC_read integrity check failed"); + return -1; + } + }else{ + if(len <= 0 || len > n){ + werrstr("!SC_read implausible record length"); + return -1; + } + if( (nr = readn(ss->fd, buf, len)) != len){ + werrstr("!SC_read expected %d bytes, but got %d", len, nr); + return -1; + } + } + ss->in.seqno++; + return len; +} + +static int +SC_write(SConn *conn, uchar *buf, int n) +{ + SS *ss = (SS*)(conn->chan); + uchar count[2], digest[SHA1dlen]; + int len; + + if(n <= 0 || n > Maxmsg+1){ + werrstr("!SC_write invalid n %d", n); + return -1; + } + len = n; + if(ss->alg) + len += SHA1dlen; + count[0] = 0x80 | len>>8; + count[1] = len; + if(write(ss->fd, count, 2) != 2){ + werrstr("!SC_write invalid count"); + return -1; + } + if(ss->alg){ + hash(ss->out.secret, buf, n, ss->out.seqno, digest); + rc4(&ss->out.rc4, digest, SHA1dlen); + rc4(&ss->out.rc4, buf, n); + if(write(ss->fd, digest, SHA1dlen) != SHA1dlen || + write(ss->fd, buf, n) != n){ + werrstr("!SC_write error on send"); + return -1; + } + }else{ + if(write(ss->fd, buf, n) != n){ + werrstr("!SC_write error on send"); + return -1; + } + } + ss->out.seqno++; + return n; +} + +static void +SC_free(SConn *conn) +{ + SS *ss = (SS*)(conn->chan); + + close(ss->fd); + free(ss); + free(conn); +} + +SConn* +newSConn(int fd) +{ + SS *ss; + SConn *conn; + + if(fd < 0) + return nil; + ss = (SS*)emalloc(sizeof(*ss)); + conn = (SConn*)emalloc(sizeof(*conn)); + ss->fd = fd; + ss->alg = 0; + conn->chan = (void*)ss; + conn->secretlen = SHA1dlen; + conn->free = SC_free; + conn->secret = SC_secret; + conn->read = SC_read; + conn->write = SC_write; + return conn; +} + +static void +writerr(SConn *conn, char *s) +{ + char buf[Maxmsg]; + + snprint(buf, Maxmsg, "!%s", s); + conn->write(conn, (uchar*)buf, strlen(buf)); +} + +static int +readstr(SConn *conn, char *s) +{ + int n; + + n = conn->read(conn, (uchar*)s, Maxmsg); + if(n >= 0){ + s[n] = 0; + if(s[0] == '!'){ + memmove(s, s+1, n); + n = -1; + } + }else{ + strcpy(s, "read error"); + } + return n; +} + +static char* +getfile(SConn *conn, uchar *key, int nkey) +{ + char *buf; + int nbuf, n, nr, len; + char s[Maxmsg+1], *gf, *p, *q; + uchar skey[SHA1dlen], ib[Maxmsg+CHK], *ibr, *ibw; + AESstate aes; + DigestState *sha; + + gf = "factotum"; + memset(&aes, 0, sizeof aes); + + snprint(s, Maxmsg, "GET %s\n", gf); + conn->write(conn, (uchar*)s, strlen(s)); + + /* get file size */ + s[0] = '\0'; + if(readstr(conn, s) < 0){ + werrstr("secstore: %r"); + return nil; + } + if((len = atoi(s)) < 0){ + werrstr("secstore: remote file %s does not exist", gf); + return nil; + }else if(len > MAXFILESIZE){//assert + werrstr("secstore: implausible file size %d for %s", len, gf); + return nil; + } + + ibr = ibw = ib; + buf = nil; + nbuf = 0; + for(nr=0; nr < len;){ + if((n = conn->read(conn, ibw, Maxmsg)) <= 0){ + werrstr("secstore: empty file chunk n=%d nr=%d len=%d: %r", n, nr, len); + return nil; + } + nr += n; + ibw += n; + if(!aes.setup){ /* first time, read 16 byte IV */ + if(n < 16){ + werrstr("secstore: no IV in file"); + return nil; + } + sha = sha1((uchar*)"aescbc file", 11, nil, nil); + sha1(key, nkey, skey, sha); + setupAESstate(&aes, skey, AESbsize, ibr); + memset(skey, 0, sizeof skey); + ibr += AESbsize; + n -= AESbsize; + } + aesCBCdecrypt(ibw-n, n, &aes); + n = ibw-ibr-CHK; + if(n > 0){ + buf = realloc(buf, nbuf+n+1); + if(buf == nil) + panic("out of memory"); + memmove(buf+nbuf, ibr, n); + nbuf += n; + ibr += n; + } + memmove(ib, ibr, ibw-ibr); + ibw = ib + (ibw-ibr); + ibr = ib; + } + n = ibw-ibr; + if((n != CHK) || (memcmp(ib, "XXXXXXXXXXXXXXXX", CHK) != 0)){ + werrstr("secstore: decrypted file failed to authenticate!"); + free(buf); + return nil; + } + if(nbuf == 0){ + werrstr("secstore got empty file"); + return nil; + } + buf[nbuf] = '\0'; + return buf; +} + +static char VERSION[] = "secstore"; + +typedef struct PAKparams{ + mpint *q, *p, *r, *g; +} PAKparams; + +static PAKparams *pak; + +// This group was generated by the seed EB7B6E35F7CD37B511D96C67D6688CC4DD440E1E. +static void +initPAKparams(void) +{ + if(pak) + return; + pak = (PAKparams*)emalloc(sizeof(*pak)); + pak->q = strtomp("E0F0EF284E10796C5A2A511E94748BA03C795C13", nil, 16, nil); + pak->p = strtomp("C41CFBE4D4846F67A3DF7DE9921A49D3B42DC33728427AB159CEC8CBBD" + "B12B5F0C244F1A734AEB9840804EA3C25036AD1B61AFF3ABBC247CD4B384224567A86" + "3A6F020E7EE9795554BCD08ABAD7321AF27E1E92E3DB1C6E7E94FAAE590AE9C48F96D9" + "3D178E809401ABE8A534A1EC44359733475A36A70C7B425125062B1142D", nil, 16, nil); + pak->r = strtomp("DF310F4E54A5FEC5D86D3E14863921E834113E060F90052AD332B3241CEF" + "2497EFA0303D6344F7C819691A0F9C4A773815AF8EAECFB7EC1D98F039F17A32A7E887" + "D97251A927D093F44A55577F4D70444AEBD06B9B45695EC23962B175F266895C67D21" + "C4656848614D888A4", nil, 16, nil); + pak->g = strtomp("2F1C308DC46B9A44B52DF7DACCE1208CCEF72F69C743ADD4D2327173444" + "ED6E65E074694246E07F9FD4AE26E0FDDD9F54F813C40CB9BCD4338EA6F242AB94CD41" + "0E676C290368A16B1A3594877437E516C53A6EEE5493A038A017E955E218E7819734E3E" + "2A6E0BAE08B14258F8C03CC1B30E0DDADFCF7CEDF0727684D3D255F1", nil, 16, nil); +} + +// H = (sha(ver,C,sha(passphrase)))^r mod p, +// a hash function expensive to attack by brute force. +static void +longhash(char *ver, char *C, uchar *passwd, mpint *H) +{ + uchar *Cp; + int i, n, nver, nC; + uchar buf[140], key[1]; + + nver = strlen(ver); + nC = strlen(C); + n = nver + nC + SHA1dlen; + Cp = (uchar*)emalloc(n); + memmove(Cp, ver, nver); + memmove(Cp+nver, C, nC); + memmove(Cp+nver+nC, passwd, SHA1dlen); + for(i = 0; i < 7; i++){ + key[0] = 'A'+i; + hmac_sha1(Cp, n, key, sizeof key, buf+i*SHA1dlen, nil); + } + memset(Cp, 0, n); + free(Cp); + betomp(buf, sizeof buf, H); + mpmod(H, pak->p, H); + mpexp(H, pak->r, pak->p, H); +} + +// Hi = H^-1 mod p +static char * +PAK_Hi(char *C, char *passphrase, mpint *H, mpint *Hi) +{ + uchar passhash[SHA1dlen]; + + sha1((uchar *)passphrase, strlen(passphrase), passhash, nil); + initPAKparams(); + longhash(VERSION, C, passhash, H); + mpinvert(H, pak->p, Hi); + return mptoa(Hi, 64, nil, 0); +} + +// another, faster, hash function for each party to +// confirm that the other has the right secrets. +static void +shorthash(char *mess, char *C, char *S, char *m, char *mu, char *sigma, char *Hi, uchar *digest) +{ + SHA1state *state; + + state = sha1((uchar*)mess, strlen(mess), 0, 0); + state = sha1((uchar*)C, strlen(C), 0, state); + state = sha1((uchar*)S, strlen(S), 0, state); + state = sha1((uchar*)m, strlen(m), 0, state); + state = sha1((uchar*)mu, strlen(mu), 0, state); + state = sha1((uchar*)sigma, strlen(sigma), 0, state); + state = sha1((uchar*)Hi, strlen(Hi), 0, state); + state = sha1((uchar*)mess, strlen(mess), 0, state); + state = sha1((uchar*)C, strlen(C), 0, state); + state = sha1((uchar*)S, strlen(S), 0, state); + state = sha1((uchar*)m, strlen(m), 0, state); + state = sha1((uchar*)mu, strlen(mu), 0, state); + state = sha1((uchar*)sigma, strlen(sigma), 0, state); + sha1((uchar*)Hi, strlen(Hi), digest, state); +} + +// On input, conn provides an open channel to the server; +// C is the name this client calls itself; +// pass is the user's passphrase +// On output, session secret has been set in conn +// (unless return code is negative, which means failure). +// If pS is not nil, it is set to the (alloc'd) name the server calls itself. +static int +PAKclient(SConn *conn, char *C, char *pass, char **pS) +{ + char *mess, *mess2, *eol, *S, *hexmu, *ks, *hexm, *hexsigma = nil, *hexHi; + char kc[2*SHA1dlen+1]; + uchar digest[SHA1dlen]; + int rc = -1, n; + mpint *x, *m = mpnew(0), *mu = mpnew(0), *sigma = mpnew(0); + mpint *H = mpnew(0), *Hi = mpnew(0); + + hexHi = PAK_Hi(C, pass, H, Hi); + + // random 1<=x<=q-1; send C, m=g**x H + x = mprand(164, genrandom, nil); + mpmod(x, pak->q, x); + if(mpcmp(x, mpzero) == 0) + mpassign(mpone, x); + mpexp(pak->g, x, pak->p, m); + mpmul(m, H, m); + mpmod(m, pak->p, m); + hexm = mptoa(m, 64, nil, 0); + mess = (char*)emalloc(2*Maxmsg+2); + mess2 = mess+Maxmsg+1; + snprint(mess, Maxmsg, "%s\tPAK\nC=%s\nm=%s\n", VERSION, C, hexm); + conn->write(conn, (uchar*)mess, strlen(mess)); + + // recv g**y, S, check hash1(g**xy) + if(readstr(conn, mess) < 0){ + fprint(2, "error: %s\n", mess); + writerr(conn, "couldn't read g**y"); + goto done; + } + eol = strchr(mess, '\n'); + if(strncmp("mu=", mess, 3) != 0 || !eol || strncmp("\nk=", eol, 3) != 0){ + writerr(conn, "verifier syntax error"); + goto done; + } + hexmu = mess+3; + *eol = 0; + ks = eol+3; + eol = strchr(ks, '\n'); + if(!eol || strncmp("\nS=", eol, 3) != 0){ + writerr(conn, "verifier syntax error for secstore 1.0"); + goto done; + } + *eol = 0; + S = eol+3; + eol = strchr(S, '\n'); + if(!eol){ + writerr(conn, "verifier syntax error for secstore 1.0"); + goto done; + } + *eol = 0; + if(pS) + *pS = strdup(S); + strtomp(hexmu, nil, 64, mu); + mpexp(mu, x, pak->p, sigma); + hexsigma = mptoa(sigma, 64, nil, 0); + shorthash("server", C, S, hexm, hexmu, hexsigma, hexHi, digest); + enc64(kc, sizeof kc, digest, SHA1dlen); + if(strcmp(ks, kc) != 0){ + writerr(conn, "verifier didn't match"); + goto done; + } + + // send hash2(g**xy) + shorthash("client", C, S, hexm, hexmu, hexsigma, hexHi, digest); + enc64(kc, sizeof kc, digest, SHA1dlen); + snprint(mess2, Maxmsg, "k'=%s\n", kc); + conn->write(conn, (uchar*)mess2, strlen(mess2)); + + // set session key + shorthash("session", C, S, hexm, hexmu, hexsigma, hexHi, digest); + memset(hexsigma, 0, strlen(hexsigma)); + n = conn->secret(conn, digest, 0); + memset(digest, 0, SHA1dlen); + if(n < 0){//assert + writerr(conn, "can't set secret"); + goto done; + } + + rc = 0; +done: + mpfree(x); + mpfree(sigma); + mpfree(mu); + mpfree(m); + mpfree(Hi); + mpfree(H); + free(hexsigma); + free(hexHi); + free(hexm); + free(mess); + return rc; +} + +char* +secstorefetch(char *addr, char *owner, char *password) +{ + int fd; + char *rv; + char s[Maxmsg+1], bye[10]; + SConn *conn; + char *pass, *sta; + + sta = nil; + conn = nil; + rv = nil; + if(password != nil && *password) + pass = strdup(password); + else + pass = readcons("secstore password", nil, 1); + if(pass==nil || strlen(pass)==0){ + werrstr("cancel"); + goto Out; + } + if((fd = secdial(addr)) < 0) + goto Out; + if((conn = newSConn(fd)) == nil) + goto Out; + if(PAKclient(conn, owner, pass, nil) < 0){ + werrstr("password mistyped?"); + goto Out; + } + if(readstr(conn, s) < 0) + goto Out; + if(strcmp(s, "STA") == 0){ + sta = readcons("STA PIN+SecureID", nil, 1); + if(sta==nil || strlen(sta)==0){ + werrstr("cancel"); + goto Out; + } + if(strlen(sta) >= sizeof s - 3){ + werrstr("STA response too long"); + goto Out; + } + strcpy(s+3, sta); + conn->write(conn, (uchar*)s, strlen(s)); + readstr(conn, s); + } + if(strcmp(s, "OK") !=0){ + werrstr("%s", s); + goto Out; + } + if((rv = getfile(conn, (uchar*)pass, strlen(pass))) == nil) + goto Out; + strcpy(bye, "BYE"); + conn->write(conn, (uchar*)bye, 3); + +Out: + if(conn) + conn->free(conn); + if(pass) + free(pass); + if(sta) + free(sta); + return rv; +} + diff --git a/win32-386/getcallerpc.c b/win32-386/getcallerpc.c new file mode 100644 index 0000000..553c750 --- /dev/null +++ b/win32-386/getcallerpc.c @@ -0,0 +1,8 @@ +#include "u.h" +#include "libc.h" + +ulong +getcallerpc(void *a) +{ + return ((ulong*)a)[-1]; +} diff --git a/win32-386/md5block.s b/win32-386/md5block.s new file mode 100644 index 0000000..602c970 --- /dev/null +++ b/win32-386/md5block.s @@ -0,0 +1,241 @@ +/* + * rfc1321 requires that I include this. The code is new. The constants + * all come from the rfc (hence the copyright). We trade a table for the + * macros in rfc. The total size is a lot less. -- presotto + * + * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + * rights reserved. + * + * License to copy and use this software is granted provided that it + * is identified as the "RSA Data Security, Inc. MD5 Message-Digest + * Algorithm" in all material mentioning or referencing this software + * or this function. + * + * License is also granted to make and use derivative works provided + * that such works are identified as "derived from the RSA Data + * Security, Inc. MD5 Message-Digest Algorithm" in all material + * mentioning or referencing the derived work. + * + * RSA Data Security, Inc. makes no representations concerning either + * the merchantability of this software or the suitability of this + * software forany particular purpose. It is provided "as is" + * without express or implied warranty of any kind. + * These notices must be retained in any copies of any part of this + * documentation and/or software. + */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +#define PAYME(x) $##x + +/* + * SI is data + * a += FN(B,C,D); + * a += x[sh] + t[sh]; + * a = (a << S11) | (a >> (32 - S11)); + * a += b; + */ + +#define BODY1(off,V,FN,SH,A,B,C,D)\ + FN(B,C,D)\ + leal V(A, %edi, 1), A;\ + addl off(%ebp), A;\ + roll PAYME(SH), A;\ + addl B, A;\ + +#define BODY(off,V,FN,SH,A,B,C,D)\ + FN(B,C,D)\ + leal V(A, %edi, 1), A;\ + addl (off)(%ebp), A;\ + roll PAYME(SH), A;\ + addl B,A;\ + +/* + * fn1 = ((c ^ d) & b) ^ d + */ +#define FN1(B,C,D)\ + movl C, %edi;\ + xorl D, %edi;\ + andl B, %edi;\ + xorl D, %edi;\ + +/* + * fn2 = ((b ^ c) & d) ^ c; + */ +#define FN2(B,C,D)\ + movl B, %edi;\ + xorl C, %edi;\ + andl D, %edi;\ + xorl C, %edi;\ + +/* + * fn3 = b ^ c ^ d; + */ +#define FN3(B,C,D)\ + movl B, %edi;\ + xorl C, %edi;\ + xorl D, %edi;\ + +/* + * fn4 = c ^ (b | ~d); + */ +#define FN4(B,C,D)\ + movl D, %edi;\ + xorl $-1, %edi;\ + orl B, %edi;\ + xorl C, %edi;\ + +#define DATA 8 +#define LEN 12 +#define STATE 16 + +#define EDATA (-4) +#define OLDEBX (-8) +#define OLDESI (-12) +#define OLDEDI (-16) + + .text + + .p2align 2,0x90 + .globl _md5block + .type _md5block, @function + _md5block: + + /* Prelude */ + pushl %ebp + movl %ebx, OLDEBX(%esp) + movl %esi, OLDESI(%esp) + movl %edi, OLDEDI(%esp) + + movl DATA(%esp), %eax + addl LEN(%esp), %eax + movl %eax, EDATA(%esp) + + movl DATA(%esp), %ebp + +mainloop: + movl STATE(%esp), %esi + movl (%esi), %eax + movl 4(%esi), %ebx + movl 8(%esi), %ecx + movl 12(%esi), %edx + + BODY1( 0*4,0xd76aa478,FN1,S11,%eax,%ebx,%ecx,%edx) + BODY1( 1*4,0xe8c7b756,FN1,S12,%edx,%eax,%ebx,%ecx) + BODY1( 2*4,0x242070db,FN1,S13,%ecx,%edx,%eax,%ebx) + BODY1( 3*4,0xc1bdceee,FN1,S14,%ebx,%ecx,%edx,%eax) + + BODY1( 4*4,0xf57c0faf,FN1,S11,%eax,%ebx,%ecx,%edx) + BODY1( 5*4,0x4787c62a,FN1,S12,%edx,%eax,%ebx,%ecx) + BODY1( 6*4,0xa8304613,FN1,S13,%ecx,%edx,%eax,%ebx) + BODY1( 7*4,0xfd469501,FN1,S14,%ebx,%ecx,%edx,%eax) + + BODY1( 8*4,0x698098d8,FN1,S11,%eax,%ebx,%ecx,%edx) + BODY1( 9*4,0x8b44f7af,FN1,S12,%edx,%eax,%ebx,%ecx) + BODY1(10*4,0xffff5bb1,FN1,S13,%ecx,%edx,%eax,%ebx) + BODY1(11*4,0x895cd7be,FN1,S14,%ebx,%ecx,%edx,%eax) + + BODY1(12*4,0x6b901122,FN1,S11,%eax,%ebx,%ecx,%edx) + BODY1(13*4,0xfd987193,FN1,S12,%edx,%eax,%ebx,%ecx) + BODY1(14*4,0xa679438e,FN1,S13,%ecx,%edx,%eax,%ebx) + BODY1(15*4,0x49b40821,FN1,S14,%ebx,%ecx,%edx,%eax) + + + BODY( 1*4,0xf61e2562,FN2,S21,%eax,%ebx,%ecx,%edx) + BODY( 6*4,0xc040b340,FN2,S22,%edx,%eax,%ebx,%ecx) + BODY(11*4,0x265e5a51,FN2,S23,%ecx,%edx,%eax,%ebx) + BODY( 0*4,0xe9b6c7aa,FN2,S24,%ebx,%ecx,%edx,%eax) + + BODY( 5*4,0xd62f105d,FN2,S21,%eax,%ebx,%ecx,%edx) + BODY(10*4,0x02441453,FN2,S22,%edx,%eax,%ebx,%ecx) + BODY(15*4,0xd8a1e681,FN2,S23,%ecx,%edx,%eax,%ebx) + BODY( 4*4,0xe7d3fbc8,FN2,S24,%ebx,%ecx,%edx,%eax) + + BODY( 9*4,0x21e1cde6,FN2,S21,%eax,%ebx,%ecx,%edx) + BODY(14*4,0xc33707d6,FN2,S22,%edx,%eax,%ebx,%ecx) + BODY( 3*4,0xf4d50d87,FN2,S23,%ecx,%edx,%eax,%ebx) + BODY( 8*4,0x455a14ed,FN2,S24,%ebx,%ecx,%edx,%eax) + + BODY(13*4,0xa9e3e905,FN2,S21,%eax,%ebx,%ecx,%edx) + BODY( 2*4,0xfcefa3f8,FN2,S22,%edx,%eax,%ebx,%ecx) + BODY( 7*4,0x676f02d9,FN2,S23,%ecx,%edx,%eax,%ebx) + BODY(12*4,0x8d2a4c8a,FN2,S24,%ebx,%ecx,%edx,%eax) + + + BODY( 5*4,0xfffa3942,FN3,S31,%eax,%ebx,%ecx,%edx) + BODY( 8*4,0x8771f681,FN3,S32,%edx,%eax,%ebx,%ecx) + BODY(11*4,0x6d9d6122,FN3,S33,%ecx,%edx,%eax,%ebx) + BODY(14*4,0xfde5380c,FN3,S34,%ebx,%ecx,%edx,%eax) + + BODY( 1*4,0xa4beea44,FN3,S31,%eax,%ebx,%ecx,%edx) + BODY( 4*4,0x4bdecfa9,FN3,S32,%edx,%eax,%ebx,%ecx) + BODY( 7*4,0xf6bb4b60,FN3,S33,%ecx,%edx,%eax,%ebx) + BODY(10*4,0xbebfbc70,FN3,S34,%ebx,%ecx,%edx,%eax) + + BODY(13*4,0x289b7ec6,FN3,S31,%eax,%ebx,%ecx,%edx) + BODY( 0*4,0xeaa127fa,FN3,S32,%edx,%eax,%ebx,%ecx) + BODY( 3*4,0xd4ef3085,FN3,S33,%ecx,%edx,%eax,%ebx) + BODY( 6*4,0x04881d05,FN3,S34,%ebx,%ecx,%edx,%eax) + + BODY( 9*4,0xd9d4d039,FN3,S31,%eax,%ebx,%ecx,%edx) + BODY(12*4,0xe6db99e5,FN3,S32,%edx,%eax,%ebx,%ecx) + BODY(15*4,0x1fa27cf8,FN3,S33,%ecx,%edx,%eax,%ebx) + BODY( 2*4,0xc4ac5665,FN3,S34,%ebx,%ecx,%edx,%eax) + + + BODY( 0*4,0xf4292244,FN4,S41,%eax,%ebx,%ecx,%edx) + BODY( 7*4,0x432aff97,FN4,S42,%edx,%eax,%ebx,%ecx) + BODY(14*4,0xab9423a7,FN4,S43,%ecx,%edx,%eax,%ebx) + BODY( 5*4,0xfc93a039,FN4,S44,%ebx,%ecx,%edx,%eax) + + BODY(12*4,0x655b59c3,FN4,S41,%eax,%ebx,%ecx,%edx) + BODY( 3*4,0x8f0ccc92,FN4,S42,%edx,%eax,%ebx,%ecx) + BODY(10*4,0xffeff47d,FN4,S43,%ecx,%edx,%eax,%ebx) + BODY( 1*4,0x85845dd1,FN4,S44,%ebx,%ecx,%edx,%eax) + + BODY( 8*4,0x6fa87e4f,FN4,S41,%eax,%ebx,%ecx,%edx) + BODY(15*4,0xfe2ce6e0,FN4,S42,%edx,%eax,%ebx,%ecx) + BODY( 6*4,0xa3014314,FN4,S43,%ecx,%edx,%eax,%ebx) + BODY(13*4,0x4e0811a1,FN4,S44,%ebx,%ecx,%edx,%eax) + + BODY( 4*4,0xf7537e82,FN4,S41,%eax,%ebx,%ecx,%edx) + BODY(11*4,0xbd3af235,FN4,S42,%edx,%eax,%ebx,%ecx) + BODY( 2*4,0x2ad7d2bb,FN4,S43,%ecx,%edx,%eax,%ebx) + BODY( 9*4,0xeb86d391,FN4,S44,%ebx,%ecx,%edx,%eax) + + addl $(16*4), %ebp + movl STATE(%esp), %edi + addl %eax,0(%edi) + addl %ebx,4(%edi) + addl %ecx,8(%edi) + addl %edx,12(%edi) + + movl EDATA(%esp), %edi + cmpl %edi, %ebp + jb mainloop + + /* Postlude */ + movl OLDEBX(%esp), %ebx + movl OLDESI(%esp), %esi + movl OLDEDI(%esp), %edi + movl %esp, %ebp + leave + ret + diff --git a/win32-386/mkfile b/win32-386/mkfile new file mode 100644 index 0000000..e163550 --- /dev/null +++ b/win32-386/mkfile @@ -0,0 +1,10 @@ +<$DSRC/mkfile-$CONF +TARG=libmachdep.$L + +OFILES=\ + getcallerpc.$O\ + tas.$O + +HFILES=\ + +<$DSRC/mklib-$CONF diff --git a/win32-386/sha1block.s b/win32-386/sha1block.s new file mode 100644 index 0000000..7d0696d --- /dev/null +++ b/win32-386/sha1block.s @@ -0,0 +1,214 @@ +.text + +.p2align 2,0x90 +.globl _sha1block + .type _sha1block, @function +_sha1block: + +/* x = (wp[off-f] ^ wp[off-8] ^ wp[off-14] ^ wp[off-16]) <<< 1; + * wp[off] = x; + * x += A <<< 5; + * E += 0xca62c1d6 + x; + * x = FN(B,C,D); + * E += x; + * B >>> 2 + */ +#define BSWAPDI BYTE $0x0f; BYTE $0xcf; + +#define BODY(off,FN,V,A,B,C,D,E)\ + movl (off-64)(%ebp), %edi;\ + xorl (off-56)(%ebp), %edi;\ + xorl (off-32)(%ebp), %edi;\ + xorl (off-12)(%ebp), %edi;\ + roll $1, %edi;\ + movl %edi, off(%ebp);\ + leal V(%edi, E, 1), E;\ + movl A, %edi;\ + roll $5, %edi;\ + addl %edi, E;\ + FN(B,C,D)\ + addl %edi, E;\ + rorl $2, B;\ + +#define BODY0(off,FN,V,A,B,C,D,E)\ + movl off(%ebx), %edi;\ + bswap %edi;\ + movl %edi, off(%ebp);\ + leal V(%edi,E,1), E;\ + movl A, %edi;\ + roll $5,%edi;\ + addl %edi,E;\ + FN(B,C,D)\ + addl %edi,E;\ + rorl $2,B;\ + +/* + * fn1 = (((C^D)&B)^D); + */ +#define FN1(B,C,D)\ + movl C, %edi;\ + xorl D, %edi;\ + andl B, %edi;\ + xorl D, %edi;\ + +/* + * fn24 = B ^ C ^ D + */ +#define FN24(B,C,D)\ + movl B, %edi;\ + xorl C, %edi;\ + xorl D, %edi;\ + +/* + * fn3 = ((B ^ C) & (D ^= B)) ^ B + * D ^= B to restore D + */ +#define FN3(B,C,D)\ + movl B, %edi;\ + xorl C, %edi;\ + xorl B, D;\ + andl D, %edi;\ + xorl B, %edi;\ + xorl B, D;\ + +/* + * stack offsets + * void sha1block(uchar *DATA, int LEN, ulong *STATE) + */ +#define DATA 8 +#define LEN 12 +#define STATE 16 + +/* + * stack offsets for locals + * ulong w[80]; + * uchar *edata; + * ulong *w15, *w40, *w60, *w80; + * register local + * ulong *wp = %ebp + * ulong a = eax, b = ebx, c = ecx, d = edx, e = esi + * ulong tmp = edi + */ +#define WARRAY (-4-(80*4)) +#define TMP1 (-8-(80*4)) +#define TMP2 (-12-(80*4)) +#define W15 (-16-(80*4)) +#define W40 (-20-(80*4)) +#define W60 (-24-(80*4)) +#define W80 (-28-(80*4)) +#define EDATA (-32-(80*4)) +#define OLDEBX (-36-(80*4)) +#define OLDESI (-40-(80*4)) +#define OLDEDI (-44-(80*4)) + + /* Prelude */ + pushl %ebp + mov %ebx, OLDEBX(%esp) + mov %esi, OLDESI(%esp) + mov %edi, OLDEDI(%esp) + + movl DATA(%esp), %eax + addl LEN(%esp), %eax + movl %eax, EDATA(%esp) + + leal (WARRAY+15*4)(%esp), %edi /* aw15 */ + movl %edi, W15(%esp) + leal (WARRAY+40*4)(%esp), %edx /* aw40 */ + movl %edx, W40(%esp) + leal (WARRAY+60*4)(%esp), %ecx /* aw60 */ + movl %ecx, W60(%esp) + leal (WARRAY+80*4)(%esp), %edi /* aw80 */ + movl %edi, W80(%esp) + +mainloop: + leal WARRAY(%esp), %ebp /* warray */ + + movl STATE(%esp), %edi /* state */ + movl (%edi),%eax + movl 4(%edi),%ebx + movl %ebx, TMP1(%esp) /* tmp1 */ + movl 8(%edi), %ecx + movl 12(%edi), %edx + movl 16(%edi), %esi + + movl DATA(%esp), %ebx /* data */ + +loop1: + BODY0(0,FN1,0x5a827999,%eax,TMP1(%esp),%ecx,%edx,%esi) + movl %esi,TMP2(%esp) + BODY0(4,FN1,0x5a827999,%esi,%eax,TMP1(%esp),%ecx,%edx) + movl TMP1(%esp),%esi + BODY0(8,FN1,0x5a827999,%edx,TMP2(%esp),%eax,%esi,%ecx) + BODY0(12,FN1,0x5a827999,%ecx,%edx,TMP2(%esp),%eax,%esi) + movl %esi,TMP1(%esp) + BODY0(16,FN1,0x5a827999,%esi,%ecx,%edx,TMP2(%esp),%eax) + movl TMP2(%esp),%esi + + addl $20, %ebx + addl $20, %ebp + cmpl W15(%esp), %ebp /* w15 */ + jb loop1 + + BODY0(0,FN1,0x5a827999,%eax,TMP1(%esp),%ecx,%edx,%esi) + addl $4, %ebx + MOVL %ebx, DATA(%esp) /* data */ + MOVL TMP1(%esp),%ebx + + BODY(4,FN1,0x5a827999,%esi,%eax,%ebx,%ecx,%edx) + BODY(8,FN1,0x5a827999,%edx,%esi,%eax,%ebx,%ecx) + BODY(12,FN1,0x5a827999,%ecx,%edx,%esi,%eax,%ebx) + BODY(16,FN1,0x5a827999,%ebx,%ecx,%edx,%esi,%eax) + + addl $20, %ebp + +loop2: + BODY(0,FN24,0x6ed9eba1,%eax,%ebx,%ecx,%edx,%esi) + BODY(4,FN24,0x6ed9eba1,%esi,%eax,%ebx,%ecx,%edx) + BODY(8,FN24,0x6ed9eba1,%edx,%esi,%eax,%ebx,%ecx) + BODY(12,FN24,0x6ed9eba1,%ecx,%edx,%esi,%eax,%ebx) + BODY(16,FN24,0x6ed9eba1,%ebx,%ecx,%edx,%esi,%eax) + + addl $20,%ebp + cmpl W40(%esp), %ebp + jb loop2 + +loop3: + BODY(0,FN3,0x8f1bbcdc,%eax,%ebx,%ecx,%edx,%esi) + BODY(4,FN3,0x8f1bbcdc,%esi,%eax,%ebx,%ecx,%edx) + BODY(8,FN3,0x8f1bbcdc,%edx,%esi,%eax,%ebx,%ecx) + BODY(12,FN3,0x8f1bbcdc,%ecx,%edx,%esi,%eax,%ebx) + BODY(16,FN3,0x8f1bbcdc,%ebx,%ecx,%edx,%esi,%eax) + + addl $20, %ebp + cmpl W60(%esp), %ebp /* w60 */ + jb loop3 + +loop4: + BODY(0,FN24,0xca62c1d6,%eax,%ebx,%ecx,%edx,%esi) + BODY(4,FN24,0xca62c1d6,%esi,%eax,%ebx,%ecx,%edx) + BODY(8,FN24,0xca62c1d6,%edx,%esi,%eax,%ebx,%ecx) + BODY(12,FN24,0xca62c1d6,%ecx,%edx,%esi,%eax,%ebx) + BODY(16,FN24,0xca62c1d6,%ebx,%ecx,%edx,%esi,%eax) + + addl $20, %ebp + cmpl W80(%esp), %ebp /* w80 */ + jb loop4 + + movl STATE(%esp), %edi /* state */ + addl %eax, 0(%edi) + addl %ebx, 4(%edi) + addl %ecx, 8(%edi) + addl %edx, 12(%edi) + addl %esi, 16(%edi) + + movl EDATA(%esp), %edi /* edata */ + cmpl %edi, DATA(%esp) /* data */ + jb mainloop + + /* Postlude */ + mov OLDEBX(%esp), %ebx + mov OLDESI(%esp), %esi + mov OLDEDI(%esp), %edi + movl %esp, %ebp + leave + ret diff --git a/win32-386/tas.c b/win32-386/tas.c new file mode 100644 index 0000000..4ca45d5 --- /dev/null +++ b/win32-386/tas.c @@ -0,0 +1,22 @@ +// could also use windozy InterlockedCompareExchange(p, 1, 0), but why +int +tas(long *p) +{ + int v; + + _asm { + mov eax, p + mov ebx, 1 + xchg ebx, [eax] + mov v, ebx + } + + switch(v) { + case 0: + case 1: + return v; + default: + print("canlock: corrupted 0x%lux\n", v); + return 1; + } +}