tInitial revision - plan9port - [fork] Plan 9 from user space | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 76193d7cb0457807b2f0b95f909ab5de19480cd7 | |
parent ed7c8e8d02c02bdbff1e88a6d8d1419f39af48ad | |
Author: rsc <devnull@localhost> | |
Date: Tue, 30 Sep 2003 17:47:42 +0000 | |
Initial revision | |
Diffstat: | |
A man/man7/regexp9.7 | 150 +++++++++++++++++++++++++++++… | |
A man/man7/utf.7 | 91 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/LICENSE | 258 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/Make.FreeBSD-386 | 7 +++++++ | |
A src/cmd/mk/Make.HP-UX-9000 | 6 ++++++ | |
A src/cmd/mk/Make.Linux-386 | 7 +++++++ | |
A src/cmd/mk/Make.OSF1-alpha | 6 ++++++ | |
A src/cmd/mk/Make.SunOS-sun4u | 2 ++ | |
A src/cmd/mk/Make.SunOS-sun4u-cc | 6 ++++++ | |
A src/cmd/mk/Make.SunOS-sun4u-gcc | 6 ++++++ | |
A src/cmd/mk/Makefile | 117 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/Makefile.MID | 45 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/arc.c | 52 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/archive.c | 180 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/bundle.ports | 46 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/env.c | 149 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/file.c | 90 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/fns.h | 84 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/graph.c | 279 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/lex.c | 147 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/main.c | 285 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/match.c | 49 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/mk.1 | 665 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/mk.c | 226 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/mk.h | 203 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/mkfile | 9 +++++++++ | |
A src/cmd/mk/mkfile.test | 5 +++++ | |
A src/cmd/mk/parse.c | 307 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/rc.c | 175 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/recipe.c | 117 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/rpm.spec | 29 +++++++++++++++++++++++++++++ | |
A src/cmd/mk/rule.c | 107 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/run.c | 296 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/sh.c | 189 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/shprint.c | 123 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/symtab.c | 95 ++++++++++++++++++++++++++++++ | |
A src/cmd/mk/unix.c | 306 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/var.c | 41 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/varsub.c | 256 +++++++++++++++++++++++++++++… | |
A src/cmd/mk/word.c | 180 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/LICENSE | 258 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/Makefile | 18 ++++++++++++++++++ | |
A src/cmd/sam/address.c | 240 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/buff.c | 302 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/cmd.c | 594 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/disk.c | 122 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/error.c | 144 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/errors.h | 65 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/file.c | 631 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/io.c | 262 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/list.c | 47 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/mesg.c | 821 ++++++++++++++++++++++++++++++ | |
A src/cmd/sam/mesg.h | 131 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/mkfile | 40 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/moveto.c | 173 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/multi.c | 123 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/parse.h | 68 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/plan9.c | 185 ++++++++++++++++++++++++++++++ | |
A src/cmd/sam/plumb.c | 9 +++++++++ | |
A src/cmd/sam/rasp.c | 325 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/regexp.c | 801 ++++++++++++++++++++++++++++++ | |
A src/cmd/sam/sam | 0 | |
A src/cmd/sam/sam.c | 739 ++++++++++++++++++++++++++++++ | |
A src/cmd/sam/sam.h | 407 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/shell.c | 152 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/unix.c | 272 +++++++++++++++++++++++++++++… | |
A src/cmd/sam/xec.c | 508 +++++++++++++++++++++++++++++… | |
A src/libdraw/BOT | 0 | |
A src/libdraw/Make.Darwin-PowerMacin… | 6 ++++++ | |
A src/libdraw/Make.FreeBSD-386 | 7 +++++++ | |
A src/libdraw/Make.HP-UX-9000 | 6 ++++++ | |
A src/libdraw/Make.Linux-386 | 7 +++++++ | |
A src/libdraw/Make.NetBSD-386 | 7 +++++++ | |
A src/libdraw/Make.OSF1-alpha | 6 ++++++ | |
A src/libdraw/Make.SunOS-sun4u | 2 ++ | |
A src/libdraw/Make.SunOS-sun4u-cc | 6 ++++++ | |
A src/libdraw/Make.SunOS-sun4u-gcc | 6 ++++++ | |
A src/libdraw/Makefile | 194 ++++++++++++++++++++++++++++++ | |
A src/libdraw/Makefile.MID | 123 +++++++++++++++++++++++++++++… | |
A src/libdraw/alloc.c | 237 +++++++++++++++++++++++++++++… | |
A src/libdraw/arith.c | 206 +++++++++++++++++++++++++++++… | |
A src/libdraw/buildfont.c | 141 +++++++++++++++++++++++++++++… | |
A src/libdraw/bytesperline.c | 34 +++++++++++++++++++++++++++++… | |
A src/libdraw/chan.c | 77 +++++++++++++++++++++++++++++… | |
A src/libdraw/creadimage.c | 113 +++++++++++++++++++++++++++++… | |
A src/libdraw/cursor.h | 7 +++++++ | |
A src/libdraw/devdraw.c | 1587 +++++++++++++++++++++++++++++… | |
A src/libdraw/draw.h | 520 +++++++++++++++++++++++++++++… | |
A src/libdraw/ellipse.c | 82 +++++++++++++++++++++++++++++… | |
A src/libdraw/emenuhit.c | 271 +++++++++++++++++++++++++++++… | |
A src/libdraw/event.c | 486 +++++++++++++++++++++++++++++… | |
A src/libdraw/event.h | 63 +++++++++++++++++++++++++++++… | |
A src/libdraw/font.c | 401 +++++++++++++++++++++++++++++… | |
A src/libdraw/getsubfont.c | 36 +++++++++++++++++++++++++++++… | |
A src/libdraw/init.c | 203 +++++++++++++++++++++++++++++… | |
A src/libdraw/keyboard.c | 102 +++++++++++++++++++++++++++++… | |
A src/libdraw/keyboard.h | 36 +++++++++++++++++++++++++++++… | |
A src/libdraw/libdraw.x | 1 + | |
A src/libdraw/md-alloc.c | 200 +++++++++++++++++++++++++++++… | |
A src/libdraw/md-arc.c | 116 ++++++++++++++++++++++++++++++ | |
A src/libdraw/md-arctest.c | 61 +++++++++++++++++++++++++++++… | |
A src/libdraw/md-cload.c | 68 +++++++++++++++++++++++++++++… | |
A src/libdraw/md-cmap.c | 320 +++++++++++++++++++++++++++++… | |
A src/libdraw/md-cread.c | 96 +++++++++++++++++++++++++++++… | |
A src/libdraw/md-defont.c | 68 +++++++++++++++++++++++++++++… | |
A src/libdraw/md-draw.c | 2487 +++++++++++++++++++++++++++++… | |
A src/libdraw/md-drawtest.c | 1004 +++++++++++++++++++++++++++++… | |
A src/libdraw/md-ellipse.c | 247 +++++++++++++++++++++++++++++… | |
A src/libdraw/md-fillpoly.c | 524 +++++++++++++++++++++++++++++… | |
A src/libdraw/md-hwdraw.c | 12 ++++++++++++ | |
A src/libdraw/md-iprint.c | 12 ++++++++++++ | |
A src/libdraw/md-line.c | 484 +++++++++++++++++++++++++++++… | |
A src/libdraw/md-load.c | 72 +++++++++++++++++++++++++++++… | |
A src/libdraw/md-mkcmap.c | 79 +++++++++++++++++++++++++++++… | |
A src/libdraw/md-openmemsubfont.c | 53 ++++++++++++++++++++++++++++++ | |
A src/libdraw/md-poly.c | 23 +++++++++++++++++++++++ | |
A src/libdraw/md-read.c | 111 ++++++++++++++++++++++++++++++ | |
A src/libdraw/md-string.c | 66 +++++++++++++++++++++++++++++… | |
A src/libdraw/md-subfont.c | 34 +++++++++++++++++++++++++++++… | |
A src/libdraw/md-unload.c | 25 +++++++++++++++++++++++++ | |
A src/libdraw/md-write.c | 183 +++++++++++++++++++++++++++++… | |
A src/libdraw/memdraw.h | 209 +++++++++++++++++++++++++++++… | |
A src/libdraw/memlayer.h | 48 +++++++++++++++++++++++++++++… | |
A src/libdraw/menuhit.c | 277 +++++++++++++++++++++++++++++… | |
A src/libdraw/mkfile | 1 + | |
A src/libdraw/ml-draw.c | 192 +++++++++++++++++++++++++++++… | |
A src/libdraw/ml-lalloc.c | 79 +++++++++++++++++++++++++++++… | |
A src/libdraw/ml-layerop.c | 112 +++++++++++++++++++++++++++++… | |
A src/libdraw/ml-ldelete.c | 67 +++++++++++++++++++++++++++++… | |
A src/libdraw/ml-lhide.c | 67 +++++++++++++++++++++++++++++… | |
A src/libdraw/ml-line.c | 122 +++++++++++++++++++++++++++++… | |
A src/libdraw/ml-load.c | 55 +++++++++++++++++++++++++++++… | |
A src/libdraw/ml-lorigin.c | 107 +++++++++++++++++++++++++++++… | |
A src/libdraw/ml-lsetrefresh.c | 35 +++++++++++++++++++++++++++++… | |
A src/libdraw/ml-ltofront.c | 80 +++++++++++++++++++++++++++++… | |
A src/libdraw/ml-ltorear.c | 69 ++++++++++++++++++++++++++++++ | |
A src/libdraw/ml-unload.c | 52 +++++++++++++++++++++++++++++… | |
A src/libdraw/mouse.c | 139 ++++++++++++++++++++++++++++++ | |
A src/libdraw/mouse.h | 44 +++++++++++++++++++++++++++++… | |
A src/libdraw/openfont.c | 32 +++++++++++++++++++++++++++++… | |
A src/libdraw/readcolmap.c | 49 +++++++++++++++++++++++++++++… | |
A src/libdraw/readimage.c | 118 +++++++++++++++++++++++++++++… | |
A src/libdraw/readsubfont.c | 58 ++++++++++++++++++++++++++++++ | |
A src/libdraw/string.c | 137 +++++++++++++++++++++++++++++… | |
A src/libdraw/stringwidth.c | 97 ++++++++++++++++++++++++++++++ | |
A src/libdraw/subfont.c | 28 ++++++++++++++++++++++++++++ | |
A src/libdraw/subfontcache.c | 39 +++++++++++++++++++++++++++++… | |
A src/libdraw/subfontname.c | 44 +++++++++++++++++++++++++++++… | |
A src/libdraw/unix.c | 16 ++++++++++++++++ | |
A src/libdraw/unloadimage.c | 53 ++++++++++++++++++++++++++++++ | |
A src/libdraw/writecolmap.c | 35 +++++++++++++++++++++++++++++… | |
A src/libdraw/x11-alloc.c | 120 +++++++++++++++++++++++++++++… | |
A src/libdraw/x11-cload.c | 19 +++++++++++++++++++ | |
A src/libdraw/x11-draw.c | 143 +++++++++++++++++++++++++++++… | |
A src/libdraw/x11-event.c | 136 +++++++++++++++++++++++++++++… | |
A src/libdraw/x11-fill.c | 57 +++++++++++++++++++++++++++++… | |
A src/libdraw/x11-get.c | 110 +++++++++++++++++++++++++++++… | |
A src/libdraw/x11-inc.h | 31 +++++++++++++++++++++++++++++… | |
A src/libdraw/x11-init.c | 584 +++++++++++++++++++++++++++++… | |
A src/libdraw/x11-itrans.c | 258 +++++++++++++++++++++++++++++… | |
A src/libdraw/x11-keyboard.c | 71 +++++++++++++++++++++++++++++… | |
A src/libdraw/x11-load.c | 19 +++++++++++++++++++ | |
A src/libdraw/x11-memdraw.h | 93 +++++++++++++++++++++++++++++… | |
A src/libdraw/x11-mouse.c | 107 +++++++++++++++++++++++++++++… | |
A src/libdraw/x11-pixelbits.c | 17 +++++++++++++++++ | |
A src/libdraw/x11-unload.c | 16 ++++++++++++++++ | |
A src/libthread/386.c | 21 +++++++++++++++++++++ | |
A src/libthread/FreeBSD-386.s | 18 ++++++++++++++++++ | |
A src/libthread/LICENSE | 258 +++++++++++++++++++++++++++++… | |
A src/libthread/Make.Darwin-PowerMac… | 6 ++++++ | |
A src/libthread/Make.FreeBSD-386 | 7 +++++++ | |
A src/libthread/Make.HP-UX-9000 | 6 ++++++ | |
A src/libthread/Make.Linux-386 | 7 +++++++ | |
A src/libthread/Make.NetBSD-386 | 7 +++++++ | |
A src/libthread/Make.OSF1-alpha | 6 ++++++ | |
A src/libthread/Make.SunOS-sun4u | 2 ++ | |
A src/libthread/Make.SunOS-sun4u-cc | 6 ++++++ | |
A src/libthread/Make.SunOS-sun4u-gcc | 6 ++++++ | |
A src/libthread/Makefile | 125 +++++++++++++++++++++++++++++… | |
A src/libthread/Makefile.MID | 54 +++++++++++++++++++++++++++++… | |
A src/libthread/NOTICE | 19 +++++++++++++++++++ | |
A src/libthread/README | 19 +++++++++++++++++++ | |
A src/libthread/asm-FreeBSD-386.s | 49 +++++++++++++++++++++++++++++… | |
A src/libthread/asm-Linux-386.s | 1 + | |
A src/libthread/bundle.ports | 42 +++++++++++++++++++++++++++++… | |
A src/libthread/channel.c | 485 +++++++++++++++++++++++++++++… | |
A src/libthread/chanprint.c | 18 ++++++++++++++++++ | |
A src/libthread/create.c | 182 +++++++++++++++++++++++++++++… | |
A src/libthread/debug.c | 48 +++++++++++++++++++++++++++++… | |
A src/libthread/exec-unix.c | 124 +++++++++++++++++++++++++++++… | |
A src/libthread/exec.c | 77 +++++++++++++++++++++++++++++… | |
A src/libthread/exit.c | 63 +++++++++++++++++++++++++++++… | |
A src/libthread/getpid.c | 8 ++++++++ | |
A src/libthread/id.c | 135 +++++++++++++++++++++++++++++… | |
A src/libthread/iocall.c | 49 +++++++++++++++++++++++++++++… | |
A src/libthread/ioclose.c | 16 ++++++++++++++++ | |
A src/libthread/iodial.c | 21 +++++++++++++++++++++ | |
A src/libthread/ioopen.c | 20 ++++++++++++++++++++ | |
A src/libthread/ioproc.3 | 179 +++++++++++++++++++++++++++++… | |
A src/libthread/ioproc.c | 74 +++++++++++++++++++++++++++++… | |
A src/libthread/ioread.c | 20 ++++++++++++++++++++ | |
A src/libthread/ioreadn.c | 21 +++++++++++++++++++++ | |
A src/libthread/iosleep.c | 16 ++++++++++++++++ | |
A src/libthread/iowrite.c | 21 +++++++++++++++++++++ | |
A src/libthread/kill.c | 89 +++++++++++++++++++++++++++++… | |
A src/libthread/label.h | 24 ++++++++++++++++++++++++ | |
A src/libthread/lib.c | 35 +++++++++++++++++++++++++++++… | |
A src/libthread/main.c | 124 +++++++++++++++++++++++++++++… | |
A src/libthread/memset.c | 8 ++++++++ | |
A src/libthread/memsetd.c | 8 ++++++++ | |
A src/libthread/mkfile | 2 ++ | |
A src/libthread/note.c | 143 +++++++++++++++++++++++++++++… | |
A src/libthread/proctab.c | 64 +++++++++++++++++++++++++++++… | |
A src/libthread/ref.c | 13 +++++++++++++ | |
A src/libthread/rendez.c | 104 +++++++++++++++++++++++++++++… | |
A src/libthread/rpm.spec | 26 ++++++++++++++++++++++++++ | |
A src/libthread/sched.c | 192 +++++++++++++++++++++++++++++… | |
A src/libthread/texec.c | 34 +++++++++++++++++++++++++++++… | |
A src/libthread/thread.3 | 576 +++++++++++++++++++++++++++++… | |
A src/libthread/thread.h | 132 +++++++++++++++++++++++++++++… | |
A src/libthread/threadimpl.h | 219 +++++++++++++++++++++++++++++… | |
A src/libthread/tprimes | 0 | |
A src/libthread/tprimes.c | 62 +++++++++++++++++++++++++++++… | |
223 files changed, 32479 insertions(+), 0 deletions(-) | |
--- | |
diff --git a/man/man7/regexp9.7 b/man/man7/regexp9.7 | |
t@@ -0,0 +1,150 @@ | |
+.TH REGEXP9 7 | |
+.de EX | |
+.nf | |
+.ft B | |
+.. | |
+.de EE | |
+.fi | |
+.ft R | |
+.. | |
+.de LR | |
+.if t .BR \\$1 \\$2 | |
+.if n .RB ` \\$1 '\\$2 | |
+.. | |
+.de L | |
+.nh | |
+.if t .B \\$1 | |
+.if n .RB ` \\$1 ' | |
+.. | |
+.SH NAME | |
+regexp9 \- Plan 9 regular expression notation | |
+.SH DESCRIPTION | |
+This manual page describes the regular expression | |
+syntax used by the Plan 9 regular expression library | |
+.IR regexp9 (3). | |
+It is the form used by | |
+.IR egrep (1) | |
+before | |
+.I egrep | |
+got complicated. | |
+.PP | |
+A | |
+.I "regular expression" | |
+specifies | |
+a set of strings of characters. | |
+A member of this set of strings is said to be | |
+.I matched | |
+by the regular expression. In many applications | |
+a delimiter character, commonly | |
+.LR / , | |
+bounds a regular expression. | |
+In the following specification for regular expressions | |
+the word `character' means any character (rune) but newline. | |
+.PP | |
+The syntax for a regular expression | |
+.B e0 | |
+is | |
+.IP | |
+.EX | |
+e3: literal | charclass | '.' | '^' | '$' | '(' e0 ')' | |
+ | |
+e2: e3 | |
+ | e2 REP | |
+ | |
+REP: '*' | '+' | '?' | |
+ | |
+e1: e2 | |
+ | e1 e2 | |
+ | |
+e0: e1 | |
+ | e0 '|' e1 | |
+.EE | |
+.PP | |
+A | |
+.B literal | |
+is any non-metacharacter, or a metacharacter | |
+(one of | |
+.BR .*+?[]()|\e^$ ), | |
+or the delimiter | |
+preceded by | |
+.LR \e . | |
+.PP | |
+A | |
+.B charclass | |
+is a nonempty string | |
+.I s | |
+bracketed | |
+.BI [ \|s\| ] | |
+(or | |
+.BI [^ s\| ]\fR); | |
+it matches any character in (or not in) | |
+.IR s . | |
+A negated character class never | |
+matches newline. | |
+A substring | |
+.IB a - b\f1, | |
+with | |
+.I a | |
+and | |
+.I b | |
+in ascending | |
+order, stands for the inclusive | |
+range of | |
+characters between | |
+.I a | |
+and | |
+.IR b . | |
+In | |
+.IR s , | |
+the metacharacters | |
+.LR - , | |
+.LR ] , | |
+an initial | |
+.LR ^ , | |
+and the regular expression delimiter | |
+must be preceded by a | |
+.LR \e ; | |
+other metacharacters | |
+have no special meaning and | |
+may appear unescaped. | |
+.PP | |
+A | |
+.L . | |
+matches any character. | |
+.PP | |
+A | |
+.L ^ | |
+matches the beginning of a line; | |
+.L $ | |
+matches the end of the line. | |
+.PP | |
+The | |
+.B REP | |
+operators match zero or more | |
+.RB ( * ), | |
+one or more | |
+.RB ( + ), | |
+zero or one | |
+.RB ( ? ), | |
+instances respectively of the preceding regular expression | |
+.BR e2 . | |
+.PP | |
+A concatenated regular expression, | |
+.BR "e1\|e2" , | |
+matches a match to | |
+.B e1 | |
+followed by a match to | |
+.BR e2 . | |
+.PP | |
+An alternative regular expression, | |
+.BR "e0\||\|e1" , | |
+matches either a match to | |
+.B e0 | |
+or a match to | |
+.BR e1 . | |
+.PP | |
+A match to any part of a regular expression | |
+extends as far as possible without preventing | |
+a match to the remainder of the regular expression. | |
+.SH "SEE ALSO" | |
+.IR regexp9 (3) | |
diff --git a/man/man7/utf.7 b/man/man7/utf.7 | |
t@@ -0,0 +1,91 @@ | |
+.TH UTF 7 | |
+.SH NAME | |
+UTF, Unicode, ASCII, rune \- character set and format | |
+.SH DESCRIPTION | |
+The Plan 9 character set and representation are | |
+based on the Unicode Standard and on the ISO multibyte | |
+.SM UTF-8 | |
+encoding (Universal Character | |
+Set Transformation Format, 8 bits wide). | |
+The Unicode Standard represents its characters in 16 | |
+bits; | |
+.SM UTF-8 | |
+represents such | |
+values in an 8-bit byte stream. | |
+Throughout this manual, | |
+.SM UTF-8 | |
+is shortened to | |
+.SM UTF. | |
+.PP | |
+In Plan 9, a | |
+.I rune | |
+is a 16-bit quantity representing a Unicode character. | |
+Internally, programs may store characters as runes. | |
+However, any external manifestation of textual information, | |
+in files or at the interface between programs, uses a | |
+machine-independent, byte-stream encoding called | |
+.SM UTF. | |
+.PP | |
+.SM UTF | |
+is designed so the 7-bit | |
+.SM ASCII | |
+set (values hexadecimal 00 to 7F), | |
+appear only as themselves | |
+in the encoding. | |
+Runes with values above 7F appear as sequences of two or more | |
+bytes with values only from 80 to FF. | |
+.PP | |
+The | |
+.SM UTF | |
+encoding of the Unicode Standard is backward compatible with | |
+.SM ASCII\c | |
+: | |
+programs presented only with | |
+.SM ASCII | |
+work on Plan 9 | |
+even if not written to deal with | |
+.SM UTF, | |
+as do | |
+programs that deal with uninterpreted byte streams. | |
+However, programs that perform semantic processing on | |
+.SM ASCII | |
+graphic | |
+characters must convert from | |
+.SM UTF | |
+to runes | |
+in order to work properly with non-\c | |
+.SM ASCII | |
+input. | |
+See | |
+.IR rune (2). | |
+.PP | |
+Letting numbers be binary, | |
+a rune x is converted to a multibyte | |
+.SM UTF | |
+sequence | |
+as follows: | |
+.PP | |
+01. x in [00000000.0bbbbbbb] → 0bbbbbbb | |
+.br | |
+10. x in [00000bbb.bbbbbbbb] → 110bbbbb, 10bbbbbb | |
+.br | |
+11. x in [bbbbbbbb.bbbbbbbb] → 1110bbbb, 10bbbbbb, 10bbbbbb | |
+.br | |
+.PP | |
+Conversion 01 provides a one-byte sequence that spans the | |
+.SM ASCII | |
+character set in a compatible way. | |
+Conversions 10 and 11 represent higher-valued characters | |
+as sequences of two or three bytes with the high bit set. | |
+Plan 9 does not support the 4, 5, and 6 byte sequences proposed by X-Open. | |
+When there are multiple ways to encode a value, for example rune 0, | |
+the shortest encoding is used. | |
+.PP | |
+In the inverse mapping, | |
+any sequence except those described above | |
+is incorrect and is converted to rune hexadecimal 0080. | |
+.SH "SEE ALSO" | |
+.IR ascii (1), | |
+.IR tcs (1), | |
+.IR rune (3), | |
+.IR "The Unicode Standard" . | |
diff --git a/src/cmd/mk/LICENSE b/src/cmd/mk/LICENSE | |
t@@ -0,0 +1,258 @@ | |
+The Plan 9 software is provided under the terms of the | |
+Lucent Public License, Version 1.02, reproduced below, | |
+with the following exceptions: | |
+ | |
+1. No right is granted to create derivative works of or | |
+ to redistribute (other than with the Plan 9 Operating System) | |
+ the screen imprinter fonts identified in subdirectory | |
+ /lib/font/bit/lucida and printer fonts (Lucida Sans Unicode, Lucida | |
+ Sans Italic, Lucida Sans Demibold, Lucida Typewriter, Lucida Sans | |
+ Typewriter83), identified in subdirectory /sys/lib/postscript/font. | |
+ These directories contain material copyrights by B&H Inc. and Y&Y Inc. | |
+ | |
+2. The printer fonts identified in subdirectory /sys/lib/ghostscript/font | |
+ are subject to the GNU GPL, reproduced in the file /LICENSE.gpl. | |
+ | |
+3. The ghostscript program in the subdirectory /sys/src/cmd/gs is | |
+ covered by the Aladdin Free Public License, reproduced in the file | |
+ /LICENSE.afpl. | |
+ | |
+=================================================================== | |
+ | |
+Lucent Public License Version 1.02 | |
+ | |
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC | |
+LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE | |
+PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. | |
+ | |
+1. DEFINITIONS | |
+ | |
+"Contribution" means: | |
+ | |
+ a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original | |
+ Program, and | |
+ b. in the case of each Contributor, | |
+ | |
+ i. changes to the Program, and | |
+ ii. additions to the Program; | |
+ | |
+ where such changes and/or additions to the Program were added to the | |
+ Program by such Contributor itself or anyone acting on such | |
+ Contributor's behalf, and the Contributor explicitly consents, in | |
+ accordance with Section 3C, to characterization of the changes and/or | |
+ additions as Contributions. | |
+ | |
+"Contributor" means LUCENT and any other entity that has Contributed a | |
+Contribution to the Program. | |
+ | |
+"Distributor" means a Recipient that distributes the Program, | |
+modifications to the Program, or any part thereof. | |
+ | |
+"Licensed Patents" mean patent claims licensable by a Contributor | |
+which are necessarily infringed by the use or sale of its Contribution | |
+alone or when combined with the Program. | |
+ | |
+"Original Program" means the original version of the software | |
+accompanying this Agreement as released by LUCENT, including source | |
+code, object code and documentation, if any. | |
+ | |
+"Program" means the Original Program and Contributions or any part | |
+thereof | |
+ | |
+"Recipient" means anyone who receives the Program under this | |
+Agreement, including all Contributors. | |
+ | |
+2. GRANT OF RIGHTS | |
+ | |
+ a. Subject to the terms of this Agreement, each Contributor hereby | |
+ grants Recipient a non-exclusive, worldwide, royalty-free copyright | |
+ license to reproduce, prepare derivative works of, publicly display, | |
+ publicly perform, distribute and sublicense the Contribution of such | |
+ Contributor, if any, and such derivative works, in source code and | |
+ object code form. | |
+ | |
+ b. Subject to the terms of this Agreement, each Contributor hereby | |
+ grants Recipient a non-exclusive, worldwide, royalty-free patent | |
+ license under Licensed Patents to make, use, sell, offer to sell, | |
+ import and otherwise transfer the Contribution of such Contributor, if | |
+ any, in source code and object code form. The patent license granted | |
+ by a Contributor shall also apply to the combination of the | |
+ Contribution of that Contributor and the Program if, at the time the | |
+ Contribution is added by the Contributor, such addition of the | |
+ Contribution causes such combination to be covered by the Licensed | |
+ Patents. The patent license granted by a Contributor shall not apply | |
+ to (i) any other combinations which include the Contribution, nor to | |
+ (ii) Contributions of other Contributors. No hardware per se is | |
+ licensed hereunder. | |
+ | |
+ c. Recipient understands that although each Contributor grants the | |
+ licenses to its Contributions set forth herein, no assurances are | |
+ provided by any Contributor that the Program does not infringe the | |
+ patent or other intellectual property rights of any other entity. Each | |
+ Contributor disclaims any liability to Recipient for claims brought by | |
+ any other entity based on infringement of intellectual property rights | |
+ or otherwise. As a condition to exercising the rights and licenses | |
+ granted hereunder, each Recipient hereby assumes sole responsibility | |
+ to secure any other intellectual property rights needed, if any. For | |
+ example, if a third party patent license is required to allow | |
+ Recipient to distribute the Program, it is Recipient's responsibility | |
+ to acquire that license before distributing the Program. | |
+ | |
+ d. Each Contributor represents that to its knowledge it has sufficient | |
+ copyright rights in its Contribution, if any, to grant the copyright | |
+ license set forth in this Agreement. | |
+ | |
+3. REQUIREMENTS | |
+ | |
+A. Distributor may choose to distribute the Program in any form under | |
+this Agreement or under its own license agreement, provided that: | |
+ | |
+ a. it complies with the terms and conditions of this Agreement; | |
+ | |
+ b. if the Program is distributed in source code or other tangible | |
+ form, a copy of this Agreement or Distributor's own license agreement | |
+ is included with each copy of the Program; and | |
+ | |
+ c. if distributed under Distributor's own license agreement, such | |
+ license agreement: | |
+ | |
+ i. effectively disclaims on behalf of all Contributors all warranties | |
+ and conditions, express and implied, including warranties or | |
+ conditions of title and non-infringement, and implied warranties or | |
+ conditions of merchantability and fitness for a particular purpose; | |
+ ii. effectively excludes on behalf of all Contributors all liability | |
+ for damages, including direct, indirect, special, incidental and | |
+ consequential damages, such as lost profits; and | |
+ iii. states that any provisions which differ from this Agreement are | |
+ offered by that Contributor alone and not by any other party. | |
+ | |
+B. Each Distributor must include the following in a conspicuous | |
+ location in the Program: | |
+ | |
+ Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights | |
+ Reserved. | |
+ | |
+C. In addition, each Contributor must identify itself as the | |
+originator of its Contribution in a manner that reasonably allows | |
+subsequent Recipients to identify the originator of the Contribution. | |
+Also, each Contributor must agree that the additions and/or changes | |
+are intended to be a Contribution. Once a Contribution is contributed, | |
+it may not thereafter be revoked. | |
+ | |
+4. COMMERCIAL DISTRIBUTION | |
+ | |
+Commercial distributors of software may accept certain | |
+responsibilities with respect to end users, business partners and the | |
+like. While this license is intended to facilitate the commercial use | |
+of the Program, the Distributor who includes the Program in a | |
+commercial product offering should do so in a manner which does not | |
+create potential liability for Contributors. Therefore, if a | |
+Distributor includes the Program in a commercial product offering, | |
+such Distributor ("Commercial Distributor") hereby agrees to defend | |
+and indemnify every Contributor ("Indemnified Contributor") against | |
+any losses, damages and costs (collectively"Losses") arising from | |
+claims, lawsuits and other legal actions brought by a third party | |
+against the Indemnified Contributor to the extent caused by the acts | |
+or omissions of such Commercial Distributor in connection with its | |
+distribution of the Program in a commercial product offering. The | |
+obligations in this section do not apply to any claims or Losses | |
+relating to any actual or alleged intellectual property infringement. | |
+In order to qualify, an Indemnified Contributor must: a) promptly | |
+notify the Commercial Distributor in writing of such claim, and b) | |
+allow the Commercial Distributor to control, and cooperate with the | |
+Commercial Distributor in, the defense and any related settlement | |
+negotiations. The Indemnified Contributor may participate in any such | |
+claim at its own expense. | |
+ | |
+For example, a Distributor might include the Program in a commercial | |
+product offering, Product X. That Distributor is then a Commercial | |
+Distributor. If that Commercial Distributor then makes performance | |
+claims, or offers warranties related to Product X, those performance | |
+claims and warranties are such Commercial Distributor's responsibility | |
+alone. Under this section, the Commercial Distributor would have to | |
+defend claims against the Contributors related to those performance | |
+claims and warranties, and if a court requires any Contributor to pay | |
+any damages as a result, the Commercial Distributor must pay those | |
+damages. | |
+ | |
+5. NO WARRANTY | |
+ | |
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS | |
+PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
+KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY | |
+WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY | |
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely | |
+responsible for determining the appropriateness of using and | |
+distributing the Program and assumes all risks associated with its | |
+exercise of rights under this Agreement, including but not limited to | |
+the risks and costs of program errors, compliance with applicable | |
+laws, damage to or loss of data, programs or equipment, and | |
+unavailability or interruption of operations. | |
+ | |
+6. DISCLAIMER OF LIABILITY | |
+ | |
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR | |
+ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, | |
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING | |
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF | |
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR | |
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED | |
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
+ | |
+7. EXPORT CONTROL | |
+ | |
+Recipient agrees that Recipient alone is responsible for compliance | |
+with the United States export administration regulations (and the | |
+export control laws and regulation of any other countries). | |
+ | |
+8. GENERAL | |
+ | |
+If any provision of this Agreement is invalid or unenforceable under | |
+applicable law, it shall not affect the validity or enforceability of | |
+the remainder of the terms of this Agreement, and without further | |
+action by the parties hereto, such provision shall be reformed to the | |
+minimum extent necessary to make such provision valid and enforceable. | |
+ | |
+If Recipient institutes patent litigation against a Contributor with | |
+respect to a patent applicable to software (including a cross-claim or | |
+counterclaim in a lawsuit), then any patent licenses granted by that | |
+Contributor to such Recipient under this Agreement shall terminate as | |
+of the date such litigation is filed. In addition, if Recipient | |
+institutes patent litigation against any entity (including a | |
+cross-claim or counterclaim in a lawsuit) alleging that the Program | |
+itself (excluding combinations of the Program with other software or | |
+hardware) infringes such Recipient's patent(s), then such Recipient's | |
+rights granted under Section 2(b) shall terminate as of the date such | |
+litigation is filed. | |
+ | |
+All Recipient's rights under this Agreement shall terminate if it | |
+fails to comply with any of the material terms or conditions of this | |
+Agreement and does not cure such failure in a reasonable period of | |
+time after becoming aware of such noncompliance. If all Recipient's | |
+rights under this Agreement terminate, Recipient agrees to cease use | |
+and distribution of the Program as soon as reasonably practicable. | |
+However, Recipient's obligations under this Agreement and any licenses | |
+granted by Recipient relating to the Program shall continue and | |
+survive. | |
+ | |
+LUCENT may publish new versions (including revisions) of this | |
+Agreement from time to time. Each new version of the Agreement will be | |
+given a distinguishing version number. The Program (including | |
+Contributions) may always be distributed subject to the version of the | |
+Agreement under which it was received. In addition, after a new | |
+version of the Agreement is published, Contributor may elect to | |
+distribute the Program (including its Contributions) under the new | |
+version. No one other than LUCENT has the right to modify this | |
+Agreement. Except as expressly stated in Sections 2(a) and 2(b) above, | |
+Recipient receives no rights or licenses to the intellectual property | |
+of any Contributor under this Agreement, whether expressly, by | |
+implication, estoppel or otherwise. All rights in the Program not | |
+expressly granted under this Agreement are reserved. | |
+ | |
+This Agreement is governed by the laws of the State of New York and | |
+the intellectual property laws of the United States of America. No | |
+party to this Agreement will bring a legal action under this Agreement | |
+more than one year after the cause of action arose. Each party waives | |
+its rights to a jury trial in any resulting litigation. | |
+ | |
diff --git a/src/cmd/mk/Make.FreeBSD-386 b/src/cmd/mk/Make.FreeBSD-386 | |
t@@ -0,0 +1,7 @@ | |
+CC=gcc | |
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -… | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME) | |
+NAN=nan64.$O | |
diff --git a/src/cmd/mk/Make.HP-UX-9000 b/src/cmd/mk/Make.HP-UX-9000 | |
t@@ -0,0 +1,6 @@ | |
+CC=cc | |
+CFLAGS=-O -c -Ae -I. | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O | |
diff --git a/src/cmd/mk/Make.Linux-386 b/src/cmd/mk/Make.Linux-386 | |
t@@ -0,0 +1,7 @@ | |
+CC=gcc | |
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME) | |
+NAN=nan64.$O | |
diff --git a/src/cmd/mk/Make.OSF1-alpha b/src/cmd/mk/Make.OSF1-alpha | |
t@@ -0,0 +1,6 @@ | |
+CC=cc | |
+CFLAGS+=-g -c -I. | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O | |
diff --git a/src/cmd/mk/Make.SunOS-sun4u b/src/cmd/mk/Make.SunOS-sun4u | |
t@@ -0,0 +1,2 @@ | |
+include Make.SunOS-sun4u-$(CC) | |
+NAN=nan64.$O | |
diff --git a/src/cmd/mk/Make.SunOS-sun4u-cc b/src/cmd/mk/Make.SunOS-sun4u-cc | |
t@@ -0,0 +1,6 @@ | |
+CC=cc | |
+CFLAGS+=-g -c -I. -O | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O | |
diff --git a/src/cmd/mk/Make.SunOS-sun4u-gcc b/src/cmd/mk/Make.SunOS-sun4u-gcc | |
t@@ -0,0 +1,6 @@ | |
+CC=gcc | |
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O | |
diff --git a/src/cmd/mk/Makefile b/src/cmd/mk/Makefile | |
t@@ -0,0 +1,117 @@ | |
+ | |
+# this works in gnu make | |
+SYSNAME:=${shell uname} | |
+OBJTYPE:=${shell uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g'} | |
+ | |
+# this works in bsd make | |
+SYSNAME!=uname | |
+OBJTYPE!=uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g' | |
+ | |
+# the gnu rules will mess up bsd but not vice versa, | |
+# hence the gnu rules come first. | |
+ | |
+include Make.$(SYSNAME)-$(OBJTYPE) | |
+ | |
+PREFIX=/usr/local | |
+ | |
+NUKEFILES= | |
+ | |
+TGZFILES= | |
+ | |
+TARG=mk | |
+VERSION=2.0 | |
+PORTPLACE=devel/mk | |
+NAME=mk | |
+ | |
+OFILES=\ | |
+ arc.$O\ | |
+ archive.$O\ | |
+ bufblock.$O\ | |
+ env.$O\ | |
+ file.$O\ | |
+ graph.$O\ | |
+ job.$O\ | |
+ lex.$O\ | |
+ main.$O\ | |
+ match.$O\ | |
+ mk.$O\ | |
+ parse.$O\ | |
+ recipe.$O\ | |
+ rule.$O\ | |
+ run.$O\ | |
+ sh.$O\ | |
+ shprint.$O\ | |
+ symtab.$O\ | |
+ var.$O\ | |
+ varsub.$O\ | |
+ word.$O\ | |
+ unix.$O\ | |
+ | |
+HFILES=\ | |
+ mk.h\ | |
+ fns.h\ | |
+ | |
+all: $(TARG) | |
+ | |
+TGZFILES+=mk.pdf | |
+ | |
+install: $(LIB) | |
+ test -d $(PREFIX)/man/man1 || mkdir $(PREFIX)/man/man1 | |
+ test -d $(PREFIX)/doc || mkdir $(PREFIX)/doc | |
+ install -m 0755 mk $(PREFIX)/bin/mk | |
+ cat mk.1 | sed 's;DOCPREFIX;$(PREFIX);g' >mk.1a | |
+ install -m 0644 mk.1a $(PREFIX)/man/man1/mk.1 | |
+ install -m 0644 mk.pdf $(PREFIX)/doc/mk.pdf | |
+ | |
+ | |
+$(TARG): $(OFILES) | |
+ $(CC) -o $(TARG) $(OFILES) -L$(PREFIX)/lib -lregexp9 -lbio -lfmt -lutf | |
+ | |
+ | |
+.c.$O: | |
+ $(CC) $(CFLAGS) -I$(PREFIX)/include $*.c | |
+ | |
+%.$O: %.c | |
+ $(CC) $(CFLAGS) -I$(PREFIX)/include $*.c | |
+ | |
+ | |
+$(OFILES): $(HFILES) | |
+ | |
+tgz: | |
+ rm -rf $(NAME)-$(VERSION) | |
+ mkdir $(NAME)-$(VERSION) | |
+ cp Makefile Make.* README LICENSE NOTICE *.[ch137] rpm.spec bundle.por… | |
+ tar cf - $(NAME)-$(VERSION) | gzip >$(NAME)-$(VERSION).tgz | |
+ rm -rf $(NAME)-$(VERSION) | |
+ | |
+clean: | |
+ rm -f $(OFILES) $(LIB) | |
+ | |
+nuke: | |
+ rm -f $(OFILES) *.tgz *.rpm $(NUKEFILES) | |
+ | |
+rpm: | |
+ make tgz | |
+ cp $(NAME)-$(VERSION).tgz /usr/src/RPM/SOURCES | |
+ rpm -ba rpm.spec | |
+ cp /usr/src/RPM/SRPMS/$(NAME)-$(VERSION)-1.src.rpm . | |
+ cp /usr/src/RPM/RPMS/i586/$(NAME)-$(VERSION)-1.i586.rpm . | |
+ scp *.rpm [email protected]:public_html/software | |
+ | |
+PORTDIR=/usr/ports/$(PORTPLACE) | |
+ | |
+ports: | |
+ make tgz | |
+ rm -rf $(PORTDIR) | |
+ mkdir $(PORTDIR) | |
+ cp $(NAME)-$(VERSION).tgz /usr/ports/distfiles | |
+ cat bundle.ports | (cd $(PORTDIR) && awk '$$1=="---" && $$3=="---" { o… | |
+ (cd $(PORTDIR); make makesum) | |
+ (cd $(PORTDIR); make) | |
+ (cd $(PORTDIR); /usr/local/bin/portlint) | |
+ rm -rf $(PORTDIR)/work | |
+ shar `find $(PORTDIR)` > ports.shar | |
+ (cd $(PORTDIR); tar cf - *) | gzip >$(NAME)-$(VERSION)-ports.tgz | |
+ scp *.tgz [email protected]:public_html/software | |
+ | |
+.phony: all clean nuke install tgz rpm ports | |
diff --git a/src/cmd/mk/Makefile.MID b/src/cmd/mk/Makefile.MID | |
t@@ -0,0 +1,45 @@ | |
+TARG=mk | |
+VERSION=2.0 | |
+PORTPLACE=devel/mk | |
+NAME=mk | |
+ | |
+OFILES=\ | |
+ arc.$O\ | |
+ archive.$O\ | |
+ bufblock.$O\ | |
+ env.$O\ | |
+ file.$O\ | |
+ graph.$O\ | |
+ job.$O\ | |
+ lex.$O\ | |
+ main.$O\ | |
+ match.$O\ | |
+ mk.$O\ | |
+ parse.$O\ | |
+ recipe.$O\ | |
+ rule.$O\ | |
+ run.$O\ | |
+ sh.$O\ | |
+ shprint.$O\ | |
+ symtab.$O\ | |
+ var.$O\ | |
+ varsub.$O\ | |
+ word.$O\ | |
+ unix.$O\ | |
+ | |
+HFILES=\ | |
+ mk.h\ | |
+ fns.h\ | |
+ | |
+all: $(TARG) | |
+ | |
+TGZFILES+=mk.pdf | |
+ | |
+install: $(LIB) | |
+ test -d $(PREFIX)/man/man1 || mkdir $(PREFIX)/man/man1 | |
+ test -d $(PREFIX)/doc || mkdir $(PREFIX)/doc | |
+ install -m 0755 mk $(PREFIX)/bin/mk | |
+ cat mk.1 | sed 's;DOCPREFIX;$(PREFIX);g' >mk.1a | |
+ install -m 0644 mk.1a $(PREFIX)/man/man1/mk.1 | |
+ install -m 0644 mk.pdf $(PREFIX)/doc/mk.pdf | |
+ | |
diff --git a/src/cmd/mk/arc.c b/src/cmd/mk/arc.c | |
t@@ -0,0 +1,52 @@ | |
+#include "mk.h" | |
+ | |
+Arc * | |
+newarc(Node *n, Rule *r, char *stem, Resub *match) | |
+{ | |
+ Arc *a; | |
+ | |
+ a = (Arc *)Malloc(sizeof(Arc)); | |
+ a->n = n; | |
+ a->r = r; | |
+ a->stem = strdup(stem); | |
+ rcopy(a->match, match, NREGEXP); | |
+ a->next = 0; | |
+ a->flag = 0; | |
+ a->prog = r->prog; | |
+ return(a); | |
+} | |
+ | |
+void | |
+dumpa(char *s, Arc *a) | |
+{ | |
+ char buf[1024]; | |
+ | |
+ Bprint(&bout, "%sArc@%p: n=%p r=%p flag=0x%x stem='%s'", | |
+ s, a, a->n, a->r, a->flag, a->stem); | |
+ if(a->prog) | |
+ Bprint(&bout, " prog='%s'", a->prog); | |
+ Bprint(&bout, "\n"); | |
+ | |
+ if(a->n){ | |
+ snprint(buf, sizeof(buf), "%s ", (*s == ' ')? s:""); | |
+ dumpn(buf, a->n); | |
+ } | |
+} | |
+ | |
+void | |
+nrep(void) | |
+{ | |
+ Symtab *sym; | |
+ Word *w; | |
+ | |
+ sym = symlook("NREP", S_VAR, 0); | |
+ if(sym){ | |
+ w = (Word *) sym->value; | |
+ if (w && w->s && *w->s) | |
+ nreps = atoi(w->s); | |
+ } | |
+ if(nreps < 1) | |
+ nreps = 1; | |
+ if(DEBUG(D_GRAPH)) | |
+ Bprint(&bout, "nreps = %d\n", nreps); | |
+} | |
diff --git a/src/cmd/mk/archive.c b/src/cmd/mk/archive.c | |
t@@ -0,0 +1,180 @@ | |
+#include "mk.h" | |
+#define ARMAG "!<arch>\n" | |
+#define SARMAG 8 | |
+ | |
+#define ARFMAG "`\n" | |
+#define SARNAME 16 | |
+ | |
+struct ar_hdr | |
+{ | |
+ char name[SARNAME]; | |
+ char date[12]; | |
+ char uid[6]; | |
+ char gid[6]; | |
+ char mode[8]; | |
+ char size[10]; | |
+ char fmag[2]; | |
+}; | |
+#define SAR_HDR (SARNAME+44) | |
+ | |
+static int dolong; | |
+ | |
+static void atimes(char *); | |
+static char *split(char*, char**); | |
+ | |
+long | |
+atimeof(int force, char *name) | |
+{ | |
+ Symtab *sym; | |
+ long t; | |
+ char *archive, *member, buf[512]; | |
+ | |
+ archive = split(name, &member); | |
+ if(archive == 0) | |
+ Exit(); | |
+ | |
+ t = mtime(archive); | |
+ sym = symlook(archive, S_AGG, 0); | |
+ if(sym){ | |
+ if(force || (t > (long)sym->value)){ | |
+ atimes(archive); | |
+ sym->value = (void *)t; | |
+ } | |
+ } | |
+ else{ | |
+ atimes(archive); | |
+ /* mark the aggegate as having been done */ | |
+ symlook(strdup(archive), S_AGG, "")->value = (void *)t; | |
+ } | |
+ /* truncate long member name to sizeof of name field in archiv… | |
+ if(dolong) | |
+ snprint(buf, sizeof(buf), "%s(%s)", archive, member); | |
+ else | |
+ snprint(buf, sizeof(buf), "%s(%.*s)", archive, SARNAME, member… | |
+ sym = symlook(buf, S_TIME, 0); | |
+ if (sym) | |
+ return (long)sym->value; /* uggh */ | |
+ return 0; | |
+} | |
+ | |
+void | |
+atouch(char *name) | |
+{ | |
+ char *archive, *member; | |
+ int fd, i; | |
+ struct ar_hdr h; | |
+ long t; | |
+ | |
+ archive = split(name, &member); | |
+ if(archive == 0) | |
+ Exit(); | |
+ | |
+ fd = open(archive, ORDWR); | |
+ if(fd < 0){ | |
+ fd = create(archive, OWRITE, 0666); | |
+ if(fd < 0){ | |
+ fprint(2, "create %s: %r\n", archive); | |
+ Exit(); | |
+ } | |
+ write(fd, ARMAG, SARMAG); | |
+ } | |
+ if(symlook(name, S_TIME, 0)){ | |
+ /* hoon off and change it in situ */ | |
+ LSEEK(fd, SARMAG, 0); | |
+ while(read(fd, (char *)&h, sizeof(h)) == sizeof(h)){ | |
+ for(i = SARNAME-1; i > 0 && h.name[i] == ' '; i--) | |
+ ; | |
+ h.name[i+1]=0; | |
+ if(strcmp(member, h.name) == 0){ | |
+ t = SARNAME-sizeof(h); /* ughgghh */ | |
+ LSEEK(fd, t, 1); | |
+ fprint(fd, "%-12ld", time(0)); | |
+ break; | |
+ } | |
+ t = atol(h.size); | |
+ if(t&01) t++; | |
+ LSEEK(fd, t, 1); | |
+ } | |
+ } | |
+ close(fd); | |
+} | |
+ | |
+static void | |
+atimes(char *ar) | |
+{ | |
+ struct ar_hdr h; | |
+ long t; | |
+ int fd, i; | |
+ char buf[BIGBLOCK]; | |
+ char name[sizeof(h.name)+1]; | |
+ | |
+ fd = open(ar, OREAD); | |
+ if(fd < 0) | |
+ return; | |
+ | |
+ if(read(fd, buf, SARMAG) != SARMAG){ | |
+ close(fd); | |
+ return; | |
+ } | |
+ while(read(fd, (char *)&h, sizeof(h)) == sizeof(h)){ | |
+ t = atol(h.date); | |
+ if(t == 0) /* as it sometimes happens; thanks ken */ | |
+ t = 1; | |
+ strncpy(name, h.name, sizeof(h.name)); | |
+ for(i = sizeof(h.name)-1; i > 0 && name[i] == ' '; i--) | |
+ ; | |
+ if(name[i] == '/') /* system V bug */ | |
+ i--; | |
+ name[i+1]=0; | |
+ sprint(buf, "%s(%s)", ar, h.size); | |
+ symlook(strdup(buf), S_TIME, (void *)t)->value = (void *)t; | |
+ t = atol(h.size); | |
+ if(t&01) t++; | |
+ LSEEK(fd, t, 1); | |
+ } | |
+ close(fd); | |
+} | |
+ | |
+static int | |
+type(char *file) | |
+{ | |
+ int fd; | |
+ char buf[SARMAG]; | |
+ | |
+ fd = open(file, OREAD); | |
+ if(fd < 0){ | |
+ if(symlook(file, S_BITCH, 0) == 0){ | |
+ Bprint(&bout, "%s doesn't exist: assuming it will be a… | |
+ symlook(file, S_BITCH, (void *)file); | |
+ } | |
+ return 1; | |
+ } | |
+ if(read(fd, buf, SARMAG) != SARMAG){ | |
+ close(fd); | |
+ return 0; | |
+ } | |
+ close(fd); | |
+ return !strncmp(ARMAG, buf, SARMAG); | |
+} | |
+ | |
+static char* | |
+split(char *name, char **member) | |
+{ | |
+ char *p, *q; | |
+ | |
+ p = strdup(name); | |
+ q = utfrune(p, '('); | |
+ if(q){ | |
+ *q++ = 0; | |
+ if(member) | |
+ *member = q; | |
+ q = utfrune(q, ')'); | |
+ if (q) | |
+ *q = 0; | |
+ if(type(p)) | |
+ return p; | |
+ free(p); | |
+ fprint(2, "mk: '%s' is not an archive\n", name); | |
+ } | |
+ return 0; | |
+} | |
diff --git a/src/cmd/mk/bundle.ports b/src/cmd/mk/bundle.ports | |
t@@ -0,0 +1,46 @@ | |
+--- Makefile --- | |
+# New ports collection makefile for: mk | |
+# Date Created: 11 Feb 2003 | |
+# Whom: rsc | |
+# | |
+# THIS LINE NEEDS REPLACING. IT'S HERE TO GET BY PORTLINT | |
+# $FreeBSD: ports/devel/mk/Makefile,v 1.1 2003/02/12 00:51:22 rsc Exp $ | |
+ | |
+PORTNAME= mk | |
+PORTVERSION= 2.0 | |
+CATEGORIES= devel | |
+MASTER_SITES= http://pdos.lcs.mit.edu/~rsc/software/ | |
+EXTRACT_SUFX= .tgz | |
+ | |
+MAINTAINER= [email protected] | |
+ | |
+DEPENDS= ${PORTSDIR}/devel/libutf \ | |
+ ${PORTSDIR}/devel/libfmt \ | |
+ ${PORTSDIR}/devel/libbio \ | |
+ ${PORTSDIR}/devel/libregexp9 | |
+ | |
+MAN1= mk.1 | |
+USE_REINPLACE= yes | |
+ | |
+.include <bsd.port.pre.mk> | |
+ | |
+post-patch: | |
+ ${REINPLACE_CMD} -e 's,$$(PREFIX),${PREFIX},g' ${WRKSRC}/Makefile | |
+ | |
+.include <bsd.port.post.mk> | |
+--- pkg-comment --- | |
+Streamlined replacement for make | |
+--- pkg-descr --- | |
+Mk is a streamlined replacement for make, written for | |
+Tenth Edition Research Unix by Andrew Hume. | |
+ | |
+WWW: http://pdos.lcs.mit.edu/~rsc/software/#mk | |
+ | |
+Russ Cox | |
[email protected] | |
+--- pkg-plist --- | |
+bin/mk | |
+doc/mk.pdf | |
+--- /dev/null --- | |
+This is just a way to make sure blank lines don't | |
+creep into pkg-plist. | |
diff --git a/src/cmd/mk/env.c b/src/cmd/mk/env.c | |
t@@ -0,0 +1,149 @@ | |
+#include "mk.h" | |
+ | |
+enum { | |
+ ENVQUANTA=10 | |
+}; | |
+ | |
+Envy *envy; | |
+static int nextv; | |
+ | |
+static char *myenv[] = | |
+{ | |
+ "target", | |
+ "stem", | |
+ "prereq", | |
+ "pid", | |
+ "nproc", | |
+ "newprereq", | |
+ "alltarget", | |
+ "newmember", | |
+ "stem0", /* must be in order from here */ | |
+ "stem1", | |
+ "stem2", | |
+ "stem3", | |
+ "stem4", | |
+ "stem5", | |
+ "stem6", | |
+ "stem7", | |
+ "stem8", | |
+ "stem9", | |
+ 0, | |
+}; | |
+ | |
+void | |
+initenv(void) | |
+{ | |
+ char **p; | |
+ | |
+ for(p = myenv; *p; p++) | |
+ symlook(*p, S_INTERNAL, (void *)""); | |
+ readenv(); /* o.s. dependent */ | |
+} | |
+ | |
+static void | |
+envinsert(char *name, Word *value) | |
+{ | |
+ static int envsize; | |
+ | |
+ if (nextv >= envsize) { | |
+ envsize += ENVQUANTA; | |
+ envy = (Envy *) Realloc((char *) envy, envsize*sizeof(Envy)); | |
+ } | |
+ envy[nextv].name = name; | |
+ envy[nextv++].values = value; | |
+} | |
+ | |
+static void | |
+envupd(char *name, Word *value) | |
+{ | |
+ Envy *e; | |
+ | |
+ for(e = envy; e->name; e++) | |
+ if(strcmp(name, e->name) == 0){ | |
+ delword(e->values); | |
+ e->values = value; | |
+ return; | |
+ } | |
+ e->name = name; | |
+ e->values = value; | |
+ envinsert(0,0); | |
+} | |
+ | |
+static void | |
+ecopy(Symtab *s) | |
+{ | |
+ char **p; | |
+ | |
+ if(symlook(s->name, S_NOEXPORT, 0)) | |
+ return; | |
+ for(p = myenv; *p; p++) | |
+ if(strcmp(*p, s->name) == 0) | |
+ return; | |
+ envinsert(s->name, (Word *) s->value); | |
+} | |
+ | |
+void | |
+execinit(void) | |
+{ | |
+ char **p; | |
+ | |
+ nextv = 0; | |
+ for(p = myenv; *p; p++) | |
+ envinsert(*p, stow("")); | |
+ | |
+ symtraverse(S_VAR, ecopy); | |
+ envinsert(0, 0); | |
+} | |
+ | |
+Envy* | |
+buildenv(Job *j, int slot) | |
+{ | |
+ char **p, *cp, *qp; | |
+ Word *w, *v, **l; | |
+ int i; | |
+ char buf[256]; | |
+ | |
+ envupd("target", wdup(j->t)); | |
+ if(j->r->attr®EXP) | |
+ envupd("stem",newword("")); | |
+ else | |
+ envupd("stem", newword(j->stem)); | |
+ envupd("prereq", wdup(j->p)); | |
+ sprint(buf, "%d", getpid()); | |
+ envupd("pid", newword(buf)); | |
+ sprint(buf, "%d", slot); | |
+ envupd("nproc", newword(buf)); | |
+ envupd("newprereq", wdup(j->np)); | |
+ envupd("alltarget", wdup(j->at)); | |
+ l = &v; | |
+ v = w = wdup(j->np); | |
+ while(w){ | |
+ cp = strchr(w->s, '('); | |
+ if(cp){ | |
+ qp = strchr(cp+1, ')'); | |
+ if(qp){ | |
+ *qp = 0; | |
+ strcpy(w->s, cp+1); | |
+ l = &w->next; | |
+ w = w->next; | |
+ continue; | |
+ } | |
+ } | |
+ *l = w->next; | |
+ free(w->s); | |
+ free(w); | |
+ w = *l; | |
+ } | |
+ envupd("newmember", v); | |
+ /* update stem0 -> stem9 */ | |
+ for(p = myenv; *p; p++) | |
+ if(strcmp(*p, "stem0") == 0) | |
+ break; | |
+ for(i = 0; *p; i++, p++){ | |
+ if((j->r->attr®EXP) && j->match[i]) | |
+ envupd(*p, newword(j->match[i])); | |
+ else | |
+ envupd(*p, newword("")); | |
+ } | |
+ return envy; | |
+} | |
diff --git a/src/cmd/mk/file.c b/src/cmd/mk/file.c | |
t@@ -0,0 +1,90 @@ | |
+#include "mk.h" | |
+ | |
+/* table-driven version in bootes dump of 12/31/96 */ | |
+ | |
+long | |
+mtime(char *name) | |
+{ | |
+ return mkmtime(name); | |
+} | |
+ | |
+long | |
+timeof(char *name, int force) | |
+{ | |
+ Symtab *sym; | |
+ long t; | |
+ | |
+ if(utfrune(name, '(')) | |
+ return atimeof(force, name); /* archive */ | |
+ | |
+ if(force) | |
+ return mtime(name); | |
+ | |
+ | |
+ sym = symlook(name, S_TIME, 0); | |
+ if (sym) | |
+ return (long) sym->value; /* uggh */ | |
+ | |
+ t = mtime(name); | |
+ if(t == 0) | |
+ return 0; | |
+ | |
+ symlook(name, S_TIME, (void*)t); /* install time in cac… | |
+ return t; | |
+} | |
+ | |
+void | |
+touch(char *name) | |
+{ | |
+ Bprint(&bout, "touch(%s)\n", name); | |
+ if(nflag) | |
+ return; | |
+ | |
+ if(utfrune(name, '(')) | |
+ atouch(name); /* archive */ | |
+ else if(chgtime(name) < 0) { | |
+ fprint(2, "%s: %r\n", name); | |
+ Exit(); | |
+ } | |
+} | |
+ | |
+void | |
+delete(char *name) | |
+{ | |
+ if(utfrune(name, '(') == 0) { /* file */ | |
+ if(remove(name) < 0) | |
+ fprint(2, "remove %s: %r\n", name); | |
+ } else | |
+ fprint(2, "hoon off; mk can'tdelete archive members\n"); | |
+} | |
+ | |
+void | |
+timeinit(char *s) | |
+{ | |
+ long t; | |
+ char *cp; | |
+ Rune r; | |
+ int c, n; | |
+ | |
+ t = time(0); | |
+ while (*s) { | |
+ cp = s; | |
+ do{ | |
+ n = chartorune(&r, s); | |
+ if (r == ' ' || r == ',' || r == '\n') | |
+ break; | |
+ s += n; | |
+ } while(*s); | |
+ c = *s; | |
+ *s = 0; | |
+ symlook(strdup(cp), S_TIME, (void *)t)->value = (void *)t; | |
+ if (c) | |
+ *s++ = c; | |
+ while(*s){ | |
+ n = chartorune(&r, s); | |
+ if(r != ' ' && r != ',' && r != '\n') | |
+ break; | |
+ s += n; | |
+ } | |
+ } | |
+} | |
diff --git a/src/cmd/mk/fns.h b/src/cmd/mk/fns.h | |
t@@ -0,0 +1,84 @@ | |
+void addrule(char*, Word*, char*, Word*, int, int, char*); | |
+void addrules(Word*, Word*, char*, int, int, char*); | |
+void addw(Word*, char*); | |
+void assert(char*, int); | |
+int assline(Biobuf *, Bufblock *); | |
+long atimeof(int,char*); | |
+void atouch(char*); | |
+void bufcpy(Bufblock *, char *, int); | |
+Envy *buildenv(Job*, int); | |
+void catchnotes(void); | |
+char *charin(char *, char *); | |
+int chgtime(char*); | |
+void clrmade(Node*); | |
+char *copyq(char*, Rune, Bufblock*); | |
+void delete(char*); | |
+void delword(Word*); | |
+int dorecipe(Node*); | |
+void dumpa(char*, Arc*); | |
+void dumpj(char*, Job*, int); | |
+void dumpn(char*, Node*); | |
+void dumpr(char*, Rule*); | |
+void dumpv(char*); | |
+void dumpw(char*, Word*); | |
+int escapetoken(Biobuf*, Bufblock*, int, int); | |
+void execinit(void); | |
+int execsh(char*, char*, Bufblock*, Envy*); | |
+void Exit(void); | |
+char *expandquote(char*, Rune, Bufblock*); | |
+void expunge(int, char*); | |
+void freebuf(Bufblock*); | |
+void front(char*); | |
+Node *graph(char*); | |
+void growbuf(Bufblock *); | |
+void initenv(void); | |
+void insert(Bufblock *, int); | |
+void ipop(void); | |
+void ipush(void); | |
+void killchildren(char*); | |
+void *Malloc(int); | |
+char *maketmp(int*); | |
+int match(char*, char*, char*); | |
+char *membername(char*, int, char*); | |
+void mk(char*); | |
+ulong mkmtime(char*); | |
+long mtime(char*); | |
+Arc *newarc(Node*, Rule*, char*, Resub*); | |
+Bufblock *newbuf(void); | |
+Job *newjob(Rule*, Node*, char*, char**, Word*, Word*, Word*, Word*); | |
+Word *newword(char*); | |
+int nextrune(Biobuf*, int); | |
+int nextslot(void); | |
+void nproc(void); | |
+void nrep(void); | |
+int outofdate(Node*, Arc*, int); | |
+void parse(char*, int, int); | |
+int pipecmd(char*, Envy*, int*); | |
+void prusage(void); | |
+void rcopy(char**, Resub*, int); | |
+void readenv(void); | |
+void *Realloc(void*, int); | |
+void rinsert(Bufblock *, Rune); | |
+char *rulecnt(void); | |
+void run(Job*); | |
+void setvar(char*, void*); | |
+char *shname(char*); | |
+void shprint(char*, Envy*, Bufblock*); | |
+Word *stow(char*); | |
+void subst(char*, char*, char*); | |
+void symdel(char*, int); | |
+void syminit(void); | |
+Symtab *symlook(char*, int, void*); | |
+void symstat(void); | |
+void symtraverse(int, void(*)(Symtab*)); | |
+void timeinit(char*); | |
+long timeof(char*, int); | |
+void touch(char*); | |
+void update(int, Node*); | |
+void usage(void); | |
+Word *varsub(char**); | |
+int waitfor(char*); | |
+int waitup(int, int*); | |
+Word *wdup(Word*); | |
+int work(Node*, Node*, Arc*); | |
+char *wtos(Word*, int); | |
diff --git a/src/cmd/mk/graph.c b/src/cmd/mk/graph.c | |
t@@ -0,0 +1,279 @@ | |
+#include "mk.h" | |
+ | |
+static Node *applyrules(char *, char *); | |
+static void togo(Node *); | |
+static int vacuous(Node *); | |
+static Node *newnode(char *); | |
+static void trace(char *, Arc *); | |
+static void cyclechk(Node *); | |
+static void ambiguous(Node *); | |
+static void attribute(Node *); | |
+ | |
+Node * | |
+graph(char *target) | |
+{ | |
+ Node *node; | |
+ char *cnt; | |
+ | |
+ cnt = rulecnt(); | |
+ node = applyrules(target, cnt); | |
+ free(cnt); | |
+ cyclechk(node); | |
+ node->flags |= PROBABLE; /* make sure it doesn't get deleted */ | |
+ vacuous(node); | |
+ ambiguous(node); | |
+ attribute(node); | |
+ return(node); | |
+} | |
+ | |
+static Node * | |
+applyrules(char *target, char *cnt) | |
+{ | |
+ Symtab *sym; | |
+ Node *node; | |
+ Rule *r; | |
+ Arc head, *a = &head; | |
+ Word *w; | |
+ char stem[NAMEBLOCK], buf[NAMEBLOCK]; | |
+ Resub rmatch[NREGEXP]; | |
+ | |
+/* print("applyrules(%lux='%s')\n", target, target);*//**/ | |
+ sym = symlook(target, S_NODE, 0); | |
+ if(sym) | |
+ return (Node *)(sym->value); | |
+ target = strdup(target); | |
+ node = newnode(target); | |
+ head.n = 0; | |
+ head.next = 0; | |
+ sym = symlook(target, S_TARGET, 0); | |
+ memset((char*)rmatch, 0, sizeof(rmatch)); | |
+ for(r = sym? (Rule *)(sym->value):0; r; r = r->chain){ | |
+ if(r->attr&META) continue; | |
+ if(strcmp(target, r->target)) continue; | |
+ if((!r->recipe || !*r->recipe) && (!r->tail || !r->tail->s || … | |
+ if(cnt[r->rule] >= nreps) continue; | |
+ cnt[r->rule]++; | |
+ node->flags |= PROBABLE; | |
+ | |
+/* if(r->attr&VIR) | |
+ * node->flags |= VIRTUAL; | |
+ * if(r->attr&NOREC) | |
+ * node->flags |= NORECIPE; | |
+ * if(r->attr&DEL) | |
+ * node->flags |= DELETE; | |
+ */ | |
+ if(!r->tail || !r->tail->s || !*r->tail->s) { | |
+ a->next = newarc((Node *)0, r, "", rmatch); | |
+ a = a->next; | |
+ } else | |
+ for(w = r->tail; w; w = w->next){ | |
+ a->next = newarc(applyrules(w->s, cnt), r, "",… | |
+ a = a->next; | |
+ } | |
+ cnt[r->rule]--; | |
+ head.n = node; | |
+ } | |
+ for(r = metarules; r; r = r->next){ | |
+ if((!r->recipe || !*r->recipe) && (!r->tail || !r->tail->s || … | |
+ if ((r->attr&NOVIRT) && a != &head && (a->r->attr&VIR)) | |
+ continue; | |
+ if(r->attr®EXP){ | |
+ stem[0] = 0; | |
+ patrule = r; | |
+ memset((char*)rmatch, 0, sizeof(rmatch)); | |
+ if(regexec(r->pat, node->name, rmatch, NREGEXP) == 0) | |
+ continue; | |
+ } else { | |
+ if(!match(node->name, r->target, stem)) continue; | |
+ } | |
+ if(cnt[r->rule] >= nreps) continue; | |
+ cnt[r->rule]++; | |
+ | |
+/* if(r->attr&VIR) | |
+ * node->flags |= VIRTUAL; | |
+ * if(r->attr&NOREC) | |
+ * node->flags |= NORECIPE; | |
+ * if(r->attr&DEL) | |
+ * node->flags |= DELETE; | |
+ */ | |
+ | |
+ if(!r->tail || !r->tail->s || !*r->tail->s) { | |
+ a->next = newarc((Node *)0, r, stem, rmatch); | |
+ a = a->next; | |
+ } else | |
+ for(w = r->tail; w; w = w->next){ | |
+ if(r->attr®EXP) | |
+ regsub(w->s, buf, sizeof buf, rmatch, … | |
+ else | |
+ subst(stem, w->s, buf); | |
+ a->next = newarc(applyrules(buf, cnt), r, stem… | |
+ a = a->next; | |
+ } | |
+ cnt[r->rule]--; | |
+ } | |
+ a->next = node->prereqs; | |
+ node->prereqs = head.next; | |
+ return(node); | |
+} | |
+ | |
+static void | |
+togo(Node *node) | |
+{ | |
+ Arc *la, *a; | |
+ | |
+ /* delete them now */ | |
+ la = 0; | |
+ for(a = node->prereqs; a; la = a, a = a->next) | |
+ if(a->flag&TOGO){ | |
+ if(a == node->prereqs) | |
+ node->prereqs = a->next; | |
+ else | |
+ la->next = a->next, a = la; | |
+ } | |
+} | |
+ | |
+static int | |
+vacuous(Node *node) | |
+{ | |
+ Arc *la, *a; | |
+ int vac = !(node->flags&PROBABLE); | |
+ | |
+ if(node->flags&READY) | |
+ return(node->flags&VACUOUS); | |
+ node->flags |= READY; | |
+ for(a = node->prereqs; a; a = a->next) | |
+ if(a->n && vacuous(a->n) && (a->r->attr&META)) | |
+ a->flag |= TOGO; | |
+ else | |
+ vac = 0; | |
+ /* if a rule generated arcs that DON'T go; no others from that rule go… | |
+ for(a = node->prereqs; a; a = a->next) | |
+ if((a->flag&TOGO) == 0) | |
+ for(la = node->prereqs; la; la = la->next) | |
+ if((la->flag&TOGO) && (la->r == a->r)){ | |
+ la->flag &= ~TOGO; | |
+ } | |
+ togo(node); | |
+ if(vac) | |
+ node->flags |= VACUOUS; | |
+ return(vac); | |
+} | |
+ | |
+static Node * | |
+newnode(char *name) | |
+{ | |
+ register Node *node; | |
+ | |
+ node = (Node *)Malloc(sizeof(Node)); | |
+ symlook(name, S_NODE, (void *)node); | |
+ node->name = name; | |
+ node->time = timeof(name, 0); | |
+ node->prereqs = 0; | |
+ node->flags = node->time? PROBABLE : 0; | |
+ node->next = 0; | |
+ return(node); | |
+} | |
+ | |
+void | |
+dumpn(char *s, Node *n) | |
+{ | |
+ char buf[1024]; | |
+ Arc *a; | |
+ | |
+ sprint(buf, "%s ", (*s == ' ')? s:""); | |
+ Bprint(&bout, "%s%s@%ld: time=%ld flags=0x%x next=%ld\n", | |
+ s, n->name, n, n->time, n->flags, n->next); | |
+ for(a = n->prereqs; a; a = a->next) | |
+ dumpa(buf, a); | |
+} | |
+ | |
+static void | |
+trace(char *s, Arc *a) | |
+{ | |
+ fprint(2, "\t%s", s); | |
+ while(a){ | |
+ fprint(2, " <-(%s:%d)- %s", a->r->file, a->r->line, | |
+ a->n? a->n->name:""); | |
+ if(a->n){ | |
+ for(a = a->n->prereqs; a; a = a->next) | |
+ if(*a->r->recipe) break; | |
+ } else | |
+ a = 0; | |
+ } | |
+ fprint(2, "\n"); | |
+} | |
+ | |
+static void | |
+cyclechk(Node *n) | |
+{ | |
+ Arc *a; | |
+ | |
+ if((n->flags&CYCLE) && n->prereqs){ | |
+ fprint(2, "mk: cycle in graph detected at target %s\n", n->nam… | |
+ Exit(); | |
+ } | |
+ n->flags |= CYCLE; | |
+ for(a = n->prereqs; a; a = a->next) | |
+ if(a->n) | |
+ cyclechk(a->n); | |
+ n->flags &= ~CYCLE; | |
+} | |
+ | |
+static void | |
+ambiguous(Node *n) | |
+{ | |
+ Arc *a; | |
+ Rule *r = 0; | |
+ Arc *la; | |
+ int bad = 0; | |
+ | |
+ la = 0; | |
+ for(a = n->prereqs; a; a = a->next){ | |
+ if(a->n) | |
+ ambiguous(a->n); | |
+ if(*a->r->recipe == 0) continue; | |
+ if(r == 0) | |
+ r = a->r, la = a; | |
+ else{ | |
+ if(r->recipe != a->r->recipe){ | |
+ if((r->attr&META) && !(a->r->attr&META)){ | |
+ la->flag |= TOGO; | |
+ r = a->r, la = a; | |
+ } else if(!(r->attr&META) && (a->r->attr&META)… | |
+ a->flag |= TOGO; | |
+ continue; | |
+ } | |
+ } | |
+ if(r->recipe != a->r->recipe){ | |
+ if(bad == 0){ | |
+ fprint(2, "mk: ambiguous recipes for %… | |
+ bad = 1; | |
+ trace(n->name, la); | |
+ } | |
+ trace(n->name, a); | |
+ } | |
+ } | |
+ } | |
+ if(bad) | |
+ Exit(); | |
+ togo(n); | |
+} | |
+ | |
+static void | |
+attribute(Node *n) | |
+{ | |
+ register Arc *a; | |
+ | |
+ for(a = n->prereqs; a; a = a->next){ | |
+ if(a->r->attr&VIR) | |
+ n->flags |= VIRTUAL; | |
+ if(a->r->attr&NOREC) | |
+ n->flags |= NORECIPE; | |
+ if(a->r->attr&DEL) | |
+ n->flags |= DELETE; | |
+ if(a->n) | |
+ attribute(a->n); | |
+ } | |
+ if(n->flags&VIRTUAL) | |
+ n->time = 0; | |
+} | |
diff --git a/src/cmd/mk/lex.c b/src/cmd/mk/lex.c | |
t@@ -0,0 +1,147 @@ | |
+#include "mk.h" | |
+ | |
+static int bquote(Biobuf*, Bufblock*); | |
+ | |
+/* | |
+ * Assemble a line skipping blank lines, comments, and eliding | |
+ * escaped newlines | |
+ */ | |
+int | |
+assline(Biobuf *bp, Bufblock *buf) | |
+{ | |
+ int c; | |
+ int lastc; | |
+ | |
+ buf->current=buf->start; | |
+ while ((c = nextrune(bp, 1)) >= 0){ | |
+ switch(c) | |
+ { | |
+ case '\r': /* consumes CRs for Win95 */ | |
+ continue; | |
+ case '\n': | |
+ if (buf->current != buf->start) { | |
+ insert(buf, 0); | |
+ return 1; | |
+ } | |
+ break; /* skip empty lines */ | |
+ case '\\': | |
+ case '\'': | |
+ case '"': | |
+ rinsert(buf, c); | |
+ if (escapetoken(bp, buf, 1, c) == 0) | |
+ Exit(); | |
+ break; | |
+ case '`': | |
+ if (bquote(bp, buf) == 0) | |
+ Exit(); | |
+ break; | |
+ case '#': | |
+ lastc = '#'; | |
+ while ((c = Bgetc(bp)) != '\n') { | |
+ if (c < 0) | |
+ goto eof; | |
+ if(c != '\r') | |
+ lastc = c; | |
+ } | |
+ mkinline++; | |
+ if (lastc == '\\') | |
+ break; /* propagate escaped new… | |
+ if (buf->current != buf->start) { | |
+ insert(buf, 0); | |
+ return 1; | |
+ } | |
+ break; | |
+ default: | |
+ rinsert(buf, c); | |
+ break; | |
+ } | |
+ } | |
+eof: | |
+ insert(buf, 0); | |
+ return *buf->start != 0; | |
+} | |
+ | |
+/* | |
+ * assemble a back-quoted shell command into a buffer | |
+ */ | |
+static int | |
+bquote(Biobuf *bp, Bufblock *buf) | |
+{ | |
+ int c, line, term; | |
+ int start; | |
+ | |
+ line = mkinline; | |
+ while((c = Bgetrune(bp)) == ' ' || c == '\t') | |
+ ; | |
+ if(c == '{'){ | |
+ term = '}'; /* rc style */ | |
+ while((c = Bgetrune(bp)) == ' ' || c == '\t') | |
+ ; | |
+ } else | |
+ term = '`'; /* sh style */ | |
+ | |
+ start = buf->current-buf->start; | |
+ for(;c > 0; c = nextrune(bp, 0)){ | |
+ if(c == term){ | |
+ insert(buf, '\n'); | |
+ insert(buf,0); | |
+ buf->current = buf->start+start; | |
+ execinit(); | |
+ execsh(0, buf->current, buf, envy); | |
+ return 1; | |
+ } | |
+ if(c == '\n') | |
+ break; | |
+ if(c == '\'' || c == '"' || c == '\\'){ | |
+ insert(buf, c); | |
+ if(!escapetoken(bp, buf, 1, c)) | |
+ return 0; | |
+ continue; | |
+ } | |
+ rinsert(buf, c); | |
+ } | |
+ SYNERR(line); | |
+ fprint(2, "missing closing %c after `\n", term); | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * get next character stripping escaped newlines | |
+ * the flag specifies whether escaped newlines are to be elided or | |
+ * replaced with a blank. | |
+ */ | |
+int | |
+nextrune(Biobuf *bp, int elide) | |
+{ | |
+ int c, c2; | |
+ static int savec; | |
+ | |
+ if(savec){ | |
+ c = savec; | |
+ savec = 0; | |
+ return c; | |
+ } | |
+ | |
+ for (;;) { | |
+ c = Bgetrune(bp); | |
+ if (c == '\\') { | |
+ c2 = Bgetrune(bp); | |
+ if(c2 == '\r'){ | |
+ savec = c2; | |
+ c2 = Bgetrune(bp); | |
+ } | |
+ if (c2 == '\n') { | |
+ savec = 0; | |
+ mkinline++; | |
+ if (elide) | |
+ continue; | |
+ return ' '; | |
+ } | |
+ Bungetrune(bp); | |
+ } | |
+ if (c == '\n') | |
+ mkinline++; | |
+ return c; | |
+ } | |
+ return 0; | |
+} | |
diff --git a/src/cmd/mk/main.c b/src/cmd/mk/main.c | |
t@@ -0,0 +1,285 @@ | |
+#include "mk.h" | |
+ | |
+#define MKFILE "mkfile" | |
+ | |
+int debug; | |
+Rule *rules, *metarules; | |
+int nflag = 0; | |
+int tflag = 0; | |
+int iflag = 0; | |
+int kflag = 0; | |
+int aflag = 0; | |
+int uflag = 0; | |
+char *explain = 0; | |
+Word *target1; | |
+int nreps = 1; | |
+Job *jobs; | |
+Biobuf bout; | |
+Rule *patrule; | |
+void badusage(void); | |
+#ifdef PROF | |
+short buf[10000]; | |
+#endif | |
+ | |
+int | |
+main(int argc, char **argv) | |
+{ | |
+ Word *w; | |
+ char *s, *temp; | |
+ char *files[256], **f = files, **ff; | |
+ int sflag = 0; | |
+ int i; | |
+ int tfd = -1; | |
+ Biobuf tb; | |
+ Bufblock *buf; | |
+ Bufblock *whatif; | |
+ | |
+ /* | |
+ * start with a copy of the current environment variables | |
+ * instead of sharing them | |
+ */ | |
+ | |
+ Binit(&bout, 1, OWRITE); | |
+ buf = newbuf(); | |
+ whatif = 0; | |
+ USED(argc); | |
+ for(argv++; *argv && (**argv == '-'); argv++) | |
+ { | |
+ bufcpy(buf, argv[0], strlen(argv[0])); | |
+ insert(buf, ' '); | |
+ switch(argv[0][1]) | |
+ { | |
+ case 'a': | |
+ aflag = 1; | |
+ break; | |
+ case 'd': | |
+ if(*(s = &argv[0][2])) | |
+ while(*s) switch(*s++) | |
+ { | |
+ case 'p': debug |= D_PARSE; break; | |
+ case 'g': debug |= D_GRAPH; break; | |
+ case 'e': debug |= D_EXEC; break; | |
+ } | |
+ else | |
+ debug = 0xFFFF; | |
+ break; | |
+ case 'e': | |
+ explain = &argv[0][2]; | |
+ break; | |
+ case 'f': | |
+ if(*++argv == 0) | |
+ badusage(); | |
+ *f++ = *argv; | |
+ bufcpy(buf, argv[0], strlen(argv[0])); | |
+ insert(buf, ' '); | |
+ break; | |
+ case 'i': | |
+ iflag = 1; | |
+ break; | |
+ case 'k': | |
+ kflag = 1; | |
+ break; | |
+ case 'n': | |
+ nflag = 1; | |
+ break; | |
+ case 's': | |
+ sflag = 1; | |
+ break; | |
+ case 't': | |
+ tflag = 1; | |
+ break; | |
+ case 'u': | |
+ uflag = 1; | |
+ break; | |
+ case 'w': | |
+ if(whatif == 0) | |
+ whatif = newbuf(); | |
+ else | |
+ insert(whatif, ' '); | |
+ if(argv[0][2]) | |
+ bufcpy(whatif, &argv[0][2], strlen(&argv[0][2]… | |
+ else { | |
+ if(*++argv == 0) | |
+ badusage(); | |
+ bufcpy(whatif, &argv[0][0], strlen(&argv[0][0]… | |
+ } | |
+ break; | |
+ default: | |
+ badusage(); | |
+ } | |
+ } | |
+#ifdef PROF | |
+ { | |
+ extern etext(); | |
+ monitor(main, etext, buf, sizeof buf, 300); | |
+ } | |
+#endif | |
+ | |
+ if(aflag) | |
+ iflag = 1; | |
+ usage(); | |
+ syminit(); | |
+ initenv(); | |
+ usage(); | |
+ | |
+ /* | |
+ assignment args become null strings | |
+ */ | |
+ temp = 0; | |
+ for(i = 0; argv[i]; i++) if(utfrune(argv[i], '=')){ | |
+ bufcpy(buf, argv[i], strlen(argv[i])); | |
+ insert(buf, ' '); | |
+ if(tfd < 0){ | |
+ temp = maketmp(&tfd); | |
+ if(temp == 0) { | |
+ fprint(2, "temp file: %r\n"); | |
+ Exit(); | |
+ } | |
+ Binit(&tb, tfd, OWRITE); | |
+ } | |
+ Bprint(&tb, "%s\n", argv[i]); | |
+ *argv[i] = 0; | |
+ } | |
+ if(tfd >= 0){ | |
+ Bflush(&tb); | |
+ LSEEK(tfd, 0L, 0); | |
+ parse("command line args", tfd, 1); | |
+ remove(temp); | |
+ } | |
+ | |
+ if (buf->current != buf->start) { | |
+ buf->current--; | |
+ insert(buf, 0); | |
+ } | |
+ symlook("MKFLAGS", S_VAR, (void *) stow(buf->start)); | |
+ buf->current = buf->start; | |
+ for(i = 0; argv[i]; i++){ | |
+ if(*argv[i] == 0) continue; | |
+ if(i) | |
+ insert(buf, ' '); | |
+ bufcpy(buf, argv[i], strlen(argv[i])); | |
+ } | |
+ insert(buf, 0); | |
+ symlook("MKARGS", S_VAR, (void *) stow(buf->start)); | |
+ freebuf(buf); | |
+ | |
+ if(f == files){ | |
+ if(access(MKFILE, 4) == 0) | |
+ parse(MKFILE, open(MKFILE, 0), 0); | |
+ } else | |
+ for(ff = files; ff < f; ff++) | |
+ parse(*ff, open(*ff, 0), 0); | |
+ if(DEBUG(D_PARSE)){ | |
+ dumpw("default targets", target1); | |
+ dumpr("rules", rules); | |
+ dumpr("metarules", metarules); | |
+ dumpv("variables"); | |
+ } | |
+ if(whatif){ | |
+ insert(whatif, 0); | |
+ timeinit(whatif->start); | |
+ freebuf(whatif); | |
+ } | |
+ execinit(); | |
+ /* skip assignment args */ | |
+ while(*argv && (**argv == 0)) | |
+ argv++; | |
+ | |
+ catchnotes(); | |
+ if(*argv == 0){ | |
+ if(target1) | |
+ for(w = target1; w; w = w->next) | |
+ mk(w->s); | |
+ else { | |
+ fprint(2, "mk: nothing to mk\n"); | |
+ Exit(); | |
+ } | |
+ } else { | |
+ if(sflag){ | |
+ for(; *argv; argv++) | |
+ if(**argv) | |
+ mk(*argv); | |
+ } else { | |
+ Word *head, *tail, *t; | |
+ | |
+ /* fake a new rule with all the args as prereqs */ | |
+ tail = 0; | |
+ t = 0; | |
+ for(; *argv; argv++) | |
+ if(**argv){ | |
+ if(tail == 0) | |
+ tail = t = newword(*argv); | |
+ else { | |
+ t->next = newword(*argv); | |
+ t = t->next; | |
+ } | |
+ } | |
+ if(tail->next == 0) | |
+ mk(tail->s); | |
+ else { | |
+ head = newword("command line arguments"); | |
+ addrules(head, tail, strdup(""), VIR, mkinline… | |
+ mk(head->s); | |
+ } | |
+ } | |
+ } | |
+ if(uflag) | |
+ prusage(); | |
+ exits(0); | |
+} | |
+ | |
+void | |
+badusage(void) | |
+{ | |
+ | |
+ fprint(2, "Usage: mk [-f file] [-n] [-a] [-e] [-t] [-k] [-i] [-d[egp]]… | |
+ Exit(); | |
+} | |
+ | |
+void * | |
+Malloc(int n) | |
+{ | |
+ register void *s; | |
+ | |
+ s = malloc(n); | |
+ if(!s) { | |
+ fprint(2, "mk: cannot alloc %d bytes\n", n); | |
+ Exit(); | |
+ } | |
+ return(s); | |
+} | |
+ | |
+void * | |
+Realloc(void *s, int n) | |
+{ | |
+ if(s) | |
+ s = realloc(s, n); | |
+ else | |
+ s = malloc(n); | |
+ if(!s) { | |
+ fprint(2, "mk: cannot alloc %d bytes\n", n); | |
+ Exit(); | |
+ } | |
+ return(s); | |
+} | |
+ | |
+void | |
+assert(char *s, int n) | |
+{ | |
+ if(!n){ | |
+ fprint(2, "mk: Assertion ``%s'' failed.\n", s); | |
+ Exit(); | |
+ } | |
+} | |
+ | |
+void | |
+regerror(char *s) | |
+{ | |
+ if(patrule) | |
+ fprint(2, "mk: %s:%d: regular expression error; %s\n", | |
+ patrule->file, patrule->line, s); | |
+ else | |
+ fprint(2, "mk: %s:%d: regular expression error; %s\n", | |
+ infile, mkinline, s); | |
+ Exit(); | |
+} | |
diff --git a/src/cmd/mk/match.c b/src/cmd/mk/match.c | |
t@@ -0,0 +1,49 @@ | |
+#include "mk.h" | |
+ | |
+int | |
+match(char *name, char *template, char *stem) | |
+{ | |
+ Rune r; | |
+ int n; | |
+ | |
+ while(*name && *template){ | |
+ n = chartorune(&r, template); | |
+ if (PERCENT(r)) | |
+ break; | |
+ while (n--) | |
+ if(*name++ != *template++) | |
+ return 0; | |
+ } | |
+ if(!PERCENT(*template)) | |
+ return 0; | |
+ n = strlen(name)-strlen(template+1); | |
+ if (n < 0) | |
+ return 0; | |
+ if (strcmp(template+1, name+n)) | |
+ return 0; | |
+ strncpy(stem, name, n); | |
+ stem[n] = 0; | |
+ if(*template == '&') | |
+ return !charin(stem, "./"); | |
+ return 1; | |
+} | |
+ | |
+void | |
+subst(char *stem, char *template, char *dest) | |
+{ | |
+ Rune r; | |
+ char *s; | |
+ int n; | |
+ | |
+ while(*template){ | |
+ n = chartorune(&r, template); | |
+ if (PERCENT(r)) { | |
+ template += n; | |
+ for (s = stem; *s; s++) | |
+ *dest++ = *s; | |
+ } else | |
+ while (n--) | |
+ *dest++ = *template++; | |
+ } | |
+ *dest = 0; | |
+} | |
diff --git a/src/cmd/mk/mk.1 b/src/cmd/mk/mk.1 | |
t@@ -0,0 +1,665 @@ | |
+.TH MK 1 | |
+.de EX | |
+.nf | |
+.ft B | |
+.. | |
+.de EE | |
+.fi | |
+.ft R | |
+.. | |
+.de LR | |
+.if t .BR \\$1 \\$2 | |
+.if n .RB ` \\$1 '\\$2 | |
+.. | |
+.de L | |
+.nh | |
+.if t .B \\$1 | |
+.if n .RB ` \\$1 ' | |
+.. | |
+.SH NAME | |
+mk \- maintain (make) related files | |
+.SH SYNOPSIS | |
+.B mk | |
+[ | |
+.B -f | |
+.I mkfile | |
+] ... | |
+[ | |
+.I option ... | |
+] | |
+[ | |
+.I target ... | |
+] | |
+.SH DESCRIPTION | |
+.I Mk | |
+uses the dependency rules specified in | |
+.I mkfile | |
+to control the update (usually by compilation) of | |
+.I targets | |
+(usually files) | |
+from the source files upon which they depend. | |
+The | |
+.I mkfile | |
+(default | |
+.LR mkfile ) | |
+contains a | |
+.I rule | |
+for each target that identifies the files and other | |
+targets upon which it depends and an | |
+.IR sh (1) | |
+script, a | |
+.IR recipe , | |
+to update the target. | |
+The script is run if the target does not exist | |
+or if it is older than any of the files it depends on. | |
+.I Mkfile | |
+may also contain | |
+.I meta-rules | |
+that define actions for updating implicit targets. | |
+If no | |
+.I target | |
+is specified, the target of the first rule (not meta-rule) in | |
+.I mkfile | |
+is updated. | |
+.PP | |
+The environment variable | |
+.B $NPROC | |
+determines how many targets may be updated simultaneously; | |
+Some operating systems, e.g., Plan 9, set | |
+.B $NPROC | |
+automatically to the number of CPUs on the current machine. | |
+.PP | |
+Options are: | |
+.TP \w'\fL-d[egp]\ 'u | |
+.B -a | |
+Assume all targets to be out of date. | |
+Thus, everything is updated. | |
+.PD 0 | |
+.TP | |
+.BR -d [ egp ] | |
+Produce debugging output | |
+.RB ( p | |
+is for parsing, | |
+.B g | |
+for graph building, | |
+.B e | |
+for execution). | |
+.TP | |
+.B -e | |
+Explain why each target is made. | |
+.TP | |
+.B -i | |
+Force any missing intermediate targets to be made. | |
+.TP | |
+.B -k | |
+Do as much work as possible in the face of errors. | |
+.TP | |
+.B -n | |
+Print, but do not execute, the commands | |
+needed to update the targets. | |
+.TP | |
+.B -s | |
+Make the command line arguments sequentially rather than in parallel. | |
+.TP | |
+.B -t | |
+Touch (update the modified date of) file targets, without | |
+executing any recipes. | |
+.TP | |
+.BI -w target1 , target2,... | |
+Pretend the modify time for each | |
+.I target | |
+is the current time; useful in conjunction with | |
+.B -n | |
+to learn what updates would be triggered by | |
+modifying the | |
+.IR targets . | |
+.PD | |
+.SS The \fLmkfile\fP | |
+A | |
+.I mkfile | |
+consists of | |
+.I assignments | |
+(described under `Environment') and | |
+.IR rules . | |
+A rule contains | |
+.I targets | |
+and a | |
+.IR tail . | |
+A target is a literal string | |
+and is normally a file name. | |
+The tail contains zero or more | |
+.I prerequisites | |
+and an optional | |
+.IR recipe , | |
+which is an | |
+.B shell | |
+script. | |
+Each line of the recipe must begin with white space. | |
+A rule takes the form | |
+.IP | |
+.EX | |
+target: prereq1 prereq2 | |
+ \f2recipe using\fP prereq1, prereq2 \f2to build\fP target | |
+.EE | |
+.PP | |
+When the recipe is executed, | |
+the first character on every line is elided. | |
+.PP | |
+After the colon on the target line, a rule may specify | |
+.IR attributes , | |
+described below. | |
+.PP | |
+A | |
+.I meta-rule | |
+has a target of the form | |
+.IB A % B | |
+where | |
+.I A | |
+and | |
+.I B | |
+are (possibly empty) strings. | |
+A meta-rule acts as a rule for any potential target whose | |
+name matches | |
+.IB A % B | |
+with | |
+.B % | |
+replaced by an arbitrary string, called the | |
+.IR stem . | |
+In interpreting a meta-rule, | |
+the stem is substituted for all occurrences of | |
+.B % | |
+in the prerequisite names. | |
+In the recipe of a meta-rule, the environment variable | |
+.B $stem | |
+contains the string matched by the | |
+.BR % . | |
+For example, a meta-rule to compile a C program using | |
+.IR cc (1) | |
+might be: | |
+.IP | |
+.EX | |
+%: %.c | |
+ cc -c $stem.c | |
+ cc -o $stem $stem.o | |
+.EE | |
+.PP | |
+Meta-rules may contain an ampersand | |
+.B & | |
+rather than a percent sign | |
+.BR % . | |
+A | |
+.B % | |
+matches a maximal length string of any characters; | |
+an | |
+.B & | |
+matches a maximal length string of any characters except period | |
+or slash. | |
+.PP | |
+The text of the | |
+.I mkfile | |
+is processed as follows. | |
+Lines beginning with | |
+.B < | |
+followed by a file name are replaced by the contents of the named | |
+file. | |
+Lines beginning with | |
+.B "<|" | |
+followed by a file name are replaced by the output | |
+of the execution of the named | |
+file. | |
+Blank lines and comments, which run from unquoted | |
+.B # | |
+characters to the following newline, are deleted. | |
+The character sequence backslash-newline is deleted, | |
+so long lines in | |
+.I mkfile | |
+may be folded. | |
+Non-recipe lines are processed by substituting for | |
+.BI `{ command } | |
+the output of the | |
+.I command | |
+when run by | |
+.IR sh . | |
+References to variables are replaced by the variables' values. | |
+Special characters may be quoted using single quotes | |
+.BR \&'' | |
+as in | |
+.IR sh (1). | |
+.PP | |
+Assignments and rules are distinguished by | |
+the first unquoted occurrence of | |
+.B : | |
+(rule) | |
+or | |
+.B = | |
+(assignment). | |
+.PP | |
+A later rule may modify or override an existing rule under the | |
+following conditions: | |
+.TP | |
+\- | |
+If the targets of the rules exactly match and one rule | |
+contains only a prerequisite clause and no recipe, the | |
+clause is added to the prerequisites of the other rule. | |
+If either or both targets are virtual, the recipe is | |
+always executed. | |
+.TP | |
+\- | |
+If the targets of the rules match exactly and the | |
+prerequisites do not match and both rules | |
+contain recipes, | |
+.I mk | |
+reports an ``ambiguous recipe'' error. | |
+.TP | |
+\- | |
+If the target and prerequisites of both rules match exactly, | |
+the second rule overrides the first. | |
+.SS Environment | |
+Rules may make use of | |
+shell | |
+environment variables. | |
+A legal reference of the form | |
+.B $OBJ | |
+or | |
+.B ${name} | |
+is expanded as in | |
+.IR sh (1). | |
+A reference of the form | |
+.BI ${name: A % B = C\fL%\fID\fL}\fR, | |
+where | |
+.I A, B, C, D | |
+are (possibly empty) strings, | |
+has the value formed by expanding | |
+.B $name | |
+and substituting | |
+.I C | |
+for | |
+.I A | |
+and | |
+.I D | |
+for | |
+.I B | |
+in each word in | |
+.B $name | |
+that matches pattern | |
+.IB A % B\f1. | |
+.PP | |
+Variables can be set by | |
+assignments of the form | |
+.I | |
+ var\fL=\fR[\fIattr\fL=\fR]\fIvalue\fR | |
+.br | |
+Blanks in the | |
+.I value | |
+break it into words. | |
+Such variables are exported | |
+to the environment of | |
+recipes as they are executed, unless | |
+.BR U , | |
+the only legal attribute | |
+.IR attr , | |
+is present. | |
+The initial value of a variable is | |
+taken from (in increasing order of precedence) | |
+the default values below, | |
+.I mk's | |
+environment, the | |
+.IR mkfiles , | |
+and any command line assignment as an argument to | |
+.IR mk . | |
+A variable assignment argument overrides the first (but not any subsequent) | |
+assignment to that variable. | |
+The variable | |
+.B MKFLAGS | |
+contains all the option arguments (arguments starting with | |
+.L - | |
+or containing | |
+.LR = ) | |
+and | |
+.B MKARGS | |
+contains all the targets in the call to | |
+.IR mk . | |
+.PP | |
+Dynamic information may be included in the mkfile by using a line of the form | |
+.IP | |
+\fR<|\fIcommand\fR \fIargs\fR | |
+.LP | |
+This runs the command | |
+.I command | |
+with the given arguments | |
+.I args | |
+and pipes its standard output to | |
+.I mk | |
+to be included as part of the mkfile. For instance, the Inferno kernels | |
+use this technique | |
+to run a shell command with an awk script and a configuration | |
+file as arguments in order for | |
+the | |
+.I awk | |
+script to process the file and output a set of variables and their values. | |
+.SS Execution | |
+.PP | |
+During execution, | |
+.I mk | |
+determines which targets must be updated, and in what order, | |
+to build the | |
+.I names | |
+specified on the command line. | |
+It then runs the associated recipes. | |
+.PP | |
+A target is considered up to date if it has no prerequisites or | |
+if all its prerequisites are up to date and it is newer | |
+than all its prerequisites. | |
+Once the recipe for a target has executed, the target is | |
+considered up to date. | |
+.PP | |
+The date stamp | |
+used to determine if a target is up to date is computed | |
+differently for different types of targets. | |
+If a target is | |
+.I virtual | |
+(the target of a rule with the | |
+.B V | |
+attribute), | |
+its date stamp is initially zero; when the target is | |
+updated the date stamp is set to | |
+the most recent date stamp of its prerequisites. | |
+Otherwise, if a target does not exist as a file, | |
+its date stamp is set to the most recent date stamp of its prerequisites, | |
+or zero if it has no prerequisites. | |
+Otherwise, the target is the name of a file and | |
+the target's date stamp is always that file's modification date. | |
+The date stamp is computed when the target is needed in | |
+the execution of a rule; it is not a static value. | |
+.PP | |
+Nonexistent targets that have prerequisites | |
+and are themselves prerequisites are treated specially. | |
+Such a target | |
+.I t | |
+is given the date stamp of its most recent prerequisite | |
+and if this causes all the targets which have | |
+.I t | |
+as a prerequisite to be up to date, | |
+.I t | |
+is considered up to date. | |
+Otherwise, | |
+.I t | |
+is made in the normal fashion. | |
+The | |
+.B -i | |
+flag overrides this special treatment. | |
+.PP | |
+Files may be made in any order that respects | |
+the preceding restrictions. | |
+.PP | |
+A recipe is executed by supplying the recipe as standard input to | |
+the command | |
+.BR /bin/sh . | |
+(Note that unlike | |
+.IR make , | |
+.I mk | |
+feeds the entire recipe to the shell rather than running each line | |
+of the recipe separately.) | |
+The environment is augmented by the following variables: | |
+.TP 14 | |
+.B $alltarget | |
+all the targets of this rule. | |
+.TP | |
+.B $newprereq | |
+the prerequisites that caused this rule to execute. | |
+.TP | |
+.B $newmember | |
+the prerequisites that are members of an aggregate | |
+that caused this rule to execute. | |
+When the prerequisites of a rule are members of an | |
+aggregate, | |
+.B $newprereq | |
+contains the name of the aggregate and out of date | |
+members, while | |
+.B $newmember | |
+contains only the name of the members. | |
+.TP | |
+.B $nproc | |
+the process slot for this recipe. | |
+It satisfies | |
+.RB 0≤ $nproc < $NPROC . | |
+.TP | |
+.B $pid | |
+the process id for the | |
+.I mk | |
+executing the recipe. | |
+.TP | |
+.B $prereq | |
+all the prerequisites for this rule. | |
+.TP | |
+.B $stem | |
+if this is a meta-rule, | |
+.B $stem | |
+is the string that matched | |
+.B % | |
+or | |
+.BR & . | |
+Otherwise, it is empty. | |
+For regular expression meta-rules (see below), the variables | |
+.LR stem0 ", ...," | |
+.L stem9 | |
+are set to the corresponding subexpressions. | |
+.TP | |
+.B $target | |
+the targets for this rule that need to be remade. | |
+.PP | |
+These variables are available only during the execution of a recipe, | |
+not while evaluating the | |
+.IR mkfile . | |
+.PP | |
+Unless the rule has the | |
+.B Q | |
+attribute, | |
+the recipe is printed prior to execution | |
+with recognizable environment variables expanded. | |
+Commands returning error status | |
+cause | |
+.I mk | |
+to terminate. | |
+.PP | |
+Recipes and backquoted | |
+.B rc | |
+commands in places such as assignments | |
+execute in a copy of | |
+.I mk's | |
+environment; changes they make to | |
+environment variables are not visible from | |
+.IR mk . | |
+.PP | |
+Variable substitution in a rule is done when | |
+the rule is read; variable substitution in the recipe is done | |
+when the recipe is executed. For example: | |
+.IP | |
+.EX | |
+bar=a.c | |
+foo: $bar | |
+ $CC -o foo $bar | |
+bar=b.c | |
+.EE | |
+.PP | |
+will compile | |
+.B b.c | |
+into | |
+.BR foo , | |
+if | |
+.B a.c | |
+is newer than | |
+.BR foo . | |
+.SS Aggregates | |
+Names of the form | |
+.IR a ( b ) | |
+refer to member | |
+.I b | |
+of the aggregate | |
+.IR a . | |
+Currently, the only aggregates supported are | |
+.IR ar (1) | |
+archives. | |
+.SS Attributes | |
+The colon separating the target from the prerequisites | |
+may be | |
+immediately followed by | |
+.I attributes | |
+and another colon. | |
+The attributes are: | |
+.TP | |
+.B D | |
+If the recipe exits with a non-null status, the target is deleted. | |
+.TP | |
+.B E | |
+Continue execution if the recipe draws errors. | |
+.TP | |
+.B N | |
+If there is no recipe, the target has its time updated. | |
+.TP | |
+.B n | |
+The rule is a meta-rule that cannot be a target of a virtual rule. | |
+Only files match the pattern in the target. | |
+.TP | |
+.B P | |
+The characters after the | |
+.B P | |
+until the terminating | |
+.B : | |
+are taken as a program name. | |
+It will be invoked as | |
+.B "sh -c prog 'arg1' 'arg2'" | |
+and should return a zero exit status | |
+if and only if arg1 is up to date with respect to arg2. | |
+Date stamps are still propagated in the normal way. | |
+.TP | |
+.B Q | |
+The recipe is not printed prior to execution. | |
+.TP | |
+.B R | |
+The rule is a meta-rule using regular expressions. | |
+In the rule, | |
+.B % | |
+has no special meaning. | |
+The target is interpreted as a regular expression as defined in | |
+.IR regexp (6). | |
+The prerequisites may contain references | |
+to subexpressions in form | |
+.BI \e n\f1, | |
+as in the substitute command of | |
+.IR sed (1). | |
+.TP | |
+.B U | |
+The targets are considered to have been updated | |
+even if the recipe did not do so. | |
+.TP | |
+.B V | |
+The targets of this rule are marked as virtual. | |
+They are distinct from files of the same name. | |
+.PD | |
+.SH EXAMPLES | |
+A simple mkfile to compile a program: | |
+.IP | |
+.EX | |
+.ta 8n +8n +8n +8n +8n +8n +8n | |
+</$objtype/mkfile | |
+ | |
+prog: a.$O b.$O c.$O | |
+ $LD $LDFLAGS -o $target $prereq | |
+ | |
+%.$O: %.c | |
+ $CC $CFLAGS $stem.c | |
+.EE | |
+.PP | |
+Override flag settings in the mkfile: | |
+.IP | |
+.EX | |
+% mk target 'CFLAGS=-S -w' | |
+.EE | |
+.PP | |
+Maintain a library: | |
+.IP | |
+.EX | |
+libc.a(%.$O):N: %.$O | |
+libc.a: libc.a(abs.$O) libc.a(access.$O) libc.a(alarm.$O) ... | |
+ ar r libc.a $newmember | |
+.EE | |
+.PP | |
+String expression variables to derive names from a master list: | |
+.IP | |
+.EX | |
+NAMES=alloc arc bquote builtins expand main match mk var word | |
+OBJ=${NAMES:%=%.$O} | |
+.EE | |
+.PP | |
+Regular expression meta-rules: | |
+.IP | |
+.EX | |
+([^/]*)/(.*)\e.$O:R: \e1/\e2.c | |
+ cd $stem1; $CC $CFLAGS $stem2.c | |
+.EE | |
+.PP | |
+A correct way to deal with | |
+.IR yacc (1) | |
+grammars. | |
+The file | |
+.B lex.c | |
+includes the file | |
+.B x.tab.h | |
+rather than | |
+.B y.tab.h | |
+in order to reflect changes in content, not just modification time. | |
+.IP | |
+.EX | |
+lex.$O: x.tab.h | |
+x.tab.h: y.tab.h | |
+ cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h | |
+y.tab.c y.tab.h: gram.y | |
+ $YACC -d gram.y | |
+.EE | |
+.PP | |
+The above example could also use the | |
+.B P | |
+attribute for the | |
+.B x.tab.h | |
+rule: | |
+.IP | |
+.EX | |
+x.tab.h:Pcmp -s: y.tab.h | |
+ cp y.tab.h x.tab.h | |
+.EE | |
+.SH SEE ALSO | |
+.IR sh (1), | |
+.IR regexp9 (7) | |
+.PP | |
+A. Hume, | |
+``Mk: a Successor to Make'' | |
+(Tenth Edition Research Unix Manuals). | |
+.PP | |
+Andrew G. Hume and Bob Flandrena, | |
+``Maintaining Files on Plan 9 with Mk''. | |
+DOCPREFIX/doc/mk.pdf | |
+.SH HISTORY | |
+Andrew Hume wrote | |
+.I mk | |
+for Tenth Edition Research Unix. | |
+It was later ported to Plan 9. | |
+This software is a port of the Plan 9 version back to Unix. | |
+.SH BUGS | |
+Identical recipes for regular expression meta-rules only have one target. | |
+.br | |
+Seemingly appropriate input like | |
+.B CFLAGS=-DHZ=60 | |
+is parsed as an erroneous attribute; correct it by inserting | |
+a space after the first | |
+.LR = . | |
+.br | |
+The recipes printed by | |
+.I mk | |
+before being passed to | |
+.I sh | |
+for execution are sometimes erroneously expanded | |
+for printing. Don't trust what's printed; rely | |
+on what | |
+.I sh | |
+does. | |
diff --git a/src/cmd/mk/mk.c b/src/cmd/mk/mk.c | |
t@@ -0,0 +1,226 @@ | |
+#include "mk.h" | |
+ | |
+int runerrs; | |
+ | |
+void | |
+mk(char *target) | |
+{ | |
+ Node *node; | |
+ int did = 0; | |
+ | |
+ nproc(); /* it can be updated dynamically */ | |
+ nrep(); /* it can be updated dynamically */ | |
+ runerrs = 0; | |
+ node = graph(target); | |
+ if(DEBUG(D_GRAPH)){ | |
+ dumpn("new target\n", node); | |
+ Bflush(&bout); | |
+ } | |
+ clrmade(node); | |
+ while(node->flags&NOTMADE){ | |
+ if(work(node, (Node *)0, (Arc *)0)) | |
+ did = 1; /* found something to do */ | |
+ else { | |
+ if(waitup(1, (int *)0) > 0){ | |
+ if(node->flags&(NOTMADE|BEINGMADE)){ | |
+ assert("must be run errors", runerrs); | |
+ break; /* nothing more waiting … | |
+ } | |
+ } | |
+ } | |
+ } | |
+ if(node->flags&BEINGMADE) | |
+ waitup(-1, (int *)0); | |
+ while(jobs) | |
+ waitup(-2, (int *)0); | |
+ assert("target didn't get done", runerrs || (node->flags&MADE)); | |
+ if(did == 0) | |
+ Bprint(&bout, "mk: '%s' is up to date\n", node->name); | |
+} | |
+ | |
+void | |
+clrmade(Node *n) | |
+{ | |
+ Arc *a; | |
+ | |
+ n->flags &= ~(CANPRETEND|PRETENDING); | |
+ if(strchr(n->name, '(') ==0 || n->time) | |
+ n->flags |= CANPRETEND; | |
+ MADESET(n, NOTMADE); | |
+ for(a = n->prereqs; a; a = a->next) | |
+ if(a->n) | |
+ clrmade(a->n); | |
+} | |
+ | |
+static void | |
+unpretend(Node *n) | |
+{ | |
+ MADESET(n, NOTMADE); | |
+ n->flags &= ~(CANPRETEND|PRETENDING); | |
+ n->time = 0; | |
+} | |
+ | |
+int | |
+work(Node *node, Node *p, Arc *parc) | |
+{ | |
+ Arc *a, *ra; | |
+ int weoutofdate; | |
+ int ready; | |
+ int did = 0; | |
+ | |
+ /*print("work(%s) flags=0x%x time=%ld\n", node->name, node->flags, nod… | |
+ if(node->flags&BEINGMADE) | |
+ return(did); | |
+ if((node->flags&MADE) && (node->flags&PRETENDING) && p && outofdate(p,… | |
+ if(explain) | |
+ fprint(1, "unpretending %s(%ld) because %s is out of d… | |
+ node->name, node->time, p->name, p->time); | |
+ unpretend(node); | |
+ } | |
+ /* | |
+ have a look if we are pretending in case | |
+ someone has been unpretended out from underneath us | |
+ */ | |
+ if(node->flags&MADE){ | |
+ if(node->flags&PRETENDING){ | |
+ node->time = 0; | |
+ }else | |
+ return(did); | |
+ } | |
+ /* consider no prerequsite case */ | |
+ if(node->prereqs == 0){ | |
+ if(node->time == 0){ | |
+ fprint(2, "mk: don't know how to make '%s'\n", node->n… | |
+ if(kflag){ | |
+ node->flags |= BEINGMADE; | |
+ runerrs++; | |
+ } else | |
+ Exit(); | |
+ } else | |
+ MADESET(node, MADE); | |
+ return(did); | |
+ } | |
+ /* | |
+ now see if we are out of date or what | |
+ */ | |
+ ready = 1; | |
+ weoutofdate = aflag; | |
+ ra = 0; | |
+ for(a = node->prereqs; a; a = a->next) | |
+ if(a->n){ | |
+ did = work(a->n, node, a) || did; | |
+ if(a->n->flags&(NOTMADE|BEINGMADE)) | |
+ ready = 0; | |
+ if(outofdate(node, a, 0)){ | |
+ weoutofdate = 1; | |
+ if((ra == 0) || (ra->n == 0) | |
+ || (ra->n->time < a->n->time)) | |
+ ra = a; | |
+ } | |
+ } else { | |
+ if(node->time == 0){ | |
+ if(ra == 0) | |
+ ra = a; | |
+ weoutofdate = 1; | |
+ } | |
+ } | |
+ if(ready == 0) /* can't do anything now */ | |
+ return(did); | |
+ if(weoutofdate == 0){ | |
+ MADESET(node, MADE); | |
+ return(did); | |
+ } | |
+ /* | |
+ can we pretend to be made? | |
+ */ | |
+ if((iflag == 0) && (node->time == 0) && (node->flags&(PRETENDING|CANPR… | |
+ && p && ra->n && !outofdate(p, ra, 0)){ | |
+ node->flags &= ~CANPRETEND; | |
+ MADESET(node, MADE); | |
+ if(explain && ((node->flags&PRETENDING) == 0)) | |
+ fprint(1, "pretending %s has time %ld\n", node->name, … | |
+ node->flags |= PRETENDING; | |
+ return(did); | |
+ } | |
+ /* | |
+ node is out of date and we REALLY do have to do something. | |
+ quickly rescan for pretenders | |
+ */ | |
+ for(a = node->prereqs; a; a = a->next) | |
+ if(a->n && (a->n->flags&PRETENDING)){ | |
+ if(explain) | |
+ Bprint(&bout, "unpretending %s because of %s b… | |
+ a->n->name, node->name, ra->n? ra->n->name : "… | |
+ | |
+ unpretend(a->n); | |
+ did = work(a->n, node, a) || did; | |
+ ready = 0; | |
+ } | |
+ if(ready == 0) /* try later unless nothing has happened for -k'… | |
+ return(did || work(node, p, parc)); | |
+ did = dorecipe(node) || did; | |
+ return(did); | |
+} | |
+ | |
+void | |
+update(int fake, Node *node) | |
+{ | |
+ Arc *a; | |
+ | |
+ MADESET(node, fake? BEINGMADE : MADE); | |
+ if(((node->flags&VIRTUAL) == 0) && (access(node->name, 0) == 0)){ | |
+ node->time = timeof(node->name, 1); | |
+ node->flags &= ~(CANPRETEND|PRETENDING); | |
+ for(a = node->prereqs; a; a = a->next) | |
+ if(a->prog) | |
+ outofdate(node, a, 1); | |
+ } else { | |
+ node->time = 1; | |
+ for(a = node->prereqs; a; a = a->next) | |
+ if(a->n && outofdate(node, a, 1)) | |
+ node->time = a->n->time; | |
+ } | |
+/* print("----node %s time=%ld flags=0x%x\n", node->name, node->time, n… | |
+} | |
+ | |
+static int | |
+pcmp(char *prog, char *p, char *q) | |
+{ | |
+ char buf[3*NAMEBLOCK]; | |
+ int pid; | |
+ | |
+ Bflush(&bout); | |
+ sprint(buf, "%s '%s' '%s'\n", prog, p, q); | |
+ pid = pipecmd(buf, 0, 0); | |
+ while(waitup(-3, &pid) >= 0) | |
+ ; | |
+ return(pid? 2:1); | |
+} | |
+ | |
+int | |
+outofdate(Node *node, Arc *arc, int eval) | |
+{ | |
+ char buf[3*NAMEBLOCK], *str; | |
+ Symtab *sym; | |
+ int ret; | |
+ | |
+ str = 0; | |
+ if(arc->prog){ | |
+ sprint(buf, "%s%c%s", node->name, 0377, arc->n->name); | |
+ sym = symlook(buf, S_OUTOFDATE, 0); | |
+ if(sym == 0 || eval){ | |
+ if(sym == 0) | |
+ str = strdup(buf); | |
+ ret = pcmp(arc->prog, node->name, arc->n->name); | |
+ if(sym) | |
+ sym->value = (void *)ret; | |
+ else | |
+ symlook(str, S_OUTOFDATE, (void *)ret); | |
+ } else | |
+ ret = (int)sym->value; | |
+ return(ret-1); | |
+ } else if(strchr(arc->n->name, '(') && arc->n->time == 0) /* missing … | |
+ return 1; | |
+ else | |
+ return node->time < arc->n->time; | |
+} | |
diff --git a/src/cmd/mk/mk.h b/src/cmd/mk/mk.h | |
t@@ -0,0 +1,203 @@ | |
+#include <utf.h> | |
+#include <fmt.h> | |
+#include <setjmp.h> | |
+#include <string.h> | |
+#include <stdlib.h> | |
+#include <unistd.h> | |
+#include <bio.h> | |
+#include <regexp9.h> | |
+#include <time.h> | |
+ | |
+#define uchar _mkuchar | |
+#define ushort _mkushort | |
+#define uint _mkuint | |
+#define ulong _mkulong | |
+#define vlong _mkvlong | |
+#define uvlong _mkuvlong | |
+ | |
+#define nil ((void*)0) | |
+ | |
+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 OREAD O_RDONLY | |
+#define OWRITE O_WRONLY | |
+#define ORDWR O_RDWR | |
+#define USED(x) if(x);else | |
+#define remove unlink | |
+#define seek lseek | |
+#define exits(s) exit((s) && ((char*)s)[0] ? 1 : 0) | |
+#define create(name, mode, perm) creat(name, perm) | |
+#define ERRMAX 256 | |
+ | |
+#undef assert | |
+#define assert mkassert | |
+extern Biobuf bout; | |
+ | |
+typedef struct Bufblock | |
+{ | |
+ struct Bufblock *next; | |
+ char *start; | |
+ char *end; | |
+ char *current; | |
+} Bufblock; | |
+ | |
+typedef struct Word | |
+{ | |
+ char *s; | |
+ struct Word *next; | |
+} Word; | |
+ | |
+typedef struct Envy | |
+{ | |
+ char *name; | |
+ Word *values; | |
+} Envy; | |
+ | |
+extern Envy *envy; | |
+ | |
+typedef struct Rule | |
+{ | |
+ char *target; /* one target */ | |
+ Word *tail; /* constituents of targets … | |
+ char *recipe; /* do it ! */ | |
+ short attr; /* attributes */ | |
+ short line; /* source line */ | |
+ char *file; /* source file */ | |
+ Word *alltargets; /* all the targets */ | |
+ int rule; /* rule number */ | |
+ Reprog *pat; /* reg exp goo */ | |
+ char *prog; /* to use in out of date */ | |
+ struct Rule *chain; /* hashed per target */ | |
+ struct Rule *next; | |
+} Rule; | |
+ | |
+extern Rule *rules, *metarules, *patrule; | |
+ | |
+/* Rule.attr */ | |
+#define META 0x0001 | |
+#define UNUSED 0x0002 | |
+#define UPD 0x0004 | |
+#define QUIET 0x0008 | |
+#define VIR 0x0010 | |
+#define REGEXP 0x0020 | |
+#define NOREC 0x0040 | |
+#define DEL 0x0080 | |
+#define NOVIRT 0x0100 | |
+ | |
+#define NREGEXP 10 | |
+ | |
+typedef struct Arc | |
+{ | |
+ short flag; | |
+ struct Node *n; | |
+ Rule *r; | |
+ char *stem; | |
+ char *prog; | |
+ char *match[NREGEXP]; | |
+ struct Arc *next; | |
+} Arc; | |
+ | |
+ /* Arc.flag */ | |
+#define TOGO 1 | |
+ | |
+typedef struct Node | |
+{ | |
+ char *name; | |
+ long time; | |
+ unsigned short flags; | |
+ Arc *prereqs; | |
+ struct Node *next; /* list for a rule */ | |
+} Node; | |
+ | |
+ /* Node.flags */ | |
+#define VIRTUAL 0x0001 | |
+#define CYCLE 0x0002 | |
+#define READY 0x0004 | |
+#define CANPRETEND 0x0008 | |
+#define PRETENDING 0x0010 | |
+#define NOTMADE 0x0020 | |
+#define BEINGMADE 0x0040 | |
+#define MADE 0x0080 | |
+#define MADESET(n,m) n->flags = (n->flags&~(NOTMADE|BEIN… | |
+#define PROBABLE 0x0100 | |
+#define VACUOUS 0x0200 | |
+#define NORECIPE 0x0400 | |
+#define DELETE 0x0800 | |
+#define NOMINUSE 0x1000 | |
+ | |
+typedef struct Job | |
+{ | |
+ Rule *r; /* master rule for job */ | |
+ Node *n; /* list of node targets */ | |
+ char *stem; | |
+ char **match; | |
+ Word *p; /* prerequistes */ | |
+ Word *np; /* new prerequistes */ | |
+ Word *t; /* targets */ | |
+ Word *at; /* all targets */ | |
+ int nproc; /* slot number */ | |
+ struct Job *next; | |
+} Job; | |
+extern Job *jobs; | |
+ | |
+typedef struct Symtab | |
+{ | |
+ short space; | |
+ char *name; | |
+ void *value; | |
+ struct Symtab *next; | |
+} Symtab; | |
+ | |
+enum { | |
+ S_VAR, /* variable -> value */ | |
+ S_TARGET, /* target -> rule */ | |
+ S_TIME, /* file -> time */ | |
+ S_PID, /* pid -> products */ | |
+ S_NODE, /* target name -> node */ | |
+ S_AGG, /* aggregate -> time */ | |
+ S_BITCH, /* bitched about aggregate not there */ | |
+ S_NOEXPORT, /* var -> noexport */ | |
+ S_OVERRIDE, /* can't override */ | |
+ S_OUTOFDATE, /* n1\377n2 -> 2(outofdate) or 1(not outofdate) */ | |
+ S_MAKEFILE, /* target -> node */ | |
+ S_MAKEVAR, /* dumpable mk variable */ | |
+ S_EXPORTED, /* var -> current exported value */ | |
+ S_WESET, /* variable; we set in the mkfile */ | |
+ S_INTERNAL /* an internal mk variable (e.g., stem, target) */ | |
+}; | |
+ | |
+extern int debug; | |
+extern int nflag, tflag, iflag, kflag, aflag, mflag; | |
+extern int mkinline; | |
+extern char *infile; | |
+extern int nreps; | |
+extern char *explain; | |
+extern char *termchars; | |
+extern int IWS; | |
+extern char *shell; | |
+extern char *shellname; | |
+extern char *shflags; | |
+ | |
+#define SYNERR(l) (fprint(2, "mk: %s:%d: syntax error; ", infile… | |
+#define RERR(r) (fprint(2, "mk: %s:%d: rule error; ", (r… | |
+#define NAMEBLOCK 1000 | |
+#define BIGBLOCK 20000 | |
+ | |
+#define SEP(c) (((c)==' ')||((c)=='\t')||((c)=='\n')) | |
+#define WORDCHR(r) ((r) > ' ' && !utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^… | |
+ | |
+#define DEBUG(x) (debug&(x)) | |
+#define D_PARSE 0x01 | |
+#define D_GRAPH 0x02 | |
+#define D_EXEC 0x04 | |
+ | |
+#define LSEEK(f,o,p) seek(f,o,p) | |
+ | |
+#define PERCENT(ch) (((ch) == '%') || ((ch) == '&')) | |
+ | |
+#include "fns.h" | |
diff --git a/src/cmd/mk/mkfile b/src/cmd/mk/mkfile | |
t@@ -0,0 +1,9 @@ | |
+all:V: Makefile Make.FreeBSD-386 Make.Linux-386 Make.HP-UX-9000 Make.OSF1-alph… | |
+ Make.SunOS-sun4u Make.SunOS-sun4u-cc Make.SunOS-sun4u-gcc \ | |
+ Make.NetBSD-386 Make.Darwin-PowerMacintosh | |
+ | |
+Makefile:D: ../libutf/Makefile.TOP Makefile.MID ../libutf/Makefile.CMD ../libu… | |
+ cat $prereq >$target | |
+ | |
+Make.%: ../libutf/Make.% | |
+ cp $prereq $target | |
diff --git a/src/cmd/mk/mkfile.test b/src/cmd/mk/mkfile.test | |
t@@ -0,0 +1,5 @@ | |
+a: b | |
+ cp b a | |
+ | |
+c:V: | |
+ echo hello world | |
diff --git a/src/cmd/mk/parse.c b/src/cmd/mk/parse.c | |
t@@ -0,0 +1,307 @@ | |
+#include "mk.h" | |
+ | |
+char *infile; | |
+int mkinline; | |
+static int rhead(char *, Word **, Word **, int *, char **); | |
+static char *rbody(Biobuf*); | |
+extern Word *target1; | |
+ | |
+void | |
+parse(char *f, int fd, int varoverride) | |
+{ | |
+ int hline; | |
+ char *body; | |
+ Word *head, *tail; | |
+ int attr, set, pid; | |
+ char *prog, *p; | |
+ int newfd; | |
+ Biobuf in; | |
+ Bufblock *buf; | |
+ | |
+ if(fd < 0){ | |
+ fprint(2, "open %s: %r\n", f); | |
+ Exit(); | |
+ } | |
+ ipush(); | |
+ infile = strdup(f); | |
+ mkinline = 1; | |
+ Binit(&in, fd, OREAD); | |
+ buf = newbuf(); | |
+ while(assline(&in, buf)){ | |
+ hline = mkinline; | |
+ switch(rhead(buf->start, &head, &tail, &attr, &prog)) | |
+ { | |
+ case '<': | |
+ p = wtos(tail, ' '); | |
+ if(*p == 0){ | |
+ SYNERR(-1); | |
+ fprint(2, "missing include file name\n"); | |
+ Exit(); | |
+ } | |
+ newfd = open(p, OREAD); | |
+ if(newfd < 0){ | |
+ fprint(2, "warning: skipping missing include f… | |
+ } else | |
+ parse(p, newfd, 0); | |
+ break; | |
+ case '|': | |
+ p = wtos(tail, ' '); | |
+ if(*p == 0){ | |
+ SYNERR(-1); | |
+ fprint(2, "missing include program name\n"); | |
+ Exit(); | |
+ } | |
+ execinit(); | |
+ pid=pipecmd(p, envy, &newfd); | |
+ if(newfd < 0){ | |
+ fprint(2, "warning: skipping missing program f… | |
+ } else | |
+ parse(p, newfd, 0); | |
+ while(waitup(-3, &pid) >= 0) | |
+ ; | |
+ if(pid != 0){ | |
+ fprint(2, "bad include program status\n"); | |
+ Exit(); | |
+ } | |
+ break; | |
+ case ':': | |
+ body = rbody(&in); | |
+ addrules(head, tail, body, attr, hline, prog); | |
+ break; | |
+ case '=': | |
+ if(head->next){ | |
+ SYNERR(-1); | |
+ fprint(2, "multiple vars on left side of assig… | |
+ Exit(); | |
+ } | |
+ if(symlook(head->s, S_OVERRIDE, 0)){ | |
+ set = varoverride; | |
+ } else { | |
+ set = 1; | |
+ if(varoverride) | |
+ symlook(head->s, S_OVERRIDE, (void *)"… | |
+ } | |
+ if(set){ | |
+/* | |
+char *cp; | |
+dumpw("tail", tail); | |
+cp = wtos(tail, ' '); print("assign %s to %s\n", head->s, cp); free(cp); | |
+*/ | |
+ setvar(head->s, (void *) tail); | |
+ symlook(head->s, S_WESET, (void *)""); | |
+ } | |
+ if(attr) | |
+ symlook(head->s, S_NOEXPORT, (void *)""); | |
+ break; | |
+ default: | |
+ SYNERR(hline); | |
+ fprint(2, "expected one of :<=\n"); | |
+ Exit(); | |
+ break; | |
+ } | |
+ } | |
+ close(fd); | |
+ freebuf(buf); | |
+ ipop(); | |
+} | |
+ | |
+void | |
+addrules(Word *head, Word *tail, char *body, int attr, int hline, char *prog) | |
+{ | |
+ Word *w; | |
+ | |
+ assert("addrules args", head && body); | |
+ /* tuck away first non-meta rule as default target*/ | |
+ if(target1 == 0 && !(attr®EXP)){ | |
+ for(w = head; w; w = w->next) | |
+ if(charin(w->s, "%&")) | |
+ break; | |
+ if(w == 0) | |
+ target1 = wdup(head); | |
+ } | |
+ for(w = head; w; w = w->next) | |
+ addrule(w->s, tail, body, head, attr, hline, prog); | |
+} | |
+ | |
+static int | |
+rhead(char *line, Word **h, Word **t, int *attr, char **prog) | |
+{ | |
+ char *p; | |
+ char *pp; | |
+ int sep; | |
+ Rune r; | |
+ int n; | |
+ Word *w; | |
+ | |
+ p = charin(line,":=<"); | |
+ if(p == 0) | |
+ return('?'); | |
+ sep = *p; | |
+ *p++ = 0; | |
+ if(sep == '<' && *p == '|'){ | |
+ sep = '|'; | |
+ p++; | |
+ } | |
+ *attr = 0; | |
+ *prog = 0; | |
+ if(sep == '='){ | |
+ pp = charin(p, termchars); /* termchars is shell-depend… | |
+ if (pp && *pp == '=') { | |
+ while (p != pp) { | |
+ n = chartorune(&r, p); | |
+ switch(r) | |
+ { | |
+ default: | |
+ SYNERR(-1); | |
+ fprint(2, "unknown attribute '%c'\n",*… | |
+ Exit(); | |
+ case 'U': | |
+ *attr = 1; | |
+ break; | |
+ } | |
+ p += n; | |
+ } | |
+ p++; /* skip trailing '=' */ | |
+ } | |
+ } | |
+ if((sep == ':') && *p && (*p != ' ') && (*p != '\t')){ | |
+ while (*p) { | |
+ n = chartorune(&r, p); | |
+ if (r == ':') | |
+ break; | |
+ p += n; | |
+ switch(r) | |
+ { | |
+ default: | |
+ SYNERR(-1); | |
+ fprint(2, "unknown attribute '%c'\n", p[-1]); | |
+ Exit(); | |
+ case 'D': | |
+ *attr |= DEL; | |
+ break; | |
+ case 'E': | |
+ *attr |= NOMINUSE; | |
+ break; | |
+ case 'n': | |
+ *attr |= NOVIRT; | |
+ break; | |
+ case 'N': | |
+ *attr |= NOREC; | |
+ break; | |
+ case 'P': | |
+ pp = utfrune(p, ':'); | |
+ if (pp == 0 || *pp == 0) | |
+ goto eos; | |
+ *pp = 0; | |
+ *prog = strdup(p); | |
+ *pp = ':'; | |
+ p = pp; | |
+ break; | |
+ case 'Q': | |
+ *attr |= QUIET; | |
+ break; | |
+ case 'R': | |
+ *attr |= REGEXP; | |
+ break; | |
+ case 'U': | |
+ *attr |= UPD; | |
+ break; | |
+ case 'V': | |
+ *attr |= VIR; | |
+ break; | |
+ } | |
+ } | |
+ if (*p++ != ':') { | |
+ eos: | |
+ SYNERR(-1); | |
+ fprint(2, "missing trailing :\n"); | |
+ Exit(); | |
+ } | |
+ } | |
+ *h = w = stow(line); | |
+ if(*w->s == 0 && sep != '<' && sep != '|') { | |
+ SYNERR(mkinline-1); | |
+ fprint(2, "no var on left side of assignment/rule\n"); | |
+ Exit(); | |
+ } | |
+ *t = stow(p); | |
+ return(sep); | |
+} | |
+ | |
+static char * | |
+rbody(Biobuf *in) | |
+{ | |
+ Bufblock *buf; | |
+ int r, lastr; | |
+ char *p; | |
+ | |
+ lastr = '\n'; | |
+ buf = newbuf(); | |
+ for(;;){ | |
+ r = Bgetrune(in); | |
+ if (r < 0) | |
+ break; | |
+ if (lastr == '\n') { | |
+ if (r == '#') | |
+ rinsert(buf, r); | |
+ else if (r != ' ' && r != '\t') { | |
+ Bungetrune(in); | |
+ break; | |
+ } | |
+ } else | |
+ rinsert(buf, r); | |
+ lastr = r; | |
+ if (r == '\n') | |
+ mkinline++; | |
+ } | |
+ insert(buf, 0); | |
+ p = strdup(buf->start); | |
+ freebuf(buf); | |
+ return p; | |
+} | |
+ | |
+struct input | |
+{ | |
+ char *file; | |
+ int line; | |
+ struct input *next; | |
+}; | |
+static struct input *inputs = 0; | |
+ | |
+void | |
+ipush(void) | |
+{ | |
+ struct input *in, *me; | |
+ | |
+ me = (struct input *)Malloc(sizeof(*me)); | |
+ me->file = infile; | |
+ me->line = mkinline; | |
+ me->next = 0; | |
+ if(inputs == 0) | |
+ inputs = me; | |
+ else { | |
+ for(in = inputs; in->next; ) | |
+ in = in->next; | |
+ in->next = me; | |
+ } | |
+} | |
+ | |
+void | |
+ipop(void) | |
+{ | |
+ struct input *in, *me; | |
+ | |
+ assert("pop input list", inputs != 0); | |
+ if(inputs->next == 0){ | |
+ me = inputs; | |
+ inputs = 0; | |
+ } else { | |
+ for(in = inputs; in->next->next; ) | |
+ in = in->next; | |
+ me = in->next; | |
+ in->next = 0; | |
+ } | |
+ infile = me->file; | |
+ mkinline = me->line; | |
+ free((char *)me); | |
+} | |
diff --git a/src/cmd/mk/rc.c b/src/cmd/mk/rc.c | |
t@@ -0,0 +1,175 @@ | |
+#include "mk.h" | |
+ | |
+char *termchars = "'= \t"; /*used in parse.c to isolate assignme… | |
+char *shflags = "-I"; /* rc flag to force non-interactive mode */ | |
+int IWS = '\1'; /* inter-word separator in env - not use… | |
+ | |
+/* | |
+ * This file contains functions that depend on rc's syntax. Most | |
+ * of the routines extract strings observing rc's escape conventions | |
+ */ | |
+ | |
+ | |
+/* | |
+ * skip a token in single quotes. | |
+ */ | |
+static char * | |
+squote(char *cp) | |
+{ | |
+ Rune r; | |
+ int n; | |
+ | |
+ while(*cp){ | |
+ n = chartorune(&r, cp); | |
+ if(r == '\'') { | |
+ n += chartorune(&r, cp+n); | |
+ if(r != '\'') | |
+ return(cp); | |
+ } | |
+ cp += n; | |
+ } | |
+ SYNERR(-1); /* should never occur */ | |
+ fprint(2, "missing closing '\n"); | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * search a string for characters in a pattern set | |
+ * characters in quotes and variable generators are escaped | |
+ */ | |
+char * | |
+charin(char *cp, char *pat) | |
+{ | |
+ Rune r; | |
+ int n, vargen; | |
+ | |
+ vargen = 0; | |
+ while(*cp){ | |
+ n = chartorune(&r, cp); | |
+ switch(r){ | |
+ case '\'': /* skip quoted string */ | |
+ cp = squote(cp+1); /* n must = 1 */ | |
+ if(!cp) | |
+ return 0; | |
+ break; | |
+ case '$': | |
+ if(*(cp+1) == '{') | |
+ vargen = 1; | |
+ break; | |
+ case '}': | |
+ if(vargen) | |
+ vargen = 0; | |
+ else if(utfrune(pat, r)) | |
+ return cp; | |
+ break; | |
+ default: | |
+ if(vargen == 0 && utfrune(pat, r)) | |
+ return cp; | |
+ break; | |
+ } | |
+ cp += n; | |
+ } | |
+ if(vargen){ | |
+ SYNERR(-1); | |
+ fprint(2, "missing closing } in pattern generator\n"); | |
+ } | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * extract an escaped token. Possible escape chars are single-quote, | |
+ * double-quote,and backslash. Only the first is valid for rc. the | |
+ * others are just inserted into the receiving buffer. | |
+ */ | |
+char* | |
+expandquote(char *s, Rune r, Bufblock *b) | |
+{ | |
+ if (r != '\'') { | |
+ rinsert(b, r); | |
+ return s; | |
+ } | |
+ | |
+ while(*s){ | |
+ s += chartorune(&r, s); | |
+ if(r == '\'') { | |
+ if(*s == '\'') | |
+ s++; | |
+ else | |
+ return s; | |
+ } | |
+ rinsert(b, r); | |
+ } | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * Input an escaped token. Possible escape chars are single-quote, | |
+ * double-quote and backslash. Only the first is a valid escape for | |
+ * rc; the others are just inserted into the receiving buffer. | |
+ */ | |
+int | |
+escapetoken(Biobuf *bp, Bufblock *buf, int preserve, int esc) | |
+{ | |
+ int c, line; | |
+ | |
+ if(esc != '\'') | |
+ return 1; | |
+ | |
+ line = mkinline; | |
+ while((c = nextrune(bp, 0)) > 0){ | |
+ if(c == '\''){ | |
+ if(preserve) | |
+ rinsert(buf, c); | |
+ c = Bgetrune(bp); | |
+ if (c < 0) | |
+ break; | |
+ if(c != '\''){ | |
+ Bungetrune(bp); | |
+ return 1; | |
+ } | |
+ } | |
+ rinsert(buf, c); | |
+ } | |
+ SYNERR(line); fprint(2, "missing closing %c\n", esc); | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * copy a single-quoted string; s points to char after opening quote | |
+ */ | |
+static char * | |
+copysingle(char *s, Bufblock *buf) | |
+{ | |
+ Rune r; | |
+ | |
+ while(*s){ | |
+ s += chartorune(&r, s); | |
+ rinsert(buf, r); | |
+ if(r == '\'') | |
+ break; | |
+ } | |
+ return s; | |
+} | |
+/* | |
+ * check for quoted strings. backquotes are handled here; single quote… | |
+ * s points to char after opening quote, q. | |
+ */ | |
+char * | |
+copyq(char *s, Rune q, Bufblock *buf) | |
+{ | |
+ if(q == '\'') /* copy quoted string */ | |
+ return copysingle(s, buf); | |
+ | |
+ if(q != '`') /* not quoted */ | |
+ return s; | |
+ | |
+ while(*s){ /* copy backquoted string */ | |
+ s += chartorune(&q, s); | |
+ rinsert(buf, q); | |
+ if(q == '}') | |
+ break; | |
+ if(q == '\'') | |
+ s = copysingle(s, buf); /* copy quoted string */ | |
+ } | |
+ return s; | |
+} | |
diff --git a/src/cmd/mk/recipe.c b/src/cmd/mk/recipe.c | |
t@@ -0,0 +1,117 @@ | |
+#include "mk.h" | |
+ | |
+int | |
+dorecipe(Node *node) | |
+{ | |
+ char buf[BIGBLOCK]; | |
+ register Node *n; | |
+ Rule *r = 0; | |
+ Arc *a, *aa; | |
+ Word head, ahead, lp, ln, *w, *ww, *aw; | |
+ Symtab *s; | |
+ int did = 0; | |
+ | |
+ aa = 0; | |
+ /* | |
+ pick up the rule | |
+ */ | |
+ for(a = node->prereqs; a; a = a->next) | |
+ if(*a->r->recipe) | |
+ r = (aa = a)->r; | |
+ /* | |
+ no recipe? go to buggery! | |
+ */ | |
+ if(r == 0){ | |
+ if(!(node->flags&VIRTUAL) && !(node->flags&NORECIPE)){ | |
+ fprint(2, "mk: no recipe to make '%s'\n", node->name); | |
+ Exit(); | |
+ } | |
+ if(strchr(node->name, '(') && node->time == 0) | |
+ MADESET(node, MADE); | |
+ else | |
+ update(0, node); | |
+ if(tflag){ | |
+ if(!(node->flags&VIRTUAL)) | |
+ touch(node->name); | |
+ else if(explain) | |
+ Bprint(&bout, "no touch of virtual '%s'\n", no… | |
+ } | |
+ return(did); | |
+ } | |
+ /* | |
+ build the node list | |
+ */ | |
+ node->next = 0; | |
+ head.next = 0; | |
+ ww = &head; | |
+ ahead.next = 0; | |
+ aw = &ahead; | |
+ if(r->attr®EXP){ | |
+ ww->next = newword(node->name); | |
+ aw->next = newword(node->name); | |
+ } else { | |
+ for(w = r->alltargets; w; w = w->next){ | |
+ if(r->attr&META) | |
+ subst(aa->stem, w->s, buf); | |
+ else | |
+ strcpy(buf, w->s); | |
+ aw->next = newword(buf); | |
+ aw = aw->next; | |
+ if((s = symlook(buf, S_NODE, 0)) == 0) | |
+ continue; /* not a node we are interest… | |
+ n = (Node *)s->value; | |
+ if(aflag == 0 && n->time) { | |
+ for(a = n->prereqs; a; a = a->next) | |
+ if(a->n && outofdate(n, a, 0)) | |
+ break; | |
+ if(a == 0) | |
+ continue; | |
+ } | |
+ ww->next = newword(buf); | |
+ ww = ww->next; | |
+ if(n == node) continue; | |
+ n->next = node->next; | |
+ node->next = n; | |
+ } | |
+ } | |
+ for(n = node; n; n = n->next) | |
+ if((n->flags&READY) == 0) | |
+ return(did); | |
+ /* | |
+ gather the params for the job | |
+ */ | |
+ lp.next = ln.next = 0; | |
+ for(n = node; n; n = n->next){ | |
+ for(a = n->prereqs; a; a = a->next){ | |
+ if(a->n){ | |
+ addw(&lp, a->n->name); | |
+ if(outofdate(n, a, 0)){ | |
+ addw(&ln, a->n->name); | |
+ if(explain) | |
+ fprint(1, "%s(%ld) < %s(%ld)\n… | |
+ n->name, n->time, a->n… | |
+ } | |
+ } else { | |
+ if(explain) | |
+ fprint(1, "%s has no prerequisites\n", | |
+ n->name); | |
+ } | |
+ } | |
+ MADESET(n, BEINGMADE); | |
+ } | |
+ /*print("lt=%s ln=%s lp=%s\n",wtos(head.next, ' '),wtos(ln.next, ' '),… | |
+ run(newjob(r, node, aa->stem, aa->match, lp.next, ln.next, head.next, … | |
+ return(1); | |
+} | |
+ | |
+void | |
+addw(Word *w, char *s) | |
+{ | |
+ Word *lw; | |
+ | |
+ for(lw = w; w = w->next; lw = w){ | |
+ if(strcmp(s, w->s) == 0) | |
+ return; | |
+ } | |
+ lw->next = newword(s); | |
+} | |
diff --git a/src/cmd/mk/rpm.spec b/src/cmd/mk/rpm.spec | |
t@@ -0,0 +1,29 @@ | |
+Summary: Streamlined replacement for make | |
+Name: mk | |
+Version: 2.0 | |
+Release: 1 | |
+Group: Development/Utils | |
+Copyright: Public Domain | |
+Packager: Russ Cox <[email protected]> | |
+Source: http://pdos.lcs.mit.edu/~rsc/software/mk-2.0.tgz | |
+URL: http://pdos.lcs.mit.edu/~rsc/software/#mk | |
+Requires: libfmt libbio libregexp9 libutf | |
+ | |
+%description | |
+Mk is a streamlined replacement for make, written for | |
+Tenth Edition Research Unix by Andrew Hume. | |
+ | |
+http://plan9.bell-labs.com/sys/doc/mk.pdf | |
+%prep | |
+%setup | |
+ | |
+%build | |
+make | |
+ | |
+%install | |
+make install | |
+ | |
+%files | |
+/usr/local/doc/mk.pdf | |
+/usr/local/man/man1/mk.1 | |
+/usr/local/bin/mk | |
diff --git a/src/cmd/mk/rule.c b/src/cmd/mk/rule.c | |
t@@ -0,0 +1,107 @@ | |
+#include "mk.h" | |
+ | |
+static Rule *lr, *lmr; | |
+static int rcmp(Rule *r, char *target, Word *tail); | |
+static int nrules = 0; | |
+ | |
+void | |
+addrule(char *head, Word *tail, char *body, Word *ahead, int attr, int hline, … | |
+{ | |
+ Rule *r; | |
+ Rule *rr; | |
+ Symtab *sym; | |
+ int reuse; | |
+ | |
+ r = 0; | |
+ reuse = 0; | |
+ if(sym = symlook(head, S_TARGET, 0)){ | |
+ for(r = (Rule *)sym->value; r; r = r->chain) | |
+ if(rcmp(r, head, tail) == 0){ | |
+ reuse = 1; | |
+ break; | |
+ } | |
+ } | |
+ if(r == 0) | |
+ r = (Rule *)Malloc(sizeof(Rule)); | |
+ r->target = head; | |
+ r->tail = tail; | |
+ r->recipe = body; | |
+ r->line = hline; | |
+ r->file = infile; | |
+ r->attr = attr; | |
+ r->alltargets = ahead; | |
+ r->prog = prog; | |
+ r->rule = nrules++; | |
+ if(!reuse){ | |
+ rr = (Rule *)symlook(head, S_TARGET, (void *)r)->value; | |
+ if(rr != r){ | |
+ r->chain = rr->chain; | |
+ rr->chain = r; | |
+ } else | |
+ r->chain = 0; | |
+ } | |
+ if(!reuse) | |
+ r->next = 0; | |
+ if((attr®EXP) || charin(head, "%&")){ | |
+ r->attr |= META; | |
+ if(reuse) | |
+ return; | |
+ if(attr®EXP){ | |
+ patrule = r; | |
+ r->pat = regcomp(head); | |
+ } | |
+ if(metarules == 0) | |
+ metarules = lmr = r; | |
+ else { | |
+ lmr->next = r; | |
+ lmr = r; | |
+ } | |
+ } else { | |
+ if(reuse) | |
+ return; | |
+ r->pat = 0; | |
+ if(rules == 0) | |
+ rules = lr = r; | |
+ else { | |
+ lr->next = r; | |
+ lr = r; | |
+ } | |
+ } | |
+} | |
+ | |
+void | |
+dumpr(char *s, Rule *r) | |
+{ | |
+ Bprint(&bout, "%s: start=%ld\n", s, r); | |
+ for(; r; r = r->next){ | |
+ Bprint(&bout, "\tRule %ld: %s[%d] attr=%x next=%ld chain=%ld a… | |
+ r, r->file, r->line, r->attr, r->next, r->chain, wtos(… | |
+ if(r->prog) | |
+ Bprint(&bout, " prog='%s'", r->prog); | |
+ Bprint(&bout, "\n\ttarget=%s: %s\n", r->target, wtos(r->tail, … | |
+ Bprint(&bout, "\trecipe@%ld='%s'\n", r->recipe, r->recipe); | |
+ } | |
+} | |
+ | |
+static int | |
+rcmp(Rule *r, char *target, Word *tail) | |
+{ | |
+ Word *w; | |
+ | |
+ if(strcmp(r->target, target)) | |
+ return 1; | |
+ for(w = r->tail; w && tail; w = w->next, tail = tail->next) | |
+ if(strcmp(w->s, tail->s)) | |
+ return 1; | |
+ return(w || tail); | |
+} | |
+ | |
+char * | |
+rulecnt(void) | |
+{ | |
+ char *s; | |
+ | |
+ s = Malloc(nrules); | |
+ memset(s, 0, nrules); | |
+ return(s); | |
+} | |
diff --git a/src/cmd/mk/run.c b/src/cmd/mk/run.c | |
t@@ -0,0 +1,296 @@ | |
+#include "mk.h" | |
+ | |
+typedef struct Event | |
+{ | |
+ int pid; | |
+ Job *job; | |
+} Event; | |
+static Event *events; | |
+static int nevents, nrunning, nproclimit; | |
+ | |
+typedef struct Process | |
+{ | |
+ int pid; | |
+ int status; | |
+ struct Process *b, *f; | |
+} Process; | |
+static Process *phead, *pfree; | |
+static void sched(void); | |
+static void pnew(int, int), pdelete(Process *); | |
+ | |
+int pidslot(int); | |
+ | |
+void | |
+run(Job *j) | |
+{ | |
+ Job *jj; | |
+ | |
+ if(jobs){ | |
+ for(jj = jobs; jj->next; jj = jj->next) | |
+ ; | |
+ jj->next = j; | |
+ } else | |
+ jobs = j; | |
+ j->next = 0; | |
+ /* this code also in waitup after parse redirect */ | |
+ if(nrunning < nproclimit) | |
+ sched(); | |
+} | |
+ | |
+static void | |
+sched(void) | |
+{ | |
+ char *flags; | |
+ Job *j; | |
+ Bufblock *buf; | |
+ int slot; | |
+ Node *n; | |
+ Envy *e; | |
+ | |
+ if(jobs == 0){ | |
+ usage(); | |
+ return; | |
+ } | |
+ j = jobs; | |
+ jobs = j->next; | |
+ if(DEBUG(D_EXEC)) | |
+ fprint(1, "firing up job for target %s\n", wtos(j->t, ' ')); | |
+ slot = nextslot(); | |
+ events[slot].job = j; | |
+ buf = newbuf(); | |
+ e = buildenv(j, slot); | |
+ shprint(j->r->recipe, e, buf); | |
+ if(!tflag && (nflag || !(j->r->attr&QUIET))) | |
+ Bwrite(&bout, buf->start, (long)strlen(buf->start)); | |
+ freebuf(buf); | |
+ if(nflag||tflag){ | |
+ for(n = j->n; n; n = n->next){ | |
+ if(tflag){ | |
+ if(!(n->flags&VIRTUAL)) | |
+ touch(n->name); | |
+ else if(explain) | |
+ Bprint(&bout, "no touch of virtual '%s… | |
+ } | |
+ n->time = time((long *)0); | |
+ MADESET(n, MADE); | |
+ } | |
+ } else { | |
+ if(DEBUG(D_EXEC)) | |
+ fprint(1, "recipe='%s'", j->r->recipe);/**/ | |
+ Bflush(&bout); | |
+ if(j->r->attr&NOMINUSE) | |
+ flags = 0; | |
+ else | |
+ flags = "-e"; | |
+ events[slot].pid = execsh(flags, j->r->recipe, 0, e); | |
+ usage(); | |
+ nrunning++; | |
+ if(DEBUG(D_EXEC)) | |
+ fprint(1, "pid for target %s = %d\n", wtos(j->t, ' '),… | |
+ } | |
+} | |
+ | |
+int | |
+waitup(int echildok, int *retstatus) | |
+{ | |
+ Envy *e; | |
+ int pid; | |
+ int slot; | |
+ Symtab *s; | |
+ Word *w; | |
+ Job *j; | |
+ char buf[ERRMAX]; | |
+ Bufblock *bp; | |
+ int uarg = 0; | |
+ int done; | |
+ Node *n; | |
+ Process *p; | |
+ extern int runerrs; | |
+ | |
+ /* first check against the proces slist */ | |
+ if(retstatus) | |
+ for(p = phead; p; p = p->f) | |
+ if(p->pid == *retstatus){ | |
+ *retstatus = p->status; | |
+ pdelete(p); | |
+ return(-1); | |
+ } | |
+again: /* rogue processes */ | |
+ pid = waitfor(buf); | |
+ if(pid == -1){ | |
+ if(echildok > 0) | |
+ return(1); | |
+ else { | |
+ fprint(2, "mk: (waitup %d): %r\n", echildok); | |
+ Exit(); | |
+ } | |
+ } | |
+ if(DEBUG(D_EXEC)) | |
+ fprint(1, "waitup got pid=%d, status='%s'\n", pid, buf); | |
+ if(retstatus && pid == *retstatus){ | |
+ *retstatus = buf[0]? 1:0; | |
+ return(-1); | |
+ } | |
+ slot = pidslot(pid); | |
+ if(slot < 0){ | |
+ if(DEBUG(D_EXEC)) | |
+ fprint(2, "mk: wait returned unexpected process %d\n",… | |
+ pnew(pid, buf[0]? 1:0); | |
+ goto again; | |
+ } | |
+ j = events[slot].job; | |
+ usage(); | |
+ nrunning--; | |
+ events[slot].pid = -1; | |
+ if(buf[0]){ | |
+ e = buildenv(j, slot); | |
+ bp = newbuf(); | |
+ shprint(j->r->recipe, e, bp); | |
+ front(bp->start); | |
+ fprint(2, "mk: %s: exit status=%s", bp->start, buf); | |
+ freebuf(bp); | |
+ for(n = j->n, done = 0; n; n = n->next) | |
+ if(n->flags&DELETE){ | |
+ if(done++ == 0) | |
+ fprint(2, ", deleting"); | |
+ fprint(2, " '%s'", n->name); | |
+ delete(n->name); | |
+ } | |
+ fprint(2, "\n"); | |
+ if(kflag){ | |
+ runerrs++; | |
+ uarg = 1; | |
+ } else { | |
+ jobs = 0; | |
+ Exit(); | |
+ } | |
+ } | |
+ for(w = j->t; w; w = w->next){ | |
+ if((s = symlook(w->s, S_NODE, 0)) == 0) | |
+ continue; /* not interested in this node */ | |
+ update(uarg, (Node *)s->value); | |
+ } | |
+ if(nrunning < nproclimit) | |
+ sched(); | |
+ return(0); | |
+} | |
+ | |
+void | |
+nproc(void) | |
+{ | |
+ Symtab *sym; | |
+ Word *w; | |
+ | |
+ if(sym = symlook("NPROC", S_VAR, 0)) { | |
+ w = (Word *) sym->value; | |
+ if (w && w->s && w->s[0]) | |
+ nproclimit = atoi(w->s); | |
+ } | |
+ if(nproclimit < 1) | |
+ nproclimit = 1; | |
+ if(DEBUG(D_EXEC)) | |
+ fprint(1, "nprocs = %d\n", nproclimit); | |
+ if(nproclimit > nevents){ | |
+ if(nevents) | |
+ events = (Event *)Realloc((char *)events, nproclimit*s… | |
+ else | |
+ events = (Event *)Malloc(nproclimit*sizeof(Event)); | |
+ while(nevents < nproclimit) | |
+ events[nevents++].pid = 0; | |
+ } | |
+} | |
+ | |
+int | |
+nextslot(void) | |
+{ | |
+ int i; | |
+ | |
+ for(i = 0; i < nproclimit; i++) | |
+ if(events[i].pid <= 0) return i; | |
+ assert("out of slots!!", 0); | |
+ return 0; /* cyntax */ | |
+} | |
+ | |
+int | |
+pidslot(int pid) | |
+{ | |
+ int i; | |
+ | |
+ for(i = 0; i < nevents; i++) | |
+ if(events[i].pid == pid) return(i); | |
+ if(DEBUG(D_EXEC)) | |
+ fprint(2, "mk: wait returned unexpected process %d\n", pid); | |
+ return(-1); | |
+} | |
+ | |
+ | |
+static void | |
+pnew(int pid, int status) | |
+{ | |
+ Process *p; | |
+ | |
+ if(pfree){ | |
+ p = pfree; | |
+ pfree = p->f; | |
+ } else | |
+ p = (Process *)Malloc(sizeof(Process)); | |
+ p->pid = pid; | |
+ p->status = status; | |
+ p->f = phead; | |
+ phead = p; | |
+ if(p->f) | |
+ p->f->b = p; | |
+ p->b = 0; | |
+} | |
+ | |
+static void | |
+pdelete(Process *p) | |
+{ | |
+ if(p->f) | |
+ p->f->b = p->b; | |
+ if(p->b) | |
+ p->b->f = p->f; | |
+ else | |
+ phead = p->f; | |
+ p->f = pfree; | |
+ pfree = p; | |
+} | |
+ | |
+void | |
+killchildren(char *msg) | |
+{ | |
+ Process *p; | |
+ | |
+ kflag = 1; /* to make sure waitup doesn't exit */ | |
+ jobs = 0; /* make sure no more get scheduled */ | |
+ for(p = phead; p; p = p->f) | |
+ expunge(p->pid, msg); | |
+ while(waitup(1, (int *)0) == 0) | |
+ ; | |
+ Bprint(&bout, "mk: %s\n", msg); | |
+ Exit(); | |
+} | |
+ | |
+static long tslot[1000]; | |
+static long tick; | |
+ | |
+void | |
+usage(void) | |
+{ | |
+ long t; | |
+ | |
+ time(&t); | |
+ if(tick) | |
+ tslot[nrunning] += (t-tick); | |
+ tick = t; | |
+} | |
+ | |
+void | |
+prusage(void) | |
+{ | |
+ int i; | |
+ | |
+ usage(); | |
+ for(i = 0; i <= nevents; i++) | |
+ fprint(1, "%d: %ld\n", i, tslot[i]); | |
+} | |
diff --git a/src/cmd/mk/sh.c b/src/cmd/mk/sh.c | |
t@@ -0,0 +1,189 @@ | |
+#include "mk.h" | |
+ | |
+char *termchars = "\"'= \t"; /*used in parse.c to isolate assign… | |
+char *shflags = 0; | |
+int IWS = ' '; /* inter-word separator in env */ | |
+ | |
+/* | |
+ * This file contains functions that depend on the shell's syntax. Most | |
+ * of the routines extract strings observing the shell's escape convent… | |
+ */ | |
+ | |
+ | |
+/* | |
+ * skip a token in quotes. | |
+ */ | |
+static char * | |
+squote(char *cp, int c) | |
+{ | |
+ Rune r; | |
+ int n; | |
+ | |
+ while(*cp){ | |
+ n = chartorune(&r, cp); | |
+ if(r == c) | |
+ return cp; | |
+ if(r == '\\') | |
+ n += chartorune(&r, cp+n); | |
+ cp += n; | |
+ } | |
+ SYNERR(-1); /* should never occur */ | |
+ fprint(2, "missing closing '\n"); | |
+ return 0; | |
+} | |
+/* | |
+ * search a string for unescaped characters in a pattern set | |
+ */ | |
+char * | |
+charin(char *cp, char *pat) | |
+{ | |
+ Rune r; | |
+ int n, vargen; | |
+ | |
+ vargen = 0; | |
+ while(*cp){ | |
+ n = chartorune(&r, cp); | |
+ switch(r){ | |
+ case '\\': /* skip escaped char */ | |
+ cp += n; | |
+ n = chartorune(&r, cp); | |
+ break; | |
+ case '\'': /* skip quoted string */ | |
+ case '"': | |
+ cp = squote(cp+1, r); /* n must = 1 */ | |
+ if(!cp) | |
+ return 0; | |
+ break; | |
+ case '$': | |
+ if(*(cp+1) == '{') | |
+ vargen = 1; | |
+ break; | |
+ case '}': | |
+ if(vargen) | |
+ vargen = 0; | |
+ else if(utfrune(pat, r)) | |
+ return cp; | |
+ break; | |
+ default: | |
+ if(vargen == 0 && utfrune(pat, r)) | |
+ return cp; | |
+ break; | |
+ } | |
+ cp += n; | |
+ } | |
+ if(vargen){ | |
+ SYNERR(-1); | |
+ fprint(2, "missing closing } in pattern generator\n"); | |
+ } | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * extract an escaped token. Possible escape chars are single-quote, | |
+ * double-quote,and backslash. | |
+ */ | |
+char* | |
+expandquote(char *s, Rune esc, Bufblock *b) | |
+{ | |
+ Rune r; | |
+ | |
+ if (esc == '\\') { | |
+ s += chartorune(&r, s); | |
+ rinsert(b, r); | |
+ return s; | |
+ } | |
+ | |
+ while(*s){ | |
+ s += chartorune(&r, s); | |
+ if(r == esc) | |
+ return s; | |
+ if (r == '\\') { | |
+ rinsert(b, r); | |
+ s += chartorune(&r, s); | |
+ } | |
+ rinsert(b, r); | |
+ } | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * Input an escaped token. Possible escape chars are single-quote, | |
+ * double-quote and backslash. | |
+ */ | |
+int | |
+escapetoken(Biobuf *bp, Bufblock *buf, int preserve, int esc) | |
+{ | |
+ int c, line; | |
+ | |
+ if(esc == '\\') { | |
+ c = Bgetrune(bp); | |
+ if(c == '\r') | |
+ c = Bgetrune(bp); | |
+ if (c == '\n') | |
+ mkinline++; | |
+ rinsert(buf, c); | |
+ return 1; | |
+ } | |
+ | |
+ line = mkinline; | |
+ while((c = nextrune(bp, 0)) >= 0){ | |
+ if(c == esc){ | |
+ if(preserve) | |
+ rinsert(buf, c); | |
+ return 1; | |
+ } | |
+ if(c == '\\') { | |
+ rinsert(buf, c); | |
+ c = Bgetrune(bp); | |
+ if(c == '\r') | |
+ c = Bgetrune(bp); | |
+ if (c < 0) | |
+ break; | |
+ if (c == '\n') | |
+ mkinline++; | |
+ } | |
+ rinsert(buf, c); | |
+ } | |
+ SYNERR(line); fprint(2, "missing closing %c\n", esc); | |
+ return 0; | |
+} | |
+ | |
+/* | |
+ * copy a quoted string; s points to char after opening quote | |
+ */ | |
+static char * | |
+copysingle(char *s, Rune q, Bufblock *buf) | |
+{ | |
+ Rune r; | |
+ | |
+ while(*s){ | |
+ s += chartorune(&r, s); | |
+ rinsert(buf, r); | |
+ if(r == q) | |
+ break; | |
+ } | |
+ return s; | |
+} | |
+/* | |
+ * check for quoted strings. backquotes are handled here; single quote… | |
+ * s points to char after opening quote, q. | |
+ */ | |
+char * | |
+copyq(char *s, Rune q, Bufblock *buf) | |
+{ | |
+ if(q == '\'' || q == '"') /* copy quoted string */ | |
+ return copysingle(s, q, buf); | |
+ | |
+ if(q != '`') /* not quoted */ | |
+ return s; | |
+ | |
+ while(*s){ /* copy backquoted string */ | |
+ s += chartorune(&q, s); | |
+ rinsert(buf, q); | |
+ if(q == '`') | |
+ break; | |
+ if(q == '\'' || q == '"') | |
+ s = copysingle(s, q, buf); /* copy quoted strin… | |
+ } | |
+ return s; | |
+} | |
diff --git a/src/cmd/mk/shprint.c b/src/cmd/mk/shprint.c | |
t@@ -0,0 +1,123 @@ | |
+#include "mk.h" | |
+ | |
+static char *vexpand(char*, Envy*, Bufblock*); | |
+ | |
+static int | |
+getfields(char *str, char **args, int max, int mflag, char *set) | |
+{ | |
+ Rune r; | |
+ int nr, intok, narg; | |
+ | |
+ if(max <= 0) | |
+ return 0; | |
+ | |
+ narg = 0; | |
+ args[narg] = str; | |
+ if(!mflag) | |
+ narg++; | |
+ intok = 0; | |
+ for(;; str += nr) { | |
+ nr = chartorune(&r, str); | |
+ if(r == 0) | |
+ break; | |
+ if(utfrune(set, r)) { | |
+ if(narg >= max) | |
+ break; | |
+ *str = 0; | |
+ intok = 0; | |
+ args[narg] = str + nr; | |
+ if(!mflag) | |
+ narg++; | |
+ } else { | |
+ if(!intok && mflag) | |
+ narg++; | |
+ intok = 1; | |
+ } | |
+ } | |
+ return narg; | |
+} | |
+ | |
+void | |
+shprint(char *s, Envy *env, Bufblock *buf) | |
+{ | |
+ int n; | |
+ Rune r; | |
+ | |
+ while(*s) { | |
+ n = chartorune(&r, s); | |
+ if (r == '$') | |
+ s = vexpand(s, env, buf); | |
+ else { | |
+ rinsert(buf, r); | |
+ s += n; | |
+ s = copyq(s, r, buf); /*handle quoted strings*/ | |
+ } | |
+ } | |
+ insert(buf, 0); | |
+} | |
+ | |
+static char * | |
+mygetenv(char *name, Envy *env) | |
+{ | |
+ if (!env) | |
+ return 0; | |
+ if (symlook(name, S_WESET, 0) == 0 && symlook(name, S_INTERNAL, 0) == … | |
+ return 0; | |
+ /* only resolve internal variables and variables we've set */ | |
+ for(; env->name; env++){ | |
+ if (strcmp(env->name, name) == 0) | |
+ return wtos(env->values, ' '); | |
+ } | |
+ return 0; | |
+} | |
+ | |
+static char * | |
+vexpand(char *w, Envy *env, Bufblock *buf) | |
+{ | |
+ char *s, carry, *p, *q; | |
+ | |
+ assert("vexpand no $", *w == '$'); | |
+ p = w+1; /* skip dollar sign */ | |
+ if(*p == '{') { | |
+ p++; | |
+ q = utfrune(p, '}'); | |
+ if (!q) | |
+ q = strchr(p, 0); | |
+ } else | |
+ q = shname(p); | |
+ carry = *q; | |
+ *q = 0; | |
+ s = mygetenv(p, env); | |
+ *q = carry; | |
+ if (carry == '}') | |
+ q++; | |
+ if (s) { | |
+ bufcpy(buf, s, strlen(s)); | |
+ free(s); | |
+ } else /* copy name intact*/ | |
+ bufcpy(buf, w, q-w); | |
+ return(q); | |
+} | |
+ | |
+void | |
+front(char *s) | |
+{ | |
+ char *t, *q; | |
+ int i, j; | |
+ char *flds[512]; | |
+ | |
+ q = strdup(s); | |
+ i = getfields(q, flds, 512, 0, " \t\n"); | |
+ if(i > 5){ | |
+ flds[4] = flds[i-1]; | |
+ flds[3] = "..."; | |
+ i = 5; | |
+ } | |
+ t = s; | |
+ for(j = 0; j < i; j++){ | |
+ for(s = flds[j]; *s; *t++ = *s++); | |
+ *t++ = ' '; | |
+ } | |
+ *t = 0; | |
+ free(q); | |
+} | |
diff --git a/src/cmd/mk/symtab.c b/src/cmd/mk/symtab.c | |
t@@ -0,0 +1,95 @@ | |
+#include "mk.h" | |
+ | |
+#define NHASH 4099 | |
+#define HASHMUL 79L /* this is a good value */ | |
+static Symtab *hash[NHASH]; | |
+ | |
+void | |
+syminit(void) | |
+{ | |
+ Symtab **s, *ss; | |
+ | |
+ for(s = hash; s < &hash[NHASH]; s++){ | |
+ for(ss = *s; ss; ss = ss->next) | |
+ free((char *)ss); | |
+ *s = 0; | |
+ } | |
+} | |
+ | |
+Symtab * | |
+symlook(char *sym, int space, void *install) | |
+{ | |
+ long h; | |
+ char *p; | |
+ Symtab *s; | |
+ | |
+ for(p = sym, h = space; *p; h += *p++) | |
+ h *= HASHMUL; | |
+ if(h < 0) | |
+ h = ~h; | |
+ h %= NHASH; | |
+ for(s = hash[h]; s; s = s->next) | |
+ if((s->space == space) && (strcmp(s->name, sym) == 0)) | |
+ return(s); | |
+ if(install == 0) | |
+ return(0); | |
+ s = (Symtab *)Malloc(sizeof(Symtab)); | |
+ s->space = space; | |
+ s->name = sym; | |
+ s->value = install; | |
+ s->next = hash[h]; | |
+ hash[h] = s; | |
+ return(s); | |
+} | |
+ | |
+void | |
+symdel(char *sym, int space) | |
+{ | |
+ long h; | |
+ char *p; | |
+ Symtab *s, *ls; | |
+ | |
+ /* multiple memory leaks */ | |
+ | |
+ for(p = sym, h = space; *p; h += *p++) | |
+ h *= HASHMUL; | |
+ if(h < 0) | |
+ h = ~h; | |
+ h %= NHASH; | |
+ for(s = hash[h], ls = 0; s; ls = s, s = s->next) | |
+ if((s->space == space) && (strcmp(s->name, sym) == 0)){ | |
+ if(ls) | |
+ ls->next = s->next; | |
+ else | |
+ hash[h] = s->next; | |
+ free((char *)s); | |
+ } | |
+} | |
+ | |
+void | |
+symtraverse(int space, void (*fn)(Symtab*)) | |
+{ | |
+ Symtab **s, *ss; | |
+ | |
+ for(s = hash; s < &hash[NHASH]; s++) | |
+ for(ss = *s; ss; ss = ss->next) | |
+ if(ss->space == space) | |
+ (*fn)(ss); | |
+} | |
+ | |
+void | |
+symstat(void) | |
+{ | |
+ Symtab **s, *ss; | |
+ int n; | |
+ int l[1000]; | |
+ | |
+ memset((char *)l, 0, sizeof(l)); | |
+ for(s = hash; s < &hash[NHASH]; s++){ | |
+ for(ss = *s, n = 0; ss; ss = ss->next) | |
+ n++; | |
+ l[n]++; | |
+ } | |
+ for(n = 0; n < 1000; n++) | |
+ if(l[n]) Bprint(&bout, "%ld of length %d\n", l[n], n); | |
+} | |
diff --git a/src/cmd/mk/unix.c b/src/cmd/mk/unix.c | |
t@@ -0,0 +1,306 @@ | |
+#include "mk.h" | |
+#include <sys/wait.h> | |
+#include <signal.h> | |
+#include <sys/stat.h> | |
+#include <sys/time.h> | |
+ | |
+char *shell = "/bin/sh"; | |
+char *shellname = "sh"; | |
+ | |
+extern char **environ; | |
+ | |
+static void | |
+mkperror(char *s) | |
+{ | |
+ fprint(2, "%s: %r\n", s); | |
+} | |
+ | |
+void | |
+readenv(void) | |
+{ | |
+ char **p, *s; | |
+ Word *w; | |
+ | |
+ for(p = environ; *p; p++){ | |
+ s = shname(*p); | |
+ if(*s == '=') { | |
+ *s = 0; | |
+ w = newword(s+1); | |
+ } else | |
+ w = newword(""); | |
+ if (symlook(*p, S_INTERNAL, 0)) | |
+ continue; | |
+ s = strdup(*p); | |
+ setvar(s, (void *)w); | |
+ symlook(s, S_EXPORTED, (void*)"")->value = (void*)""; | |
+ } | |
+} | |
+ | |
+/* | |
+ * done on child side of fork, so parent's env is not affected | |
+ * and we don't care about freeing memory because we're going | |
+ * to exec immediately after this. | |
+ */ | |
+void | |
+exportenv(Envy *e) | |
+{ | |
+ int i; | |
+ char **p; | |
+ char buf[4096]; | |
+ | |
+ p = 0; | |
+ for(i = 0; e->name; e++, i++) { | |
+ p = (char**) Realloc(p, (i+2)*sizeof(char*)); | |
+ if(e->values) | |
+ sprint(buf, "%s=%s", e->name, wtos(e->values, IWS)); | |
+ else | |
+ sprint(buf, "%s=", e->name); | |
+ p[i] = strdup(buf); | |
+ } | |
+ p[i] = 0; | |
+ environ = p; | |
+} | |
+ | |
+int | |
+waitfor(char *msg) | |
+{ | |
+ int status; | |
+ int pid; | |
+ | |
+ *msg = 0; | |
+ pid = wait(&status); | |
+ if(pid > 0) { | |
+ if(status&0x7f) { | |
+ if(status&0x80) | |
+ snprint(msg, ERRMAX, "signal %d, core dumped",… | |
+ else | |
+ snprint(msg, ERRMAX, "signal %d", status&0x7f); | |
+ } else if(status&0xff00) | |
+ snprint(msg, ERRMAX, "exit(%d)", (status>>8)&0xff); | |
+ } | |
+ return pid; | |
+} | |
+ | |
+void | |
+expunge(int pid, char *msg) | |
+{ | |
+ if(strcmp(msg, "interrupt")) | |
+ kill(pid, SIGINT); | |
+ else | |
+ kill(pid, SIGHUP); | |
+} | |
+ | |
+int | |
+execsh(char *args, char *cmd, Bufblock *buf, Envy *e) | |
+{ | |
+ char *p; | |
+ int tot, n, pid, in[2], out[2]; | |
+ | |
+ if(buf && pipe(out) < 0){ | |
+ mkperror("pipe"); | |
+ Exit(); | |
+ } | |
+ pid = fork(); | |
+ if(pid < 0){ | |
+ mkperror("mk fork"); | |
+ Exit(); | |
+ } | |
+ if(pid == 0){ | |
+ if(buf) | |
+ close(out[0]); | |
+ if(pipe(in) < 0){ | |
+ mkperror("pipe"); | |
+ Exit(); | |
+ } | |
+ pid = fork(); | |
+ if(pid < 0){ | |
+ mkperror("mk fork"); | |
+ Exit(); | |
+ } | |
+ if(pid != 0){ | |
+ dup2(in[0], 0); | |
+ if(buf){ | |
+ dup2(out[1], 1); | |
+ close(out[1]); | |
+ } | |
+ close(in[0]); | |
+ close(in[1]); | |
+ if (e) | |
+ exportenv(e); | |
+ if(shflags) | |
+ execl(shell, shellname, shflags, args, 0); | |
+ else | |
+ execl(shell, shellname, args, 0); | |
+ mkperror(shell); | |
+ _exit(1); | |
+ } | |
+ close(out[1]); | |
+ close(in[0]); | |
+ if(DEBUG(D_EXEC)) | |
+ fprint(1, "starting: %s\n", cmd); | |
+ p = cmd+strlen(cmd); | |
+ while(cmd < p){ | |
+ n = write(in[1], cmd, p-cmd); | |
+ if(n < 0) | |
+ break; | |
+ cmd += n; | |
+ } | |
+ close(in[1]); | |
+ _exit(0); | |
+ } | |
+ if(buf){ | |
+ close(out[1]); | |
+ tot = 0; | |
+ for(;;){ | |
+ if (buf->current >= buf->end) | |
+ growbuf(buf); | |
+ n = read(out[0], buf->current, buf->end-buf->current); | |
+ if(n <= 0) | |
+ break; | |
+ buf->current += n; | |
+ tot += n; | |
+ } | |
+ if (tot && buf->current[-1] == '\n') | |
+ buf->current--; | |
+ close(out[0]); | |
+ } | |
+ return pid; | |
+} | |
+ | |
+int | |
+pipecmd(char *cmd, Envy *e, int *fd) | |
+{ | |
+ int pid, pfd[2]; | |
+ | |
+ if(DEBUG(D_EXEC)) | |
+ fprint(1, "pipecmd='%s'\n", cmd);/**/ | |
+ | |
+ if(fd && pipe(pfd) < 0){ | |
+ mkperror("pipe"); | |
+ Exit(); | |
+ } | |
+ pid = fork(); | |
+ if(pid < 0){ | |
+ mkperror("mk fork"); | |
+ Exit(); | |
+ } | |
+ if(pid == 0){ | |
+ if(fd){ | |
+ close(pfd[0]); | |
+ dup2(pfd[1], 1); | |
+ close(pfd[1]); | |
+ } | |
+ if(e) | |
+ exportenv(e); | |
+ if(shflags) | |
+ execl(shell, shellname, shflags, "-c", cmd, 0); | |
+ else | |
+ execl(shell, shellname, "-c", cmd, 0); | |
+ mkperror(shell); | |
+ _exit(1); | |
+ } | |
+ if(fd){ | |
+ close(pfd[1]); | |
+ *fd = pfd[0]; | |
+ } | |
+ return pid; | |
+} | |
+ | |
+void | |
+Exit(void) | |
+{ | |
+ while(wait(0) >= 0) | |
+ ; | |
+ exits("error"); | |
+} | |
+ | |
+static struct | |
+{ | |
+ int sig; | |
+ char *msg; | |
+} sigmsgs[] = | |
+{ | |
+ SIGALRM, "alarm", | |
+ SIGFPE, "sys: fp: fptrap", | |
+ SIGPIPE, "sys: write on closed pipe", | |
+ SIGILL, "sys: trap: illegal instruction", | |
+ SIGSEGV, "sys: segmentation violation", | |
+ 0, 0 | |
+}; | |
+ | |
+static void | |
+notifyf(int sig) | |
+{ | |
+ int i; | |
+ | |
+ for(i = 0; sigmsgs[i].msg; i++) | |
+ if(sigmsgs[i].sig == sig) | |
+ killchildren(sigmsgs[i].msg); | |
+ | |
+ /* should never happen */ | |
+ signal(sig, SIG_DFL); | |
+ kill(getpid(), sig); | |
+} | |
+ | |
+void | |
+catchnotes() | |
+{ | |
+ int i; | |
+ | |
+ for(i = 0; sigmsgs[i].msg; i++) | |
+ signal(sigmsgs[i].sig, notifyf); | |
+} | |
+ | |
+char* | |
+maketmp(int *pfd) | |
+{ | |
+ static char temp[] = "/tmp/mkargXXXXXX"; | |
+ static char buf[100]; | |
+ int fd; | |
+ | |
+ strcpy(buf, temp); | |
+ fd = mkstemp(buf); | |
+ if(fd < 0) | |
+ return 0; | |
+ *pfd = fd; | |
+ return buf; | |
+} | |
+ | |
+int | |
+chgtime(char *name) | |
+{ | |
+ if(access(name, 0) >= 0) | |
+ return utimes(name, 0); | |
+ return close(creat(name, 0666)); | |
+} | |
+ | |
+void | |
+rcopy(char **to, Resub *match, int n) | |
+{ | |
+ int c; | |
+ char *p; | |
+ | |
+ *to = match->s.sp; /* stem0 matches complete target */ | |
+ for(to++, match++; --n > 0; to++, match++){ | |
+ if(match->s.sp && match->e.ep){ | |
+ p = match->e.ep; | |
+ c = *p; | |
+ *p = 0; | |
+ *to = strdup(match->s.sp); | |
+ *p = c; | |
+ } | |
+ else | |
+ *to = 0; | |
+ } | |
+} | |
+ | |
+ulong | |
+mkmtime(char *name) | |
+{ | |
+ struct stat st; | |
+ | |
+ if(stat(name, &st) < 0) | |
+ return 0; | |
+ | |
+ return st.st_mtime; | |
+} | |
diff --git a/src/cmd/mk/var.c b/src/cmd/mk/var.c | |
t@@ -0,0 +1,41 @@ | |
+#include "mk.h" | |
+ | |
+void | |
+setvar(char *name, void *value) | |
+{ | |
+ symlook(name, S_VAR, value)->value = value; | |
+ symlook(name, S_MAKEVAR, (void*)""); | |
+} | |
+ | |
+static void | |
+print1(Symtab *s) | |
+{ | |
+ Word *w; | |
+ | |
+ Bprint(&bout, "\t%s=", s->name); | |
+ for (w = (Word *) s->value; w; w = w->next) | |
+ Bprint(&bout, "'%s'", w->s); | |
+ Bprint(&bout, "\n"); | |
+} | |
+ | |
+void | |
+dumpv(char *s) | |
+{ | |
+ Bprint(&bout, "%s:\n", s); | |
+ symtraverse(S_VAR, print1); | |
+} | |
+ | |
+char * | |
+shname(char *a) | |
+{ | |
+ Rune r; | |
+ int n; | |
+ | |
+ while (*a) { | |
+ n = chartorune(&r, a); | |
+ if (!WORDCHR(r)) | |
+ break; | |
+ a += n; | |
+ } | |
+ return a; | |
+} | |
diff --git a/src/cmd/mk/varsub.c b/src/cmd/mk/varsub.c | |
t@@ -0,0 +1,256 @@ | |
+#include "mk.h" | |
+ | |
+static Word *subsub(Word*, char*, char*); | |
+static Word *expandvar(char**); | |
+static Bufblock *varname(char**); | |
+static Word *extractpat(char*, char**, char*, char*); | |
+static int submatch(char*, Word*, Word*, int*, char**); | |
+static Word *varmatch(char *, char**); | |
+ | |
+Word * | |
+varsub(char **s) | |
+{ | |
+ Bufblock *b; | |
+ Word *w; | |
+ | |
+ if(**s == '{') /* either ${name} or ${name: A%B==C%D}*/ | |
+ return expandvar(s); | |
+ | |
+ b = varname(s); | |
+ if(b == 0) | |
+ return 0; | |
+ | |
+ w = varmatch(b->start, s); | |
+ freebuf(b); | |
+ return w; | |
+} | |
+ | |
+/* | |
+ * extract a variable name | |
+ */ | |
+static Bufblock* | |
+varname(char **s) | |
+{ | |
+ Bufblock *b; | |
+ char *cp; | |
+ Rune r; | |
+ int n; | |
+ | |
+ b = newbuf(); | |
+ cp = *s; | |
+ for(;;){ | |
+ n = chartorune(&r, cp); | |
+ if (!WORDCHR(r)) | |
+ break; | |
+ rinsert(b, r); | |
+ cp += n; | |
+ } | |
+ if (b->current == b->start){ | |
+ SYNERR(-1); | |
+ fprint(2, "missing variable name <%s>\n", *s); | |
+ freebuf(b); | |
+ return 0; | |
+ } | |
+ *s = cp; | |
+ insert(b, 0); | |
+ return b; | |
+} | |
+ | |
+static Word* | |
+varmatch(char *name, char **s) | |
+{ | |
+ Word *w; | |
+ Symtab *sym; | |
+ char *cp; | |
+ | |
+ sym = symlook(name, S_VAR, 0); | |
+ if(sym){ | |
+ /* check for at least one non-NULL value */ | |
+ for (w = (Word*)sym->value; w; w = w->next) | |
+ if(w->s && *w->s) | |
+ return wdup(w); | |
+ } | |
+ for(cp = *s; *cp == ' ' || *cp == '\t'; cp++) /* skip trailing … | |
+ ; | |
+ *s = cp; | |
+ return 0; | |
+} | |
+ | |
+static Word* | |
+expandvar(char **s) | |
+{ | |
+ Word *w; | |
+ Bufblock *buf; | |
+ Symtab *sym; | |
+ char *cp, *begin, *end; | |
+ | |
+ begin = *s; | |
+ (*s)++; /* skip the '{'… | |
+ buf = varname(s); | |
+ if (buf == 0) | |
+ return 0; | |
+ cp = *s; | |
+ if (*cp == '}') { /* ${name} variant*/ | |
+ (*s)++; /* skip the '}'… | |
+ w = varmatch(buf->start, s); | |
+ freebuf(buf); | |
+ return w; | |
+ } | |
+ if (*cp != ':') { | |
+ SYNERR(-1); | |
+ fprint(2, "bad variable name <%s>\n", buf->start); | |
+ freebuf(buf); | |
+ return 0; | |
+ } | |
+ cp++; | |
+ end = charin(cp , "}"); | |
+ if(end == 0){ | |
+ SYNERR(-1); | |
+ fprint(2, "missing '}': %s\n", begin); | |
+ Exit(); | |
+ } | |
+ *end = 0; | |
+ *s = end+1; | |
+ | |
+ sym = symlook(buf->start, S_VAR, 0); | |
+ if(sym == 0 || sym->value == 0) | |
+ w = newword(buf->start); | |
+ else | |
+ w = subsub((Word*) sym->value, cp, end); | |
+ freebuf(buf); | |
+ return w; | |
+} | |
+ | |
+static Word* | |
+extractpat(char *s, char **r, char *term, char *end) | |
+{ | |
+ int save; | |
+ char *cp; | |
+ Word *w; | |
+ | |
+ cp = charin(s, term); | |
+ if(cp){ | |
+ *r = cp; | |
+ if(cp == s) | |
+ return 0; | |
+ save = *cp; | |
+ *cp = 0; | |
+ w = stow(s); | |
+ *cp = save; | |
+ } else { | |
+ *r = end; | |
+ w = stow(s); | |
+ } | |
+ return w; | |
+} | |
+ | |
+static Word* | |
+subsub(Word *v, char *s, char *end) | |
+{ | |
+ int nmid; | |
+ Word *head, *tail, *w, *h; | |
+ Word *a, *b, *c, *d; | |
+ Bufblock *buf; | |
+ char *cp, *enda; | |
+ | |
+ a = extractpat(s, &cp, "=%&", end); | |
+ b = c = d = 0; | |
+ if(PERCENT(*cp)) | |
+ b = extractpat(cp+1, &cp, "=", end); | |
+ if(*cp == '=') | |
+ c = extractpat(cp+1, &cp, "&%", end); | |
+ if(PERCENT(*cp)) | |
+ d = stow(cp+1); | |
+ else if(*cp) | |
+ d = stow(cp); | |
+ | |
+ head = tail = 0; | |
+ buf = newbuf(); | |
+ for(; v; v = v->next){ | |
+ h = w = 0; | |
+ if(submatch(v->s, a, b, &nmid, &enda)){ | |
+ /* enda points to end of A match in source; | |
+ * nmid = number of chars between end of A and start o… | |
+ */ | |
+ if(c){ | |
+ h = w = wdup(c); | |
+ while(w->next) | |
+ w = w->next; | |
+ } | |
+ if(PERCENT(*cp) && nmid > 0){ | |
+ if(w){ | |
+ bufcpy(buf, w->s, strlen(w->s)); | |
+ bufcpy(buf, enda, nmid); | |
+ insert(buf, 0); | |
+ free(w->s); | |
+ w->s = strdup(buf->start); | |
+ } else { | |
+ bufcpy(buf, enda, nmid); | |
+ insert(buf, 0); | |
+ h = w = newword(buf->start); | |
+ } | |
+ buf->current = buf->start; | |
+ } | |
+ if(d && *d->s){ | |
+ if(w){ | |
+ | |
+ bufcpy(buf, w->s, strlen(w->s)); | |
+ bufcpy(buf, d->s, strlen(d->s)); | |
+ insert(buf, 0); | |
+ free(w->s); | |
+ w->s = strdup(buf->start); | |
+ w->next = wdup(d->next); | |
+ while(w->next) | |
+ w = w->next; | |
+ buf->current = buf->start; | |
+ } else | |
+ h = w = wdup(d); | |
+ } | |
+ } | |
+ if(w == 0) | |
+ h = w = newword(v->s); | |
+ | |
+ if(head == 0) | |
+ head = h; | |
+ else | |
+ tail->next = h; | |
+ tail = w; | |
+ } | |
+ freebuf(buf); | |
+ delword(a); | |
+ delword(b); | |
+ delword(c); | |
+ delword(d); | |
+ return head; | |
+} | |
+ | |
+static int | |
+submatch(char *s, Word *a, Word *b, int *nmid, char **enda) | |
+{ | |
+ Word *w; | |
+ int n; | |
+ char *end; | |
+ | |
+ n = 0; | |
+ for(w = a; w; w = w->next){ | |
+ n = strlen(w->s); | |
+ if(strncmp(s, w->s, n) == 0) | |
+ break; | |
+ } | |
+ if(a && w == 0) /* a == NULL matches everything*/ | |
+ return 0; | |
+ | |
+ *enda = s+n; /* pointer to end a A part match */ | |
+ *nmid = strlen(s)-n; /* size of remainder of source */ | |
+ end = *enda+*nmid; | |
+ for(w = b; w; w = w->next){ | |
+ n = strlen(w->s); | |
+ if(strcmp(w->s, end-n) == 0){ | |
+ *nmid -= n; | |
+ break; | |
+ } | |
+ } | |
+ if(b && w == 0) /* b == NULL matches everything */ | |
+ return 0; | |
+ return 1; | |
+} | |
diff --git a/src/cmd/mk/word.c b/src/cmd/mk/word.c | |
t@@ -0,0 +1,180 @@ | |
+#include "mk.h" | |
+ | |
+static Word *nextword(char**); | |
+ | |
+Word* | |
+newword(char *s) | |
+{ | |
+ Word *w; | |
+ | |
+ w = (Word *)Malloc(sizeof(Word)); | |
+ w->s = strdup(s); | |
+ w->next = 0; | |
+ return(w); | |
+} | |
+ | |
+Word * | |
+stow(char *s) | |
+{ | |
+ Word *head, *w, *new; | |
+ | |
+ w = head = 0; | |
+ while(*s){ | |
+ new = nextword(&s); | |
+ if(new == 0) | |
+ break; | |
+ if (w) | |
+ w->next = new; | |
+ else | |
+ head = w = new; | |
+ while(w->next) | |
+ w = w->next; | |
+ | |
+ } | |
+ if (!head) | |
+ head = newword(""); | |
+ return(head); | |
+} | |
+ | |
+char * | |
+wtos(Word *w, int sep) | |
+{ | |
+ Bufblock *buf; | |
+ char *cp; | |
+ | |
+ buf = newbuf(); | |
+ for(; w; w = w->next){ | |
+ for(cp = w->s; *cp; cp++) | |
+ insert(buf, *cp); | |
+ if(w->next) | |
+ insert(buf, sep); | |
+ } | |
+ insert(buf, 0); | |
+ cp = strdup(buf->start); | |
+ freebuf(buf); | |
+ return(cp); | |
+} | |
+ | |
+Word* | |
+wdup(Word *w) | |
+{ | |
+ Word *v, *new, *base; | |
+ | |
+ v = base = 0; | |
+ while(w){ | |
+ new = newword(w->s); | |
+ if(v) | |
+ v->next = new; | |
+ else | |
+ base = new; | |
+ v = new; | |
+ w = w->next; | |
+ } | |
+ return base; | |
+} | |
+ | |
+void | |
+delword(Word *w) | |
+{ | |
+ Word *v; | |
+ | |
+ while(v = w){ | |
+ w = w->next; | |
+ if(v->s) | |
+ free(v->s); | |
+ free(v); | |
+ } | |
+} | |
+ | |
+/* | |
+ * break out a word from a string handling quotes, executions, | |
+ * and variable expansions. | |
+ */ | |
+static Word* | |
+nextword(char **s) | |
+{ | |
+ Bufblock *b; | |
+ Word *head, *tail, *w; | |
+ Rune r; | |
+ char *cp; | |
+ | |
+ cp = *s; | |
+ b = newbuf(); | |
+ head = tail = 0; | |
+ while(*cp == ' ' || *cp == '\t') /* leading white space… | |
+ cp++; | |
+ while(*cp){ | |
+ cp += chartorune(&r, cp); | |
+ switch(r) | |
+ { | |
+ case ' ': | |
+ case '\t': | |
+ case '\n': | |
+ goto out; | |
+ case '\\': | |
+ case '\'': | |
+ case '"': | |
+ cp = expandquote(cp, r, b); | |
+ if(cp == 0){ | |
+ fprint(2, "missing closing quote: %s\n", *s); | |
+ Exit(); | |
+ } | |
+ break; | |
+ case '$': | |
+ w = varsub(&cp); | |
+ if(w == 0) | |
+ break; | |
+ if(b->current != b->start){ | |
+ bufcpy(b, w->s, strlen(w->s)); | |
+ insert(b, 0); | |
+ free(w->s); | |
+ w->s = strdup(b->start); | |
+ b->current = b->start; | |
+ } | |
+ if(head){ | |
+ bufcpy(b, tail->s, strlen(tail->s)); | |
+ bufcpy(b, w->s, strlen(w->s)); | |
+ insert(b, 0); | |
+ free(tail->s); | |
+ tail->s = strdup(b->start); | |
+ tail->next = w->next; | |
+ free(w->s); | |
+ free(w); | |
+ b->current = b->start; | |
+ } else | |
+ tail = head = w; | |
+ while(tail->next) | |
+ tail = tail->next; | |
+ break; | |
+ default: | |
+ rinsert(b, r); | |
+ break; | |
+ } | |
+ } | |
+out: | |
+ *s = cp; | |
+ if(b->current != b->start){ | |
+ if(head){ | |
+ cp = b->current; | |
+ bufcpy(b, tail->s, strlen(tail->s)); | |
+ bufcpy(b, b->start, cp-b->start); | |
+ insert(b, 0); | |
+ free(tail->s); | |
+ tail->s = strdup(cp); | |
+ } else { | |
+ insert(b, 0); | |
+ head = newword(b->start); | |
+ } | |
+ } | |
+ freebuf(b); | |
+ return head; | |
+} | |
+ | |
+void | |
+dumpw(char *s, Word *w) | |
+{ | |
+ Bprint(&bout, "%s", s); | |
+ for(; w; w = w->next) | |
+ Bprint(&bout, " '%s'", w->s); | |
+ Bputc(&bout, '\n'); | |
+} | |
diff --git a/src/cmd/sam/LICENSE b/src/cmd/sam/LICENSE | |
t@@ -0,0 +1,258 @@ | |
+The Plan 9 software is provided under the terms of the | |
+Lucent Public License, Version 1.02, reproduced below, | |
+with the following exceptions: | |
+ | |
+1. No right is granted to create derivative works of or | |
+ to redistribute (other than with the Plan 9 Operating System) | |
+ the screen imprinter fonts identified in subdirectory | |
+ /lib/font/bit/lucida and printer fonts (Lucida Sans Unicode, Lucida | |
+ Sans Italic, Lucida Sans Demibold, Lucida Typewriter, Lucida Sans | |
+ Typewriter83), identified in subdirectory /sys/lib/postscript/font. | |
+ These directories contain material copyrights by B&H Inc. and Y&Y Inc. | |
+ | |
+2. The printer fonts identified in subdirectory /sys/lib/ghostscript/font | |
+ are subject to the GNU GPL, reproduced in the file /LICENSE.gpl. | |
+ | |
+3. The ghostscript program in the subdirectory /sys/src/cmd/gs is | |
+ covered by the Aladdin Free Public License, reproduced in the file | |
+ /LICENSE.afpl. | |
+ | |
+=================================================================== | |
+ | |
+Lucent Public License Version 1.02 | |
+ | |
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC | |
+LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE | |
+PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. | |
+ | |
+1. DEFINITIONS | |
+ | |
+"Contribution" means: | |
+ | |
+ a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original | |
+ Program, and | |
+ b. in the case of each Contributor, | |
+ | |
+ i. changes to the Program, and | |
+ ii. additions to the Program; | |
+ | |
+ where such changes and/or additions to the Program were added to the | |
+ Program by such Contributor itself or anyone acting on such | |
+ Contributor's behalf, and the Contributor explicitly consents, in | |
+ accordance with Section 3C, to characterization of the changes and/or | |
+ additions as Contributions. | |
+ | |
+"Contributor" means LUCENT and any other entity that has Contributed a | |
+Contribution to the Program. | |
+ | |
+"Distributor" means a Recipient that distributes the Program, | |
+modifications to the Program, or any part thereof. | |
+ | |
+"Licensed Patents" mean patent claims licensable by a Contributor | |
+which are necessarily infringed by the use or sale of its Contribution | |
+alone or when combined with the Program. | |
+ | |
+"Original Program" means the original version of the software | |
+accompanying this Agreement as released by LUCENT, including source | |
+code, object code and documentation, if any. | |
+ | |
+"Program" means the Original Program and Contributions or any part | |
+thereof | |
+ | |
+"Recipient" means anyone who receives the Program under this | |
+Agreement, including all Contributors. | |
+ | |
+2. GRANT OF RIGHTS | |
+ | |
+ a. Subject to the terms of this Agreement, each Contributor hereby | |
+ grants Recipient a non-exclusive, worldwide, royalty-free copyright | |
+ license to reproduce, prepare derivative works of, publicly display, | |
+ publicly perform, distribute and sublicense the Contribution of such | |
+ Contributor, if any, and such derivative works, in source code and | |
+ object code form. | |
+ | |
+ b. Subject to the terms of this Agreement, each Contributor hereby | |
+ grants Recipient a non-exclusive, worldwide, royalty-free patent | |
+ license under Licensed Patents to make, use, sell, offer to sell, | |
+ import and otherwise transfer the Contribution of such Contributor, if | |
+ any, in source code and object code form. The patent license granted | |
+ by a Contributor shall also apply to the combination of the | |
+ Contribution of that Contributor and the Program if, at the time the | |
+ Contribution is added by the Contributor, such addition of the | |
+ Contribution causes such combination to be covered by the Licensed | |
+ Patents. The patent license granted by a Contributor shall not apply | |
+ to (i) any other combinations which include the Contribution, nor to | |
+ (ii) Contributions of other Contributors. No hardware per se is | |
+ licensed hereunder. | |
+ | |
+ c. Recipient understands that although each Contributor grants the | |
+ licenses to its Contributions set forth herein, no assurances are | |
+ provided by any Contributor that the Program does not infringe the | |
+ patent or other intellectual property rights of any other entity. Each | |
+ Contributor disclaims any liability to Recipient for claims brought by | |
+ any other entity based on infringement of intellectual property rights | |
+ or otherwise. As a condition to exercising the rights and licenses | |
+ granted hereunder, each Recipient hereby assumes sole responsibility | |
+ to secure any other intellectual property rights needed, if any. For | |
+ example, if a third party patent license is required to allow | |
+ Recipient to distribute the Program, it is Recipient's responsibility | |
+ to acquire that license before distributing the Program. | |
+ | |
+ d. Each Contributor represents that to its knowledge it has sufficient | |
+ copyright rights in its Contribution, if any, to grant the copyright | |
+ license set forth in this Agreement. | |
+ | |
+3. REQUIREMENTS | |
+ | |
+A. Distributor may choose to distribute the Program in any form under | |
+this Agreement or under its own license agreement, provided that: | |
+ | |
+ a. it complies with the terms and conditions of this Agreement; | |
+ | |
+ b. if the Program is distributed in source code or other tangible | |
+ form, a copy of this Agreement or Distributor's own license agreement | |
+ is included with each copy of the Program; and | |
+ | |
+ c. if distributed under Distributor's own license agreement, such | |
+ license agreement: | |
+ | |
+ i. effectively disclaims on behalf of all Contributors all warranties | |
+ and conditions, express and implied, including warranties or | |
+ conditions of title and non-infringement, and implied warranties or | |
+ conditions of merchantability and fitness for a particular purpose; | |
+ ii. effectively excludes on behalf of all Contributors all liability | |
+ for damages, including direct, indirect, special, incidental and | |
+ consequential damages, such as lost profits; and | |
+ iii. states that any provisions which differ from this Agreement are | |
+ offered by that Contributor alone and not by any other party. | |
+ | |
+B. Each Distributor must include the following in a conspicuous | |
+ location in the Program: | |
+ | |
+ Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights | |
+ Reserved. | |
+ | |
+C. In addition, each Contributor must identify itself as the | |
+originator of its Contribution in a manner that reasonably allows | |
+subsequent Recipients to identify the originator of the Contribution. | |
+Also, each Contributor must agree that the additions and/or changes | |
+are intended to be a Contribution. Once a Contribution is contributed, | |
+it may not thereafter be revoked. | |
+ | |
+4. COMMERCIAL DISTRIBUTION | |
+ | |
+Commercial distributors of software may accept certain | |
+responsibilities with respect to end users, business partners and the | |
+like. While this license is intended to facilitate the commercial use | |
+of the Program, the Distributor who includes the Program in a | |
+commercial product offering should do so in a manner which does not | |
+create potential liability for Contributors. Therefore, if a | |
+Distributor includes the Program in a commercial product offering, | |
+such Distributor ("Commercial Distributor") hereby agrees to defend | |
+and indemnify every Contributor ("Indemnified Contributor") against | |
+any losses, damages and costs (collectively"Losses") arising from | |
+claims, lawsuits and other legal actions brought by a third party | |
+against the Indemnified Contributor to the extent caused by the acts | |
+or omissions of such Commercial Distributor in connection with its | |
+distribution of the Program in a commercial product offering. The | |
+obligations in this section do not apply to any claims or Losses | |
+relating to any actual or alleged intellectual property infringement. | |
+In order to qualify, an Indemnified Contributor must: a) promptly | |
+notify the Commercial Distributor in writing of such claim, and b) | |
+allow the Commercial Distributor to control, and cooperate with the | |
+Commercial Distributor in, the defense and any related settlement | |
+negotiations. The Indemnified Contributor may participate in any such | |
+claim at its own expense. | |
+ | |
+For example, a Distributor might include the Program in a commercial | |
+product offering, Product X. That Distributor is then a Commercial | |
+Distributor. If that Commercial Distributor then makes performance | |
+claims, or offers warranties related to Product X, those performance | |
+claims and warranties are such Commercial Distributor's responsibility | |
+alone. Under this section, the Commercial Distributor would have to | |
+defend claims against the Contributors related to those performance | |
+claims and warranties, and if a court requires any Contributor to pay | |
+any damages as a result, the Commercial Distributor must pay those | |
+damages. | |
+ | |
+5. NO WARRANTY | |
+ | |
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS | |
+PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
+KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY | |
+WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY | |
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely | |
+responsible for determining the appropriateness of using and | |
+distributing the Program and assumes all risks associated with its | |
+exercise of rights under this Agreement, including but not limited to | |
+the risks and costs of program errors, compliance with applicable | |
+laws, damage to or loss of data, programs or equipment, and | |
+unavailability or interruption of operations. | |
+ | |
+6. DISCLAIMER OF LIABILITY | |
+ | |
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR | |
+ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, | |
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING | |
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF | |
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR | |
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED | |
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
+ | |
+7. EXPORT CONTROL | |
+ | |
+Recipient agrees that Recipient alone is responsible for compliance | |
+with the United States export administration regulations (and the | |
+export control laws and regulation of any other countries). | |
+ | |
+8. GENERAL | |
+ | |
+If any provision of this Agreement is invalid or unenforceable under | |
+applicable law, it shall not affect the validity or enforceability of | |
+the remainder of the terms of this Agreement, and without further | |
+action by the parties hereto, such provision shall be reformed to the | |
+minimum extent necessary to make such provision valid and enforceable. | |
+ | |
+If Recipient institutes patent litigation against a Contributor with | |
+respect to a patent applicable to software (including a cross-claim or | |
+counterclaim in a lawsuit), then any patent licenses granted by that | |
+Contributor to such Recipient under this Agreement shall terminate as | |
+of the date such litigation is filed. In addition, if Recipient | |
+institutes patent litigation against any entity (including a | |
+cross-claim or counterclaim in a lawsuit) alleging that the Program | |
+itself (excluding combinations of the Program with other software or | |
+hardware) infringes such Recipient's patent(s), then such Recipient's | |
+rights granted under Section 2(b) shall terminate as of the date such | |
+litigation is filed. | |
+ | |
+All Recipient's rights under this Agreement shall terminate if it | |
+fails to comply with any of the material terms or conditions of this | |
+Agreement and does not cure such failure in a reasonable period of | |
+time after becoming aware of such noncompliance. If all Recipient's | |
+rights under this Agreement terminate, Recipient agrees to cease use | |
+and distribution of the Program as soon as reasonably practicable. | |
+However, Recipient's obligations under this Agreement and any licenses | |
+granted by Recipient relating to the Program shall continue and | |
+survive. | |
+ | |
+LUCENT may publish new versions (including revisions) of this | |
+Agreement from time to time. Each new version of the Agreement will be | |
+given a distinguishing version number. The Program (including | |
+Contributions) may always be distributed subject to the version of the | |
+Agreement under which it was received. In addition, after a new | |
+version of the Agreement is published, Contributor may elect to | |
+distribute the Program (including its Contributions) under the new | |
+version. No one other than LUCENT has the right to modify this | |
+Agreement. Except as expressly stated in Sections 2(a) and 2(b) above, | |
+Recipient receives no rights or licenses to the intellectual property | |
+of any Contributor under this Agreement, whether expressly, by | |
+implication, estoppel or otherwise. All rights in the Program not | |
+expressly granted under this Agreement are reserved. | |
+ | |
+This Agreement is governed by the laws of the State of New York and | |
+the intellectual property laws of the United States of America. No | |
+party to this Agreement will bring a legal action under this Agreement | |
+more than one year after the cause of action arose. Each party waives | |
+its rights to a jury trial in any resulting litigation. | |
+ | |
diff --git a/src/cmd/sam/Makefile b/src/cmd/sam/Makefile | |
t@@ -0,0 +1,18 @@ | |
+H=errors.h mesg.h parse.h plumb.h sam.h | |
+SRC= address.c buff.c cmd.c disk.c error.c file.c io.c\ | |
+ list.c mesg.c moveto.c multi.c unix.c rasp.c regexp.c\ | |
+ sam.c shell.c string.c sys.c util.c xec.c plumb.c | |
+ | |
+CC=gcc | |
+PREFIX=$(HOME) | |
+#PREFIX=/usr/local | |
+CFLAGS=-I. -I$(PREFIX)/include -O -g | |
+LDFLAGS=-L$(PREFIX)/lib | |
+LDLIBS=-l9 -lfmt -lutf | |
+ | |
+all: sam | |
+sam: $(SRC) $(H) | |
+ $(CC) -o $@ $(CFLAGS) $(SRC) $(LDFLAGS) $(LDLIBS) | |
+clean: | |
+ rm -f *.o *~ | |
+ rm -f sam | |
diff --git a/src/cmd/sam/address.c b/src/cmd/sam/address.c | |
t@@ -0,0 +1,240 @@ | |
+#include "sam.h" | |
+#include "parse.h" | |
+ | |
+Address addr; | |
+String lastpat; | |
+int patset; | |
+File *menu; | |
+ | |
+File *matchfile(String*); | |
+Address charaddr(Posn, Address, int); | |
+ | |
+Address | |
+address(Addr *ap, Address a, int sign) | |
+{ | |
+ File *f = a.f; | |
+ Address a1, a2; | |
+ | |
+ do{ | |
+ switch(ap->type){ | |
+ case 'l': | |
+ case '#': | |
+ a = (*(ap->type=='#'?charaddr:lineaddr))(ap->num, a, s… | |
+ break; | |
+ | |
+ case '.': | |
+ a = f->dot; | |
+ break; | |
+ | |
+ case '$': | |
+ a.r.p1 = a.r.p2 = f->_.nc; | |
+ break; | |
+ | |
+ case '\'': | |
+ a.r = f->mark; | |
+ break; | |
+ | |
+ case '?': | |
+ sign = -sign; | |
+ if(sign == 0) | |
+ sign = -1; | |
+ /* fall through */ | |
+ case '/': | |
+ nextmatch(f, ap->are, sign>=0? a.r.p2 : a.r.p1, sign); | |
+ a.r = sel.p[0]; | |
+ break; | |
+ | |
+ case '"': | |
+ a = matchfile(ap->are)->dot; | |
+ f = a.f; | |
+ if(f->unread) | |
+ load(f); | |
+ break; | |
+ | |
+ case '*': | |
+ a.r.p1 = 0, a.r.p2 = f->_.nc; | |
+ return a; | |
+ | |
+ case ',': | |
+ case ';': | |
+ if(ap->left) | |
+ a1 = address(ap->left, a, 0); | |
+ else | |
+ a1.f = a.f, a1.r.p1 = a1.r.p2 = 0; | |
+ if(ap->type == ';'){ | |
+ f = a1.f; | |
+ a = a1; | |
+ f->dot = a1; | |
+ } | |
+ if(ap->next) | |
+ a2 = address(ap->next, a, 0); | |
+ else | |
+ a2.f = a.f, a2.r.p1 = a2.r.p2 = f->_.nc; | |
+ if(a1.f != a2.f) | |
+ error(Eorder); | |
+ a.f = a1.f, a.r.p1 = a1.r.p1, a.r.p2 = a2.r.p2; | |
+ if(a.r.p2 < a.r.p1) | |
+ error(Eorder); | |
+ return a; | |
+ | |
+ case '+': | |
+ case '-': | |
+ sign = 1; | |
+ if(ap->type == '-') | |
+ sign = -1; | |
+ if(ap->next==0 || ap->next->type=='+' || ap->next->typ… | |
+ a = lineaddr(1L, a, sign); | |
+ break; | |
+ default: | |
+ panic("address"); | |
+ return a; | |
+ } | |
+ }while(ap = ap->next); /* assign = */ | |
+ return a; | |
+} | |
+ | |
+void | |
+nextmatch(File *f, String *r, Posn p, int sign) | |
+{ | |
+ compile(r); | |
+ if(sign >= 0){ | |
+ if(!execute(f, p, INFINITY)) | |
+ error(Esearch); | |
+ if(sel.p[0].p1==sel.p[0].p2 && sel.p[0].p1==p){ | |
+ if(++p>f->_.nc) | |
+ p = 0; | |
+ if(!execute(f, p, INFINITY)) | |
+ panic("address"); | |
+ } | |
+ }else{ | |
+ if(!bexecute(f, p)) | |
+ error(Esearch); | |
+ if(sel.p[0].p1==sel.p[0].p2 && sel.p[0].p2==p){ | |
+ if(--p<0) | |
+ p = f->_.nc; | |
+ if(!bexecute(f, p)) | |
+ panic("address"); | |
+ } | |
+ } | |
+} | |
+ | |
+File * | |
+matchfile(String *r) | |
+{ | |
+ File *f; | |
+ File *match = 0; | |
+ int i; | |
+ | |
+ for(i = 0; i<file.nused; i++){ | |
+ f = file.filepptr[i]; | |
+ if(f == cmd) | |
+ continue; | |
+ if(filematch(f, r)){ | |
+ if(match) | |
+ error(Emanyfiles); | |
+ match = f; | |
+ } | |
+ } | |
+ if(!match) | |
+ error(Efsearch); | |
+ return match; | |
+} | |
+ | |
+int | |
+filematch(File *f, String *r) | |
+{ | |
+ char *c, buf[STRSIZE+100]; | |
+ String *t; | |
+ | |
+ c = Strtoc(&f->name); | |
+ sprint(buf, "%c%c%c %s\n", " '"[f->mod], | |
+ "-+"[f->rasp!=0], " ."[f==curfile], c); | |
+ free(c); | |
+ t = tmpcstr(buf); | |
+ Strduplstr(&genstr, t); | |
+ freetmpstr(t); | |
+ /* A little dirty... */ | |
+ if(menu == 0) | |
+ menu = fileopen(); | |
+ bufreset(menu); | |
+ bufinsert(menu, 0, genstr.s, genstr.n); | |
+ compile(r); | |
+ return execute(menu, 0, menu->_.nc); | |
+} | |
+ | |
+Address | |
+charaddr(Posn l, Address addr, int sign) | |
+{ | |
+ if(sign == 0) | |
+ addr.r.p1 = addr.r.p2 = l; | |
+ else if(sign < 0) | |
+ addr.r.p2 = addr.r.p1-=l; | |
+ else if(sign > 0) | |
+ addr.r.p1 = addr.r.p2+=l; | |
+ if(addr.r.p1<0 || addr.r.p2>addr.f->_.nc) | |
+ error(Erange); | |
+ return addr; | |
+} | |
+ | |
+Address | |
+lineaddr(Posn l, Address addr, int sign) | |
+{ | |
+ int n; | |
+ int c; | |
+ File *f = addr.f; | |
+ Address a; | |
+ Posn p; | |
+ | |
+ a.f = f; | |
+ if(sign >= 0){ | |
+ if(l == 0){ | |
+ if(sign==0 || addr.r.p2==0){ | |
+ a.r.p1 = a.r.p2 = 0; | |
+ return a; | |
+ } | |
+ a.r.p1 = addr.r.p2; | |
+ p = addr.r.p2-1; | |
+ }else{ | |
+ if(sign==0 || addr.r.p2==0){ | |
+ p = (Posn)0; | |
+ n = 1; | |
+ }else{ | |
+ p = addr.r.p2-1; | |
+ n = filereadc(f, p++)=='\n'; | |
+ } | |
+ while(n < l){ | |
+ if(p >= f->_.nc) | |
+ error(Erange); | |
+ if(filereadc(f, p++) == '\n') | |
+ n++; | |
+ } | |
+ a.r.p1 = p; | |
+ } | |
+ while(p < f->_.nc && filereadc(f, p++)!='\n') | |
+ ; | |
+ a.r.p2 = p; | |
+ }else{ | |
+ p = addr.r.p1; | |
+ if(l == 0) | |
+ a.r.p2 = addr.r.p1; | |
+ else{ | |
+ for(n = 0; n<l; ){ /* always runs once */ | |
+ if(p == 0){ | |
+ if(++n != l) | |
+ error(Erange); | |
+ }else{ | |
+ c = filereadc(f, p-1); | |
+ if(c != '\n' || ++n != l) | |
+ p--; | |
+ } | |
+ } | |
+ a.r.p2 = p; | |
+ if(p > 0) | |
+ p--; | |
+ } | |
+ while(p > 0 && filereadc(f, p-1)!='\n') /* lines start … | |
+ p--; | |
+ a.r.p1 = p; | |
+ } | |
+ return a; | |
+} | |
diff --git a/src/cmd/sam/buff.c b/src/cmd/sam/buff.c | |
t@@ -0,0 +1,302 @@ | |
+#include "sam.h" | |
+ | |
+enum | |
+{ | |
+ Slop = 100, /* room to grow with reallocation */ | |
+}; | |
+ | |
+static | |
+void | |
+sizecache(Buffer *b, uint n) | |
+{ | |
+ if(n <= b->cmax) | |
+ return; | |
+ b->cmax = n+Slop; | |
+ b->c = runerealloc(b->c, b->cmax); | |
+} | |
+ | |
+static | |
+void | |
+addblock(Buffer *b, uint i, uint n) | |
+{ | |
+ if(i > b->nbl) | |
+ panic("internal error: addblock"); | |
+ | |
+ b->bl = realloc(b->bl, (b->nbl+1)*sizeof b->bl[0]); | |
+ if(i < b->nbl) | |
+ memmove(b->bl+i+1, b->bl+i, (b->nbl-i)*sizeof(Block*)); | |
+ b->bl[i] = disknewblock(disk, n); | |
+ b->nbl++; | |
+} | |
+ | |
+ | |
+static | |
+void | |
+delblock(Buffer *b, uint i) | |
+{ | |
+ if(i >= b->nbl) | |
+ panic("internal error: delblock"); | |
+ | |
+ diskrelease(disk, b->bl[i]); | |
+ b->nbl--; | |
+ if(i < b->nbl) | |
+ memmove(b->bl+i, b->bl+i+1, (b->nbl-i)*sizeof(Block*)); | |
+ b->bl = realloc(b->bl, b->nbl*sizeof b->bl[0]); | |
+} | |
+ | |
+/* | |
+ * Move cache so b->cq <= q0 < b->cq+b->cnc. | |
+ * If at very end, q0 will fall on end of cache block. | |
+ */ | |
+ | |
+static | |
+void | |
+flush(Buffer *b) | |
+{ | |
+ if(b->cdirty || b->cnc==0){ | |
+ if(b->cnc == 0) | |
+ delblock(b, b->cbi); | |
+ else | |
+ diskwrite(disk, &b->bl[b->cbi], b->c, b->cnc); | |
+ b->cdirty = FALSE; | |
+ } | |
+} | |
+ | |
+static | |
+void | |
+setcache(Buffer *b, uint q0) | |
+{ | |
+ Block **blp, *bl; | |
+ uint i, q; | |
+ | |
+ if(q0 > b->nc) | |
+ panic("internal error: setcache"); | |
+ /* | |
+ * flush and reload if q0 is not in cache. | |
+ */ | |
+ if(b->nc == 0 || (b->cq<=q0 && q0<b->cq+b->cnc)) | |
+ return; | |
+ /* | |
+ * if q0 is at end of file and end of cache, continue to grow this blo… | |
+ */ | |
+ if(q0==b->nc && q0==b->cq+b->cnc && b->cnc<=Maxblock) | |
+ return; | |
+ flush(b); | |
+ /* find block */ | |
+ if(q0 < b->cq){ | |
+ q = 0; | |
+ i = 0; | |
+ }else{ | |
+ q = b->cq; | |
+ i = b->cbi; | |
+ } | |
+ blp = &b->bl[i]; | |
+ while(q+(*blp)->_.n <= q0 && q+(*blp)->_.n < b->nc){ | |
+ q += (*blp)->_.n; | |
+ i++; | |
+ blp++; | |
+ if(i >= b->nbl) | |
+ panic("block not found"); | |
+ } | |
+ bl = *blp; | |
+ /* remember position */ | |
+ b->cbi = i; | |
+ b->cq = q; | |
+ sizecache(b, bl->_.n); | |
+ b->cnc = bl->_.n; | |
+ /*read block*/ | |
+ diskread(disk, bl, b->c, b->cnc); | |
+} | |
+ | |
+void | |
+bufinsert(Buffer *b, uint q0, Rune *s, uint n) | |
+{ | |
+ uint i, m, t, off; | |
+ | |
+ if(q0 > b->nc) | |
+ panic("internal error: bufinsert"); | |
+ | |
+ while(n > 0){ | |
+ setcache(b, q0); | |
+ off = q0-b->cq; | |
+ if(b->cnc+n <= Maxblock){ | |
+ /* Everything fits in one block. */ | |
+ t = b->cnc+n; | |
+ m = n; | |
+ if(b->bl == nil){ /* allocate */ | |
+ if(b->cnc != 0) | |
+ panic("internal error: bufinsert1 cnc!… | |
+ addblock(b, 0, t); | |
+ b->cbi = 0; | |
+ } | |
+ sizecache(b, t); | |
+ runemove(b->c+off+m, b->c+off, b->cnc-off); | |
+ runemove(b->c+off, s, m); | |
+ b->cnc = t; | |
+ goto Tail; | |
+ } | |
+ /* | |
+ * We must make a new block. If q0 is at | |
+ * the very beginning or end of this block, | |
+ * just make a new block and fill it. | |
+ */ | |
+ if(q0==b->cq || q0==b->cq+b->cnc){ | |
+ if(b->cdirty) | |
+ flush(b); | |
+ m = min(n, Maxblock); | |
+ if(b->bl == nil){ /* allocate */ | |
+ if(b->cnc != 0) | |
+ panic("internal error: bufinsert2 cnc!… | |
+ i = 0; | |
+ }else{ | |
+ i = b->cbi; | |
+ if(q0 > b->cq) | |
+ i++; | |
+ } | |
+ addblock(b, i, m); | |
+ sizecache(b, m); | |
+ runemove(b->c, s, m); | |
+ b->cq = q0; | |
+ b->cbi = i; | |
+ b->cnc = m; | |
+ goto Tail; | |
+ } | |
+ /* | |
+ * Split the block; cut off the right side and | |
+ * let go of it. | |
+ */ | |
+ m = b->cnc-off; | |
+ if(m > 0){ | |
+ i = b->cbi+1; | |
+ addblock(b, i, m); | |
+ diskwrite(disk, &b->bl[i], b->c+off, m); | |
+ b->cnc -= m; | |
+ } | |
+ /* | |
+ * Now at end of block. Take as much input | |
+ * as possible and tack it on end of block. | |
+ */ | |
+ m = min(n, Maxblock-b->cnc); | |
+ sizecache(b, b->cnc+m); | |
+ runemove(b->c+b->cnc, s, m); | |
+ b->cnc += m; | |
+ Tail: | |
+ b->nc += m; | |
+ q0 += m; | |
+ s += m; | |
+ n -= m; | |
+ b->cdirty = TRUE; | |
+ } | |
+} | |
+ | |
+void | |
+bufdelete(Buffer *b, uint q0, uint q1) | |
+{ | |
+ uint m, n, off; | |
+ | |
+ if(!(q0<=q1 && q0<=b->nc && q1<=b->nc)) | |
+ panic("internal error: bufdelete"); | |
+ while(q1 > q0){ | |
+ setcache(b, q0); | |
+ off = q0-b->cq; | |
+ if(q1 > b->cq+b->cnc) | |
+ n = b->cnc - off; | |
+ else | |
+ n = q1-q0; | |
+ m = b->cnc - (off+n); | |
+ if(m > 0) | |
+ runemove(b->c+off, b->c+off+n, m); | |
+ b->cnc -= n; | |
+ b->cdirty = TRUE; | |
+ q1 -= n; | |
+ b->nc -= n; | |
+ } | |
+} | |
+ | |
+uint | |
+bufload(Buffer *b, uint q0, int fd, int *nulls) | |
+{ | |
+ char *p; | |
+ Rune *r; | |
+ int l, m, n, nb, nr; | |
+ uint q1; | |
+ | |
+ if(q0 > b->nc) | |
+ panic("internal error: bufload"); | |
+ p = malloc((Maxblock+UTFmax+1)*sizeof p[0]); | |
+ if(p == nil) | |
+ panic("bufload: malloc failed"); | |
+ r = runemalloc(Maxblock); | |
+ m = 0; | |
+ n = 1; | |
+ q1 = q0; | |
+ /* | |
+ * At top of loop, may have m bytes left over from | |
+ * last pass, possibly representing a partial rune. | |
+ */ | |
+ while(n > 0){ | |
+ n = read(fd, p+m, Maxblock); | |
+ if(n < 0){ | |
+ error(Ebufload); | |
+ break; | |
+ } | |
+ m += n; | |
+ p[m] = 0; | |
+ l = m; | |
+ if(n > 0) | |
+ l -= UTFmax; | |
+ cvttorunes(p, l, r, &nb, &nr, nulls); | |
+ memmove(p, p+nb, m-nb); | |
+ m -= nb; | |
+ bufinsert(b, q1, r, nr); | |
+ q1 += nr; | |
+ } | |
+ free(p); | |
+ free(r); | |
+ return q1-q0; | |
+} | |
+ | |
+void | |
+bufread(Buffer *b, uint q0, Rune *s, uint n) | |
+{ | |
+ uint m; | |
+ | |
+ if(!(q0<=b->nc && q0+n<=b->nc)) | |
+ panic("bufread: internal error"); | |
+ | |
+ while(n > 0){ | |
+ setcache(b, q0); | |
+ m = min(n, b->cnc-(q0-b->cq)); | |
+ runemove(s, b->c+(q0-b->cq), m); | |
+ q0 += m; | |
+ s += m; | |
+ n -= m; | |
+ } | |
+} | |
+ | |
+void | |
+bufreset(Buffer *b) | |
+{ | |
+ int i; | |
+ | |
+ b->nc = 0; | |
+ b->cnc = 0; | |
+ b->cq = 0; | |
+ b->cdirty = 0; | |
+ b->cbi = 0; | |
+ /* delete backwards to avoid n² behavior */ | |
+ for(i=b->nbl-1; --i>=0; ) | |
+ delblock(b, i); | |
+} | |
+ | |
+void | |
+bufclose(Buffer *b) | |
+{ | |
+ bufreset(b); | |
+ free(b->c); | |
+ b->c = nil; | |
+ b->cnc = 0; | |
+ free(b->bl); | |
+ b->bl = nil; | |
+ b->nbl = 0; | |
+} | |
diff --git a/src/cmd/sam/cmd.c b/src/cmd/sam/cmd.c | |
t@@ -0,0 +1,594 @@ | |
+#include "sam.h" | |
+#include "parse.h" | |
+ | |
+static char linex[]="\n"; | |
+static char wordx[]=" \t\n"; | |
+struct cmdtab cmdtab[]={ | |
+/* cmdc text regexp addr defcmd defa… | |
+ '\n', 0, 0, 0, 0, aDot, 0, … | |
+ 'a', 1, 0, 0, 0, aDot, 0, … | |
+ 'b', 0, 0, 0, 0, aNo, 0, … | |
+ 'B', 0, 0, 0, 0, aNo, 0, … | |
+ 'c', 1, 0, 0, 0, aDot, 0, … | |
+ 'd', 0, 0, 0, 0, aDot, 0, … | |
+ 'D', 0, 0, 0, 0, aNo, 0, … | |
+ 'e', 0, 0, 0, 0, aNo, 0, … | |
+ 'f', 0, 0, 0, 0, aNo, 0, … | |
+ 'g', 0, 1, 0, 'p', aDot, 0, … | |
+ 'i', 1, 0, 0, 0, aDot, 0, … | |
+ 'k', 0, 0, 0, 0, aDot, 0, … | |
+ 'm', 0, 0, 1, 0, aDot, 0, … | |
+ 'n', 0, 0, 0, 0, aNo, 0, … | |
+ 'p', 0, 0, 0, 0, aDot, 0, … | |
+ 'q', 0, 0, 0, 0, aNo, 0, … | |
+ 'r', 0, 0, 0, 0, aDot, 0, … | |
+ 's', 0, 1, 0, 0, aDot, 1, … | |
+ 't', 0, 0, 1, 0, aDot, 0, … | |
+ 'u', 0, 0, 0, 0, aNo, 2, … | |
+ 'v', 0, 1, 0, 'p', aDot, 0, … | |
+ 'w', 0, 0, 0, 0, aAll, 0, … | |
+ 'x', 0, 1, 0, 'p', aDot, 0, … | |
+ 'y', 0, 1, 0, 'p', aDot, 0, … | |
+ 'X', 0, 1, 0, 'f', aNo, 0, … | |
+ 'Y', 0, 1, 0, 'f', aNo, 0, … | |
+ '!', 0, 0, 0, 0, aNo, 0, … | |
+ '>', 0, 0, 0, 0, aDot, 0, … | |
+ '<', 0, 0, 0, 0, aDot, 0, … | |
+ '|', 0, 0, 0, 0, aDot, 0, … | |
+ '=', 0, 0, 0, 0, aDot, 0, … | |
+ 'c'|0x100,0, 0, 0, 0, aNo, 0, … | |
+ 0, 0, 0, 0, 0, 0, 0, … | |
+}; | |
+Cmd *parsecmd(int); | |
+Addr *compoundaddr(void); | |
+Addr *simpleaddr(void); | |
+void freecmd(void); | |
+void okdelim(int); | |
+ | |
+Rune line[BLOCKSIZE]; | |
+Rune termline[BLOCKSIZE]; | |
+Rune *linep = line; | |
+Rune *terminp = termline; | |
+Rune *termoutp = termline; | |
+List cmdlist; | |
+List addrlist; | |
+List relist; | |
+List stringlist; | |
+int eof; | |
+ | |
+void | |
+resetcmd(void) | |
+{ | |
+ linep = line; | |
+ *linep = 0; | |
+ terminp = termoutp = termline; | |
+ freecmd(); | |
+} | |
+ | |
+int | |
+inputc(void) | |
+{ | |
+ int n, nbuf; | |
+ char buf[3]; | |
+ Rune r; | |
+ | |
+ Again: | |
+ nbuf = 0; | |
+ if(downloaded){ | |
+ while(termoutp == terminp){ | |
+ cmdupdate(); | |
+ if(patset) | |
+ tellpat(); | |
+ while(termlocked > 0){ | |
+ outT0(Hunlock); | |
+ termlocked--; | |
+ } | |
+ if(rcv() == 0) | |
+ return -1; | |
+ } | |
+ r = *termoutp++; | |
+ if(termoutp == terminp) | |
+ terminp = termoutp = termline; | |
+ }else{ | |
+ do{ | |
+ n = read(0, buf+nbuf, 1); | |
+ if(n <= 0) | |
+ return -1; | |
+ nbuf += n; | |
+ }while(!fullrune(buf, nbuf)); | |
+ chartorune(&r, buf); | |
+ } | |
+ if(r == 0){ | |
+ warn(Wnulls); | |
+ goto Again; | |
+ } | |
+ return r; | |
+} | |
+ | |
+int | |
+inputline(void) | |
+{ | |
+ int i, c; | |
+ | |
+ linep = line; | |
+ i = 0; | |
+ do{ | |
+ if((c = inputc())<=0) | |
+ return -1; | |
+ if(i == (sizeof line)/RUNESIZE-1) | |
+ error(Etoolong); | |
+ }while((line[i++]=c) != '\n'); | |
+ line[i] = 0; | |
+ return 1; | |
+} | |
+ | |
+int | |
+getch(void) | |
+{ | |
+ if(eof) | |
+ return -1; | |
+ if(*linep==0 && inputline()<0){ | |
+ eof = TRUE; | |
+ return -1; | |
+ } | |
+ return *linep++; | |
+} | |
+ | |
+int | |
+nextc(void) | |
+{ | |
+ if(*linep == 0) | |
+ return -1; | |
+ return *linep; | |
+} | |
+ | |
+void | |
+ungetch(void) | |
+{ | |
+ if(--linep < line) | |
+ panic("ungetch"); | |
+} | |
+ | |
+Posn | |
+getnum(int signok) | |
+{ | |
+ Posn n=0; | |
+ int c, sign; | |
+ | |
+ sign = 1; | |
+ if(signok>1 && nextc()=='-'){ | |
+ sign = -1; | |
+ getch(); | |
+ } | |
+ if((c=nextc())<'0' || '9'<c) /* no number defaults to 1 */ | |
+ return sign; | |
+ while('0'<=(c=getch()) && c<='9') | |
+ n = n*10 + (c-'0'); | |
+ ungetch(); | |
+ return sign*n; | |
+} | |
+ | |
+int | |
+skipbl(void) | |
+{ | |
+ int c; | |
+ do | |
+ c = getch(); | |
+ while(c==' ' || c=='\t'); | |
+ if(c >= 0) | |
+ ungetch(); | |
+ return c; | |
+} | |
+ | |
+void | |
+termcommand(void) | |
+{ | |
+ Posn p; | |
+ | |
+ for(p=cmdpt; p<cmd->_.nc; p++){ | |
+ if(terminp >= &termline[BLOCKSIZE]){ | |
+ cmdpt = cmd->_.nc; | |
+ error(Etoolong); | |
+ } | |
+ *terminp++ = filereadc(cmd, p); | |
+ } | |
+ cmdpt = cmd->_.nc; | |
+} | |
+ | |
+void | |
+cmdloop(void) | |
+{ | |
+ Cmd *cmdp; | |
+ File *ocurfile; | |
+ int loaded; | |
+ | |
+ for(;;){ | |
+ if(!downloaded && curfile && curfile->unread) | |
+ load(curfile); | |
+ if((cmdp = parsecmd(0))==0){ | |
+ if(downloaded){ | |
+ rescue(); | |
+ exits("eof"); | |
+ } | |
+ break; | |
+ } | |
+ ocurfile = curfile; | |
+ loaded = curfile && !curfile->unread; | |
+ if(cmdexec(curfile, cmdp) == 0) | |
+ break; | |
+ freecmd(); | |
+ cmdupdate(); | |
+ update(); | |
+ if(downloaded && curfile && | |
+ (ocurfile!=curfile || (!loaded && !curfile->unread))) | |
+ outTs(Hcurrent, curfile->tag); | |
+ /* don't allow type ahead on files that aren't bound */ | |
+ if(downloaded && curfile && curfile->rasp == 0) | |
+ terminp = termoutp; | |
+ } | |
+} | |
+ | |
+Cmd * | |
+newcmd(void){ | |
+ Cmd *p; | |
+ | |
+ p = emalloc(sizeof(Cmd)); | |
+ inslist(&cmdlist, cmdlist.nused, (long)p); | |
+ return p; | |
+} | |
+ | |
+Addr* | |
+newaddr(void) | |
+{ | |
+ Addr *p; | |
+ | |
+ p = emalloc(sizeof(Addr)); | |
+ inslist(&addrlist, addrlist.nused, (long)p); | |
+ return p; | |
+} | |
+ | |
+String* | |
+newre(void) | |
+{ | |
+ String *p; | |
+ | |
+ p = emalloc(sizeof(String)); | |
+ inslist(&relist, relist.nused, (long)p); | |
+ Strinit(p); | |
+ return p; | |
+} | |
+ | |
+String* | |
+newstring(void) | |
+{ | |
+ String *p; | |
+ | |
+ p = emalloc(sizeof(String)); | |
+ inslist(&stringlist, stringlist.nused, (long)p); | |
+ Strinit(p); | |
+ return p; | |
+} | |
+ | |
+void | |
+freecmd(void) | |
+{ | |
+ int i; | |
+ | |
+ while(cmdlist.nused > 0) | |
+ free(cmdlist.ucharpptr[--cmdlist.nused]); | |
+ while(addrlist.nused > 0) | |
+ free(addrlist.ucharpptr[--addrlist.nused]); | |
+ while(relist.nused > 0){ | |
+ i = --relist.nused; | |
+ Strclose(relist.stringpptr[i]); | |
+ free(relist.stringpptr[i]); | |
+ } | |
+ while(stringlist.nused>0){ | |
+ i = --stringlist.nused; | |
+ Strclose(stringlist.stringpptr[i]); | |
+ free(stringlist.stringpptr[i]); | |
+ } | |
+} | |
+ | |
+int | |
+lookup(int c) | |
+{ | |
+ int i; | |
+ | |
+ for(i=0; cmdtab[i].cmdc; i++) | |
+ if(cmdtab[i].cmdc == c) | |
+ return i; | |
+ return -1; | |
+} | |
+ | |
+void | |
+okdelim(int c) | |
+{ | |
+ if(c=='\\' || ('a'<=c && c<='z') | |
+ || ('A'<=c && c<='Z') || ('0'<=c && c<='9')) | |
+ error_c(Edelim, c); | |
+} | |
+ | |
+void | |
+atnl(void) | |
+{ | |
+ skipbl(); | |
+ if(getch() != '\n') | |
+ error(Enewline); | |
+} | |
+ | |
+void | |
+getrhs(String *s, int delim, int cmd) | |
+{ | |
+ int c; | |
+ | |
+ while((c = getch())>0 && c!=delim && c!='\n'){ | |
+ if(c == '\\'){ | |
+ if((c=getch()) <= 0) | |
+ error(Ebadrhs); | |
+ if(c == '\n'){ | |
+ ungetch(); | |
+ c='\\'; | |
+ }else if(c == 'n') | |
+ c='\n'; | |
+ else if(c!=delim && (cmd=='s' || c!='\\')) /* s… | |
+ Straddc(s, '\\'); | |
+ } | |
+ Straddc(s, c); | |
+ } | |
+ ungetch(); /* let client read whether delimeter, '\n' or whatev… | |
+} | |
+ | |
+String * | |
+collecttoken(char *end) | |
+{ | |
+ String *s = newstring(); | |
+ int c; | |
+ | |
+ while((c=nextc())==' ' || c=='\t') | |
+ Straddc(s, getch()); /* blanks significant for getname() */ | |
+ while((c=getch())>0 && utfrune(end, c)==0) | |
+ Straddc(s, c); | |
+ Straddc(s, 0); | |
+ if(c != '\n') | |
+ atnl(); | |
+ return s; | |
+} | |
+ | |
+String * | |
+collecttext(void) | |
+{ | |
+ String *s = newstring(); | |
+ int begline, i, c, delim; | |
+ | |
+ if(skipbl()=='\n'){ | |
+ getch(); | |
+ i = 0; | |
+ do{ | |
+ begline = i; | |
+ while((c = getch())>0 && c!='\n') | |
+ i++, Straddc(s, c); | |
+ i++, Straddc(s, '\n'); | |
+ if(c < 0) | |
+ goto Return; | |
+ }while(s->s[begline]!='.' || s->s[begline+1]!='\n'); | |
+ Strdelete(s, s->n-2, s->n); | |
+ }else{ | |
+ okdelim(delim = getch()); | |
+ getrhs(s, delim, 'a'); | |
+ if(nextc()==delim) | |
+ getch(); | |
+ atnl(); | |
+ } | |
+ Return: | |
+ Straddc(s, 0); /* JUST FOR CMDPRINT() */ | |
+ return s; | |
+} | |
+ | |
+Cmd * | |
+parsecmd(int nest) | |
+{ | |
+ int i, c; | |
+ struct cmdtab *ct; | |
+ Cmd *cp, *ncp; | |
+ Cmd cmd; | |
+ | |
+ cmd.next = cmd.ccmd = 0; | |
+ cmd.re = 0; | |
+ cmd.flag = cmd.num = 0; | |
+ cmd.addr = compoundaddr(); | |
+ if(skipbl() == -1) | |
+ return 0; | |
+ if((c=getch())==-1) | |
+ return 0; | |
+ cmd.cmdc = c; | |
+ if(cmd.cmdc=='c' && nextc()=='d'){ /* sleazy two-character case… | |
+ getch(); /* the 'd' */ | |
+ cmd.cmdc='c'|0x100; | |
+ } | |
+ i = lookup(cmd.cmdc); | |
+ if(i >= 0){ | |
+ if(cmd.cmdc == '\n') | |
+ goto Return; /* let nl_cmd work it all out */ | |
+ ct = &cmdtab[i]; | |
+ if(ct->defaddr==aNo && cmd.addr) | |
+ error(Enoaddr); | |
+ if(ct->count) | |
+ cmd.num = getnum(ct->count); | |
+ if(ct->regexp){ | |
+ /* x without pattern -> .*\n, indicated by cmd.re==0 */ | |
+ /* X without pattern is all files */ | |
+ if((ct->cmdc!='x' && ct->cmdc!='X') || | |
+ ((c = nextc())!=' ' && c!='\t' && c!='\n')){ | |
+ skipbl(); | |
+ if((c = getch())=='\n' || c<0) | |
+ error(Enopattern); | |
+ okdelim(c); | |
+ cmd.re = getregexp(c); | |
+ if(ct->cmdc == 's'){ | |
+ cmd.ctext = newstring(); | |
+ getrhs(cmd.ctext, c, 's'); | |
+ if(nextc() == c){ | |
+ getch(); | |
+ if(nextc() == 'g') | |
+ cmd.flag = getch(); | |
+ } | |
+ | |
+ } | |
+ } | |
+ } | |
+ if(ct->addr && (cmd.caddr=simpleaddr())==0) | |
+ error(Eaddress); | |
+ if(ct->defcmd){ | |
+ if(skipbl() == '\n'){ | |
+ getch(); | |
+ cmd.ccmd = newcmd(); | |
+ cmd.ccmd->cmdc = ct->defcmd; | |
+ }else if((cmd.ccmd = parsecmd(nest))==0) | |
+ panic("defcmd"); | |
+ }else if(ct->text) | |
+ cmd.ctext = collecttext(); | |
+ else if(ct->token) | |
+ cmd.ctext = collecttoken(ct->token); | |
+ else | |
+ atnl(); | |
+ }else | |
+ switch(cmd.cmdc){ | |
+ case '{': | |
+ cp = 0; | |
+ do{ | |
+ if(skipbl()=='\n') | |
+ getch(); | |
+ ncp = parsecmd(nest+1); | |
+ if(cp) | |
+ cp->next = ncp; | |
+ else | |
+ cmd.ccmd = ncp; | |
+ }while(cp = ncp); | |
+ break; | |
+ case '}': | |
+ atnl(); | |
+ if(nest==0) | |
+ error(Enolbrace); | |
+ return 0; | |
+ default: | |
+ error_c(Eunk, cmd.cmdc); | |
+ } | |
+ Return: | |
+ cp = newcmd(); | |
+ *cp = cmd; | |
+ return cp; | |
+} | |
+ | |
+String* /* BUGGERED */ | |
+getregexp(int delim) | |
+{ | |
+ String *r = newre(); | |
+ int c; | |
+ | |
+ for(Strzero(&genstr); ; Straddc(&genstr, c)) | |
+ if((c = getch())=='\\'){ | |
+ if(nextc()==delim) | |
+ c = getch(); | |
+ else if(nextc()=='\\'){ | |
+ Straddc(&genstr, c); | |
+ c = getch(); | |
+ } | |
+ }else if(c==delim || c=='\n') | |
+ break; | |
+ if(c!=delim && c) | |
+ ungetch(); | |
+ if(genstr.n > 0){ | |
+ patset = TRUE; | |
+ Strduplstr(&lastpat, &genstr); | |
+ Straddc(&lastpat, '\0'); | |
+ } | |
+ if(lastpat.n <= 1) | |
+ error(Epattern); | |
+ Strduplstr(r, &lastpat); | |
+ return r; | |
+} | |
+ | |
+Addr * | |
+simpleaddr(void) | |
+{ | |
+ Addr addr; | |
+ Addr *ap, *nap; | |
+ | |
+ addr.next = 0; | |
+ addr.left = 0; | |
+ switch(skipbl()){ | |
+ case '#': | |
+ addr.type = getch(); | |
+ addr.num = getnum(1); | |
+ break; | |
+ case '0': case '1': case '2': case '3': case '4': | |
+ case '5': case '6': case '7': case '8': case '9': | |
+ addr.num = getnum(1); | |
+ addr.type='l'; | |
+ break; | |
+ case '/': case '?': case '"': | |
+ addr.are = getregexp(addr.type = getch()); | |
+ break; | |
+ case '.': | |
+ case '$': | |
+ case '+': | |
+ case '-': | |
+ case '\'': | |
+ addr.type = getch(); | |
+ break; | |
+ default: | |
+ return 0; | |
+ } | |
+ if(addr.next = simpleaddr()) | |
+ switch(addr.next->type){ | |
+ case '.': | |
+ case '$': | |
+ case '\'': | |
+ if(addr.type!='"') | |
+ case '"': | |
+ error(Eaddress); | |
+ break; | |
+ case 'l': | |
+ case '#': | |
+ if(addr.type=='"') | |
+ break; | |
+ /* fall through */ | |
+ case '/': | |
+ case '?': | |
+ if(addr.type!='+' && addr.type!='-'){ | |
+ /* insert the missing '+' */ | |
+ nap = newaddr(); | |
+ nap->type='+'; | |
+ nap->next = addr.next; | |
+ addr.next = nap; | |
+ } | |
+ break; | |
+ case '+': | |
+ case '-': | |
+ break; | |
+ default: | |
+ panic("simpleaddr"); | |
+ } | |
+ ap = newaddr(); | |
+ *ap = addr; | |
+ return ap; | |
+} | |
+ | |
+Addr * | |
+compoundaddr(void) | |
+{ | |
+ Addr addr; | |
+ Addr *ap, *next; | |
+ | |
+ addr.left = simpleaddr(); | |
+ if((addr.type = skipbl())!=',' && addr.type!=';') | |
+ return addr.left; | |
+ getch(); | |
+ next = addr.next = compoundaddr(); | |
+ if(next && (next->type==',' || next->type==';') && next->left==0) | |
+ error(Eaddress); | |
+ ap = newaddr(); | |
+ *ap = addr; | |
+ return ap; | |
+} | |
diff --git a/src/cmd/sam/disk.c b/src/cmd/sam/disk.c | |
t@@ -0,0 +1,122 @@ | |
+#include "sam.h" | |
+ | |
+static Block *blist; | |
+ | |
+#if 0 | |
+static int | |
+tempdisk(void) | |
+{ | |
+ char buf[128]; | |
+ int i, fd; | |
+ | |
+ snprint(buf, sizeof buf, "/tmp/X%d.%.4ssam", getpid(), getuser()); | |
+ for(i='A'; i<='Z'; i++){ | |
+ buf[5] = i; | |
+ if(access(buf, AEXIST) == 0) | |
+ continue; | |
+ fd = create(buf, ORDWR|ORCLOSE|OCEXEC, 0600); | |
+ if(fd >= 0) | |
+ return fd; | |
+ } | |
+ return -1; | |
+} | |
+#else | |
+extern int tempdisk(void); | |
+#endif | |
+ | |
+Disk* | |
+diskinit() | |
+{ | |
+ Disk *d; | |
+ | |
+ d = emalloc(sizeof(Disk)); | |
+ d->fd = tempdisk(); | |
+ if(d->fd < 0){ | |
+ fprint(2, "sam: can't create temp file: %r\n"); | |
+ exits("diskinit"); | |
+ } | |
+ return d; | |
+} | |
+ | |
+static | |
+uint | |
+ntosize(uint n, uint *ip) | |
+{ | |
+ uint size; | |
+ | |
+ if(n > Maxblock) | |
+ panic("internal error: ntosize"); | |
+ size = n; | |
+ if(size & (Blockincr-1)) | |
+ size += Blockincr - (size & (Blockincr-1)); | |
+ /* last bucket holds blocks of exactly Maxblock */ | |
+ if(ip) | |
+ *ip = size/Blockincr; | |
+ return size * sizeof(Rune); | |
+} | |
+ | |
+Block* | |
+disknewblock(Disk *d, uint n) | |
+{ | |
+ uint i, j, size; | |
+ Block *b; | |
+ | |
+ size = ntosize(n, &i); | |
+ b = d->free[i]; | |
+ if(b) | |
+ d->free[i] = b->_.next; | |
+ else{ | |
+ /* allocate in chunks to reduce malloc overhead */ | |
+ if(blist == nil){ | |
+ blist = emalloc(100*sizeof(Block)); | |
+ for(j=0; j<100-1; j++) | |
+ blist[j]._.next = &blist[j+1]; | |
+ } | |
+ b = blist; | |
+ blist = b->_.next; | |
+ b->addr = d->addr; | |
+ d->addr += size; | |
+ } | |
+ b->_.n = n; | |
+ return b; | |
+} | |
+ | |
+void | |
+diskrelease(Disk *d, Block *b) | |
+{ | |
+ uint i; | |
+ | |
+ ntosize(b->_.n, &i); | |
+ b->_.next = d->free[i]; | |
+ d->free[i] = b; | |
+} | |
+ | |
+void | |
+diskwrite(Disk *d, Block **bp, Rune *r, uint n) | |
+{ | |
+ int size, nsize; | |
+ Block *b; | |
+ | |
+ b = *bp; | |
+ size = ntosize(b->_.n, nil); | |
+ nsize = ntosize(n, nil); | |
+ if(size != nsize){ | |
+ diskrelease(d, b); | |
+ b = disknewblock(d, n); | |
+ *bp = b; | |
+ } | |
+ if(pwrite(d->fd, r, n*sizeof(Rune), b->addr) != n*sizeof(Rune)) | |
+ panic("write error to temp file"); | |
+ b->_.n = n; | |
+} | |
+ | |
+void | |
+diskread(Disk *d, Block *b, Rune *r, uint n) | |
+{ | |
+ if(n > b->_.n) | |
+ panic("internal error: diskread"); | |
+ | |
+ ntosize(b->_.n, nil); /* called only for sanity check on Maxblo… | |
+ if(pread(d->fd, r, n*sizeof(Rune), b->addr) != n*sizeof(Rune)) | |
+ panic("read error from temp file"); | |
+} | |
diff --git a/src/cmd/sam/error.c b/src/cmd/sam/error.c | |
t@@ -0,0 +1,144 @@ | |
+#include "sam.h" | |
+ | |
+static char *emsg[]={ | |
+ /* error_s */ | |
+ "can't open", | |
+ "can't create", | |
+ "not in menu:", | |
+ "changes to", | |
+ "I/O error:", | |
+ "can't write while changing:", | |
+ /* error_c */ | |
+ "unknown command", | |
+ "no operand for", | |
+ "bad delimiter", | |
+ /* error */ | |
+ "can't fork", | |
+ "interrupt", | |
+ "address", | |
+ "search", | |
+ "pattern", | |
+ "newline expected", | |
+ "blank expected", | |
+ "pattern expected", | |
+ "can't nest X or Y", | |
+ "unmatched `}'", | |
+ "command takes no address", | |
+ "addresses overlap", | |
+ "substitution", | |
+ "& match too long", | |
+ "bad \\ in rhs", | |
+ "address range", | |
+ "changes not in sequence", | |
+ "addresses out of order", | |
+ "no file name", | |
+ "unmatched `('", | |
+ "unmatched `)'", | |
+ "malformed `[]'", | |
+ "malformed regexp", | |
+ "reg. exp. list overflow", | |
+ "plan 9 command", | |
+ "can't pipe", | |
+ "no current file", | |
+ "string too long", | |
+ "changed files", | |
+ "empty string", | |
+ "file search", | |
+ "non-unique match for \"\"", | |
+ "tag match too long", | |
+ "too many subexpressions", | |
+ "temporary file too large", | |
+ "file is append-only", | |
+ "no destination for plumb message", | |
+ "internal read error in buffer load", | |
+}; | |
+static char *wmsg[]={ | |
+ /* warn_s */ | |
+ "duplicate file name", | |
+ "no such file", | |
+ "write might change good version of", | |
+ /* warn_S */ | |
+ "files might be aliased", | |
+ /* warn */ | |
+ "null characters elided", | |
+ "can't run pwd", | |
+ "last char not newline", | |
+ "exit status not 0", | |
+}; | |
+ | |
+void | |
+error(Err s) | |
+{ | |
+ char buf[512]; | |
+ | |
+ sprint(buf, "?%s", emsg[s]); | |
+ hiccough(buf); | |
+} | |
+ | |
+void | |
+error_s(Err s, char *a) | |
+{ | |
+ char buf[512]; | |
+ | |
+ sprint(buf, "?%s \"%s\"", emsg[s], a); | |
+ hiccough(buf); | |
+} | |
+ | |
+void | |
+error_r(Err s, char *a) | |
+{ | |
+ char buf[512]; | |
+ | |
+ sprint(buf, "?%s \"%s\": %r", emsg[s], a); | |
+ hiccough(buf); | |
+} | |
+ | |
+void | |
+error_c(Err s, int c) | |
+{ | |
+ char buf[512]; | |
+ | |
+ sprint(buf, "?%s `%C'", emsg[s], c); | |
+ hiccough(buf); | |
+} | |
+ | |
+void | |
+warn(Warn s) | |
+{ | |
+ dprint("?warning: %s\n", wmsg[s]); | |
+} | |
+ | |
+void | |
+warn_S(Warn s, String *a) | |
+{ | |
+ print_s(wmsg[s], a); | |
+} | |
+ | |
+void | |
+warn_SS(Warn s, String *a, String *b) | |
+{ | |
+ print_ss(wmsg[s], a, b); | |
+} | |
+ | |
+void | |
+warn_s(Warn s, char *a) | |
+{ | |
+ dprint("?warning: %s `%s'\n", wmsg[s], a); | |
+} | |
+ | |
+void | |
+termwrite(char *s) | |
+{ | |
+ String *p; | |
+ | |
+ if(downloaded){ | |
+ p = tmpcstr(s); | |
+ if(cmd) | |
+ loginsert(cmd, cmdpt, p->s, p->n); | |
+ else | |
+ Strinsert(&cmdstr, p, cmdstr.n); | |
+ cmdptadv += p->n; | |
+ free(p); | |
+ }else | |
+ Write(2, s, strlen(s)); | |
+} | |
diff --git a/src/cmd/sam/errors.h b/src/cmd/sam/errors.h | |
t@@ -0,0 +1,65 @@ | |
+typedef enum Err{ | |
+ /* error_s */ | |
+ Eopen, | |
+ Ecreate, | |
+ Emenu, | |
+ Emodified, | |
+ Eio, | |
+ Ewseq, | |
+ /* error_c */ | |
+ Eunk, | |
+ Emissop, | |
+ Edelim, | |
+ /* error */ | |
+ Efork, | |
+ Eintr, | |
+ Eaddress, | |
+ Esearch, | |
+ Epattern, | |
+ Enewline, | |
+ Eblank, | |
+ Enopattern, | |
+ EnestXY, | |
+ Enolbrace, | |
+ Enoaddr, | |
+ Eoverlap, | |
+ Enosub, | |
+ Elongrhs, | |
+ Ebadrhs, | |
+ Erange, | |
+ Esequence, | |
+ Eorder, | |
+ Enoname, | |
+ Eleftpar, | |
+ Erightpar, | |
+ Ebadclass, | |
+ Ebadregexp, | |
+ Eoverflow, | |
+ Enocmd, | |
+ Epipe, | |
+ Enofile, | |
+ Etoolong, | |
+ Echanges, | |
+ Eempty, | |
+ Efsearch, | |
+ Emanyfiles, | |
+ Elongtag, | |
+ Esubexp, | |
+ Etmpovfl, | |
+ Eappend, | |
+ Ecantplumb, | |
+ Ebufload, | |
+}Err; | |
+typedef enum Warn{ | |
+ /* warn_s */ | |
+ Wdupname, | |
+ Wfile, | |
+ Wdate, | |
+ /* warn_ss */ | |
+ Wdupfile, | |
+ /* warn */ | |
+ Wnulls, | |
+ Wpwd, | |
+ Wnotnewline, | |
+ Wbadstatus, | |
+}Warn; | |
diff --git a/src/cmd/sam/file.c b/src/cmd/sam/file.c | |
t@@ -0,0 +1,631 @@ | |
+#include "sam.h" | |
+ | |
+/* | |
+ * Structure of Undo list: | |
+ * The Undo structure follows any associated data, so the list | |
+ * can be read backwards: read the structure, then read whatever | |
+ * data is associated (insert string, file name) and precedes it. | |
+ * The structure includes the previous value of the modify bit | |
+ * and a sequence number; successive Undo structures with the | |
+ * same sequence number represent simultaneous changes. | |
+ */ | |
+ | |
+typedef struct Undo Undo; | |
+typedef struct Merge Merge; | |
+ | |
+struct Undo | |
+{ | |
+ short type; /* Delete, Insert, Filename, Dot, Ma… | |
+ short mod; /* modify bit */ | |
+ uint seq; /* sequence number */ | |
+ uint p0; /* location of change (unused in f) */ | |
+ uint n; /* # runes in string or file name */ | |
+}; | |
+ | |
+struct Merge | |
+{ | |
+ File *f; | |
+ uint seq; /* of logged change */ | |
+ uint p0; /* location of change (unused in f) */ | |
+ uint n; /* # runes to delete */ | |
+ uint nbuf; /* # runes to insert */ | |
+ Rune buf[RBUFSIZE]; | |
+}; | |
+ | |
+enum | |
+{ | |
+ Maxmerge = 50, | |
+ Undosize = sizeof(Undo)/sizeof(Rune), | |
+}; | |
+ | |
+static Merge merge; | |
+ | |
+File* | |
+fileopen(void) | |
+{ | |
+ File *f; | |
+ | |
+ f = emalloc(sizeof(File)); | |
+ f->dot.f = f; | |
+ f->ndot.f = f; | |
+ f->seq = 0; | |
+ f->mod = FALSE; | |
+ f->unread = TRUE; | |
+ Strinit0(&f->name); | |
+ return f; | |
+} | |
+ | |
+int | |
+fileisdirty(File *f) | |
+{ | |
+ return f->seq != f->cleanseq; | |
+} | |
+ | |
+static void | |
+wrinsert(Buffer *delta, int seq, int mod, uint p0, Rune *s, uint ns) | |
+{ | |
+ Undo u; | |
+ | |
+ u.type = Insert; | |
+ u.mod = mod; | |
+ u.seq = seq; | |
+ u.p0 = p0; | |
+ u.n = ns; | |
+ bufinsert(delta, delta->nc, s, ns); | |
+ bufinsert(delta, delta->nc, (Rune*)&u, Undosize); | |
+} | |
+ | |
+static void | |
+wrdelete(Buffer *delta, int seq, int mod, uint p0, uint p1) | |
+{ | |
+ Undo u; | |
+ | |
+ u.type = Delete; | |
+ u.mod = mod; | |
+ u.seq = seq; | |
+ u.p0 = p0; | |
+ u.n = p1 - p0; | |
+ bufinsert(delta, delta->nc, (Rune*)&u, Undosize); | |
+} | |
+ | |
+void | |
+flushmerge(void) | |
+{ | |
+ File *f; | |
+ | |
+ f = merge.f; | |
+ if(f == nil) | |
+ return; | |
+ if(merge.seq != f->seq) | |
+ panic("flushmerge seq mismatch"); | |
+ if(merge.n != 0) | |
+ wrdelete(&f->epsilon, f->seq, TRUE, merge.p0, merge.p0+merge.n… | |
+ if(merge.nbuf != 0) | |
+ wrinsert(&f->epsilon, f->seq, TRUE, merge.p0+merge.n, merge.bu… | |
+ merge.f = nil; | |
+ merge.n = 0; | |
+ merge.nbuf = 0; | |
+} | |
+ | |
+void | |
+mergeextend(File *f, uint p0) | |
+{ | |
+ uint mp0n; | |
+ | |
+ mp0n = merge.p0+merge.n; | |
+ if(mp0n != p0){ | |
+ bufread(f, mp0n, merge.buf+merge.nbuf, p0-mp0n); | |
+ merge.nbuf += p0-mp0n; | |
+ merge.n = p0-merge.p0; | |
+ } | |
+} | |
+ | |
+/* | |
+ * like fileundelete, but get the data from arguments | |
+ */ | |
+void | |
+loginsert(File *f, uint p0, Rune *s, uint ns) | |
+{ | |
+ if(f->rescuing) | |
+ return; | |
+ if(ns == 0) | |
+ return; | |
+ if(ns<0 || ns>STRSIZE) | |
+ panic("loginsert"); | |
+ if(f->seq < seq) | |
+ filemark(f); | |
+ if(p0 < f->hiposn) | |
+ error(Esequence); | |
+ | |
+ if(merge.f != f | |
+ || p0-(merge.p0+merge.n)>Maxmerge /* too far */ | |
+ || merge.nbuf+((p0+ns)-(merge.p0+merge.n))>RBUFSIZE) /* too lon… | |
+ flushmerge(); | |
+ | |
+ if(ns>=RBUFSIZE){ | |
+ if(!(merge.n == 0 && merge.nbuf == 0 && merge.f == nil)) | |
+ panic("loginsert bad merge state"); | |
+ wrinsert(&f->epsilon, f->seq, TRUE, p0, s, ns); | |
+ }else{ | |
+ if(merge.f != f){ | |
+ merge.f = f; | |
+ merge.p0 = p0; | |
+ merge.seq = f->seq; | |
+ } | |
+ mergeextend(f, p0); | |
+ | |
+ /* append string to merge */ | |
+ runemove(merge.buf+merge.nbuf, s, ns); | |
+ merge.nbuf += ns; | |
+ } | |
+ | |
+ f->hiposn = p0; | |
+ if(!f->unread && !f->mod) | |
+ state(f, Dirty); | |
+} | |
+ | |
+void | |
+logdelete(File *f, uint p0, uint p1) | |
+{ | |
+ if(f->rescuing) | |
+ return; | |
+ if(p0 == p1) | |
+ return; | |
+ if(f->seq < seq) | |
+ filemark(f); | |
+ if(p0 < f->hiposn) | |
+ error(Esequence); | |
+ | |
+ if(merge.f != f | |
+ || p0-(merge.p0+merge.n)>Maxmerge /* too far */ | |
+ || merge.nbuf+(p0-(merge.p0+merge.n))>RBUFSIZE){ /* too long */ | |
+ flushmerge(); | |
+ merge.f = f; | |
+ merge.p0 = p0; | |
+ merge.seq = f->seq; | |
+ } | |
+ | |
+ mergeextend(f, p0); | |
+ | |
+ /* add to deletion */ | |
+ merge.n = p1-merge.p0; | |
+ | |
+ f->hiposn = p1; | |
+ if(!f->unread && !f->mod) | |
+ state(f, Dirty); | |
+} | |
+ | |
+/* | |
+ * like fileunsetname, but get the data from arguments | |
+ */ | |
+void | |
+logsetname(File *f, String *s) | |
+{ | |
+ Undo u; | |
+ Buffer *delta; | |
+ | |
+ if(f->rescuing) | |
+ return; | |
+ | |
+ if(f->unread){ /* This is setting initial file name */ | |
+ filesetname(f, s); | |
+ return; | |
+ } | |
+ | |
+ if(f->seq < seq) | |
+ filemark(f); | |
+ | |
+ /* undo a file name change by restoring old name */ | |
+ delta = &f->epsilon; | |
+ u.type = Filename; | |
+ u.mod = TRUE; | |
+ u.seq = f->seq; | |
+ u.p0 = 0; /* unused */ | |
+ u.n = s->n; | |
+ if(s->n) | |
+ bufinsert(delta, delta->nc, s->s, s->n); | |
+ bufinsert(delta, delta->nc, (Rune*)&u, Undosize); | |
+ if(!f->unread && !f->mod) | |
+ state(f, Dirty); | |
+} | |
+ | |
+#ifdef NOTEXT | |
+File* | |
+fileaddtext(File *f, Text *t) | |
+{ | |
+ if(f == nil){ | |
+ f = emalloc(sizeof(File)); | |
+ f->unread = TRUE; | |
+ } | |
+ f->text = realloc(f->text, (f->ntext+1)*sizeof(Text*)); | |
+ f->text[f->ntext++] = t; | |
+ f->curtext = t; | |
+ return f; | |
+} | |
+ | |
+void | |
+filedeltext(File *f, Text *t) | |
+{ | |
+ int i; | |
+ | |
+ for(i=0; i<f->ntext; i++) | |
+ if(f->text[i] == t) | |
+ goto Found; | |
+ panic("can't find text in filedeltext"); | |
+ | |
+ Found: | |
+ f->ntext--; | |
+ if(f->ntext == 0){ | |
+ fileclose(f); | |
+ return; | |
+ } | |
+ memmove(f->text+i, f->text+i+1, (f->ntext-i)*sizeof(Text*)); | |
+ if(f->curtext == t) | |
+ f->curtext = f->text[0]; | |
+} | |
+#endif | |
+ | |
+void | |
+fileinsert(File *f, uint p0, Rune *s, uint ns) | |
+{ | |
+ if(p0 > f->_.nc) | |
+ panic("internal error: fileinsert"); | |
+ if(f->seq > 0) | |
+ fileuninsert(f, &f->delta, p0, ns); | |
+ bufinsert(f, p0, s, ns); | |
+ if(ns) | |
+ f->mod = TRUE; | |
+} | |
+ | |
+void | |
+fileuninsert(File *f, Buffer *delta, uint p0, uint ns) | |
+{ | |
+ Undo u; | |
+ | |
+ /* undo an insertion by deleting */ | |
+ u.type = Delete; | |
+ u.mod = f->mod; | |
+ u.seq = f->seq; | |
+ u.p0 = p0; | |
+ u.n = ns; | |
+ bufinsert(delta, delta->nc, (Rune*)&u, Undosize); | |
+} | |
+ | |
+void | |
+filedelete(File *f, uint p0, uint p1) | |
+{ | |
+ if(!(p0<=p1 && p0<=f->_.nc && p1<=f->_.nc)) | |
+ panic("internal error: filedelete"); | |
+ if(f->seq > 0) | |
+ fileundelete(f, &f->delta, p0, p1); | |
+ bufdelete(f, p0, p1); | |
+ if(p1 > p0) | |
+ f->mod = TRUE; | |
+} | |
+ | |
+void | |
+fileundelete(File *f, Buffer *delta, uint p0, uint p1) | |
+{ | |
+ Undo u; | |
+ Rune *buf; | |
+ uint i, n; | |
+ | |
+ /* undo a deletion by inserting */ | |
+ u.type = Insert; | |
+ u.mod = f->mod; | |
+ u.seq = f->seq; | |
+ u.p0 = p0; | |
+ u.n = p1-p0; | |
+ buf = fbufalloc(); | |
+ for(i=p0; i<p1; i+=n){ | |
+ n = p1 - i; | |
+ if(n > RBUFSIZE) | |
+ n = RBUFSIZE; | |
+ bufread(f, i, buf, n); | |
+ bufinsert(delta, delta->nc, buf, n); | |
+ } | |
+ fbuffree(buf); | |
+ bufinsert(delta, delta->nc, (Rune*)&u, Undosize); | |
+ | |
+} | |
+ | |
+int | |
+filereadc(File *f, uint q) | |
+{ | |
+ Rune r; | |
+ | |
+ if(q >= f->_.nc) | |
+ return -1; | |
+ bufread(f, q, &r, 1); | |
+ return r; | |
+} | |
+ | |
+void | |
+filesetname(File *f, String *s) | |
+{ | |
+ if(!f->unread) /* This is setting initial file name */ | |
+ fileunsetname(f, &f->delta); | |
+ Strduplstr(&f->name, s); | |
+ sortname(f); | |
+ f->unread = TRUE; | |
+} | |
+ | |
+void | |
+fileunsetname(File *f, Buffer *delta) | |
+{ | |
+ String s; | |
+ Undo u; | |
+ | |
+ /* undo a file name change by restoring old name */ | |
+ u.type = Filename; | |
+ u.mod = f->mod; | |
+ u.seq = f->seq; | |
+ u.p0 = 0; /* unused */ | |
+ Strinit(&s); | |
+ Strduplstr(&s, &f->name); | |
+ fullname(&s); | |
+ u.n = s.n; | |
+ if(s.n) | |
+ bufinsert(delta, delta->nc, s.s, s.n); | |
+ bufinsert(delta, delta->nc, (Rune*)&u, Undosize); | |
+ Strclose(&s); | |
+} | |
+ | |
+void | |
+fileunsetdot(File *f, Buffer *delta, Range dot) | |
+{ | |
+ Undo u; | |
+ | |
+ u.type = Dot; | |
+ u.mod = f->mod; | |
+ u.seq = f->seq; | |
+ u.p0 = dot.p1; | |
+ u.n = dot.p2 - dot.p1; | |
+ bufinsert(delta, delta->nc, (Rune*)&u, Undosize); | |
+} | |
+ | |
+void | |
+fileunsetmark(File *f, Buffer *delta, Range mark) | |
+{ | |
+ Undo u; | |
+ | |
+ u.type = Mark; | |
+ u.mod = f->mod; | |
+ u.seq = f->seq; | |
+ u.p0 = mark.p1; | |
+ u.n = mark.p2 - mark.p1; | |
+ bufinsert(delta, delta->nc, (Rune*)&u, Undosize); | |
+} | |
+ | |
+uint | |
+fileload(File *f, uint p0, int fd, int *nulls) | |
+{ | |
+ if(f->seq > 0) | |
+ panic("undo in file.load unimplemented"); | |
+ return bufload(f, p0, fd, nulls); | |
+} | |
+ | |
+int | |
+fileupdate(File *f, int notrans, int toterm) | |
+{ | |
+ uint p1, p2; | |
+ int mod; | |
+ | |
+ if(f->rescuing) | |
+ return FALSE; | |
+ | |
+ flushmerge(); | |
+ | |
+ /* | |
+ * fix the modification bit | |
+ * subtle point: don't save it away in the log. | |
+ * | |
+ * if another change is made, the correct f->mod | |
+ * state is saved in the undo log by filemark | |
+ * when setting the dot and mark. | |
+ * | |
+ * if the change is undone, the correct state is | |
+ * saved from f in the fileun... routines. | |
+ */ | |
+ mod = f->mod; | |
+ f->mod = f->prevmod; | |
+ if(f == cmd) | |
+ notrans = TRUE; | |
+ else{ | |
+ fileunsetdot(f, &f->delta, f->prevdot); | |
+ fileunsetmark(f, &f->delta, f->prevmark); | |
+ } | |
+ f->dot = f->ndot; | |
+ fileundo(f, FALSE, !notrans, &p1, &p2, toterm); | |
+ f->mod = mod; | |
+ | |
+ if(f->delta.nc == 0) | |
+ f->seq = 0; | |
+ | |
+ if(f == cmd) | |
+ return FALSE; | |
+ | |
+ if(f->mod){ | |
+ f->closeok = 0; | |
+ quitok = 0; | |
+ }else | |
+ f->closeok = 1; | |
+ return TRUE; | |
+} | |
+ | |
+long | |
+prevseq(Buffer *b) | |
+{ | |
+ Undo u; | |
+ uint up; | |
+ | |
+ up = b->nc; | |
+ if(up == 0) | |
+ return 0; | |
+ up -= Undosize; | |
+ bufread(b, up, (Rune*)&u, Undosize); | |
+ return u.seq; | |
+} | |
+ | |
+long | |
+undoseq(File *f, int isundo) | |
+{ | |
+ if(isundo) | |
+ return f->seq; | |
+ | |
+ return prevseq(&f->epsilon); | |
+} | |
+ | |
+void | |
+fileundo(File *f, int isundo, int canredo, uint *q0p, uint *q1p, int flag) | |
+{ | |
+ Undo u; | |
+ Rune *buf; | |
+ uint i, n, up; | |
+ uint stop; | |
+ Buffer *delta, *epsilon; | |
+ | |
+ if(isundo){ | |
+ /* undo; reverse delta onto epsilon, seq decreases */ | |
+ delta = &f->delta; | |
+ epsilon = &f->epsilon; | |
+ stop = f->seq; | |
+ }else{ | |
+ /* redo; reverse epsilon onto delta, seq increases */ | |
+ delta = &f->epsilon; | |
+ epsilon = &f->delta; | |
+ stop = 0; /* don't know yet */ | |
+ } | |
+ | |
+ raspstart(f); | |
+ while(delta->nc > 0){ | |
+ up = delta->nc-Undosize; | |
+ bufread(delta, up, (Rune*)&u, Undosize); | |
+ if(isundo){ | |
+ if(u.seq < stop){ | |
+ f->seq = u.seq; | |
+ raspdone(f, flag); | |
+ return; | |
+ } | |
+ }else{ | |
+ if(stop == 0) | |
+ stop = u.seq; | |
+ if(u.seq > stop){ | |
+ raspdone(f, flag); | |
+ return; | |
+ } | |
+ } | |
+ switch(u.type){ | |
+ default: | |
+ panic("undo unknown u.type"); | |
+ break; | |
+ | |
+ case Delete: | |
+ f->seq = u.seq; | |
+ if(canredo) | |
+ fileundelete(f, epsilon, u.p0, u.p0+u.n); | |
+ f->mod = u.mod; | |
+ bufdelete(f, u.p0, u.p0+u.n); | |
+ raspdelete(f, u.p0, u.p0+u.n, flag); | |
+ *q0p = u.p0; | |
+ *q1p = u.p0; | |
+ break; | |
+ | |
+ case Insert: | |
+ f->seq = u.seq; | |
+ if(canredo) | |
+ fileuninsert(f, epsilon, u.p0, u.n); | |
+ f->mod = u.mod; | |
+ up -= u.n; | |
+ buf = fbufalloc(); | |
+ for(i=0; i<u.n; i+=n){ | |
+ n = u.n - i; | |
+ if(n > RBUFSIZE) | |
+ n = RBUFSIZE; | |
+ bufread(delta, up+i, buf, n); | |
+ bufinsert(f, u.p0+i, buf, n); | |
+ raspinsert(f, u.p0+i, buf, n, flag); | |
+ } | |
+ fbuffree(buf); | |
+ *q0p = u.p0; | |
+ *q1p = u.p0+u.n; | |
+ break; | |
+ | |
+ case Filename: | |
+ f->seq = u.seq; | |
+ if(canredo) | |
+ fileunsetname(f, epsilon); | |
+ f->mod = u.mod; | |
+ up -= u.n; | |
+ | |
+ Strinsure(&f->name, u.n+1); | |
+ bufread(delta, up, f->name.s, u.n); | |
+ f->name.s[u.n] = 0; | |
+ f->name.n = u.n; | |
+ fixname(&f->name); | |
+ sortname(f); | |
+ break; | |
+ case Dot: | |
+ f->seq = u.seq; | |
+ if(canredo) | |
+ fileunsetdot(f, epsilon, f->dot.r); | |
+ f->mod = u.mod; | |
+ f->dot.r.p1 = u.p0; | |
+ f->dot.r.p2 = u.p0 + u.n; | |
+ break; | |
+ case Mark: | |
+ f->seq = u.seq; | |
+ if(canredo) | |
+ fileunsetmark(f, epsilon, f->mark); | |
+ f->mod = u.mod; | |
+ f->mark.p1 = u.p0; | |
+ f->mark.p2 = u.p0 + u.n; | |
+ break; | |
+ } | |
+ bufdelete(delta, up, delta->nc); | |
+ } | |
+ if(isundo) | |
+ f->seq = 0; | |
+ raspdone(f, flag); | |
+} | |
+ | |
+void | |
+filereset(File *f) | |
+{ | |
+ bufreset(&f->delta); | |
+ bufreset(&f->epsilon); | |
+ f->seq = 0; | |
+} | |
+ | |
+void | |
+fileclose(File *f) | |
+{ | |
+ Strclose(&f->name); | |
+ bufclose(f); | |
+ bufclose(&f->delta); | |
+ bufclose(&f->epsilon); | |
+ if(f->rasp) | |
+ listfree(f->rasp); | |
+ free(f); | |
+} | |
+ | |
+void | |
+filemark(File *f) | |
+{ | |
+ | |
+ if(f->unread) | |
+ return; | |
+ if(f->epsilon.nc) | |
+ bufdelete(&f->epsilon, 0, f->epsilon.nc); | |
+ | |
+ if(f != cmd){ | |
+ f->prevdot = f->dot.r; | |
+ f->prevmark = f->mark; | |
+ f->prevseq = f->seq; | |
+ f->prevmod = f->mod; | |
+ } | |
+ | |
+ f->ndot = f->dot; | |
+ f->seq = seq; | |
+ f->hiposn = 0; | |
+} | |
diff --git a/src/cmd/sam/io.c b/src/cmd/sam/io.c | |
t@@ -0,0 +1,262 @@ | |
+#include "sam.h" | |
+ | |
+#define NSYSFILE 3 | |
+#define NOFILE 128 | |
+ | |
+void | |
+checkqid(File *f) | |
+{ | |
+ int i, w; | |
+ File *g; | |
+ | |
+ w = whichmenu(f); | |
+ for(i=1; i<file.nused; i++){ | |
+ g = file.filepptr[i]; | |
+ if(w == i) | |
+ continue; | |
+ if(f->dev==g->dev && f->qidpath==g->qidpath) | |
+ warn_SS(Wdupfile, &f->name, &g->name); | |
+ } | |
+} | |
+ | |
+void | |
+writef(File *f) | |
+{ | |
+ Posn n; | |
+ char *name; | |
+ int i, samename, newfile; | |
+ ulong dev; | |
+ uvlong qid; | |
+ long mtime, appendonly, length; | |
+ | |
+ newfile = 0; | |
+ samename = Strcmp(&genstr, &f->name) == 0; | |
+ name = Strtoc(&f->name); | |
+ i = statfile(name, &dev, &qid, &mtime, 0, 0); | |
+ if(i == -1) | |
+ newfile++; | |
+ else if(samename && | |
+ (f->dev!=dev || f->qidpath!=qid || f->mtime<mtime)){ | |
+ f->dev = dev; | |
+ f->qidpath = qid; | |
+ f->mtime = mtime; | |
+ warn_S(Wdate, &genstr); | |
+ return; | |
+ } | |
+ if(genc) | |
+ free(genc); | |
+ genc = Strtoc(&genstr); | |
+ if((io=create(genc, 1, 0666L)) < 0) | |
+ error_r(Ecreate, genc); | |
+ dprint("%s: ", genc); | |
+ if(statfd(io, 0, 0, 0, &length, &appendonly) > 0 && appendonly && leng… | |
+ error(Eappend); | |
+ n = writeio(f); | |
+ if(f->name.s[0]==0 || samename){ | |
+ if(addr.r.p1==0 && addr.r.p2==f->_.nc) | |
+ f->cleanseq = f->seq; | |
+ state(f, f->cleanseq==f->seq? Clean : Dirty); | |
+ } | |
+ if(newfile) | |
+ dprint("(new file) "); | |
+ if(addr.r.p2>0 && filereadc(f, addr.r.p2-1)!='\n') | |
+ warn(Wnotnewline); | |
+ closeio(n); | |
+ if(f->name.s[0]==0 || samename){ | |
+ if(statfile(name, &dev, &qid, &mtime, 0, 0) > 0){ | |
+ f->dev = dev; | |
+ f->qidpath = qid; | |
+ f->mtime = mtime; | |
+ checkqid(f); | |
+ } | |
+ } | |
+} | |
+ | |
+Posn | |
+readio(File *f, int *nulls, int setdate, int toterm) | |
+{ | |
+ int n, b, w; | |
+ Rune *r; | |
+ Posn nt; | |
+ Posn p = addr.r.p2; | |
+ ulong dev; | |
+ uvlong qid; | |
+ long mtime; | |
+ char buf[BLOCKSIZE+1], *s; | |
+ | |
+ *nulls = FALSE; | |
+ b = 0; | |
+ if(f->unread){ | |
+ nt = bufload(f, 0, io, nulls); | |
+ if(toterm) | |
+ raspload(f); | |
+ }else | |
+ for(nt = 0; (n = read(io, buf+b, BLOCKSIZE-b))>0; nt+=(r-genbu… | |
+ n += b; | |
+ b = 0; | |
+ r = genbuf; | |
+ s = buf; | |
+ while(n > 0){ | |
+ if((*r = *(uchar*)s) < Runeself){ | |
+ if(*r) | |
+ r++; | |
+ else | |
+ *nulls = TRUE; | |
+ --n; | |
+ s++; | |
+ continue; | |
+ } | |
+ if(fullrune(s, n)){ | |
+ w = chartorune(r, s); | |
+ if(*r) | |
+ r++; | |
+ else | |
+ *nulls = TRUE; | |
+ n -= w; | |
+ s += w; | |
+ continue; | |
+ } | |
+ b = n; | |
+ memmove(buf, s, b); | |
+ break; | |
+ } | |
+ loginsert(f, p, genbuf, r-genbuf); | |
+ } | |
+ if(b) | |
+ *nulls = TRUE; | |
+ if(*nulls) | |
+ warn(Wnulls); | |
+ if(setdate){ | |
+ if(statfd(io, &dev, &qid, &mtime, 0, 0) > 0){ | |
+ f->dev = dev; | |
+ f->qidpath = qid; | |
+ f->mtime = mtime; | |
+ checkqid(f); | |
+ } | |
+ } | |
+ return nt; | |
+} | |
+ | |
+Posn | |
+writeio(File *f) | |
+{ | |
+ int m, n; | |
+ Posn p = addr.r.p1; | |
+ char *c; | |
+ | |
+ while(p < addr.r.p2){ | |
+ if(addr.r.p2-p>BLOCKSIZE) | |
+ n = BLOCKSIZE; | |
+ else | |
+ n = addr.r.p2-p; | |
+ bufread(f, p, genbuf, n); | |
+ c = Strtoc(tmprstr(genbuf, n)); | |
+ m = strlen(c); | |
+ if(Write(io, c, m) != m){ | |
+ free(c); | |
+ if(p > 0) | |
+ p += n; | |
+ break; | |
+ } | |
+ free(c); | |
+ p += n; | |
+ } | |
+ return p-addr.r.p1; | |
+} | |
+void | |
+closeio(Posn p) | |
+{ | |
+ close(io); | |
+ io = 0; | |
+ if(p >= 0) | |
+ dprint("#%lud\n", p); | |
+} | |
+ | |
+int remotefd0 = 0; | |
+int remotefd1 = 1; | |
+ | |
+void | |
+bootterm(char *machine, char **argv, char **end) | |
+{ | |
+ int ph2t[2], pt2h[2]; | |
+ | |
+ if(machine){ | |
+ dup(remotefd0, 0); | |
+ dup(remotefd1, 1); | |
+ close(remotefd0); | |
+ close(remotefd1); | |
+ argv[0] = "samterm"; | |
+ *end = 0; | |
+ exec(samterm, argv); | |
+ fprint(2, "can't exec: "); | |
+ perror(samterm); | |
+ _exits("damn"); | |
+ } | |
+ if(pipe(ph2t)==-1 || pipe(pt2h)==-1) | |
+ panic("pipe"); | |
+ switch(fork()){ | |
+ case 0: | |
+ dup(ph2t[0], 0); | |
+ dup(pt2h[1], 1); | |
+ close(ph2t[0]); | |
+ close(ph2t[1]); | |
+ close(pt2h[0]); | |
+ close(pt2h[1]); | |
+ argv[0] = "samterm"; | |
+ *end = 0; | |
+ exec(samterm, argv); | |
+ fprint(2, "can't exec: "); | |
+ perror(samterm); | |
+ _exits("damn"); | |
+ case -1: | |
+ panic("can't fork samterm"); | |
+ } | |
+ dup(pt2h[0], 0); | |
+ dup(ph2t[1], 1); | |
+ close(ph2t[0]); | |
+ close(ph2t[1]); | |
+ close(pt2h[0]); | |
+ close(pt2h[1]); | |
+} | |
+ | |
+void | |
+connectto(char *machine) | |
+{ | |
+ int p1[2], p2[2]; | |
+ | |
+ if(pipe(p1)<0 || pipe(p2)<0){ | |
+ dprint("can't pipe\n"); | |
+ exits("pipe"); | |
+ } | |
+ remotefd0 = p1[0]; | |
+ remotefd1 = p2[1]; | |
+ switch(fork()){ | |
+ case 0: | |
+ dup(p2[0], 0); | |
+ dup(p1[1], 1); | |
+ close(p1[0]); | |
+ close(p1[1]); | |
+ close(p2[0]); | |
+ close(p2[1]); | |
+ execl(RXPATH, RX, machine, rsamname, "-R", (char*)0); | |
+ dprint("can't exec %s\n", RXPATH); | |
+ exits("exec"); | |
+ | |
+ case -1: | |
+ dprint("can't fork\n"); | |
+ exits("fork"); | |
+ } | |
+ close(p1[1]); | |
+ close(p2[0]); | |
+} | |
+ | |
+void | |
+startup(char *machine, int Rflag, char **argv, char **end) | |
+{ | |
+ if(machine) | |
+ connectto(machine); | |
+ if(!Rflag) | |
+ bootterm(machine, argv, end); | |
+ downloaded = 1; | |
+ outTs(Hversion, VERSION); | |
+} | |
diff --git a/src/cmd/sam/list.c b/src/cmd/sam/list.c | |
t@@ -0,0 +1,47 @@ | |
+#include "sam.h" | |
+ | |
+/* | |
+ * Check that list has room for one more element. | |
+ */ | |
+void | |
+growlist(List *l) | |
+{ | |
+ if(l->listptr==0 || l->nalloc==0){ | |
+ l->nalloc = INCR; | |
+ l->listptr = emalloc(INCR*sizeof(long)); | |
+ l->nused = 0; | |
+ }else if(l->nused == l->nalloc){ | |
+ l->listptr = erealloc(l->listptr, (l->nalloc+INCR)*sizeof(long… | |
+ memset((void*)(l->longptr+l->nalloc), 0, INCR*sizeof(long)); | |
+ l->nalloc += INCR; | |
+ } | |
+} | |
+ | |
+/* | |
+ * Remove the ith element from the list | |
+ */ | |
+void | |
+dellist(List *l, int i) | |
+{ | |
+ memmove(&l->longptr[i], &l->longptr[i+1], (l->nused-(i+1))*sizeof(long… | |
+ l->nused--; | |
+} | |
+ | |
+/* | |
+ * Add a new element, whose position is i, to the list | |
+ */ | |
+void | |
+inslist(List *l, int i, long val) | |
+{ | |
+ growlist(l); | |
+ memmove(&l->longptr[i+1], &l->longptr[i], (l->nused-i)*sizeof(long)); | |
+ l->longptr[i] = val; | |
+ l->nused++; | |
+} | |
+ | |
+void | |
+listfree(List *l) | |
+{ | |
+ free(l->listptr); | |
+ free(l); | |
+} | |
diff --git a/src/cmd/sam/mesg.c b/src/cmd/sam/mesg.c | |
t@@ -0,0 +1,821 @@ | |
+#include "sam.h" | |
+ | |
+Header h; | |
+uchar indata[DATASIZE]; | |
+uchar outdata[2*DATASIZE+3]; /* room for overflow message */ | |
+uchar *inp; | |
+uchar *outp; | |
+uchar *outmsg = outdata; | |
+Posn cmdpt; | |
+Posn cmdptadv; | |
+Buffer snarfbuf; | |
+int waitack; | |
+int noflush; | |
+int tversion; | |
+ | |
+long inlong(void); | |
+long invlong(void); | |
+int inshort(void); | |
+int inmesg(Tmesg); | |
+void setgenstr(File*, Posn, Posn); | |
+ | |
+#ifdef DEBUG | |
+char *hname[] = { | |
+ [Hversion] "Hversion", | |
+ [Hbindname] "Hbindname", | |
+ [Hcurrent] "Hcurrent", | |
+ [Hnewname] "Hnewname", | |
+ [Hmovname] "Hmovname", | |
+ [Hgrow] "Hgrow", | |
+ [Hcheck0] "Hcheck0", | |
+ [Hcheck] "Hcheck", | |
+ [Hunlock] "Hunlock", | |
+ [Hdata] "Hdata", | |
+ [Horigin] "Horigin", | |
+ [Hunlockfile] "Hunlockfile", | |
+ [Hsetdot] "Hsetdot", | |
+ [Hgrowdata] "Hgrowdata", | |
+ [Hmoveto] "Hmoveto", | |
+ [Hclean] "Hclean", | |
+ [Hdirty] "Hdirty", | |
+ [Hcut] "Hcut", | |
+ [Hsetpat] "Hsetpat", | |
+ [Hdelname] "Hdelname", | |
+ [Hclose] "Hclose", | |
+ [Hsetsnarf] "Hsetsnarf", | |
+ [Hsnarflen] "Hsnarflen", | |
+ [Hack] "Hack", | |
+ [Hexit] "Hexit", | |
+ [Hplumb] "Hplumb", | |
+}; | |
+ | |
+char *tname[] = { | |
+ [Tversion] "Tversion", | |
+ [Tstartcmdfile] "Tstartcmdfile", | |
+ [Tcheck] "Tcheck", | |
+ [Trequest] "Trequest", | |
+ [Torigin] "Torigin", | |
+ [Tstartfile] "Tstartfile", | |
+ [Tworkfile] "Tworkfile", | |
+ [Ttype] "Ttype", | |
+ [Tcut] "Tcut", | |
+ [Tpaste] "Tpaste", | |
+ [Tsnarf] "Tsnarf", | |
+ [Tstartnewfile] "Tstartnewfile", | |
+ [Twrite] "Twrite", | |
+ [Tclose] "Tclose", | |
+ [Tlook] "Tlook", | |
+ [Tsearch] "Tsearch", | |
+ [Tsend] "Tsend", | |
+ [Tdclick] "Tdclick", | |
+ [Tstartsnarf] "Tstartsnarf", | |
+ [Tsetsnarf] "Tsetsnarf", | |
+ [Tack] "Tack", | |
+ [Texit] "Texit", | |
+ [Tplumb] "Tplumb", | |
+}; | |
+ | |
+void | |
+journal(int out, char *s) | |
+{ | |
+ static int fd = 0; | |
+ | |
+ if(fd <= 0) | |
+ fd = create("/tmp/sam.out", 1, 0666L); | |
+ fprint(fd, "%s%s\n", out? "out: " : "in: ", s); | |
+} | |
+ | |
+void | |
+journaln(int out, long n) | |
+{ | |
+ char buf[32]; | |
+ | |
+ sprint(buf, "%ld", n); | |
+ journal(out, buf); | |
+} | |
+#else | |
+#define journal(a, b) | |
+#define journaln(a, b) | |
+#endif | |
+ | |
+int | |
+rcvchar(void){ | |
+ static uchar buf[64]; | |
+ static i, nleft = 0; | |
+ | |
+ if(nleft <= 0){ | |
+ nleft = read(0, (char *)buf, sizeof buf); | |
+ if(nleft <= 0) | |
+ return -1; | |
+ i = 0; | |
+ } | |
+ --nleft; | |
+ return buf[i++]; | |
+} | |
+ | |
+int | |
+rcv(void){ | |
+ int c; | |
+ static state = 0; | |
+ static count = 0; | |
+ static i = 0; | |
+ | |
+ while((c=rcvchar()) != -1) | |
+ switch(state){ | |
+ case 0: | |
+ h.type = c; | |
+ state++; | |
+ break; | |
+ | |
+ case 1: | |
+ h.count0 = c; | |
+ state++; | |
+ break; | |
+ | |
+ case 2: | |
+ h.count1 = c; | |
+ count = h.count0|(h.count1<<8); | |
+ i = 0; | |
+ if(count > DATASIZE) | |
+ panic("count>DATASIZE"); | |
+ if(count == 0) | |
+ goto zerocount; | |
+ state++; | |
+ break; | |
+ | |
+ case 3: | |
+ indata[i++] = c; | |
+ if(i == count){ | |
+ zerocount: | |
+ indata[i] = 0; | |
+ state = count = 0; | |
+ return inmesg(h.type); | |
+ } | |
+ break; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+File * | |
+whichfile(int tag) | |
+{ | |
+ int i; | |
+ | |
+ for(i = 0; i<file.nused; i++) | |
+ if(file.filepptr[i]->tag==tag) | |
+ return file.filepptr[i]; | |
+ hiccough((char *)0); | |
+ return 0; | |
+} | |
+ | |
+int | |
+inmesg(Tmesg type) | |
+{ | |
+ Rune buf[1025]; | |
+ char cbuf[64]; | |
+ int i, m; | |
+ short s; | |
+ long l, l1; | |
+ File *f; | |
+ Posn p0, p1, p; | |
+ Range r; | |
+ String *str; | |
+ char *c, *wdir; | |
+ Rune *rp; | |
+ Plumbmsg *pm; | |
+ | |
+ if(type > TMAX) | |
+ panic("inmesg"); | |
+ | |
+ journal(0, tname[type]); | |
+ | |
+ inp = indata; | |
+ switch(type){ | |
+ case -1: | |
+ panic("rcv error"); | |
+ | |
+ default: | |
+ fprint(2, "unknown type %d\n", type); | |
+ panic("rcv unknown"); | |
+ | |
+ case Tversion: | |
+ tversion = inshort(); | |
+ journaln(0, tversion); | |
+ break; | |
+ | |
+ case Tstartcmdfile: | |
+ l = invlong(); /* for 64-bit pointers */ | |
+ journaln(0, l); | |
+ Strdupl(&genstr, samname); | |
+ cmd = newfile(); | |
+ cmd->unread = 0; | |
+ outTsv(Hbindname, cmd->tag, l); | |
+ outTs(Hcurrent, cmd->tag); | |
+ logsetname(cmd, &genstr); | |
+ cmd->rasp = emalloc(sizeof(List)); | |
+ cmd->mod = 0; | |
+ if(cmdstr.n){ | |
+ loginsert(cmd, 0L, cmdstr.s, cmdstr.n); | |
+ Strdelete(&cmdstr, 0L, (Posn)cmdstr.n); | |
+ } | |
+ fileupdate(cmd, FALSE, TRUE); | |
+ outT0(Hunlock); | |
+ break; | |
+ | |
+ case Tcheck: | |
+ /* go through whichfile to check the tag */ | |
+ outTs(Hcheck, whichfile(inshort())->tag); | |
+ break; | |
+ | |
+ case Trequest: | |
+ f = whichfile(inshort()); | |
+ p0 = inlong(); | |
+ p1 = p0+inshort(); | |
+ journaln(0, p0); | |
+ journaln(0, p1-p0); | |
+ if(f->unread) | |
+ panic("Trequest: unread"); | |
+ if(p1>f->_.nc) | |
+ p1 = f->_.nc; | |
+ if(p0>f->_.nc) /* can happen e.g. scrolling during command */ | |
+ p0 = f->_.nc; | |
+ if(p0 == p1){ | |
+ i = 0; | |
+ r.p1 = r.p2 = p0; | |
+ }else{ | |
+ r = rdata(f->rasp, p0, p1-p0); | |
+ i = r.p2-r.p1; | |
+ bufread(f, r.p1, buf, i); | |
+ } | |
+ buf[i]=0; | |
+ outTslS(Hdata, f->tag, r.p1, tmprstr(buf, i+1)); | |
+ break; | |
+ | |
+ case Torigin: | |
+ s = inshort(); | |
+ l = inlong(); | |
+ l1 = inlong(); | |
+ journaln(0, l1); | |
+ lookorigin(whichfile(s), l, l1); | |
+ break; | |
+ | |
+ case Tstartfile: | |
+ termlocked++; | |
+ f = whichfile(inshort()); | |
+ if(!f->rasp) /* this might be a duplicate message */ | |
+ f->rasp = emalloc(sizeof(List)); | |
+ current(f); | |
+ outTsv(Hbindname, f->tag, invlong()); /* for 64-bit poi… | |
+ outTs(Hcurrent, f->tag); | |
+ journaln(0, f->tag); | |
+ if(f->unread) | |
+ load(f); | |
+ else{ | |
+ if(f->_.nc>0){ | |
+ rgrow(f->rasp, 0L, f->_.nc); | |
+ outTsll(Hgrow, f->tag, 0L, f->_.nc); | |
+ } | |
+ outTs(Hcheck0, f->tag); | |
+ moveto(f, f->dot.r); | |
+ } | |
+ break; | |
+ | |
+ case Tworkfile: | |
+ i = inshort(); | |
+ f = whichfile(i); | |
+ current(f); | |
+ f->dot.r.p1 = inlong(); | |
+ f->dot.r.p2 = inlong(); | |
+ f->tdot = f->dot.r; | |
+ journaln(0, i); | |
+ journaln(0, f->dot.r.p1); | |
+ journaln(0, f->dot.r.p2); | |
+ break; | |
+ | |
+ case Ttype: | |
+ f = whichfile(inshort()); | |
+ p0 = inlong(); | |
+ journaln(0, p0); | |
+ journal(0, (char*)inp); | |
+ str = tmpcstr((char*)inp); | |
+ i = str->n; | |
+ loginsert(f, p0, str->s, str->n); | |
+ if(fileupdate(f, FALSE, FALSE)) | |
+ seq++; | |
+ if(f==cmd && p0==f->_.nc-i && i>0 && str->s[i-1]=='\n'){ | |
+ freetmpstr(str); | |
+ termlocked++; | |
+ termcommand(); | |
+ }else | |
+ freetmpstr(str); | |
+ f->dot.r.p1 = f->dot.r.p2 = p0+i; /* terminal knows this alrea… | |
+ f->tdot = f->dot.r; | |
+ break; | |
+ | |
+ case Tcut: | |
+ f = whichfile(inshort()); | |
+ p0 = inlong(); | |
+ p1 = inlong(); | |
+ journaln(0, p0); | |
+ journaln(0, p1); | |
+ logdelete(f, p0, p1); | |
+ if(fileupdate(f, FALSE, FALSE)) | |
+ seq++; | |
+ f->dot.r.p1 = f->dot.r.p2 = p0; | |
+ f->tdot = f->dot.r; /* terminal knows the value of dot alrea… | |
+ break; | |
+ | |
+ case Tpaste: | |
+ f = whichfile(inshort()); | |
+ p0 = inlong(); | |
+ journaln(0, p0); | |
+ for(l=0; l<snarfbuf.nc; l+=m){ | |
+ m = snarfbuf.nc-l; | |
+ if(m>BLOCKSIZE) | |
+ m = BLOCKSIZE; | |
+ bufread(&snarfbuf, l, genbuf, m); | |
+ loginsert(f, p0, tmprstr(genbuf, m)->s, m); | |
+ } | |
+ if(fileupdate(f, FALSE, TRUE)) | |
+ seq++; | |
+ f->dot.r.p1 = p0; | |
+ f->dot.r.p2 = p0+snarfbuf.nc; | |
+ f->tdot.p1 = -1; /* force telldot to tell (arguably a BUG) */ | |
+ telldot(f); | |
+ outTs(Hunlockfile, f->tag); | |
+ break; | |
+ | |
+ case Tsnarf: | |
+ i = inshort(); | |
+ p0 = inlong(); | |
+ p1 = inlong(); | |
+ snarf(whichfile(i), p0, p1, &snarfbuf, 0); | |
+ break; | |
+ | |
+ case Tstartnewfile: | |
+ l = invlong(); | |
+ Strdupl(&genstr, empty); | |
+ f = newfile(); | |
+ f->rasp = emalloc(sizeof(List)); | |
+ outTsv(Hbindname, f->tag, l); | |
+ logsetname(f, &genstr); | |
+ outTs(Hcurrent, f->tag); | |
+ current(f); | |
+ load(f); | |
+ break; | |
+ | |
+ case Twrite: | |
+ termlocked++; | |
+ i = inshort(); | |
+ journaln(0, i); | |
+ f = whichfile(i); | |
+ addr.r.p1 = 0; | |
+ addr.r.p2 = f->_.nc; | |
+ if(f->name.s[0] == 0) | |
+ error(Enoname); | |
+ Strduplstr(&genstr, &f->name); | |
+ writef(f); | |
+ break; | |
+ | |
+ case Tclose: | |
+ termlocked++; | |
+ i = inshort(); | |
+ journaln(0, i); | |
+ f = whichfile(i); | |
+ current(f); | |
+ trytoclose(f); | |
+ /* if trytoclose fails, will error out */ | |
+ delete(f); | |
+ break; | |
+ | |
+ case Tlook: | |
+ f = whichfile(inshort()); | |
+ termlocked++; | |
+ p0 = inlong(); | |
+ p1 = inlong(); | |
+ journaln(0, p0); | |
+ journaln(0, p1); | |
+ setgenstr(f, p0, p1); | |
+ for(l = 0; l<genstr.n; l++){ | |
+ i = genstr.s[l]; | |
+ if(utfrune(".*+?(|)\\[]^$", i)) | |
+ Strinsert(&genstr, tmpcstr("\\"), l++); | |
+ } | |
+ Straddc(&genstr, '\0'); | |
+ nextmatch(f, &genstr, p1, 1); | |
+ moveto(f, sel.p[0]); | |
+ break; | |
+ | |
+ case Tsearch: | |
+ termlocked++; | |
+ if(curfile == 0) | |
+ error(Enofile); | |
+ if(lastpat.s[0] == 0) | |
+ panic("Tsearch"); | |
+ nextmatch(curfile, &lastpat, curfile->dot.r.p2, 1); | |
+ moveto(curfile, sel.p[0]); | |
+ break; | |
+ | |
+ case Tsend: | |
+ termlocked++; | |
+ inshort(); /* ignored */ | |
+ p0 = inlong(); | |
+ p1 = inlong(); | |
+ setgenstr(cmd, p0, p1); | |
+ bufreset(&snarfbuf); | |
+ bufinsert(&snarfbuf, (Posn)0, genstr.s, genstr.n); | |
+ outTl(Hsnarflen, genstr.n); | |
+ if(genstr.s[genstr.n-1] != '\n') | |
+ Straddc(&genstr, '\n'); | |
+ loginsert(cmd, cmd->_.nc, genstr.s, genstr.n); | |
+ fileupdate(cmd, FALSE, TRUE); | |
+ cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->_.nc; | |
+ telldot(cmd); | |
+ termcommand(); | |
+ break; | |
+ | |
+ case Tdclick: | |
+ f = whichfile(inshort()); | |
+ p1 = inlong(); | |
+ doubleclick(f, p1); | |
+ f->tdot.p1 = f->tdot.p2 = p1; | |
+ telldot(f); | |
+ outTs(Hunlockfile, f->tag); | |
+ break; | |
+ | |
+ case Tstartsnarf: | |
+ if (snarfbuf.nc <= 0) { /* nothing to export */ | |
+ outTs(Hsetsnarf, 0); | |
+ break; | |
+ } | |
+ c = 0; | |
+ i = 0; | |
+ m = snarfbuf.nc; | |
+ if(m > SNARFSIZE) { | |
+ m = SNARFSIZE; | |
+ dprint("?warning: snarf buffer truncated\n"); | |
+ } | |
+ rp = malloc(m*sizeof(Rune)); | |
+ if(rp){ | |
+ bufread(&snarfbuf, 0, rp, m); | |
+ c = Strtoc(tmprstr(rp, m)); | |
+ free(rp); | |
+ i = strlen(c); | |
+ } | |
+ outTs(Hsetsnarf, i); | |
+ if(c){ | |
+ Write(1, c, i); | |
+ free(c); | |
+ } else | |
+ dprint("snarf buffer too long\n"); | |
+ break; | |
+ | |
+ case Tsetsnarf: | |
+ m = inshort(); | |
+ if(m > SNARFSIZE) | |
+ error(Etoolong); | |
+ c = malloc(m+1); | |
+ if(c){ | |
+ for(i=0; i<m; i++) | |
+ c[i] = rcvchar(); | |
+ c[m] = 0; | |
+ str = tmpcstr(c); | |
+ free(c); | |
+ bufreset(&snarfbuf); | |
+ bufinsert(&snarfbuf, (Posn)0, str->s, str->n); | |
+ freetmpstr(str); | |
+ outT0(Hunlock); | |
+ } | |
+ break; | |
+ | |
+ case Tack: | |
+ waitack = 0; | |
+ break; | |
+ | |
+ case Tplumb: | |
+ f = whichfile(inshort()); | |
+ p0 = inlong(); | |
+ p1 = inlong(); | |
+ pm = emalloc(sizeof(Plumbmsg)); | |
+ pm->src = strdup("sam"); | |
+ pm->dst = 0; | |
+ /* construct current directory */ | |
+ c = Strtoc(&f->name); | |
+ if(c[0] == '/') | |
+ pm->wdir = c; | |
+ else{ | |
+ wdir = emalloc(1024); | |
+ getwd(wdir, 1024); | |
+ pm->wdir = emalloc(1024); | |
+ snprint(pm->wdir, 1024, "%s/%s", wdir, c); | |
+ cleanname(pm->wdir); | |
+ free(wdir); | |
+ free(c); | |
+ } | |
+ c = strrchr(pm->wdir, '/'); | |
+ if(c) | |
+ *c = '\0'; | |
+ pm->type = strdup("text"); | |
+ if(p1 > p0) | |
+ pm->attr = nil; | |
+ else{ | |
+ p = p0; | |
+ while(p0>0 && (i=filereadc(f, p0 - 1))!=' ' && i!='\t'… | |
+ p0--; | |
+ while(p1<f->_.nc && (i=filereadc(f, p1))!=' ' && i!='\… | |
+ p1++; | |
+ sprint(cbuf, "click=%ld", p-p0); | |
+ pm->attr = plumbunpackattr(cbuf); | |
+ } | |
+ if(p0==p1 || p1-p0>=BLOCKSIZE){ | |
+ plumbfree(pm); | |
+ break; | |
+ } | |
+ setgenstr(f, p0, p1); | |
+ pm->data = Strtoc(&genstr); | |
+ pm->ndata = strlen(pm->data); | |
+ c = plumbpack(pm, &i); | |
+ if(c != 0){ | |
+ outTs(Hplumb, i); | |
+ Write(1, c, i); | |
+ free(c); | |
+ } | |
+ plumbfree(pm); | |
+ break; | |
+ | |
+ case Texit: | |
+ exits(0); | |
+ } | |
+ return TRUE; | |
+} | |
+ | |
+void | |
+snarf(File *f, Posn p1, Posn p2, Buffer *buf, int emptyok) | |
+{ | |
+ Posn l; | |
+ int i; | |
+ | |
+ if(!emptyok && p1==p2) | |
+ return; | |
+ bufreset(buf); | |
+ /* Stage through genbuf to avoid compaction problems (vestigial) */ | |
+ if(p2 > f->_.nc){ | |
+ fprint(2, "bad snarf addr p1=%ld p2=%ld f->_.nc=%d\n", p1, p2,… | |
+ p2 = f->_.nc; | |
+ } | |
+ for(l=p1; l<p2; l+=i){ | |
+ i = p2-l>BLOCKSIZE? BLOCKSIZE : p2-l; | |
+ bufread(f, l, genbuf, i); | |
+ bufinsert(buf, buf->nc, tmprstr(genbuf, i)->s, i); | |
+ } | |
+} | |
+ | |
+int | |
+inshort(void) | |
+{ | |
+ ushort n; | |
+ | |
+ n = inp[0] | (inp[1]<<8); | |
+ inp += 2; | |
+ return n; | |
+} | |
+ | |
+long | |
+inlong(void) | |
+{ | |
+ ulong n; | |
+ | |
+ n = inp[0] | (inp[1]<<8) | (inp[2]<<16) | (inp[3]<<24); | |
+ inp += 4; | |
+ return n; | |
+} | |
+ | |
+long | |
+invlong(void) | |
+{ | |
+ ulong n; | |
+ | |
+ n = (inp[7]<<24) | (inp[6]<<16) | (inp[5]<<8) | inp[4]; | |
+ n = (n<<16) | (inp[3]<<8) | inp[2]; | |
+ n = (n<<16) | (inp[1]<<8) | inp[0]; | |
+ inp += 8; | |
+ return n; | |
+} | |
+ | |
+void | |
+setgenstr(File *f, Posn p0, Posn p1) | |
+{ | |
+ if(p0 != p1){ | |
+ if(p1-p0 >= TBLOCKSIZE) | |
+ error(Etoolong); | |
+ Strinsure(&genstr, p1-p0); | |
+ bufread(f, p0, genbuf, p1-p0); | |
+ memmove(genstr.s, genbuf, RUNESIZE*(p1-p0)); | |
+ genstr.n = p1-p0; | |
+ }else{ | |
+ if(snarfbuf.nc == 0) | |
+ error(Eempty); | |
+ if(snarfbuf.nc > TBLOCKSIZE) | |
+ error(Etoolong); | |
+ bufread(&snarfbuf, (Posn)0, genbuf, snarfbuf.nc); | |
+ Strinsure(&genstr, snarfbuf.nc); | |
+ memmove(genstr.s, genbuf, RUNESIZE*snarfbuf.nc); | |
+ genstr.n = snarfbuf.nc; | |
+ } | |
+} | |
+ | |
+void | |
+outT0(Hmesg type) | |
+{ | |
+ outstart(type); | |
+ outsend(); | |
+} | |
+ | |
+void | |
+outTl(Hmesg type, long l) | |
+{ | |
+ outstart(type); | |
+ outlong(l); | |
+ outsend(); | |
+} | |
+ | |
+void | |
+outTs(Hmesg type, int s) | |
+{ | |
+ outstart(type); | |
+ journaln(1, s); | |
+ outshort(s); | |
+ outsend(); | |
+} | |
+ | |
+void | |
+outS(String *s) | |
+{ | |
+ char *c; | |
+ int i; | |
+ | |
+ c = Strtoc(s); | |
+ i = strlen(c); | |
+ outcopy(i, c); | |
+ if(i > 99) | |
+ c[99] = 0; | |
+ journaln(1, i); | |
+ journal(1, c); | |
+ free(c); | |
+} | |
+ | |
+void | |
+outTsS(Hmesg type, int s1, String *s) | |
+{ | |
+ outstart(type); | |
+ outshort(s1); | |
+ outS(s); | |
+ outsend(); | |
+} | |
+ | |
+void | |
+outTslS(Hmesg type, int s1, Posn l1, String *s) | |
+{ | |
+ outstart(type); | |
+ outshort(s1); | |
+ journaln(1, s1); | |
+ outlong(l1); | |
+ journaln(1, l1); | |
+ outS(s); | |
+ outsend(); | |
+} | |
+ | |
+void | |
+outTS(Hmesg type, String *s) | |
+{ | |
+ outstart(type); | |
+ outS(s); | |
+ outsend(); | |
+} | |
+ | |
+void | |
+outTsllS(Hmesg type, int s1, Posn l1, Posn l2, String *s) | |
+{ | |
+ outstart(type); | |
+ outshort(s1); | |
+ outlong(l1); | |
+ outlong(l2); | |
+ journaln(1, l1); | |
+ journaln(1, l2); | |
+ outS(s); | |
+ outsend(); | |
+} | |
+ | |
+void | |
+outTsll(Hmesg type, int s, Posn l1, Posn l2) | |
+{ | |
+ outstart(type); | |
+ outshort(s); | |
+ outlong(l1); | |
+ outlong(l2); | |
+ journaln(1, l1); | |
+ journaln(1, l2); | |
+ outsend(); | |
+} | |
+ | |
+void | |
+outTsl(Hmesg type, int s, Posn l) | |
+{ | |
+ outstart(type); | |
+ outshort(s); | |
+ outlong(l); | |
+ journaln(1, l); | |
+ outsend(); | |
+} | |
+ | |
+void | |
+outTsv(Hmesg type, int s, Posn l) | |
+{ | |
+ outstart(type); | |
+ outshort(s); | |
+ outvlong((void*)l); | |
+ journaln(1, l); | |
+ outsend(); | |
+} | |
+ | |
+void | |
+outstart(Hmesg type) | |
+{ | |
+ journal(1, hname[type]); | |
+ outmsg[0] = type; | |
+ outp = outmsg+3; | |
+} | |
+ | |
+void | |
+outcopy(int count, void *data) | |
+{ | |
+ memmove(outp, data, count); | |
+ outp += count; | |
+} | |
+ | |
+void | |
+outshort(int s) | |
+{ | |
+ *outp++ = s; | |
+ *outp++ = s>>8; | |
+} | |
+ | |
+void | |
+outlong(long l) | |
+{ | |
+ *outp++ = l; | |
+ *outp++ = l>>8; | |
+ *outp++ = l>>16; | |
+ *outp++ = l>>24; | |
+} | |
+ | |
+void | |
+outvlong(void *v) | |
+{ | |
+ int i; | |
+ ulong l; | |
+ | |
+ l = (ulong) v; | |
+ for(i = 0; i < 8; i++, l >>= 8) | |
+ *outp++ = l; | |
+} | |
+ | |
+void | |
+outsend(void) | |
+{ | |
+ int outcount; | |
+ | |
+ outcount = outp-outmsg; | |
+ outcount -= 3; | |
+ outmsg[1] = outcount; | |
+ outmsg[2] = outcount>>8; | |
+ outmsg = outp; | |
+ if(!noflush){ | |
+ outcount = outmsg-outdata; | |
+ if (write(1, (char*) outdata, outcount) != outcount) | |
+ rescue(); | |
+ outmsg = outdata; | |
+ return; | |
+ } | |
+ if(outmsg < outdata+DATASIZE) | |
+ return; | |
+ outflush(); | |
+} | |
+ | |
+void | |
+outflush(void) | |
+{ | |
+ if(outmsg == outdata) | |
+ return; | |
+ noflush = 0; | |
+ outT0(Hack); | |
+ waitack = 1; | |
+ do | |
+ if(rcv() == 0){ | |
+ rescue(); | |
+ exits("eof"); | |
+ } | |
+ while(waitack); | |
+ outmsg = outdata; | |
+ noflush = 1; | |
+} | |
diff --git a/src/cmd/sam/mesg.h b/src/cmd/sam/mesg.h | |
t@@ -0,0 +1,131 @@ | |
+/* VERSION 1 introduces plumbing | |
+ 2 increases SNARFSIZE from 4096 to 32000 | |
+ */ | |
+#define VERSION 2 | |
+ | |
+#define TBLOCKSIZE 512 /* largest piece of text sent t… | |
+#define DATASIZE (UTFmax*TBLOCKSIZE+30) /* ... including protocol head… | |
+#define SNARFSIZE 32000 /* maximum length of exchanged s… | |
+/* | |
+ * Messages originating at the terminal | |
+ */ | |
+typedef enum Tmesg | |
+{ | |
+ Tversion, /* version */ | |
+ Tstartcmdfile, /* terminal just opened command frame */ | |
+ Tcheck, /* ask host to poke with Hcheck */ | |
+ Trequest, /* request data to fill a hole */ | |
+ Torigin, /* gimme an Horigin near here */ | |
+ Tstartfile, /* terminal just opened a file's frame */ | |
+ Tworkfile, /* set file to which commands apply */ | |
+ Ttype, /* add some characters, but terminal already kno… | |
+ Tcut, | |
+ Tpaste, | |
+ Tsnarf, | |
+ Tstartnewfile, /* terminal just opened a new frame */ | |
+ Twrite, /* write file */ | |
+ Tclose, /* terminal requests file close; check mod. sta… | |
+ Tlook, /* search for literal current text */ | |
+ Tsearch, /* search for last regular expression */ | |
+ Tsend, /* pretend he typed stuff */ | |
+ Tdclick, /* double click */ | |
+ Tstartsnarf, /* initiate snarf buffer exchange */ | |
+ Tsetsnarf, /* remember string in snarf buffer */ | |
+ Tack, /* acknowledge Hack */ | |
+ Texit, /* exit */ | |
+ Tplumb, /* send plumb message */ | |
+ TMAX, | |
+}Tmesg; | |
+/* | |
+ * Messages originating at the host | |
+ */ | |
+typedef enum Hmesg | |
+{ | |
+ Hversion, /* version */ | |
+ Hbindname, /* attach name[0] to text in terminal */ | |
+ Hcurrent, /* make named file the typing file */ | |
+ Hnewname, /* create "" name in menu */ | |
+ Hmovname, /* move file name in menu */ | |
+ Hgrow, /* insert space in rasp */ | |
+ Hcheck0, /* see below */ | |
+ Hcheck, /* ask terminal to check whether it needs more … | |
+ Hunlock, /* command is finished; user can do things */ | |
+ Hdata, /* store this data in previously allocated space… | |
+ Horigin, /* set origin of file/frame in terminal */ | |
+ Hunlockfile, /* unlock file in terminal */ | |
+ Hsetdot, /* set dot in terminal */ | |
+ Hgrowdata, /* Hgrow + Hdata folded together */ | |
+ Hmoveto, /* scrolling, context search, etc. */ | |
+ Hclean, /* named file is now 'clean' */ | |
+ Hdirty, /* named file is now 'dirty' */ | |
+ Hcut, /* remove space from rasp */ | |
+ Hsetpat, /* set remembered regular expression */ | |
+ Hdelname, /* delete file name from menu */ | |
+ Hclose, /* close file and remove from menu */ | |
+ Hsetsnarf, /* remember string in snarf buffer */ | |
+ Hsnarflen, /* report length of implicit snarf */ | |
+ Hack, /* request acknowledgement */ | |
+ Hexit, | |
+ Hplumb, /* return plumb message to terminal */ | |
+ HMAX, | |
+}Hmesg; | |
+typedef struct Header{ | |
+ uchar type; /* one of the above */ | |
+ uchar count0; /* low bits of data size */ | |
+ uchar count1; /* high bits of data size */ | |
+ uchar data[1]; /* variable size */ | |
+}Header; | |
+ | |
+/* | |
+ * File transfer protocol schematic, a la Holzmann | |
+ * #define N 6 | |
+ * | |
+ * chan h = [4] of { mtype }; | |
+ * chan t = [4] of { mtype }; | |
+ * | |
+ * mtype = { Hgrow, Hdata, | |
+ * Hcheck, Hcheck0, | |
+ * Trequest, Tcheck, | |
+ * }; | |
+ * | |
+ * active proctype host() | |
+ * { byte n; | |
+ * | |
+ * do | |
+ * :: n < N -> n++; t!Hgrow | |
+ * :: n == N -> n++; t!Hcheck0 | |
+ * | |
+ * :: h?Trequest -> t!Hdata | |
+ * :: h?Tcheck -> t!Hcheck | |
+ * od | |
+ * } | |
+ * | |
+ * active proctype term() | |
+ * { | |
+ * do | |
+ * :: t?Hgrow -> h!Trequest | |
+ * :: t?Hdata -> skip | |
+ * :: t?Hcheck0 -> h!Tcheck | |
+ * :: t?Hcheck -> | |
+ * if | |
+ * :: h!Trequest -> progress: h!Tcheck | |
+ * :: break | |
+ * fi | |
+ * od; | |
+ * printf("term exits\n") | |
+ * } | |
+ * | |
+ * From: [email protected] | |
+ * Date: Tue Jul 17 13:47:23 EDT 2001 | |
+ * To: [email protected] | |
+ * | |
+ * spin -c (or -a) spec | |
+ * pcc -DNP -o pan pan.c | |
+ * pan -l | |
+ * | |
+ * proves that there are no non-progress cycles | |
+ * (infinite executions *not* passing through | |
+ * the statement marked with a label starting | |
+ * with the prefix "progress") | |
+ * | |
+ */ | |
diff --git a/src/cmd/sam/mkfile b/src/cmd/sam/mkfile | |
t@@ -0,0 +1,40 @@ | |
+</$objtype/mkfile | |
+ | |
+TARG=sam | |
+OFILES=sam.$O\ | |
+ address.$O\ | |
+ buff.$O\ | |
+ cmd.$O\ | |
+ disk.$O\ | |
+ error.$O\ | |
+ file.$O\ | |
+ io.$O\ | |
+ list.$O\ | |
+ mesg.$O\ | |
+ moveto.$O\ | |
+ multi.$O\ | |
+ plan9.$O\ | |
+ rasp.$O\ | |
+ regexp.$O\ | |
+ shell.$O\ | |
+ string.$O\ | |
+ sys.$O\ | |
+ util.$O\ | |
+ xec.$O\ | |
+ | |
+HFILES=sam.h\ | |
+ errors.h\ | |
+ mesg.h\ | |
+ | |
+BIN=/$objtype/bin | |
+</sys/src/cmd/mkone | |
+ | |
+address.$O cmd.$O parse.$O xec.$O unix.$O: parse.h | |
+ | |
+safeinstall: $O.out | |
+ mv $BIN/$TARG $BIN/o$TARG | |
+ cp $prereq $BIN/$TARG | |
+ | |
+safeinstallall:V: | |
+ for (objtype in $CPUS) | |
+ mk safeinstall | |
diff --git a/src/cmd/sam/moveto.c b/src/cmd/sam/moveto.c | |
t@@ -0,0 +1,173 @@ | |
+#include "sam.h" | |
+ | |
+void | |
+moveto(File *f, Range r) | |
+{ | |
+ Posn p1 = r.p1, p2 = r.p2; | |
+ | |
+ f->dot.r.p1 = p1; | |
+ f->dot.r.p2 = p2; | |
+ if(f->rasp){ | |
+ telldot(f); | |
+ outTsl(Hmoveto, f->tag, f->dot.r.p1); | |
+ } | |
+} | |
+ | |
+void | |
+telldot(File *f) | |
+{ | |
+ if(f->rasp == 0) | |
+ panic("telldot"); | |
+ if(f->dot.r.p1==f->tdot.p1 && f->dot.r.p2==f->tdot.p2) | |
+ return; | |
+ outTsll(Hsetdot, f->tag, f->dot.r.p1, f->dot.r.p2); | |
+ f->tdot = f->dot.r; | |
+} | |
+ | |
+void | |
+tellpat(void) | |
+{ | |
+ outTS(Hsetpat, &lastpat); | |
+ patset = FALSE; | |
+} | |
+ | |
+#define CHARSHIFT 128 | |
+ | |
+void | |
+lookorigin(File *f, Posn p0, Posn ls) | |
+{ | |
+ int nl, nc, c; | |
+ Posn p, oldp0; | |
+ | |
+ if(p0 > f->_.nc) | |
+ p0 = f->_.nc; | |
+ oldp0 = p0; | |
+ p = p0; | |
+ for(nl=nc=c=0; c!=-1 && nl<ls && nc<ls*CHARSHIFT; nc++) | |
+ if((c=filereadc(f, --p)) == '\n'){ | |
+ nl++; | |
+ oldp0 = p0-nc; | |
+ } | |
+ if(c == -1) | |
+ p0 = 0; | |
+ else if(nl==0){ | |
+ if(p0>=CHARSHIFT/2) | |
+ p0-=CHARSHIFT/2; | |
+ else | |
+ p0 = 0; | |
+ }else | |
+ p0 = oldp0; | |
+ outTsl(Horigin, f->tag, p0); | |
+} | |
+ | |
+int | |
+alnum(int c) | |
+{ | |
+ /* | |
+ * Hard to get absolutely right. Use what we know about ASCII | |
+ * and assume anything above the Latin control characters is | |
+ * potentially an alphanumeric. | |
+ */ | |
+ if(c<=' ') | |
+ return 0; | |
+ if(0x7F<=c && c<=0xA0) | |
+ return 0; | |
+ if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c)) | |
+ return 0; | |
+ return 1; | |
+} | |
+ | |
+int | |
+clickmatch(File *f, int cl, int cr, int dir, Posn *p) | |
+{ | |
+ int c; | |
+ int nest = 1; | |
+ | |
+ for(;;){ | |
+ if(dir > 0){ | |
+ if(*p >= f->_.nc) | |
+ break; | |
+ c = filereadc(f, (*p)++); | |
+ }else{ | |
+ if(*p == 0) | |
+ break; | |
+ c = filereadc(f, --(*p)); | |
+ } | |
+ if(c == cr){ | |
+ if(--nest==0) | |
+ return 1; | |
+ }else if(c == cl) | |
+ nest++; | |
+ } | |
+ return cl=='\n' && nest==1; | |
+} | |
+ | |
+Rune* | |
+strrune(Rune *s, Rune c) | |
+{ | |
+ Rune c1; | |
+ | |
+ if(c == 0) { | |
+ while(*s++) | |
+ ; | |
+ return s-1; | |
+ } | |
+ | |
+ while(c1 = *s++) | |
+ if(c1 == c) | |
+ return s-1; | |
+ return 0; | |
+} | |
+ | |
+void | |
+doubleclick(File *f, Posn p1) | |
+{ | |
+ int c, i; | |
+ Rune *r, *l; | |
+ Posn p; | |
+ | |
+ if(p1 > f->_.nc) | |
+ return; | |
+ f->dot.r.p1 = f->dot.r.p2 = p1; | |
+ for(i=0; left[i]; i++){ | |
+ l = left[i]; | |
+ r = right[i]; | |
+ /* try left match */ | |
+ p = p1; | |
+ if(p1 == 0) | |
+ c = '\n'; | |
+ else | |
+ c = filereadc(f, p - 1); | |
+ if(strrune(l, c)){ | |
+ if(clickmatch(f, c, r[strrune(l, c)-l], 1, &p)){ | |
+ f->dot.r.p1 = p1; | |
+ f->dot.r.p2 = p-(c!='\n'); | |
+ } | |
+ return; | |
+ } | |
+ /* try right match */ | |
+ p = p1; | |
+ if(p1 == f->_.nc) | |
+ c = '\n'; | |
+ else | |
+ c = filereadc(f, p); | |
+ if(strrune(r, c)){ | |
+ if(clickmatch(f, c, l[strrune(r, c)-r], -1, &p)){ | |
+ f->dot.r.p1 = p; | |
+ if(c!='\n' || p!=0 || filereadc(f, 0)=='\n') | |
+ f->dot.r.p1++; | |
+ f->dot.r.p2 = p1+(p1<f->_.nc && c=='\n'); | |
+ } | |
+ return; | |
+ } | |
+ } | |
+ /* try filling out word to right */ | |
+ p = p1; | |
+ while(p < f->_.nc && alnum(filereadc(f, p++))) | |
+ f->dot.r.p2++; | |
+ /* try filling out word to left */ | |
+ p = p1; | |
+ while(--p >= 0 && alnum(filereadc(f, p))) | |
+ f->dot.r.p1--; | |
+} | |
+ | |
diff --git a/src/cmd/sam/multi.c b/src/cmd/sam/multi.c | |
t@@ -0,0 +1,123 @@ | |
+#include "sam.h" | |
+ | |
+List file; | |
+ushort tag; | |
+ | |
+File * | |
+newfile(void) | |
+{ | |
+ File *f; | |
+ | |
+ f = fileopen(); | |
+ inslist(&file, 0, (long)f); | |
+ f->tag = tag++; | |
+ if(downloaded) | |
+ outTs(Hnewname, f->tag); | |
+ /* already sorted; file name is "" */ | |
+ return f; | |
+} | |
+ | |
+int | |
+whichmenu(File *f) | |
+{ | |
+ int i; | |
+ | |
+ for(i=0; i<file.nused; i++) | |
+ if(file.filepptr[i]==f) | |
+ return i; | |
+ return -1; | |
+} | |
+ | |
+void | |
+delfile(File *f) | |
+{ | |
+ int w = whichmenu(f); | |
+ | |
+ if(w < 0) /* e.g. x/./D */ | |
+ return; | |
+ if(downloaded) | |
+ outTs(Hdelname, f->tag); | |
+ dellist(&file, w); | |
+ fileclose(f); | |
+} | |
+ | |
+void | |
+fullname(String *name) | |
+{ | |
+ if(name->n > 0 && name->s[0]!='/' && name->s[0]!=0) | |
+ Strinsert(name, &curwd, (Posn)0); | |
+} | |
+ | |
+void | |
+fixname(String *name) | |
+{ | |
+ String *t; | |
+ char *s; | |
+ | |
+ fullname(name); | |
+ s = Strtoc(name); | |
+ if(strlen(s) > 0) | |
+ s = cleanname(s); | |
+ t = tmpcstr(s); | |
+ Strduplstr(name, t); | |
+ free(s); | |
+ freetmpstr(t); | |
+ | |
+ if(Strispre(&curwd, name)) | |
+ Strdelete(name, 0, curwd.n); | |
+} | |
+ | |
+void | |
+sortname(File *f) | |
+{ | |
+ int i, cmp, w; | |
+ int dupwarned; | |
+ | |
+ w = whichmenu(f); | |
+ dupwarned = FALSE; | |
+ dellist(&file, w); | |
+ if(f == cmd) | |
+ i = 0; | |
+ else{ | |
+ for(i=0; i<file.nused; i++){ | |
+ cmp = Strcmp(&f->name, &file.filepptr[i]->name); | |
+ if(cmp==0 && !dupwarned){ | |
+ dupwarned = TRUE; | |
+ warn_S(Wdupname, &f->name); | |
+ }else if(cmp<0 && (i>0 || cmd==0)) | |
+ break; | |
+ } | |
+ } | |
+ inslist(&file, i, (long)f); | |
+ if(downloaded) | |
+ outTsS(Hmovname, f->tag, &f->name); | |
+} | |
+ | |
+void | |
+state(File *f, int cleandirty) | |
+{ | |
+ if(f == cmd) | |
+ return; | |
+ f->unread = FALSE; | |
+ if(downloaded && whichmenu(f)>=0){ /* else flist or menu */ | |
+ if(f->mod && cleandirty!=Dirty) | |
+ outTs(Hclean, f->tag); | |
+ else if(!f->mod && cleandirty==Dirty) | |
+ outTs(Hdirty, f->tag); | |
+ } | |
+ if(cleandirty == Clean) | |
+ f->mod = FALSE; | |
+ else | |
+ f->mod = TRUE; | |
+} | |
+ | |
+File * | |
+lookfile(String *s) | |
+{ | |
+ int i; | |
+ | |
+ for(i=0; i<file.nused; i++) | |
+ if(Strcmp(&file.filepptr[i]->name, s) == 0) | |
+ return file.filepptr[i]; | |
+ return 0; | |
+} | |
diff --git a/src/cmd/sam/parse.h b/src/cmd/sam/parse.h | |
t@@ -0,0 +1,68 @@ | |
+typedef struct Addr Addr; | |
+typedef struct Cmd Cmd; | |
+struct Addr | |
+{ | |
+ char type; /* # (char addr), l (line addr), / ? . $ + - … | |
+ union{ | |
+ String *re; | |
+ Addr *aleft; /* left side of , and ; */ | |
+ } g; | |
+ Posn num; | |
+ Addr *next; /* or right side of , and ; … | |
+}; | |
+ | |
+#define are g.re | |
+#define left g.aleft | |
+ | |
+struct Cmd | |
+{ | |
+ Addr *addr; /* address (range of text) */ | |
+ String *re; /* regular expression for e.… | |
+ union{ | |
+ Cmd *cmd; /* target of x, g, {, etc. */ | |
+ String *text; /* text of a, c, i; rhs of… | |
+ Addr *addr; /* address for m, t */ | |
+ } g; | |
+ Cmd *next; /* pointer to next element in… | |
+ short num; | |
+ ushort flag; /* whatever */ | |
+ ushort cmdc; /* command character; 'x' e… | |
+}; | |
+ | |
+#define ccmd g.cmd | |
+#define ctext g.text | |
+#define caddr g.addr | |
+ | |
+extern struct cmdtab{ | |
+ ushort cmdc; /* command character */ | |
+ uchar text; /* takes a textual argument? */ | |
+ uchar regexp; /* takes a regular expression? */ | |
+ uchar addr; /* takes an address (m or t)? */ | |
+ uchar defcmd; /* default command; 0==>none */ | |
+ uchar defaddr; /* default address */ | |
+ uchar count; /* takes a count e.g. s2/// */ | |
+ char *token; /* takes text terminated by one of … | |
+ int (*fn)(File*, Cmd*); /* function to call with parse t… | |
+}cmdtab[]; | |
+ | |
+enum Defaddr{ /* default addresses */ | |
+ aNo, | |
+ aDot, | |
+ aAll, | |
+}; | |
+ | |
+int nl_cmd(File*, Cmd*), a_cmd(File*, Cmd*), b_cmd(File*, Cmd*); | |
+int c_cmd(File*, Cmd*), cd_cmd(File*, Cmd*), d_cmd(File*, Cmd*); | |
+int D_cmd(File*, Cmd*), e_cmd(File*, Cmd*); | |
+int f_cmd(File*, Cmd*), g_cmd(File*, Cmd*), i_cmd(File*, Cmd*); | |
+int k_cmd(File*, Cmd*), m_cmd(File*, Cmd*), n_cmd(File*, Cmd*); | |
+int p_cmd(File*, Cmd*), q_cmd(File*, Cmd*); | |
+int s_cmd(File*, Cmd*), u_cmd(File*, Cmd*), w_cmd(File*, Cmd*); | |
+int x_cmd(File*, Cmd*), X_cmd(File*, Cmd*), plan9_cmd(File*, Cmd*); | |
+int eq_cmd(File*, Cmd*); | |
+ | |
+ | |
+String *getregexp(int); | |
+Addr *newaddr(void); | |
+Address address(Addr*, Address, int); | |
+int cmdexec(File*, Cmd*); | |
diff --git a/src/cmd/sam/plan9.c b/src/cmd/sam/plan9.c | |
t@@ -0,0 +1,185 @@ | |
+#include "sam.h" | |
+ | |
+Rune samname[] = L"~~sam~~"; | |
+ | |
+Rune *left[]= { | |
+ L"{[(<«", | |
+ L"\n", | |
+ L"'\"`", | |
+ 0 | |
+}; | |
+Rune *right[]= { | |
+ L"}])>»", | |
+ L"\n", | |
+ L"'\"`", | |
+ 0 | |
+}; | |
+ | |
+char RSAM[] = "sam"; | |
+char SAMTERM[] = "/bin/aux/samterm"; | |
+char HOME[] = "home"; | |
+char TMPDIR[] = "/tmp"; | |
+char SH[] = "rc"; | |
+char SHPATH[] = "/bin/rc"; | |
+char RX[] = "rx"; | |
+char RXPATH[] = "/bin/rx"; | |
+char SAMSAVECMD[] = "/bin/rc\n/sys/lib/samsave"; | |
+ | |
+void | |
+dprint(char *z, ...) | |
+{ | |
+ char buf[BLOCKSIZE]; | |
+ va_list arg; | |
+ | |
+ va_start(arg, z); | |
+ vseprint(buf, &buf[BLOCKSIZE], z, arg); | |
+ va_end(arg); | |
+ termwrite(buf); | |
+} | |
+ | |
+void | |
+print_ss(char *s, String *a, String *b) | |
+{ | |
+ dprint("?warning: %s: `%.*S' and `%.*S'\n", s, a->n, a->s, b->n, b->s); | |
+} | |
+ | |
+void | |
+print_s(char *s, String *a) | |
+{ | |
+ dprint("?warning: %s `%.*S'\n", s, a->n, a->s); | |
+} | |
+ | |
+char* | |
+getuser(void) | |
+{ | |
+ static char user[64]; | |
+ int fd; | |
+ | |
+ if(user[0] == 0){ | |
+ fd = open("/dev/user", 0); | |
+ if(fd<0 || read(fd, user, sizeof user-1)<=0) | |
+ strcpy(user, "none"); | |
+ close(fd); | |
+ } | |
+ return user; | |
+} | |
+ | |
+int | |
+statfile(char *name, ulong *dev, uvlong *id, long *time, long *length, long *a… | |
+{ | |
+ Dir *dirb; | |
+ | |
+ dirb = dirstat(name); | |
+ if(dirb == nil) | |
+ return -1; | |
+ if(dev) | |
+ *dev = dirb->type|(dirb->dev<<16); | |
+ if(id) | |
+ *id = dirb->qid.path; | |
+ if(time) | |
+ *time = dirb->mtime; | |
+ if(length) | |
+ *length = dirb->length; | |
+ if(appendonly) | |
+ *appendonly = dirb->mode & DMAPPEND; | |
+ free(dirb); | |
+ return 1; | |
+} | |
+ | |
+int | |
+statfd(int fd, ulong *dev, uvlong *id, long *time, long *length, long *appendo… | |
+{ | |
+ Dir *dirb; | |
+ | |
+ dirb = dirfstat(fd); | |
+ if(dirb == nil) | |
+ return -1; | |
+ if(dev) | |
+ *dev = dirb->type|(dirb->dev<<16); | |
+ if(id) | |
+ *id = dirb->qid.path; | |
+ if(time) | |
+ *time = dirb->mtime; | |
+ if(length) | |
+ *length = dirb->length; | |
+ if(appendonly) | |
+ *appendonly = dirb->mode & DMAPPEND; | |
+ free(dirb); | |
+ return 1; | |
+} | |
+ | |
+void | |
+notifyf(void *a, char *s) | |
+{ | |
+ USED(a); | |
+ if(bpipeok && strcmp(s, "sys: write on closed pipe") == 0) | |
+ noted(NCONT); | |
+ if(strcmp(s, "interrupt") == 0) | |
+ noted(NCONT); | |
+ panicking = 1; | |
+ rescue(); | |
+ noted(NDFLT); | |
+} | |
+ | |
+int | |
+newtmp(int num) | |
+{ | |
+ int i, fd; | |
+ static char tempnam[30]; | |
+ | |
+ i = getpid(); | |
+ do | |
+ snprint(tempnam, sizeof tempnam, "%s/%d%.4s%dsam", TMPDIR, num… | |
+ while(access(tempnam, 0) == 0); | |
+ fd = create(tempnam, ORDWR|OCEXEC|ORCLOSE, 0000); | |
+ if(fd < 0){ | |
+ remove(tempnam); | |
+ fd = create(tempnam, ORDWR|OCEXEC|ORCLOSE, 0000); | |
+ } | |
+ return fd; | |
+} | |
+ | |
+int | |
+waitfor(int pid) | |
+{ | |
+ int msg; | |
+ Waitmsg *w; | |
+ | |
+ while((w = wait()) != nil){ | |
+ if(w->pid != pid){ | |
+ free(w); | |
+ continue; | |
+ } | |
+ msg = (w->msg[0] != '\0'); | |
+ free(w); | |
+ return msg; | |
+ } | |
+ return -1; | |
+} | |
+ | |
+void | |
+samerr(char *buf) | |
+{ | |
+ sprint(buf, "%s/sam.err", TMPDIR); | |
+} | |
+ | |
+void* | |
+emalloc(ulong n) | |
+{ | |
+ void *p; | |
+ | |
+ p = malloc(n); | |
+ if(p == 0) | |
+ panic("malloc fails"); | |
+ memset(p, 0, n); | |
+ return p; | |
+} | |
+ | |
+void* | |
+erealloc(void *p, ulong n) | |
+{ | |
+ p = realloc(p, n); | |
+ if(p == 0) | |
+ panic("realloc fails"); | |
+ return p; | |
+} | |
diff --git a/src/cmd/sam/plumb.c b/src/cmd/sam/plumb.c | |
t@@ -0,0 +1,9 @@ | |
+#include <u.h> | |
+#include "plumb.h" | |
+ | |
+/* XXX - Can we do better than this? */ | |
+char *cleanname(char *s) { return s; } | |
+char *plumbunpackattr(char *cbuf) { return 0; } | |
+char *plumbpack(Plumbmsg *pm, int *i) { return 0; } | |
+int plumbfree(Plumbmsg *pm) { return 0; } | |
+ | |
diff --git a/src/cmd/sam/rasp.c b/src/cmd/sam/rasp.c | |
t@@ -0,0 +1,325 @@ | |
+#include "sam.h" | |
+/* | |
+ * GROWDATASIZE must be big enough that all errors go out as Hgrowdata's, | |
+ * so they will be scrolled into visibility in the ~~sam~~ window (yuck!). | |
+ */ | |
+#define GROWDATASIZE 50 /* if size is > this, send data w… | |
+ | |
+void rcut(List*, Posn, Posn); | |
+int rterm(List*, Posn); | |
+void rgrow(List*, Posn, Posn); | |
+ | |
+static Posn growpos; | |
+static Posn grown; | |
+static Posn shrinkpos; | |
+static Posn shrunk; | |
+ | |
+/* | |
+ * rasp routines inform the terminal of changes to the file. | |
+ * | |
+ * a rasp is a list of spans within the file, and an indication | |
+ * of whether the terminal knows about the span. | |
+ * | |
+ * optimize by coalescing multiple updates to the same span | |
+ * if it is not known by the terminal. | |
+ * | |
+ * other possible optimizations: flush terminal's rasp by cut everything, | |
+ * insert everything if rasp gets too large. | |
+ */ | |
+ | |
+/* | |
+ * only called for initial load of file | |
+ */ | |
+void | |
+raspload(File *f) | |
+{ | |
+ if(f->rasp == nil) | |
+ return; | |
+ grown = f->_.nc; | |
+ growpos = 0; | |
+ if(f->_.nc) | |
+ rgrow(f->rasp, 0, f->_.nc); | |
+ raspdone(f, 1); | |
+} | |
+ | |
+void | |
+raspstart(File *f) | |
+{ | |
+ if(f->rasp == nil) | |
+ return; | |
+ grown = 0; | |
+ shrunk = 0; | |
+ noflush = 1; | |
+} | |
+ | |
+void | |
+raspdone(File *f, int toterm) | |
+{ | |
+ if(f->dot.r.p1 > f->_.nc) | |
+ f->dot.r.p1 = f->_.nc; | |
+ if(f->dot.r.p2 > f->_.nc) | |
+ f->dot.r.p2 = f->_.nc; | |
+ if(f->mark.p1 > f->_.nc) | |
+ f->mark.p1 = f->_.nc; | |
+ if(f->mark.p2 > f->_.nc) | |
+ f->mark.p2 = f->_.nc; | |
+ if(f->rasp == nil) | |
+ return; | |
+ if(grown) | |
+ outTsll(Hgrow, f->tag, growpos, grown); | |
+ else if(shrunk) | |
+ outTsll(Hcut, f->tag, shrinkpos, shrunk); | |
+ if(toterm) | |
+ outTs(Hcheck0, f->tag); | |
+ outflush(); | |
+ noflush = 0; | |
+ if(f == cmd){ | |
+ cmdpt += cmdptadv; | |
+ cmdptadv = 0; | |
+ } | |
+} | |
+ | |
+void | |
+raspdelete(File *f, uint p1, uint p2, int toterm) | |
+{ | |
+ long n; | |
+ | |
+ n = p2 - p1; | |
+ if(n == 0) | |
+ return; | |
+ | |
+ if(p2 <= f->dot.r.p1){ | |
+ f->dot.r.p1 -= n; | |
+ f->dot.r.p2 -= n; | |
+ } | |
+ if(p2 <= f->mark.p1){ | |
+ f->mark.p1 -= n; | |
+ f->mark.p2 -= n; | |
+ } | |
+ | |
+ if(f->rasp == nil) | |
+ return; | |
+ | |
+ if(f==cmd && p1<cmdpt){ | |
+ if(p2 <= cmdpt) | |
+ cmdpt -= n; | |
+ else | |
+ cmdpt = p1; | |
+ } | |
+ if(toterm){ | |
+ if(grown){ | |
+ outTsll(Hgrow, f->tag, growpos, grown); | |
+ grown = 0; | |
+ }else if(shrunk && shrinkpos!=p1 && shrinkpos!=p2){ | |
+ outTsll(Hcut, f->tag, shrinkpos, shrunk); | |
+ shrunk = 0; | |
+ } | |
+ if(!shrunk || shrinkpos==p2) | |
+ shrinkpos = p1; | |
+ shrunk += n; | |
+ } | |
+ rcut(f->rasp, p1, p2); | |
+} | |
+ | |
+void | |
+raspinsert(File *f, uint p1, Rune *buf, uint n, int toterm) | |
+{ | |
+ Range r; | |
+ | |
+ if(n == 0) | |
+ return; | |
+ | |
+ if(p1 < f->dot.r.p1){ | |
+ f->dot.r.p1 += n; | |
+ f->dot.r.p2 += n; | |
+ } | |
+ if(p1 < f->mark.p1){ | |
+ f->mark.p1 += n; | |
+ f->mark.p2 += n; | |
+ } | |
+ | |
+ | |
+ if(f->rasp == nil) | |
+ return; | |
+ if(f==cmd && p1<cmdpt) | |
+ cmdpt += n; | |
+ if(toterm){ | |
+ if(shrunk){ | |
+ outTsll(Hcut, f->tag, shrinkpos, shrunk); | |
+ shrunk = 0; | |
+ } | |
+ if(n>GROWDATASIZE || !rterm(f->rasp, p1)){ | |
+ rgrow(f->rasp, p1, n); | |
+ if(grown && growpos+grown!=p1 && growpos!=p1){ | |
+ outTsll(Hgrow, f->tag, growpos, grown); | |
+ grown = 0; | |
+ } | |
+ if(!grown) | |
+ growpos = p1; | |
+ grown += n; | |
+ }else{ | |
+ if(grown){ | |
+ outTsll(Hgrow, f->tag, growpos, grown); | |
+ grown = 0; | |
+ } | |
+ rgrow(f->rasp, p1, n); | |
+ r = rdata(f->rasp, p1, n); | |
+ if(r.p1!=p1 || r.p2!=p1+n) | |
+ panic("rdata in toterminal"); | |
+ outTsllS(Hgrowdata, f->tag, p1, n, tmprstr(buf, n)); | |
+ } | |
+ }else{ | |
+ rgrow(f->rasp, p1, n); | |
+ r = rdata(f->rasp, p1, n); | |
+ if(r.p1!=p1 || r.p2!=p1+n) | |
+ panic("rdata in toterminal"); | |
+ } | |
+} | |
+ | |
+#define M 0x80000000L | |
+#define P(i) r->longptr[i] | |
+#define T(i) (P(i)&M) /* in terminal */ | |
+#define L(i) (P(i)&~M) /* length of this piece */ | |
+ | |
+void | |
+rcut(List *r, Posn p1, Posn p2) | |
+{ | |
+ Posn p, x; | |
+ int i; | |
+ | |
+ if(p1 == p2) | |
+ panic("rcut 0"); | |
+ for(p=0,i=0; i<r->nused && p+L(i)<=p1; p+=L(i++)) | |
+ ; | |
+ if(i == r->nused) | |
+ panic("rcut 1"); | |
+ if(p < p1){ /* chop this piece */ | |
+ if(p+L(i) < p2){ | |
+ x = p1-p; | |
+ p += L(i); | |
+ }else{ | |
+ x = L(i)-(p2-p1); | |
+ p = p2; | |
+ } | |
+ if(T(i)) | |
+ P(i) = x|M; | |
+ else | |
+ P(i) = x; | |
+ i++; | |
+ } | |
+ while(i<r->nused && p+L(i)<=p2){ | |
+ p += L(i); | |
+ dellist(r, i); | |
+ } | |
+ if(p < p2){ | |
+ if(i == r->nused) | |
+ panic("rcut 2"); | |
+ x = L(i)-(p2-p); | |
+ if(T(i)) | |
+ P(i) = x|M; | |
+ else | |
+ P(i) = x; | |
+ } | |
+ /* can we merge i and i-1 ? */ | |
+ if(i>0 && i<r->nused && T(i-1)==T(i)){ | |
+ x = L(i-1)+L(i); | |
+ dellist(r, i--); | |
+ if(T(i)) | |
+ P(i)=x|M; | |
+ else | |
+ P(i)=x; | |
+ } | |
+} | |
+ | |
+void | |
+rgrow(List *r, Posn p1, Posn n) | |
+{ | |
+ Posn p; | |
+ int i; | |
+ | |
+ if(n == 0) | |
+ panic("rgrow 0"); | |
+ for(p=0,i=0; i<r->nused && p+L(i)<=p1; p+=L(i++)) | |
+ ; | |
+ if(i == r->nused){ /* stick on end of file */ | |
+ if(p!=p1) | |
+ panic("rgrow 1"); | |
+ if(i>0 && !T(i-1)) | |
+ P(i-1)+=n; | |
+ else | |
+ inslist(r, i, n); | |
+ }else if(!T(i)) /* goes in this empty piece */ | |
+ P(i)+=n; | |
+ else if(p==p1 && i>0 && !T(i-1)) /* special case; simplifies li… | |
+ P(i-1)+=n; | |
+ else if(p==p1) | |
+ inslist(r, i, n); | |
+ else{ /* must break piece in terminal */ | |
+ inslist(r, i+1, (L(i)-(p1-p))|M); | |
+ inslist(r, i+1, n); | |
+ P(i) = (p1-p)|M; | |
+ } | |
+} | |
+ | |
+int | |
+rterm(List *r, Posn p1) | |
+{ | |
+ Posn p; | |
+ int i; | |
+ | |
+ for(p = 0,i = 0; i<r->nused && p+L(i)<=p1; p+=L(i++)) | |
+ ; | |
+ if(i==r->nused && (i==0 || !T(i-1))) | |
+ return 0; | |
+ return T(i); | |
+} | |
+ | |
+Range | |
+rdata(List *r, Posn p1, Posn n) | |
+{ | |
+ Posn p; | |
+ int i; | |
+ Range rg; | |
+ | |
+ if(n==0) | |
+ panic("rdata 0"); | |
+ for(p = 0,i = 0; i<r->nused && p+L(i)<=p1; p+=L(i++)) | |
+ ; | |
+ if(i==r->nused) | |
+ panic("rdata 1"); | |
+ if(T(i)){ | |
+ n-=L(i)-(p1-p); | |
+ if(n<=0){ | |
+ rg.p1 = rg.p2 = p1; | |
+ return rg; | |
+ } | |
+ p+=L(i++); | |
+ p1 = p; | |
+ } | |
+ if(T(i) || i==r->nused) | |
+ panic("rdata 2"); | |
+ if(p+L(i)<p1+n) | |
+ n = L(i)-(p1-p); | |
+ rg.p1 = p1; | |
+ rg.p2 = p1+n; | |
+ if(p!=p1){ | |
+ inslist(r, i+1, L(i)-(p1-p)); | |
+ P(i)=p1-p; | |
+ i++; | |
+ } | |
+ if(L(i)!=n){ | |
+ inslist(r, i+1, L(i)-n); | |
+ P(i)=n; | |
+ } | |
+ P(i)|=M; | |
+ /* now i is set; can we merge? */ | |
+ if(i<r->nused-1 && T(i+1)){ | |
+ P(i)=(n+=L(i+1))|M; | |
+ dellist(r, i+1); | |
+ } | |
+ if(i>0 && T(i-1)){ | |
+ P(i)=(n+L(i-1))|M; | |
+ dellist(r, i-1); | |
+ } | |
+ return rg; | |
+} | |
diff --git a/src/cmd/sam/regexp.c b/src/cmd/sam/regexp.c | |
t@@ -0,0 +1,801 @@ | |
+#include "sam.h" | |
+ | |
+Rangeset sel; | |
+String lastregexp; | |
+/* | |
+ * Machine Information | |
+ */ | |
+typedef struct Inst Inst; | |
+ | |
+struct Inst | |
+{ | |
+ long type; /* < 0x10000 ==> literal, otherwise action */ | |
+ union { | |
+ int rsid; | |
+ int rsubid; | |
+ int class; | |
+ struct Inst *rother; | |
+ struct Inst *rright; | |
+ } r; | |
+ union{ | |
+ struct Inst *lleft; | |
+ struct Inst *lnext; | |
+ } l; | |
+}; | |
+#define sid r.rsid | |
+#define subid r.rsubid | |
+#define rclass r.class | |
+#define other r.rother | |
+#define right r.rright | |
+#define left l.lleft | |
+#define next l.lnext | |
+ | |
+#define NPROG 1024 | |
+Inst program[NPROG]; | |
+Inst *progp; | |
+Inst *startinst; /* First inst. of program; might not be program… | |
+Inst *bstartinst; /* same for backwards machine */ | |
+ | |
+typedef struct Ilist Ilist; | |
+struct Ilist | |
+{ | |
+ Inst *inst; /* Instruction of the thread */ | |
+ Rangeset se; | |
+ Posn startp; /* first char of match */ | |
+}; | |
+ | |
+#define NLIST 128 | |
+ | |
+Ilist *tl, *nl; /* This list, next list */ | |
+Ilist list[2][NLIST]; | |
+static Rangeset sempty; | |
+ | |
+/* | |
+ * Actions and Tokens | |
+ * | |
+ * 0x100xx are operators, value == precedence | |
+ * 0x200xx are tokens, i.e. operands for operators | |
+ */ | |
+#define OPERATOR 0x10000 /* Bitmask of all operators */ | |
+#define START 0x10000 /* Start, used for marker o… | |
+#define RBRA 0x10001 /* Right bracket, ) */ | |
+#define LBRA 0x10002 /* Left bracket, ( */ | |
+#define OR 0x10003 /* Alternation, | */ | |
+#define CAT 0x10004 /* Concatentation, implicit o… | |
+#define STAR 0x10005 /* Closure, * */ | |
+#define PLUS 0x10006 /* a+ == aa* */ | |
+#define QUEST 0x10007 /* a? == a|nothing, i.e. 0 … | |
+#define ANY 0x20000 /* Any character but newline,… | |
+#define NOP 0x20001 /* No operation, internal use… | |
+#define BOL 0x20002 /* Beginning of line, ^ */ | |
+#define EOL 0x20003 /* End of line, $ */ | |
+#define CCLASS 0x20004 /* Character class, [] */ | |
+#define NCCLASS 0x20005 /* Negated character clas… | |
+#define END 0x20077 /* Terminate: match found */ | |
+ | |
+#define ISATOR 0x10000 | |
+#define ISAND 0x20000 | |
+ | |
+/* | |
+ * Parser Information | |
+ */ | |
+typedef struct Node Node; | |
+struct Node | |
+{ | |
+ Inst *first; | |
+ Inst *last; | |
+}; | |
+ | |
+#define NSTACK 20 | |
+Node andstack[NSTACK]; | |
+Node *andp; | |
+int atorstack[NSTACK]; | |
+int *atorp; | |
+int lastwasand; /* Last token was operand */ | |
+int cursubid; | |
+int subidstack[NSTACK]; | |
+int *subidp; | |
+int backwards; | |
+int nbra; | |
+Rune *exprp; /* pointer to next character in source expr… | |
+#define DCLASS 10 /* allocation increment */ | |
+int nclass; /* number active */ | |
+int Nclass; /* high water mark */ | |
+Rune **class; | |
+int negateclass; | |
+ | |
+void addinst(Ilist *l, Inst *inst, Rangeset *sep); | |
+void newmatch(Rangeset*); | |
+void bnewmatch(Rangeset*); | |
+void pushand(Inst*, Inst*); | |
+void pushator(int); | |
+Node *popand(int); | |
+int popator(void); | |
+void startlex(Rune*); | |
+int lex(void); | |
+void operator(int); | |
+void operand(int); | |
+void evaluntil(int); | |
+void optimize(Inst*); | |
+void bldcclass(void); | |
+ | |
+void | |
+regerror(Err e) | |
+{ | |
+ Strzero(&lastregexp); | |
+ error(e); | |
+} | |
+ | |
+void | |
+regerror_c(Err e, int c) | |
+{ | |
+ Strzero(&lastregexp); | |
+ error_c(e, c); | |
+} | |
+ | |
+Inst * | |
+newinst(int t) | |
+{ | |
+ if(progp >= &program[NPROG]) | |
+ regerror(Etoolong); | |
+ progp->type = t; | |
+ progp->left = 0; | |
+ progp->right = 0; | |
+ return progp++; | |
+} | |
+ | |
+Inst * | |
+realcompile(Rune *s) | |
+{ | |
+ int token; | |
+ | |
+ startlex(s); | |
+ atorp = atorstack; | |
+ andp = andstack; | |
+ subidp = subidstack; | |
+ cursubid = 0; | |
+ lastwasand = FALSE; | |
+ /* Start with a low priority operator to prime parser */ | |
+ pushator(START-1); | |
+ while((token=lex()) != END){ | |
+ if((token&ISATOR) == OPERATOR) | |
+ operator(token); | |
+ else | |
+ operand(token); | |
+ } | |
+ /* Close with a low priority operator */ | |
+ evaluntil(START); | |
+ /* Force END */ | |
+ operand(END); | |
+ evaluntil(START); | |
+ if(nbra) | |
+ regerror(Eleftpar); | |
+ --andp; /* points to first and only operand */ | |
+ return andp->first; | |
+} | |
+ | |
+void | |
+compile(String *s) | |
+{ | |
+ int i; | |
+ Inst *oprogp; | |
+ | |
+ if(Strcmp(s, &lastregexp)==0) | |
+ return; | |
+ for(i=0; i<nclass; i++) | |
+ free(class[i]); | |
+ nclass = 0; | |
+ progp = program; | |
+ backwards = FALSE; | |
+ startinst = realcompile(s->s); | |
+ optimize(program); | |
+ oprogp = progp; | |
+ backwards = TRUE; | |
+ bstartinst = realcompile(s->s); | |
+ optimize(oprogp); | |
+ Strduplstr(&lastregexp, s); | |
+} | |
+ | |
+void | |
+operand(int t) | |
+{ | |
+ Inst *i; | |
+ if(lastwasand) | |
+ operator(CAT); /* catenate is implicit */ | |
+ i = newinst(t); | |
+ if(t == CCLASS){ | |
+ if(negateclass) | |
+ i->type = NCCLASS; /* UGH */ | |
+ i->rclass = nclass-1; /* UGH */ | |
+ } | |
+ pushand(i, i); | |
+ lastwasand = TRUE; | |
+} | |
+ | |
+void | |
+operator(int t) | |
+{ | |
+ if(t==RBRA && --nbra<0) | |
+ regerror(Erightpar); | |
+ if(t==LBRA){ | |
+/* | |
+ * if(++cursubid >= NSUBEXP) | |
+ * regerror(Esubexp); | |
+ */ | |
+ cursubid++; /* silently ignored */ | |
+ nbra++; | |
+ if(lastwasand) | |
+ operator(CAT); | |
+ }else | |
+ evaluntil(t); | |
+ if(t!=RBRA) | |
+ pushator(t); | |
+ lastwasand = FALSE; | |
+ if(t==STAR || t==QUEST || t==PLUS || t==RBRA) | |
+ lastwasand = TRUE; /* these look like operands */ | |
+} | |
+ | |
+void | |
+cant(char *s) | |
+{ | |
+ char buf[100]; | |
+ | |
+ sprint(buf, "regexp: can't happen: %s", s); | |
+ panic(buf); | |
+} | |
+ | |
+void | |
+pushand(Inst *f, Inst *l) | |
+{ | |
+ if(andp >= &andstack[NSTACK]) | |
+ cant("operand stack overflow"); | |
+ andp->first = f; | |
+ andp->last = l; | |
+ andp++; | |
+} | |
+ | |
+void | |
+pushator(int t) | |
+{ | |
+ if(atorp >= &atorstack[NSTACK]) | |
+ cant("operator stack overflow"); | |
+ *atorp++=t; | |
+ if(cursubid >= NSUBEXP) | |
+ *subidp++= -1; | |
+ else | |
+ *subidp++=cursubid; | |
+} | |
+ | |
+Node * | |
+popand(int op) | |
+{ | |
+ if(andp <= &andstack[0]) | |
+ if(op) | |
+ regerror_c(Emissop, op); | |
+ else | |
+ regerror(Ebadregexp); | |
+ return --andp; | |
+} | |
+ | |
+int | |
+popator(void) | |
+{ | |
+ if(atorp <= &atorstack[0]) | |
+ cant("operator stack underflow"); | |
+ --subidp; | |
+ return *--atorp; | |
+} | |
+ | |
+void | |
+evaluntil(int pri) | |
+{ | |
+ Node *op1, *op2, *t; | |
+ Inst *inst1, *inst2; | |
+ | |
+ while(pri==RBRA || atorp[-1]>=pri){ | |
+ switch(popator()){ | |
+ case LBRA: | |
+ op1 = popand('('); | |
+ inst2 = newinst(RBRA); | |
+ inst2->subid = *subidp; | |
+ op1->last->next = inst2; | |
+ inst1 = newinst(LBRA); | |
+ inst1->subid = *subidp; | |
+ inst1->next = op1->first; | |
+ pushand(inst1, inst2); | |
+ return; /* must have been RBRA */ | |
+ default: | |
+ panic("unknown regexp operator"); | |
+ break; | |
+ case OR: | |
+ op2 = popand('|'); | |
+ op1 = popand('|'); | |
+ inst2 = newinst(NOP); | |
+ op2->last->next = inst2; | |
+ op1->last->next = inst2; | |
+ inst1 = newinst(OR); | |
+ inst1->right = op1->first; | |
+ inst1->left = op2->first; | |
+ pushand(inst1, inst2); | |
+ break; | |
+ case CAT: | |
+ op2 = popand(0); | |
+ op1 = popand(0); | |
+ if(backwards && op2->first->type!=END) | |
+ t = op1, op1 = op2, op2 = t; | |
+ op1->last->next = op2->first; | |
+ pushand(op1->first, op2->last); | |
+ break; | |
+ case STAR: | |
+ op2 = popand('*'); | |
+ inst1 = newinst(OR); | |
+ op2->last->next = inst1; | |
+ inst1->right = op2->first; | |
+ pushand(inst1, inst1); | |
+ break; | |
+ case PLUS: | |
+ op2 = popand('+'); | |
+ inst1 = newinst(OR); | |
+ op2->last->next = inst1; | |
+ inst1->right = op2->first; | |
+ pushand(op2->first, inst1); | |
+ break; | |
+ case QUEST: | |
+ op2 = popand('?'); | |
+ inst1 = newinst(OR); | |
+ inst2 = newinst(NOP); | |
+ inst1->left = inst2; | |
+ inst1->right = op2->first; | |
+ op2->last->next = inst2; | |
+ pushand(inst1, inst2); | |
+ break; | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+optimize(Inst *start) | |
+{ | |
+ Inst *inst, *target; | |
+ | |
+ for(inst=start; inst->type!=END; inst++){ | |
+ target = inst->next; | |
+ while(target->type == NOP) | |
+ target = target->next; | |
+ inst->next = target; | |
+ } | |
+} | |
+ | |
+#ifdef DEBUG | |
+void | |
+dumpstack(void){ | |
+ Node *stk; | |
+ int *ip; | |
+ | |
+ dprint("operators\n"); | |
+ for(ip = atorstack; ip<atorp; ip++) | |
+ dprint("0%o\n", *ip); | |
+ dprint("operands\n"); | |
+ for(stk = andstack; stk<andp; stk++) | |
+ dprint("0%o\t0%o\n", stk->first->type, stk->last->type); | |
+} | |
+void | |
+dump(void){ | |
+ Inst *l; | |
+ | |
+ l = program; | |
+ do{ | |
+ dprint("%d:\t0%o\t%d\t%d\n", l-program, l->type, | |
+ l->left-program, l->right-program); | |
+ }while(l++->type); | |
+} | |
+#endif | |
+ | |
+void | |
+startlex(Rune *s) | |
+{ | |
+ exprp = s; | |
+ nbra = 0; | |
+} | |
+ | |
+ | |
+int | |
+lex(void){ | |
+ int c= *exprp++; | |
+ | |
+ switch(c){ | |
+ case '\\': | |
+ if(*exprp) | |
+ if((c= *exprp++)=='n') | |
+ c='\n'; | |
+ break; | |
+ case 0: | |
+ c = END; | |
+ --exprp; /* In case we come here again */ | |
+ break; | |
+ case '*': | |
+ c = STAR; | |
+ break; | |
+ case '?': | |
+ c = QUEST; | |
+ break; | |
+ case '+': | |
+ c = PLUS; | |
+ break; | |
+ case '|': | |
+ c = OR; | |
+ break; | |
+ case '.': | |
+ c = ANY; | |
+ break; | |
+ case '(': | |
+ c = LBRA; | |
+ break; | |
+ case ')': | |
+ c = RBRA; | |
+ break; | |
+ case '^': | |
+ c = BOL; | |
+ break; | |
+ case '$': | |
+ c = EOL; | |
+ break; | |
+ case '[': | |
+ c = CCLASS; | |
+ bldcclass(); | |
+ break; | |
+ } | |
+ return c; | |
+} | |
+ | |
+long | |
+nextrec(void){ | |
+ if(exprp[0]==0 || (exprp[0]=='\\' && exprp[1]==0)) | |
+ regerror(Ebadclass); | |
+ if(exprp[0] == '\\'){ | |
+ exprp++; | |
+ if(*exprp=='n'){ | |
+ exprp++; | |
+ return '\n'; | |
+ } | |
+ return *exprp++|0x10000; | |
+ } | |
+ return *exprp++; | |
+} | |
+ | |
+void | |
+bldcclass(void) | |
+{ | |
+ long c1, c2, n, na; | |
+ Rune *classp; | |
+ | |
+ classp = emalloc(DCLASS*RUNESIZE); | |
+ n = 0; | |
+ na = DCLASS; | |
+ /* we have already seen the '[' */ | |
+ if(*exprp == '^'){ | |
+ classp[n++] = '\n'; /* don't match newline in negate ca… | |
+ negateclass = TRUE; | |
+ exprp++; | |
+ }else | |
+ negateclass = FALSE; | |
+ while((c1 = nextrec()) != ']'){ | |
+ if(c1 == '-'){ | |
+ Error: | |
+ free(classp); | |
+ regerror(Ebadclass); | |
+ } | |
+ if(n+4 >= na){ /* 3 runes plus NUL */ | |
+ na += DCLASS; | |
+ classp = erealloc(classp, na*RUNESIZE); | |
+ } | |
+ if(*exprp == '-'){ | |
+ exprp++; /* eat '-' */ | |
+ if((c2 = nextrec()) == ']') | |
+ goto Error; | |
+ classp[n+0] = 0xFFFF; | |
+ classp[n+1] = c1; | |
+ classp[n+2] = c2; | |
+ n += 3; | |
+ }else | |
+ classp[n++] = c1; | |
+ } | |
+ classp[n] = 0; | |
+ if(nclass == Nclass){ | |
+ Nclass += DCLASS; | |
+ class = erealloc(class, Nclass*sizeof(Rune*)); | |
+ } | |
+ class[nclass++] = classp; | |
+} | |
+ | |
+int | |
+classmatch(int classno, int c, int negate) | |
+{ | |
+ Rune *p; | |
+ | |
+ p = class[classno]; | |
+ while(*p){ | |
+ if(*p == 0xFFFF){ | |
+ if(p[1]<=c && c<=p[2]) | |
+ return !negate; | |
+ p += 3; | |
+ }else if(*p++ == c) | |
+ return !negate; | |
+ } | |
+ return negate; | |
+} | |
+ | |
+/* | |
+ * Note optimization in addinst: | |
+ * *l must be pending when addinst called; if *l has been looked | |
+ * at already, the optimization is a bug. | |
+ */ | |
+void | |
+addinst(Ilist *l, Inst *inst, Rangeset *sep) | |
+{ | |
+ Ilist *p; | |
+ | |
+ for(p = l; p->inst; p++){ | |
+ if(p->inst==inst){ | |
+ if((sep)->p[0].p1 < p->se.p[0].p1) | |
+ p->se= *sep; /* this would be bug */ | |
+ return; /* It's already there */ | |
+ } | |
+ } | |
+ p->inst = inst; | |
+ p->se= *sep; | |
+ (p+1)->inst = 0; | |
+} | |
+ | |
+int | |
+execute(File *f, Posn startp, Posn eof) | |
+{ | |
+ int flag = 0; | |
+ Inst *inst; | |
+ Ilist *tlp; | |
+ Posn p = startp; | |
+ int nnl = 0, ntl; | |
+ int c; | |
+ int wrapped = 0; | |
+ int startchar = startinst->type<OPERATOR? startinst->type : 0; | |
+ | |
+ list[0][0].inst = list[1][0].inst = 0; | |
+ sel.p[0].p1 = -1; | |
+ /* Execute machine once for each character */ | |
+ for(;;p++){ | |
+ doloop: | |
+ c = filereadc(f, p); | |
+ if(p>=eof || c<0){ | |
+ switch(wrapped++){ | |
+ case 0: /* let loop run one more click … | |
+ case 2: | |
+ break; | |
+ case 1: /* expired; wrap to beginning */ | |
+ if(sel.p[0].p1>=0 || eof!=INFINITY) | |
+ goto Return; | |
+ list[0][0].inst = list[1][0].inst = 0; | |
+ p = 0; | |
+ goto doloop; | |
+ default: | |
+ goto Return; | |
+ } | |
+ }else if(((wrapped && p>=startp) || sel.p[0].p1>0) && nnl==0) | |
+ break; | |
+ /* fast check for first char */ | |
+ if(startchar && nnl==0 && c!=startchar) | |
+ continue; | |
+ tl = list[flag]; | |
+ nl = list[flag^=1]; | |
+ nl->inst = 0; | |
+ ntl = nnl; | |
+ nnl = 0; | |
+ if(sel.p[0].p1<0 && (!wrapped || p<startp || startp==eof)){ | |
+ /* Add first instruction to this list */ | |
+ if(++ntl >= NLIST) | |
+ Overflow: | |
+ error(Eoverflow); | |
+ sempty.p[0].p1 = p; | |
+ addinst(tl, startinst, &sempty); | |
+ } | |
+ /* Execute machine until this list is empty */ | |
+ for(tlp = tl; inst = tlp->inst; tlp++){ /* assignment =… | |
+ Switchstmt: | |
+ switch(inst->type){ | |
+ default: /* regular character */ | |
+ if(inst->type==c){ | |
+ Addinst: | |
+ if(++nnl >= NLIST) | |
+ goto Overflow; | |
+ addinst(nl, inst->next, &tlp->se); | |
+ } | |
+ break; | |
+ case LBRA: | |
+ if(inst->subid>=0) | |
+ tlp->se.p[inst->subid].p1 = p; | |
+ inst = inst->next; | |
+ goto Switchstmt; | |
+ case RBRA: | |
+ if(inst->subid>=0) | |
+ tlp->se.p[inst->subid].p2 = p; | |
+ inst = inst->next; | |
+ goto Switchstmt; | |
+ case ANY: | |
+ if(c!='\n') | |
+ goto Addinst; | |
+ break; | |
+ case BOL: | |
+ if(p==0 || filereadc(f, p - 1)=='\n'){ | |
+ Step: | |
+ inst = inst->next; | |
+ goto Switchstmt; | |
+ } | |
+ break; | |
+ case EOL: | |
+ if(c == '\n') | |
+ goto Step; | |
+ break; | |
+ case CCLASS: | |
+ if(c>=0 && classmatch(inst->rclass, c, 0)) | |
+ goto Addinst; | |
+ break; | |
+ case NCCLASS: | |
+ if(c>=0 && classmatch(inst->rclass, c, 1)) | |
+ goto Addinst; | |
+ break; | |
+ case OR: | |
+ /* evaluate right choice later */ | |
+ if(++ntl >= NLIST) | |
+ goto Overflow; | |
+ addinst(tlp, inst->right, &tlp->se); | |
+ /* efficiency: advance and re-evaluate */ | |
+ inst = inst->left; | |
+ goto Switchstmt; | |
+ case END: /* Match! */ | |
+ tlp->se.p[0].p2 = p; | |
+ newmatch(&tlp->se); | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ Return: | |
+ return sel.p[0].p1>=0; | |
+} | |
+ | |
+void | |
+newmatch(Rangeset *sp) | |
+{ | |
+ int i; | |
+ | |
+ if(sel.p[0].p1<0 || sp->p[0].p1<sel.p[0].p1 || | |
+ (sp->p[0].p1==sel.p[0].p1 && sp->p[0].p2>sel.p[0].p2)) | |
+ for(i = 0; i<NSUBEXP; i++) | |
+ sel.p[i] = sp->p[i]; | |
+} | |
+ | |
+int | |
+bexecute(File *f, Posn startp) | |
+{ | |
+ int flag = 0; | |
+ Inst *inst; | |
+ Ilist *tlp; | |
+ Posn p = startp; | |
+ int nnl = 0, ntl; | |
+ int c; | |
+ int wrapped = 0; | |
+ int startchar = bstartinst->type<OPERATOR? bstartinst->type : 0; | |
+ | |
+ list[0][0].inst = list[1][0].inst = 0; | |
+ sel.p[0].p1= -1; | |
+ /* Execute machine once for each character, including terminal NUL */ | |
+ for(;;--p){ | |
+ doloop: | |
+ if((c = filereadc(f, p - 1))==-1){ | |
+ switch(wrapped++){ | |
+ case 0: /* let loop run one more click … | |
+ case 2: | |
+ break; | |
+ case 1: /* expired; wrap to end */ | |
+ if(sel.p[0].p1>=0) | |
+ case 3: | |
+ goto Return; | |
+ list[0][0].inst = list[1][0].inst = 0; | |
+ p = f->_.nc; | |
+ goto doloop; | |
+ default: | |
+ goto Return; | |
+ } | |
+ }else if(((wrapped && p<=startp) || sel.p[0].p1>0) && nnl==0) | |
+ break; | |
+ /* fast check for first char */ | |
+ if(startchar && nnl==0 && c!=startchar) | |
+ continue; | |
+ tl = list[flag]; | |
+ nl = list[flag^=1]; | |
+ nl->inst = 0; | |
+ ntl = nnl; | |
+ nnl = 0; | |
+ if(sel.p[0].p1<0 && (!wrapped || p>startp)){ | |
+ /* Add first instruction to this list */ | |
+ if(++ntl >= NLIST) | |
+ Overflow: | |
+ error(Eoverflow); | |
+ /* the minus is so the optimizations in addinst work */ | |
+ sempty.p[0].p1 = -p; | |
+ addinst(tl, bstartinst, &sempty); | |
+ } | |
+ /* Execute machine until this list is empty */ | |
+ for(tlp = tl; inst = tlp->inst; tlp++){ /* assignment =… | |
+ Switchstmt: | |
+ switch(inst->type){ | |
+ default: /* regular character */ | |
+ if(inst->type == c){ | |
+ Addinst: | |
+ if(++nnl >= NLIST) | |
+ goto Overflow; | |
+ addinst(nl, inst->next, &tlp->se); | |
+ } | |
+ break; | |
+ case LBRA: | |
+ if(inst->subid>=0) | |
+ tlp->se.p[inst->subid].p1 = p; | |
+ inst = inst->next; | |
+ goto Switchstmt; | |
+ case RBRA: | |
+ if(inst->subid >= 0) | |
+ tlp->se.p[inst->subid].p2 = p; | |
+ inst = inst->next; | |
+ goto Switchstmt; | |
+ case ANY: | |
+ if(c != '\n') | |
+ goto Addinst; | |
+ break; | |
+ case BOL: | |
+ if(c=='\n' || p==0){ | |
+ Step: | |
+ inst = inst->next; | |
+ goto Switchstmt; | |
+ } | |
+ break; | |
+ case EOL: | |
+ if(p==f->_.nc || filereadc(f, p)=='\n') | |
+ goto Step; | |
+ break; | |
+ case CCLASS: | |
+ if(c>=0 && classmatch(inst->rclass, c, 0)) | |
+ goto Addinst; | |
+ break; | |
+ case NCCLASS: | |
+ if(c>=0 && classmatch(inst->rclass, c, 1)) | |
+ goto Addinst; | |
+ break; | |
+ case OR: | |
+ /* evaluate right choice later */ | |
+ if(++ntl >= NLIST) | |
+ goto Overflow; | |
+ addinst(tlp, inst->right, &tlp->se); | |
+ /* efficiency: advance and re-evaluate */ | |
+ inst = inst->left; | |
+ goto Switchstmt; | |
+ case END: /* Match! */ | |
+ tlp->se.p[0].p1 = -tlp->se.p[0].p1; /* minus s… | |
+ tlp->se.p[0].p2 = p; | |
+ bnewmatch(&tlp->se); | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ Return: | |
+ return sel.p[0].p1>=0; | |
+} | |
+ | |
+void | |
+bnewmatch(Rangeset *sp) | |
+{ | |
+ int i; | |
+ if(sel.p[0].p1<0 || sp->p[0].p1>sel.p[0].p2 || (sp->p[0].p1==sel.p[0].… | |
+ for(i = 0; i<NSUBEXP; i++){ /* note the reversal; p1<=p2… | |
+ sel.p[i].p1 = sp->p[i].p2; | |
+ sel.p[i].p2 = sp->p[i].p1; | |
+ } | |
+} | |
diff --git a/src/cmd/sam/sam b/src/cmd/sam/sam | |
Binary files differ. | |
diff --git a/src/cmd/sam/sam.c b/src/cmd/sam/sam.c | |
t@@ -0,0 +1,739 @@ | |
+#include "sam.h" | |
+ | |
+Rune genbuf[BLOCKSIZE]; | |
+int io; | |
+int panicking; | |
+int rescuing; | |
+String genstr; | |
+String rhs; | |
+String curwd; | |
+String cmdstr; | |
+Rune empty[] = { 0 }; | |
+char *genc; | |
+File *curfile; | |
+File *flist; | |
+File *cmd; | |
+jmp_buf mainloop; | |
+List tempfile; | |
+int quitok = TRUE; | |
+int downloaded; | |
+int dflag; | |
+int Rflag; | |
+char *machine; | |
+char *home; | |
+int bpipeok; | |
+int termlocked; | |
+char *samterm = SAMTERM; | |
+char *rsamname = RSAM; | |
+File *lastfile; | |
+Disk *disk; | |
+long seq; | |
+ | |
+Rune baddir[] = { '<', 'b', 'a', 'd', 'd', 'i', 'r', '>', '\n'}; | |
+ | |
+void usage(void); | |
+ | |
+int main(int argc, char *argv[]) | |
+{ | |
+ int i; | |
+ String *t; | |
+ char **ap, **arg; | |
+ | |
+ { // libfmt-2.0 uses %lu where we need %lud | |
+ extern int __flagfmt(Fmt*); | |
+ fmtinstall('u', __flagfmt); | |
+ } | |
+ | |
+ arg = argv++; | |
+ ap = argv; | |
+ while(argc>1 && argv[0] && argv[0][0]=='-'){ | |
+ switch(argv[0][1]){ | |
+ case 'd': | |
+ dflag++; | |
+ break; | |
+ | |
+ case 'r': | |
+ --argc, argv++; | |
+ if(argc == 1) | |
+ usage(); | |
+ machine = *argv; | |
+ break; | |
+ | |
+ case 'R': | |
+ Rflag++; | |
+ break; | |
+ | |
+ case 't': | |
+ --argc, argv++; | |
+ if(argc == 1) | |
+ usage(); | |
+ samterm = *argv; | |
+ break; | |
+ | |
+ case 's': | |
+ --argc, argv++; | |
+ if(argc == 1) | |
+ usage(); | |
+ rsamname = *argv; | |
+ break; | |
+ | |
+ case 'x': /* x11 option - strip the x */ | |
+ strcpy(*argv+1, *argv+2); | |
+ *ap++ = *argv++; | |
+ *ap++ = *argv; | |
+ argc--; | |
+ break; | |
+ | |
+ default: | |
+ dprint("sam: unknown flag %c\n", argv[0][1]); | |
+ exits("usage"); | |
+ } | |
+ --argc, argv++; | |
+ } | |
+ Strinit(&cmdstr); | |
+ Strinit0(&lastpat); | |
+ Strinit0(&lastregexp); | |
+ Strinit0(&genstr); | |
+ Strinit0(&rhs); | |
+ Strinit0(&curwd); | |
+ tempfile.listptr = emalloc(1); /* so it can be freed later */ | |
+ Strinit0(&plan9cmd); | |
+ home = getenv(HOME); | |
+ disk = diskinit(); | |
+ if(home == 0) | |
+ home = "/"; | |
+ if(!dflag) | |
+ startup(machine, Rflag, arg, ap); | |
+ notify(notifyf); | |
+ getcurwd(); | |
+ if(argc>1){ | |
+ for(i=0; i<argc-1; i++){ | |
+ if(!setjmp(mainloop)){ | |
+ t = tmpcstr(argv[i]); | |
+ Straddc(t, '\0'); | |
+ Strduplstr(&genstr, t); | |
+ freetmpstr(t); | |
+ fixname(&genstr); | |
+ logsetname(newfile(), &genstr); | |
+ } | |
+ } | |
+ }else if(!downloaded) | |
+ newfile(); | |
+ seq++; | |
+ if(file.nused) | |
+ current(file.filepptr[0]); | |
+ setjmp(mainloop); | |
+ cmdloop(); | |
+ trytoquit(); /* if we already q'ed, quitok will be TRUE */ | |
+ exits(0); | |
+} | |
+ | |
+void | |
+usage(void) | |
+{ | |
+ dprint("usage: sam [-d] [-t samterm] [-s sam name] -r machine\n"); | |
+ exits("usage"); | |
+} | |
+ | |
+void | |
+rescue(void) | |
+{ | |
+ int i, nblank = 0; | |
+ File *f; | |
+ char *c; | |
+ char buf[256]; | |
+ | |
+ if(rescuing++) | |
+ return; | |
+ io = -1; | |
+ for(i=0; i<file.nused; i++){ | |
+ f = file.filepptr[i]; | |
+ if(f==cmd || f->_.nc==0 || !fileisdirty(f)) | |
+ continue; | |
+ if(io == -1){ | |
+ sprint(buf, "%s/sam.save", home); | |
+ io = create(buf, 1, 0777); | |
+ if(io<0) | |
+ return; | |
+ } | |
+ if(f->name.s[0]){ | |
+ c = Strtoc(&f->name); | |
+ strncpy(buf, c, sizeof buf-1); | |
+ buf[sizeof buf-1] = 0; | |
+ free(c); | |
+ }else | |
+ sprint(buf, "nameless.%d", nblank++); | |
+ fprint(io, "#!%s '%s' $* <<'---%s'\n", SAMSAVECMD, buf, buf); | |
+ addr.r.p1 = 0, addr.r.p2 = f->_.nc; | |
+ writeio(f); | |
+ fprint(io, "\n---%s\n", (char *)buf); | |
+ } | |
+} | |
+ | |
+void | |
+panic(char *s) | |
+{ | |
+ int wasd; | |
+ | |
+ if(!panicking++ && !setjmp(mainloop)){ | |
+ wasd = downloaded; | |
+ downloaded = 0; | |
+ dprint("sam: panic: %s: %r\n", s); | |
+ if(wasd) | |
+ fprint(2, "sam: panic: %s: %r\n", s); | |
+ rescue(); | |
+ abort(); | |
+ } | |
+} | |
+ | |
+void | |
+hiccough(char *s) | |
+{ | |
+ File *f; | |
+ int i; | |
+ | |
+ if(rescuing) | |
+ exits("rescue"); | |
+ if(s) | |
+ dprint("%s\n", s); | |
+ resetcmd(); | |
+ resetxec(); | |
+ resetsys(); | |
+ if(io > 0) | |
+ close(io); | |
+ | |
+ /* | |
+ * back out any logged changes & restore old sequences | |
+ */ | |
+ for(i=0; i<file.nused; i++){ | |
+ f = file.filepptr[i]; | |
+ if(f==cmd) | |
+ continue; | |
+ if(f->seq==seq){ | |
+ bufdelete(&f->epsilon, 0, f->epsilon.nc); | |
+ f->seq = f->prevseq; | |
+ f->dot.r = f->prevdot; | |
+ f->mark = f->prevmark; | |
+ state(f, f->prevmod ? Dirty: Clean); | |
+ } | |
+ } | |
+ | |
+ update(); | |
+ if (curfile) { | |
+ if (curfile->unread) | |
+ curfile->unread = FALSE; | |
+ else if (downloaded) | |
+ outTs(Hcurrent, curfile->tag); | |
+ } | |
+ longjmp(mainloop, 1); | |
+} | |
+ | |
+void | |
+intr(void) | |
+{ | |
+ error(Eintr); | |
+} | |
+ | |
+void | |
+trytoclose(File *f) | |
+{ | |
+ char *t; | |
+ char buf[256]; | |
+ | |
+ if(f == cmd) /* possible? */ | |
+ return; | |
+ if(f->deleted) | |
+ return; | |
+ if(fileisdirty(f) && !f->closeok){ | |
+ f->closeok = TRUE; | |
+ if(f->name.s[0]){ | |
+ t = Strtoc(&f->name); | |
+ strncpy(buf, t, sizeof buf-1); | |
+ free(t); | |
+ }else | |
+ strcpy(buf, "nameless file"); | |
+ error_s(Emodified, buf); | |
+ } | |
+ f->deleted = TRUE; | |
+} | |
+ | |
+void | |
+trytoquit(void) | |
+{ | |
+ int c; | |
+ File *f; | |
+ | |
+ if(!quitok){ | |
+ for(c = 0; c<file.nused; c++){ | |
+ f = file.filepptr[c]; | |
+ if(f!=cmd && fileisdirty(f)){ | |
+ quitok = TRUE; | |
+ eof = FALSE; | |
+ error(Echanges); | |
+ } | |
+ } | |
+ } | |
+} | |
+ | |
+void | |
+load(File *f) | |
+{ | |
+ Address saveaddr; | |
+ | |
+ Strduplstr(&genstr, &f->name); | |
+ filename(f); | |
+ if(f->name.s[0]){ | |
+ saveaddr = addr; | |
+ edit(f, 'I'); | |
+ addr = saveaddr; | |
+ }else{ | |
+ f->unread = 0; | |
+ f->cleanseq = f->seq; | |
+ } | |
+ | |
+ fileupdate(f, TRUE, TRUE); | |
+} | |
+ | |
+void | |
+cmdupdate(void) | |
+{ | |
+ if(cmd && cmd->seq!=0){ | |
+ fileupdate(cmd, FALSE, downloaded); | |
+ cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->_.nc; | |
+ telldot(cmd); | |
+ } | |
+} | |
+ | |
+void | |
+delete(File *f) | |
+{ | |
+ if(downloaded && f->rasp) | |
+ outTs(Hclose, f->tag); | |
+ delfile(f); | |
+ if(f == curfile) | |
+ current(0); | |
+} | |
+ | |
+void | |
+update(void) | |
+{ | |
+ int i, anymod; | |
+ File *f; | |
+ | |
+ settempfile(); | |
+ for(anymod = i=0; i<tempfile.nused; i++){ | |
+ f = tempfile.filepptr[i]; | |
+ if(f==cmd) /* cmd gets done in main() */ | |
+ continue; | |
+ if(f->deleted) { | |
+ delete(f); | |
+ continue; | |
+ } | |
+ if(f->seq==seq && fileupdate(f, FALSE, downloaded)) | |
+ anymod++; | |
+ if(f->rasp) | |
+ telldot(f); | |
+ } | |
+ if(anymod) | |
+ seq++; | |
+} | |
+ | |
+File * | |
+current(File *f) | |
+{ | |
+ return curfile = f; | |
+} | |
+ | |
+void | |
+edit(File *f, int cmd) | |
+{ | |
+ int empty = TRUE; | |
+ Posn p; | |
+ int nulls; | |
+ | |
+ if(cmd == 'r') | |
+ logdelete(f, addr.r.p1, addr.r.p2); | |
+ if(cmd=='e' || cmd=='I'){ | |
+ logdelete(f, (Posn)0, f->_.nc); | |
+ addr.r.p2 = f->_.nc; | |
+ }else if(f->_.nc!=0 || (f->name.s[0] && Strcmp(&genstr, &f->name)!=0)) | |
+ empty = FALSE; | |
+ if((io = open(genc, OREAD))<0) { | |
+ if (curfile && curfile->unread) | |
+ curfile->unread = FALSE; | |
+ error_r(Eopen, genc); | |
+ } | |
+ p = readio(f, &nulls, empty, TRUE); | |
+ closeio((cmd=='e' || cmd=='I')? -1 : p); | |
+ if(cmd == 'r') | |
+ f->ndot.r.p1 = addr.r.p2, f->ndot.r.p2 = addr.r.p2+p; | |
+ else | |
+ f->ndot.r.p1 = f->ndot.r.p2 = 0; | |
+ f->closeok = empty; | |
+ if (quitok) | |
+ quitok = empty; | |
+ else | |
+ quitok = FALSE; | |
+ state(f, empty && !nulls? Clean : Dirty); | |
+ if(empty && !nulls) | |
+ f->cleanseq = f->seq; | |
+ if(cmd == 'e') | |
+ filename(f); | |
+} | |
+ | |
+int | |
+getname(File *f, String *s, int save) | |
+{ | |
+ int c, i; | |
+ | |
+ Strzero(&genstr); | |
+ if(genc){ | |
+ free(genc); | |
+ genc = 0; | |
+ } | |
+ if(s==0 || (c = s->s[0])==0){ /* no name provided */ | |
+ if(f) | |
+ Strduplstr(&genstr, &f->name); | |
+ goto Return; | |
+ } | |
+ if(c!=' ' && c!='\t') | |
+ error(Eblank); | |
+ for(i=0; (c=s->s[i])==' ' || c=='\t'; i++) | |
+ ; | |
+ while(s->s[i] > ' ') | |
+ Straddc(&genstr, s->s[i++]); | |
+ if(s->s[i]) | |
+ error(Enewline); | |
+ fixname(&genstr); | |
+ if(f && (save || f->name.s[0]==0)){ | |
+ logsetname(f, &genstr); | |
+ if(Strcmp(&f->name, &genstr)){ | |
+ quitok = f->closeok = FALSE; | |
+ f->qidpath = 0; | |
+ f->mtime = 0; | |
+ state(f, Dirty); /* if it's 'e', fix later */ | |
+ } | |
+ } | |
+ Return: | |
+ genc = Strtoc(&genstr); | |
+ i = genstr.n; | |
+ if(i && genstr.s[i-1]==0) | |
+ i--; | |
+ return i; /* strlen(name) */ | |
+} | |
+ | |
+void | |
+filename(File *f) | |
+{ | |
+ if(genc) | |
+ free(genc); | |
+ genc = Strtoc(&genstr); | |
+ dprint("%c%c%c %s\n", " '"[f->mod], | |
+ "-+"[f->rasp!=0], " ."[f==curfile], genc); | |
+} | |
+ | |
+void | |
+undostep(File *f, int isundo) | |
+{ | |
+ uint p1, p2; | |
+ int mod; | |
+ | |
+ mod = f->mod; | |
+ fileundo(f, isundo, 1, &p1, &p2, TRUE); | |
+ f->ndot = f->dot; | |
+ if(f->mod){ | |
+ f->closeok = 0; | |
+ quitok = 0; | |
+ }else | |
+ f->closeok = 1; | |
+ | |
+ if(f->mod != mod){ | |
+ f->mod = mod; | |
+ if(mod) | |
+ mod = Clean; | |
+ else | |
+ mod = Dirty; | |
+ state(f, mod); | |
+ } | |
+} | |
+ | |
+int | |
+undo(int isundo) | |
+{ | |
+ File *f; | |
+ int i; | |
+ Mod max; | |
+ | |
+ max = undoseq(curfile, isundo); | |
+ if(max == 0) | |
+ return 0; | |
+ settempfile(); | |
+ for(i = 0; i<tempfile.nused; i++){ | |
+ f = tempfile.filepptr[i]; | |
+ if(f!=cmd && undoseq(f, isundo)==max) | |
+ undostep(f, isundo); | |
+ } | |
+ return 1; | |
+} | |
+ | |
+int | |
+readcmd(String *s) | |
+{ | |
+ int retcode; | |
+ | |
+ if(flist != 0) | |
+ fileclose(flist); | |
+ flist = fileopen(); | |
+ | |
+ addr.r.p1 = 0, addr.r.p2 = flist->_.nc; | |
+ retcode = plan9(flist, '<', s, FALSE); | |
+ fileupdate(flist, FALSE, FALSE); | |
+ flist->seq = 0; | |
+ if (flist->_.nc > BLOCKSIZE) | |
+ error(Etoolong); | |
+ Strzero(&genstr); | |
+ Strinsure(&genstr, flist->_.nc); | |
+ bufread(flist, (Posn)0, genbuf, flist->_.nc); | |
+ memmove(genstr.s, genbuf, flist->_.nc*RUNESIZE); | |
+ genstr.n = flist->_.nc; | |
+ Straddc(&genstr, '\0'); | |
+ return retcode; | |
+} | |
+ | |
+void | |
+getcurwd(void) | |
+{ | |
+ String *t; | |
+ char buf[256]; | |
+ | |
+ buf[0] = 0; | |
+ getwd(buf, sizeof(buf)); | |
+ t = tmpcstr(buf); | |
+ Strduplstr(&curwd, t); | |
+ freetmpstr(t); | |
+ if(curwd.n == 0) | |
+ warn(Wpwd); | |
+ else if(curwd.s[curwd.n-1] != '/') | |
+ Straddc(&curwd, '/'); | |
+} | |
+ | |
+void | |
+cd(String *str) | |
+{ | |
+ int i, fd; | |
+ char *s; | |
+ File *f; | |
+ String owd; | |
+ | |
+ getcurwd(); | |
+ if(getname((File *)0, str, FALSE)) | |
+ s = genc; | |
+ else | |
+ s = home; | |
+ if(chdir(s)) | |
+ syserror("chdir"); | |
+ fd = open("/dev/wdir", OWRITE); | |
+ if(fd > 0) | |
+ write(fd, s, strlen(s)); | |
+ dprint("!\n"); | |
+ Strinit(&owd); | |
+ Strduplstr(&owd, &curwd); | |
+ getcurwd(); | |
+ settempfile(); | |
+ for(i=0; i<tempfile.nused; i++){ | |
+ f = tempfile.filepptr[i]; | |
+ if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){ | |
+ Strinsert(&f->name, &owd, (Posn)0); | |
+ fixname(&f->name); | |
+ sortname(f); | |
+ }else if(f != cmd && Strispre(&curwd, &f->name)){ | |
+ fixname(&f->name); | |
+ sortname(f); | |
+ } | |
+ } | |
+ Strclose(&owd); | |
+} | |
+ | |
+int | |
+loadflist(String *s) | |
+{ | |
+ int c, i; | |
+ | |
+ c = s->s[0]; | |
+ for(i = 0; s->s[i]==' ' || s->s[i]=='\t'; i++) | |
+ ; | |
+ if((c==' ' || c=='\t') && s->s[i]!='\n'){ | |
+ if(s->s[i]=='<'){ | |
+ Strdelete(s, 0L, (long)i+1); | |
+ readcmd(s); | |
+ }else{ | |
+ Strzero(&genstr); | |
+ while((c = s->s[i++]) && c!='\n') | |
+ Straddc(&genstr, c); | |
+ Straddc(&genstr, '\0'); | |
+ } | |
+ }else{ | |
+ if(c != '\n') | |
+ error(Eblank); | |
+ Strdupl(&genstr, empty); | |
+ } | |
+ if(genc) | |
+ free(genc); | |
+ genc = Strtoc(&genstr); | |
+ return genstr.s[0]; | |
+} | |
+ | |
+File * | |
+readflist(int readall, int delete) | |
+{ | |
+ Posn i; | |
+ int c; | |
+ File *f; | |
+ String t; | |
+ | |
+ Strinit(&t); | |
+ for(i=0,f=0; f==0 || readall || delete; i++){ /* ++ skips blank… | |
+ Strdelete(&genstr, (Posn)0, i); | |
+ for(i=0; (c = genstr.s[i])==' ' || c=='\t' || c=='\n'; i++) | |
+ ; | |
+ if(i >= genstr.n) | |
+ break; | |
+ Strdelete(&genstr, (Posn)0, i); | |
+ for(i=0; (c=genstr.s[i]) && c!=' ' && c!='\t' && c!='\n'; i++) | |
+ ; | |
+ | |
+ if(i == 0) | |
+ break; | |
+ genstr.s[i] = 0; | |
+ Strduplstr(&t, tmprstr(genstr.s, i+1)); | |
+ fixname(&t); | |
+ f = lookfile(&t); | |
+ if(delete){ | |
+ if(f == 0) | |
+ warn_S(Wfile, &t); | |
+ else | |
+ trytoclose(f); | |
+ }else if(f==0 && readall) | |
+ logsetname(f = newfile(), &t); | |
+ } | |
+ Strclose(&t); | |
+ return f; | |
+} | |
+ | |
+File * | |
+tofile(String *s) | |
+{ | |
+ File *f; | |
+ | |
+ if(s->s[0] != ' ') | |
+ error(Eblank); | |
+ if(loadflist(s) == 0){ | |
+ f = lookfile(&genstr); /* empty string ==> nameless fil… | |
+ if(f == 0) | |
+ error_s(Emenu, genc); | |
+ }else if((f=readflist(FALSE, FALSE)) == 0) | |
+ error_s(Emenu, genc); | |
+ return current(f); | |
+} | |
+ | |
+File * | |
+getfile(String *s) | |
+{ | |
+ File *f; | |
+ | |
+ if(loadflist(s) == 0) | |
+ logsetname(f = newfile(), &genstr); | |
+ else if((f=readflist(TRUE, FALSE)) == 0) | |
+ error(Eblank); | |
+ return current(f); | |
+} | |
+ | |
+void | |
+closefiles(File *f, String *s) | |
+{ | |
+ if(s->s[0] == 0){ | |
+ if(f == 0) | |
+ error(Enofile); | |
+ trytoclose(f); | |
+ return; | |
+ } | |
+ if(s->s[0] != ' ') | |
+ error(Eblank); | |
+ if(loadflist(s) == 0) | |
+ error(Enewline); | |
+ readflist(FALSE, TRUE); | |
+} | |
+ | |
+void | |
+copy(File *f, Address addr2) | |
+{ | |
+ Posn p; | |
+ int ni; | |
+ for(p=addr.r.p1; p<addr.r.p2; p+=ni){ | |
+ ni = addr.r.p2-p; | |
+ if(ni > BLOCKSIZE) | |
+ ni = BLOCKSIZE; | |
+ bufread(f, p, genbuf, ni); | |
+ loginsert(addr2.f, addr2.r.p2, tmprstr(genbuf, ni)->s, ni); | |
+ } | |
+ addr2.f->ndot.r.p2 = addr2.r.p2+(f->dot.r.p2-f->dot.r.p1); | |
+ addr2.f->ndot.r.p1 = addr2.r.p2; | |
+} | |
+ | |
+void | |
+move(File *f, Address addr2) | |
+{ | |
+ if(addr.r.p2 <= addr2.r.p2){ | |
+ logdelete(f, addr.r.p1, addr.r.p2); | |
+ copy(f, addr2); | |
+ }else if(addr.r.p1 >= addr2.r.p2){ | |
+ copy(f, addr2); | |
+ logdelete(f, addr.r.p1, addr.r.p2); | |
+ }else | |
+ error(Eoverlap); | |
+} | |
+ | |
+Posn | |
+nlcount(File *f, Posn p0, Posn p1) | |
+{ | |
+ Posn nl = 0; | |
+ | |
+ while(p0 < p1) | |
+ if(filereadc(f, p0++)=='\n') | |
+ nl++; | |
+ return nl; | |
+} | |
+ | |
+void | |
+printposn(File *f, int charsonly) | |
+{ | |
+ Posn l1, l2; | |
+ | |
+ if(!charsonly){ | |
+ l1 = 1+nlcount(f, (Posn)0, addr.r.p1); | |
+ l2 = l1+nlcount(f, addr.r.p1, addr.r.p2); | |
+ /* check if addr ends with '\n' */ | |
+ if(addr.r.p2>0 && addr.r.p2>addr.r.p1 && filereadc(f, addr.r.p… | |
+ --l2; | |
+ dprint("%lud", l1); | |
+ if(l2 != l1) | |
+ dprint(",%lud", l2); | |
+ dprint("; "); | |
+ } | |
+ dprint("#%lud", addr.r.p1); | |
+ if(addr.r.p2 != addr.r.p1) | |
+ dprint(",#%lud", addr.r.p2); | |
+ dprint("\n"); | |
+} | |
+ | |
+void | |
+settempfile(void) | |
+{ | |
+ if(tempfile.nalloc < file.nused){ | |
+ free(tempfile.listptr); | |
+ tempfile.listptr = emalloc(sizeof(*tempfile.filepptr)*file.nus… | |
+ tempfile.nalloc = file.nused; | |
+ } | |
+ tempfile.nused = file.nused; | |
+ memmove(&tempfile.filepptr[0], &file.filepptr[0], file.nused*sizeof(Fi… | |
+} | |
diff --git a/src/cmd/sam/sam.h b/src/cmd/sam/sam.h | |
t@@ -0,0 +1,407 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <plumb.h> | |
+#include "errors.h" | |
+ | |
+/* | |
+ * BLOCKSIZE is relatively small to keep memory consumption down. | |
+ */ | |
+ | |
+#define BLOCKSIZE 2048 | |
+#define RUNESIZE sizeof(Rune) | |
+#define NDISC 5 | |
+#define NBUFFILES 3+2*NDISC /* plan 9+undo+snarf+NDISC*(t… | |
+#define NSUBEXP 10 | |
+ | |
+#define TRUE 1 | |
+#define FALSE 0 | |
+ | |
+#define INFINITY 0x7FFFFFFFL | |
+#define INCR 25 | |
+#define STRSIZE (2*BLOCKSIZE) | |
+ | |
+typedef long Posn; /* file position or address */ | |
+typedef ushort Mod; /* modification numbe… | |
+ | |
+typedef struct Address Address; | |
+typedef struct Block Block; | |
+typedef struct Buffer Buffer; | |
+typedef struct Disk Disk; | |
+typedef struct Discdesc Discdesc; | |
+typedef struct File File; | |
+typedef struct List List; | |
+typedef struct Range Range; | |
+typedef struct Rangeset Rangeset; | |
+typedef struct String String; | |
+ | |
+enum State | |
+{ | |
+ Clean = ' ', | |
+ Dirty = '\'', | |
+ Unread = '-', | |
+}; | |
+ | |
+struct Range | |
+{ | |
+ Posn p1, p2; | |
+}; | |
+ | |
+struct Rangeset | |
+{ | |
+ Range p[NSUBEXP]; | |
+}; | |
+ | |
+struct Address | |
+{ | |
+ Range r; | |
+ File *f; | |
+}; | |
+ | |
+struct String | |
+{ | |
+ short n; | |
+ short size; | |
+ Rune *s; | |
+}; | |
+ | |
+struct List /* code depends on a long being able to hold a pointer */ | |
+{ | |
+ int nalloc; | |
+ int nused; | |
+ union{ | |
+ void *listp; | |
+ Block *blkp; | |
+ long *longp; | |
+ uchar* *ucharp; | |
+ String* *stringp; | |
+ File* *filep; | |
+ long listv; | |
+ }g; | |
+}; | |
+ | |
+#define listptr g.listp | |
+#define blkptr g.blkp | |
+#define longptr g.longp | |
+#define ucharpptr g.ucharp | |
+#define stringpptr g.stringp | |
+#define filepptr g.filep | |
+#define listval g.listv | |
+ | |
+enum | |
+{ | |
+ Blockincr = 256, | |
+ Maxblock = 8*1024, | |
+ | |
+ BUFSIZE = Maxblock, /* size from fbufalloc() */ | |
+ RBUFSIZE = BUFSIZE/sizeof(Rune), | |
+}; | |
+ | |
+ | |
+enum | |
+{ | |
+ Null = '-', | |
+ Delete = 'd', | |
+ Insert = 'i', | |
+ Filename = 'f', | |
+ Dot = 'D', | |
+ Mark = 'm', | |
+}; | |
+ | |
+struct Block | |
+{ | |
+ uint addr; /* disk address in bytes */ | |
+ union | |
+ { | |
+ uint n; /* number of used runes in block */ | |
+ Block *next; /* pointer to next in free list */ | |
+ } _; | |
+}; | |
+ | |
+struct Disk | |
+{ | |
+ int fd; | |
+ uint addr; /* length of temp file */ | |
+ Block *free[Maxblock/Blockincr+1]; | |
+}; | |
+ | |
+Disk* diskinit(void); | |
+Block* disknewblock(Disk*, uint); | |
+void diskrelease(Disk*, Block*); | |
+void diskread(Disk*, Block*, Rune*, uint); | |
+void diskwrite(Disk*, Block**, Rune*, uint); | |
+ | |
+struct Buffer | |
+{ | |
+ uint nc; | |
+ Rune *c; /* cache */ | |
+ uint cnc; /* bytes in cache */ | |
+ uint cmax; /* size of allocated cache */ | |
+ uint cq; /* position of cache */ | |
+ int cdirty; /* cache needs to be written */ | |
+ uint cbi; /* index of cache Block */ | |
+ Block **bl; /* array of blocks */ | |
+ uint nbl; /* number of blocks */ | |
+}; | |
+void bufinsert(Buffer*, uint, Rune*, uint); | |
+void bufdelete(Buffer*, uint, uint); | |
+uint bufload(Buffer*, uint, int, int*); | |
+void bufread(Buffer*, uint, Rune*, uint); | |
+void bufclose(Buffer*); | |
+void bufreset(Buffer*); | |
+ | |
+struct File | |
+{ | |
+ Buffer _; /* the data */ | |
+ Buffer delta; /* transcript of changes */ | |
+ Buffer epsilon; /* inversion of delta for redo */ | |
+ String name; /* name of associated file … | |
+ uvlong qidpath; /* of file when read */ | |
+ uint mtime; /* of file when read */ | |
+ int dev; /* of file when read */ | |
+ int unread; /* file has not been read fr… | |
+ | |
+ long seq; /* if seq==0, File acts like B… | |
+ long cleanseq; /* f->seq at last read/write of f… | |
+ int mod; /* file appears modified in men… | |
+ char rescuing; /* sam exiting; this file unusabl… | |
+ | |
+// Text *curtext; /* most recently used associate… | |
+// Text **text; /* list of associated tex… | |
+// int ntext; | |
+// int dumpid; /* used in dumping zeroxed… | |
+ | |
+ Posn hiposn; /* highest address touched … | |
+ Address dot; /* current position */ | |
+ Address ndot; /* new current position af… | |
+ Range tdot; /* what terminal thinks is c… | |
+ Range mark; /* tagged spot in text (don'… | |
+ List *rasp; /* map of what terminal's go… | |
+ short tag; /* for communicating with ter… | |
+ char closeok; /* ok to close file? */ | |
+ char deleted; /* delete at completion of command… | |
+ Range prevdot; /* state before start of change */ | |
+ Range prevmark; | |
+ long prevseq; | |
+ int prevmod; | |
+}; | |
+//File* fileaddtext(File*, Text*); | |
+void fileclose(File*); | |
+void filedelete(File*, uint, uint); | |
+//void filedeltext(File*, Text*); | |
+void fileinsert(File*, uint, Rune*, uint); | |
+uint fileload(File*, uint, int, int*); | |
+void filemark(File*); | |
+void filereset(File*); | |
+void filesetname(File*, String*); | |
+void fileundelete(File*, Buffer*, uint, uint); | |
+void fileuninsert(File*, Buffer*, uint, uint); | |
+void fileunsetname(File*, Buffer*); | |
+void fileundo(File*, int, int, uint*, uint*, int); | |
+int fileupdate(File*, int, int); | |
+ | |
+int filereadc(File*, uint); | |
+File *fileopen(void); | |
+void loginsert(File*, uint, Rune*, uint); | |
+void logdelete(File*, uint, uint); | |
+void logsetname(File*, String*); | |
+int fileisdirty(File*); | |
+long undoseq(File*, int); | |
+long prevseq(Buffer*); | |
+ | |
+void raspload(File*); | |
+void raspstart(File*); | |
+void raspdelete(File*, uint, uint, int); | |
+void raspinsert(File*, uint, Rune*, uint, int); | |
+void raspdone(File*, int); | |
+ | |
+/* | |
+ * acme fns | |
+ */ | |
+void* fbufalloc(void); | |
+void fbuffree(void*); | |
+uint min(uint, uint); | |
+void cvttorunes(char*, int, Rune*, int*, int*, int*); | |
+ | |
+#define runemalloc(a) (Rune*)emalloc((a)*sizeof(Rune)) | |
+#define runerealloc(a, b) (Rune*)realloc((a), (b)*sizeof(Rune)) | |
+#define runemove(a, b, c) memmove((a), (b), (c)*sizeof(Rune)) | |
+ | |
+int alnum(int); | |
+int Read(int, void*, int); | |
+void Seek(int, long, int); | |
+int plan9(File*, int, String*, int); | |
+int Write(int, void*, int); | |
+int bexecute(File*, Posn); | |
+void cd(String*); | |
+void closefiles(File*, String*); | |
+void closeio(Posn); | |
+void cmdloop(void); | |
+void cmdupdate(void); | |
+void compile(String*); | |
+void copy(File*, Address); | |
+File *current(File*); | |
+void delete(File*); | |
+void delfile(File*); | |
+void dellist(List*, int); | |
+void doubleclick(File*, Posn); | |
+void dprint(char*, ...); | |
+void edit(File*, int); | |
+void *emalloc(ulong); | |
+void *erealloc(void*, ulong); | |
+void error(Err); | |
+void error_c(Err, int); | |
+void error_r(Err, char*); | |
+void error_s(Err, char*); | |
+int execute(File*, Posn, Posn); | |
+int filematch(File*, String*); | |
+void filename(File*); | |
+void fixname(String*); | |
+void fullname(String*); | |
+void getcurwd(void); | |
+File *getfile(String*); | |
+int getname(File*, String*, int); | |
+long getnum(int); | |
+void hiccough(char*); | |
+void inslist(List*, int, long); | |
+Address lineaddr(Posn, Address, int); | |
+void listfree(List*); | |
+void load(File*); | |
+File *lookfile(String*); | |
+void lookorigin(File*, Posn, Posn); | |
+int lookup(int); | |
+void move(File*, Address); | |
+void moveto(File*, Range); | |
+File *newfile(void); | |
+void nextmatch(File*, String*, Posn, int); | |
+int newtmp(int); | |
+void notifyf(void*, char*); | |
+void panic(char*); | |
+void printposn(File*, int); | |
+void print_ss(char*, String*, String*); | |
+void print_s(char*, String*); | |
+int rcv(void); | |
+Range rdata(List*, Posn, Posn); | |
+Posn readio(File*, int*, int, int); | |
+void rescue(void); | |
+void resetcmd(void); | |
+void resetsys(void); | |
+void resetxec(void); | |
+void rgrow(List*, Posn, Posn); | |
+void samerr(char*); | |
+void settempfile(void); | |
+int skipbl(void); | |
+void snarf(File*, Posn, Posn, Buffer*, int); | |
+void sortname(File*); | |
+void startup(char*, int, char**, char**); | |
+void state(File*, int); | |
+int statfd(int, ulong*, uvlong*, long*, long*, long*); | |
+int statfile(char*, ulong*, uvlong*, long*, long*, long*); | |
+void Straddc(String*, int); | |
+void Strclose(String*); | |
+int Strcmp(String*, String*); | |
+void Strdelete(String*, Posn, Posn); | |
+void Strdupl(String*, Rune*); | |
+void Strduplstr(String*, String*); | |
+void Strinit(String*); | |
+void Strinit0(String*); | |
+void Strinsert(String*, String*, Posn); | |
+void Strinsure(String*, ulong); | |
+int Strispre(String*, String*); | |
+void Strzero(String*); | |
+int Strlen(Rune*); | |
+char *Strtoc(String*); | |
+void syserror(char*); | |
+void telldot(File*); | |
+void tellpat(void); | |
+String *tmpcstr(char*); | |
+String *tmprstr(Rune*, int); | |
+void freetmpstr(String*); | |
+void termcommand(void); | |
+void termwrite(char*); | |
+File *tofile(String*); | |
+void trytoclose(File*); | |
+void trytoquit(void); | |
+int undo(int); | |
+void update(void); | |
+int waitfor(int); | |
+void warn(Warn); | |
+void warn_s(Warn, char*); | |
+void warn_SS(Warn, String*, String*); | |
+void warn_S(Warn, String*); | |
+int whichmenu(File*); | |
+void writef(File*); | |
+Posn writeio(File*); | |
+Discdesc *Dstart(void); | |
+ | |
+extern Rune samname[]; /* compiler dependent */ | |
+extern Rune *left[]; | |
+extern Rune *right[]; | |
+ | |
+extern char RSAM[]; /* system dependent */ | |
+extern char SAMTERM[]; | |
+extern char HOME[]; | |
+extern char TMPDIR[]; | |
+extern char SH[]; | |
+extern char SHPATH[]; | |
+extern char RX[]; | |
+extern char RXPATH[]; | |
+extern char SAMSAVECMD[]; | |
+ | |
+/* | |
+ * acme globals | |
+ */ | |
+extern long seq; | |
+extern Disk *disk; | |
+ | |
+extern char *rsamname; /* globals */ | |
+extern char *samterm; | |
+extern Rune genbuf[]; | |
+extern char *genc; | |
+extern int io; | |
+extern int patset; | |
+extern int quitok; | |
+extern Address addr; | |
+extern Buffer snarfbuf; | |
+extern Buffer plan9buf; | |
+extern List file; | |
+extern List tempfile; | |
+extern File *cmd; | |
+extern File *curfile; | |
+extern File *lastfile; | |
+extern Mod modnum; | |
+extern Posn cmdpt; | |
+extern Posn cmdptadv; | |
+extern Rangeset sel; | |
+extern String curwd; | |
+extern String cmdstr; | |
+extern String genstr; | |
+extern String lastpat; | |
+extern String lastregexp; | |
+extern String plan9cmd; | |
+extern int downloaded; | |
+extern int eof; | |
+extern int bpipeok; | |
+extern int panicking; | |
+extern Rune empty[]; | |
+extern int termlocked; | |
+extern int noflush; | |
+ | |
+#include "mesg.h" | |
+ | |
+void outTs(Hmesg, int); | |
+void outT0(Hmesg); | |
+void outTl(Hmesg, long); | |
+void outTslS(Hmesg, int, long, String*); | |
+void outTS(Hmesg, String*); | |
+void outTsS(Hmesg, int, String*); | |
+void outTsllS(Hmesg, int, long, long, String*); | |
+void outTsll(Hmesg, int, long, long); | |
+void outTsl(Hmesg, int, long); | |
+void outTsv(Hmesg, int, long); | |
+void outstart(Hmesg); | |
+void outcopy(int, void*); | |
+void outshort(int); | |
+void outlong(long); | |
+void outvlong(void*); | |
+void outsend(void); | |
+void outflush(void); | |
diff --git a/src/cmd/sam/shell.c b/src/cmd/sam/shell.c | |
t@@ -0,0 +1,152 @@ | |
+#include "sam.h" | |
+#include "parse.h" | |
+ | |
+extern jmp_buf mainloop; | |
+ | |
+char errfile[64]; | |
+String plan9cmd; /* null terminated */ | |
+Buffer plan9buf; | |
+void checkerrs(void); | |
+ | |
+int | |
+plan9(File *f, int type, String *s, int nest) | |
+{ | |
+ long l; | |
+ int m; | |
+ int pid, fd; | |
+ int retcode; | |
+ int pipe1[2], pipe2[2]; | |
+ | |
+ if(s->s[0]==0 && plan9cmd.s[0]==0) | |
+ error(Enocmd); | |
+ else if(s->s[0]) | |
+ Strduplstr(&plan9cmd, s); | |
+ if(downloaded){ | |
+ samerr(errfile); | |
+ remove(errfile); | |
+ } | |
+ if(type!='!' && pipe(pipe1)==-1) | |
+ error(Epipe); | |
+ if(type=='|') | |
+ snarf(f, addr.r.p1, addr.r.p2, &plan9buf, 1); | |
+ if((pid=fork()) == 0){ | |
+ if(downloaded){ /* also put nasty fd's into errfile */ | |
+ fd = create(errfile, 1, 0666L); | |
+ if(fd < 0) | |
+ fd = create("/dev/null", 1, 0666L); | |
+ dup(fd, 2); | |
+ close(fd); | |
+ /* 2 now points at err file */ | |
+ if(type == '>') | |
+ dup(2, 1); | |
+ else if(type=='!'){ | |
+ dup(2, 1); | |
+ fd = open("/dev/null", 0); | |
+ dup(fd, 0); | |
+ close(fd); | |
+ } | |
+ } | |
+ if(type != '!') { | |
+ if(type=='<' || type=='|') | |
+ dup(pipe1[1], 1); | |
+ else if(type == '>') | |
+ dup(pipe1[0], 0); | |
+ close(pipe1[0]); | |
+ close(pipe1[1]); | |
+ } | |
+ if(type == '|'){ | |
+ if(pipe(pipe2) == -1) | |
+ exits("pipe"); | |
+ if((pid = fork())==0){ | |
+ /* | |
+ * It's ok if we get SIGPIPE here | |
+ */ | |
+ close(pipe2[0]); | |
+ io = pipe2[1]; | |
+ if(retcode=!setjmp(mainloop)){ /* assig… | |
+ char *c; | |
+ for(l = 0; l<plan9buf.nc; l+=m){ | |
+ m = plan9buf.nc-l; | |
+ if(m>BLOCKSIZE-1) | |
+ m = BLOCKSIZE-1; | |
+ bufread(&plan9buf, l, genbuf, … | |
+ genbuf[m] = 0; | |
+ c = Strtoc(tmprstr(genbuf, m+1… | |
+ Write(pipe2[1], c, strlen(c)); | |
+ free(c); | |
+ } | |
+ } | |
+ exits(retcode? "error" : 0); | |
+ } | |
+ if(pid==-1){ | |
+ fprint(2, "Can't fork?!\n"); | |
+ exits("fork"); | |
+ } | |
+ dup(pipe2[0], 0); | |
+ close(pipe2[0]); | |
+ close(pipe2[1]); | |
+ } | |
+ if(type=='<'){ | |
+ close(0); /* so it won't read from terminal */ | |
+ open("/dev/null", 0); | |
+ } | |
+ execl(SHPATH, SH, "-c", Strtoc(&plan9cmd), (char *)0); | |
+ exits("exec"); | |
+ } | |
+ if(pid == -1) | |
+ error(Efork); | |
+ if(type=='<' || type=='|'){ | |
+ int nulls; | |
+ if(downloaded && addr.r.p1 != addr.r.p2) | |
+ outTl(Hsnarflen, addr.r.p2-addr.r.p1); | |
+ snarf(f, addr.r.p1, addr.r.p2, &snarfbuf, 0); | |
+ logdelete(f, addr.r.p1, addr.r.p2); | |
+ close(pipe1[1]); | |
+ io = pipe1[0]; | |
+ f->tdot.p1 = -1; | |
+ f->ndot.r.p2 = addr.r.p2+readio(f, &nulls, 0, FALSE); | |
+ f->ndot.r.p1 = addr.r.p2; | |
+ closeio((Posn)-1); | |
+ }else if(type=='>'){ | |
+ close(pipe1[0]); | |
+ io = pipe1[1]; | |
+ bpipeok = 1; | |
+ writeio(f); | |
+ bpipeok = 0; | |
+ closeio((Posn)-1); | |
+ } | |
+ retcode = waitfor(pid); | |
+ if(type=='|' || type=='<') | |
+ if(retcode!=0) | |
+ warn(Wbadstatus); | |
+ if(downloaded) | |
+ checkerrs(); | |
+ if(!nest) | |
+ dprint("!\n"); | |
+ return retcode; | |
+} | |
+ | |
+void | |
+checkerrs(void) | |
+{ | |
+ char buf[256]; | |
+ int f, n, nl; | |
+ char *p; | |
+ long l; | |
+ | |
+ if(statfile(errfile, 0, 0, 0, &l, 0) > 0 && l != 0){ | |
+ if((f=open((char *)errfile, 0)) != -1){ | |
+ if((n=read(f, buf, sizeof buf-1)) > 0){ | |
+ for(nl=0,p=buf; nl<3 && p<&buf[n]; p++) | |
+ if(*p=='\n') | |
+ nl++; | |
+ *p = 0; | |
+ dprint("%s", buf); | |
+ if(p-buf < l-1) | |
+ dprint("(sam: more in %s)\n", errfile); | |
+ } | |
+ close(f); | |
+ } | |
+ }else | |
+ remove((char *)errfile); | |
+} | |
diff --git a/src/cmd/sam/unix.c b/src/cmd/sam/unix.c | |
t@@ -0,0 +1,272 @@ | |
+#include <sys/types.h> | |
+#include <sys/stat.h> | |
+#include <sys/wait.h> | |
+#include <pwd.h> | |
+#include <signal.h> | |
+#include <fcntl.h> | |
+#include <errno.h> | |
+ | |
+#include "sam.h" | |
+ | |
+Rune samname[] = { '~', '~', 's', 'a', 'm', '~', '~', 0 }; | |
+ | |
+static Rune l1[] = { '{', '[', '(', '<', 0253, 0}; | |
+static Rune l2[] = { '\n', 0}; | |
+static Rune l3[] = { '\'', '"', '`', 0}; | |
+Rune *left[]= { l1, l2, l3, 0}; | |
+ | |
+static Rune r1[] = {'}', ']', ')', '>', 0273, 0}; | |
+static Rune r2[] = {'\n', 0}; | |
+static Rune r3[] = {'\'', '"', '`', 0}; | |
+Rune *right[]= { r1, r2, r3, 0}; | |
+ | |
+#ifndef SAMTERMNAME | |
+#define SAMTERMNAME "/usr/local/bin/samterm" | |
+#endif | |
+#ifndef TMPDIRNAME | |
+#define TMPDIRNAME "/tmp" | |
+#endif | |
+#ifndef SHNAME | |
+#define SHNAME "rc" | |
+#endif | |
+#ifndef SHPATHNAME | |
+#define SHPATHNAME "/bin/rc" | |
+#endif | |
+#ifndef RXNAME | |
+#define RXNAME "ssh" | |
+#endif | |
+#ifndef RXPATHNAME | |
+#define RXPATHNAME "/usr/local/bin/ssh" | |
+#endif | |
+#ifndef SAMSAVECMDNAME | |
+#define SAMSAVECMDNAME "/bin/rc\n/usr/local/bin/samsave" | |
+#endif | |
+ | |
+char RSAM[] = "sam"; | |
+char SAMTERM[] = SAMTERMNAME; | |
+char HOME[] = "HOME"; | |
+char TMPDIR[] = TMPDIRNAME; | |
+char SH[] = SHNAME; | |
+char SHPATH[] = SHPATHNAME; | |
+char RX[] = RXNAME; | |
+char RXPATH[] = RXPATHNAME; | |
+char SAMSAVECMD[] = SAMSAVECMDNAME; | |
+ | |
+ | |
+void | |
+dprint(char *z, ...) | |
+{ | |
+ char buf[BLOCKSIZE]; | |
+ va_list arg; | |
+ | |
+ va_start(arg, z); | |
+ vseprint(buf, &buf[BLOCKSIZE], z, arg); | |
+ va_end(arg); | |
+ termwrite(buf); | |
+} | |
+ | |
+void | |
+print_ss(char *s, String *a, String *b) | |
+{ | |
+ dprint("?warning: %s: `%.*S' and `%.*S'\n", s, a->n, a->s, b->n, b->s); | |
+} | |
+ | |
+void | |
+print_s(char *s, String *a) | |
+{ | |
+ dprint("?warning: %s `%.*S'\n", s, a->n, a->s); | |
+} | |
+ | |
+char* | |
+getuser(void) | |
+{ | |
+ static char user[64]; | |
+ if(user[0] == 0){ | |
+ struct passwd *pw = getpwuid(getuid()); | |
+ strcpy(user, pw ? pw->pw_name : "nobody"); | |
+ } | |
+ return user; | |
+} | |
+ | |
+int | |
+statfile(char *name, ulong *dev, uvlong *id, long *time, long *length, long *a… | |
+{ | |
+ struct stat dirb; | |
+ | |
+ if (stat(name, &dirb) == -1) | |
+ return -1; | |
+ if (dev) | |
+ *dev = dirb.st_dev; | |
+ if (id) | |
+ *id = dirb.st_ino; | |
+ if (time) | |
+ *time = dirb.st_mtime; | |
+ if (length) | |
+ *length = dirb.st_size; | |
+ if(appendonly) | |
+ *appendonly = 0; | |
+ return 1; | |
+} | |
+ | |
+int | |
+statfd(int fd, ulong *dev, uvlong *id, long *time, long *length, long *appendo… | |
+{ | |
+ struct stat dirb; | |
+ | |
+ if (fstat(fd, &dirb) == -1) | |
+ return -1; | |
+ if (dev) | |
+ *dev = dirb.st_dev; | |
+ if (id) | |
+ *id = dirb.st_ino; | |
+ if (time) | |
+ *time = dirb.st_mtime; | |
+ if (length) | |
+ *length = dirb.st_size; | |
+ if(appendonly) | |
+ *appendonly = 0; | |
+ return 1; | |
+} | |
+ | |
+void | |
+hup(int sig) | |
+{ | |
+ panicking = 1; // ??? | |
+ rescue(); | |
+ exit(1); | |
+} | |
+ | |
+int | |
+notify (void(*f)(void *, char *)) | |
+{ | |
+ signal(SIGINT, SIG_IGN); | |
+ signal(SIGPIPE, SIG_IGN); // XXX - bpipeok? | |
+ signal(SIGHUP, hup); | |
+ return 1; | |
+} | |
+ | |
+void | |
+notifyf(void *a, char *b) /* never called; hup is instead */ | |
+{ | |
+} | |
+ | |
+static int | |
+temp_file(char *buf, int bufsize) | |
+{ | |
+ char *tmp; | |
+ int n, fd; | |
+ | |
+ tmp = getenv("TMPDIR"); | |
+ if (!tmp) | |
+ tmp = TMPDIR; | |
+ | |
+ n = snprint(buf, bufsize, "%s/sam.%d.XXXXXXX", tmp, getuid()); | |
+ if (bufsize <= n) | |
+ return -1; | |
+ if ((fd = mkstemp(buf)) < 0) /* SES - linux sometimes uses mode 0666 … | |
+ return -1; | |
+ if (fcntl(fd, F_SETFD, fcntl(fd,F_GETFD,0) | FD_CLOEXEC) < 0) | |
+ return -1; | |
+ return fd; | |
+} | |
+ | |
+int | |
+tempdisk(void) | |
+{ | |
+ char buf[4096]; | |
+ int fd = temp_file(buf, sizeof buf); | |
+ if (fd >= 0) | |
+ remove(buf); | |
+ return fd; | |
+} | |
+ | |
+#undef wait | |
+int | |
+waitfor(int pid) | |
+{ | |
+ int wm; | |
+ int rpid; | |
+ | |
+ do; while((rpid = wait(&wm)) != pid && rpid != -1); | |
+ return (WEXITSTATUS(wm)); | |
+} | |
+ | |
+void | |
+samerr(char *buf) | |
+{ | |
+ sprint(buf, "%s/sam.%s.err", TMPDIR, getuser()); | |
+} | |
+ | |
+void* | |
+emalloc(ulong n) | |
+{ | |
+ void *p; | |
+ | |
+ p = malloc(n); | |
+ if(p == 0) | |
+ panic("malloc fails"); | |
+ memset(p, 0, n); | |
+ return p; | |
+} | |
+ | |
+void* | |
+erealloc(void *p, ulong n) | |
+{ | |
+ p = realloc(p, n); | |
+ if(p == 0) | |
+ panic("realloc fails"); | |
+ return p; | |
+} | |
+ | |
+#if 0 | |
+char * | |
+strdup(const char *s) | |
+{ | |
+ return strcpy(emalloc(strlen(s)), s); | |
+} | |
+#endif | |
+ | |
+/* | |
+void exits(const char *s) | |
+{ | |
+ if (s) fprint(2, "exit: %s\n", s); | |
+ exit(s != 0); | |
+} | |
+ | |
+void | |
+_exits(const char *s) | |
+{ | |
+ if (s) fprint(2, "exit: %s\n", s); | |
+ _exit(s != 0); | |
+} | |
+ | |
+int errstr(char *buf, int size) | |
+{ | |
+ extern int errno; | |
+ | |
+ snprint(buf, size, "%s", strerror(errno)); | |
+ return 1; | |
+} | |
+*/ | |
+ | |
+int create(char *name, int omode, int perm) | |
+{ | |
+ int mode; | |
+ int fd; | |
+ | |
+ if (omode & OWRITE) mode = O_WRONLY; | |
+ else if (omode & OREAD) mode = O_RDONLY; | |
+ else mode = O_RDWR; | |
+ | |
+ if ((fd = open(name, mode|O_CREAT|O_TRUNC, perm)) < 0) | |
+ return fd; | |
+ | |
+ if (omode & OCEXEC) | |
+ fcntl(fd, F_SETFD, fcntl(fd,F_GETFD,0) | FD_CLOEXEC); | |
+ | |
+ /* SES - not exactly right, but hopefully good enough. */ | |
+ if (omode & ORCLOSE) | |
+ remove(name); | |
+ | |
+ return fd; | |
+} | |
diff --git a/src/cmd/sam/xec.c b/src/cmd/sam/xec.c | |
t@@ -0,0 +1,508 @@ | |
+#include "sam.h" | |
+#include "parse.h" | |
+ | |
+int Glooping; | |
+int nest; | |
+ | |
+int append(File*, Cmd*, Posn); | |
+int display(File*); | |
+void looper(File*, Cmd*, int); | |
+void filelooper(Cmd*, int); | |
+void linelooper(File*, Cmd*); | |
+ | |
+void | |
+resetxec(void) | |
+{ | |
+ Glooping = nest = 0; | |
+} | |
+ | |
+int | |
+cmdexec(File *f, Cmd *cp) | |
+{ | |
+ int i; | |
+ Addr *ap; | |
+ Address a; | |
+ | |
+ if(f && f->unread) | |
+ load(f); | |
+ if(f==0 && (cp->addr==0 || cp->addr->type!='"') && | |
+ !utfrune("bBnqUXY!", cp->cmdc) && | |
+ cp->cmdc!=('c'|0x100) && !(cp->cmdc=='D' && cp->ctext)) | |
+ error(Enofile); | |
+ i = lookup(cp->cmdc); | |
+ if(i >= 0 && cmdtab[i].defaddr != aNo){ | |
+ if((ap=cp->addr)==0 && cp->cmdc!='\n'){ | |
+ cp->addr = ap = newaddr(); | |
+ ap->type = '.'; | |
+ if(cmdtab[i].defaddr == aAll) | |
+ ap->type = '*'; | |
+ }else if(ap && ap->type=='"' && ap->next==0 && cp->cmdc!='\n'){ | |
+ ap->next = newaddr(); | |
+ ap->next->type = '.'; | |
+ if(cmdtab[i].defaddr == aAll) | |
+ ap->next->type = '*'; | |
+ } | |
+ if(cp->addr){ /* may be false for '\n' (only) */ | |
+ static Address none = {0,0,0}; | |
+ if(f) | |
+ addr = address(ap, f->dot, 0); | |
+ else /* a " */ | |
+ addr = address(ap, none, 0); | |
+ f = addr.f; | |
+ } | |
+ } | |
+ current(f); | |
+ switch(cp->cmdc){ | |
+ case '{': | |
+ a = cp->addr? address(cp->addr, f->dot, 0): f->dot; | |
+ for(cp = cp->ccmd; cp; cp = cp->next){ | |
+ a.f->dot = a; | |
+ cmdexec(a.f, cp); | |
+ } | |
+ break; | |
+ default: | |
+ i=(*cmdtab[i].fn)(f, cp); | |
+ return i; | |
+ } | |
+ return 1; | |
+} | |
+ | |
+ | |
+int | |
+a_cmd(File *f, Cmd *cp) | |
+{ | |
+ return append(f, cp, addr.r.p2); | |
+} | |
+ | |
+int | |
+b_cmd(File *f, Cmd *cp) | |
+{ | |
+ USED(f); | |
+ f = cp->cmdc=='b'? tofile(cp->ctext) : getfile(cp->ctext); | |
+ if(f->unread) | |
+ load(f); | |
+ else if(nest == 0) | |
+ filename(f); | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+c_cmd(File *f, Cmd *cp) | |
+{ | |
+ logdelete(f, addr.r.p1, addr.r.p2); | |
+ f->ndot.r.p1 = f->ndot.r.p2 = addr.r.p2; | |
+ return append(f, cp, addr.r.p2); | |
+} | |
+ | |
+int | |
+d_cmd(File *f, Cmd *cp) | |
+{ | |
+ USED(cp); | |
+ logdelete(f, addr.r.p1, addr.r.p2); | |
+ f->ndot.r.p1 = f->ndot.r.p2 = addr.r.p1; | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+D_cmd(File *f, Cmd *cp) | |
+{ | |
+ closefiles(f, cp->ctext); | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+e_cmd(File *f, Cmd *cp) | |
+{ | |
+ if(getname(f, cp->ctext, cp->cmdc=='e')==0) | |
+ error(Enoname); | |
+ edit(f, cp->cmdc); | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+f_cmd(File *f, Cmd *cp) | |
+{ | |
+ getname(f, cp->ctext, TRUE); | |
+ filename(f); | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+g_cmd(File *f, Cmd *cp) | |
+{ | |
+ if(f!=addr.f)panic("g_cmd f!=addr.f"); | |
+ compile(cp->re); | |
+ if(execute(f, addr.r.p1, addr.r.p2) ^ cp->cmdc=='v'){ | |
+ f->dot = addr; | |
+ return cmdexec(f, cp->ccmd); | |
+ } | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+i_cmd(File *f, Cmd *cp) | |
+{ | |
+ return append(f, cp, addr.r.p1); | |
+} | |
+ | |
+int | |
+k_cmd(File *f, Cmd *cp) | |
+{ | |
+ USED(cp); | |
+ f->mark = addr.r; | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+m_cmd(File *f, Cmd *cp) | |
+{ | |
+ Address addr2; | |
+ | |
+ addr2 = address(cp->caddr, f->dot, 0); | |
+ if(cp->cmdc=='m') | |
+ move(f, addr2); | |
+ else | |
+ copy(f, addr2); | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+n_cmd(File *f, Cmd *cp) | |
+{ | |
+ int i; | |
+ USED(f); | |
+ USED(cp); | |
+ for(i = 0; i<file.nused; i++){ | |
+ if(file.filepptr[i] == cmd) | |
+ continue; | |
+ f = file.filepptr[i]; | |
+ Strduplstr(&genstr, &f->name); | |
+ filename(f); | |
+ } | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+p_cmd(File *f, Cmd *cp) | |
+{ | |
+ USED(cp); | |
+ return display(f); | |
+} | |
+ | |
+int | |
+q_cmd(File *f, Cmd *cp) | |
+{ | |
+ USED(cp); | |
+ USED(f); | |
+ trytoquit(); | |
+ if(downloaded){ | |
+ outT0(Hexit); | |
+ return TRUE; | |
+ } | |
+ return FALSE; | |
+} | |
+ | |
+int | |
+s_cmd(File *f, Cmd *cp) | |
+{ | |
+ int i, j, c, n; | |
+ Posn p1, op, didsub = 0, delta = 0; | |
+ | |
+ n = cp->num; | |
+ op= -1; | |
+ compile(cp->re); | |
+ for(p1 = addr.r.p1; p1<=addr.r.p2 && execute(f, p1, addr.r.p2); ){ | |
+ if(sel.p[0].p1==sel.p[0].p2){ /* empty match? */ | |
+ if(sel.p[0].p1==op){ | |
+ p1++; | |
+ continue; | |
+ } | |
+ p1 = sel.p[0].p2+1; | |
+ }else | |
+ p1 = sel.p[0].p2; | |
+ op = sel.p[0].p2; | |
+ if(--n>0) | |
+ continue; | |
+ Strzero(&genstr); | |
+ for(i = 0; i<cp->ctext->n; i++) | |
+ if((c = cp->ctext->s[i])=='\\' && i<cp->ctext->n-1){ | |
+ c = cp->ctext->s[++i]; | |
+ if('1'<=c && c<='9') { | |
+ j = c-'0'; | |
+ if(sel.p[j].p2-sel.p[j].p1>BLOCKSIZE) | |
+ error(Elongtag); | |
+ bufread(f, sel.p[j].p1, genbuf, sel.p[… | |
+ Strinsert(&genstr, tmprstr(genbuf, (se… | |
+ }else | |
+ Straddc(&genstr, c); | |
+ }else if(c!='&') | |
+ Straddc(&genstr, c); | |
+ else{ | |
+ if(sel.p[0].p2-sel.p[0].p1>BLOCKSIZE) | |
+ error(Elongrhs); | |
+ bufread(f, sel.p[0].p1, genbuf, sel.p[0].p2-se… | |
+ Strinsert(&genstr, | |
+ tmprstr(genbuf, (int)(sel.p[0].p2-sel.… | |
+ genstr.n); | |
+ } | |
+ if(sel.p[0].p1!=sel.p[0].p2){ | |
+ logdelete(f, sel.p[0].p1, sel.p[0].p2); | |
+ delta-=sel.p[0].p2-sel.p[0].p1; | |
+ } | |
+ if(genstr.n){ | |
+ loginsert(f, sel.p[0].p2, genstr.s, genstr.n); | |
+ delta+=genstr.n; | |
+ } | |
+ didsub = 1; | |
+ if(!cp->flag) | |
+ break; | |
+ } | |
+ if(!didsub && nest==0) | |
+ error(Enosub); | |
+ f->ndot.r.p1 = addr.r.p1, f->ndot.r.p2 = addr.r.p2+delta; | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+u_cmd(File *f, Cmd *cp) | |
+{ | |
+ int n; | |
+ | |
+ USED(f); | |
+ USED(cp); | |
+ n = cp->num; | |
+ if(n >= 0) | |
+ while(n-- && undo(TRUE)) | |
+ ; | |
+ else | |
+ while(n++ && undo(FALSE)) | |
+ ; | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+w_cmd(File *f, Cmd *cp) | |
+{ | |
+ int fseq; | |
+ | |
+ fseq = f->seq; | |
+ if(getname(f, cp->ctext, FALSE)==0) | |
+ error(Enoname); | |
+ if(fseq == seq) | |
+ error_s(Ewseq, genc); | |
+ writef(f); | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+x_cmd(File *f, Cmd *cp) | |
+{ | |
+ if(cp->re) | |
+ looper(f, cp, cp->cmdc=='x'); | |
+ else | |
+ linelooper(f, cp); | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+X_cmd(File *f, Cmd *cp) | |
+{ | |
+ USED(f); | |
+ filelooper(cp, cp->cmdc=='X'); | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+plan9_cmd(File *f, Cmd *cp) | |
+{ | |
+ plan9(f, cp->cmdc, cp->ctext, nest); | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+eq_cmd(File *f, Cmd *cp) | |
+{ | |
+ int charsonly; | |
+ | |
+ switch(cp->ctext->n){ | |
+ case 1: | |
+ charsonly = FALSE; | |
+ break; | |
+ case 2: | |
+ if(cp->ctext->s[0]=='#'){ | |
+ charsonly = TRUE; | |
+ break; | |
+ } | |
+ default: | |
+ SET(charsonly); | |
+ error(Enewline); | |
+ } | |
+ printposn(f, charsonly); | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+nl_cmd(File *f, Cmd *cp) | |
+{ | |
+ Address a; | |
+ | |
+ if(cp->addr == 0){ | |
+ /* First put it on newline boundaries */ | |
+ addr = lineaddr((Posn)0, f->dot, -1); | |
+ a = lineaddr((Posn)0, f->dot, 1); | |
+ addr.r.p2 = a.r.p2; | |
+ if(addr.r.p1==f->dot.r.p1 && addr.r.p2==f->dot.r.p2) | |
+ addr = lineaddr((Posn)1, f->dot, 1); | |
+ display(f); | |
+ }else if(downloaded) | |
+ moveto(f, addr.r); | |
+ else | |
+ display(f); | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+cd_cmd(File *f, Cmd *cp) | |
+{ | |
+ USED(f); | |
+ cd(cp->ctext); | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+append(File *f, Cmd *cp, Posn p) | |
+{ | |
+ if(cp->ctext->n>0 && cp->ctext->s[cp->ctext->n-1]==0) | |
+ --cp->ctext->n; | |
+ if(cp->ctext->n>0) | |
+ loginsert(f, p, cp->ctext->s, cp->ctext->n); | |
+ f->ndot.r.p1 = p; | |
+ f->ndot.r.p2 = p+cp->ctext->n; | |
+ return TRUE; | |
+} | |
+ | |
+int | |
+display(File *f) | |
+{ | |
+ Posn p1, p2; | |
+ int np; | |
+ char *c; | |
+ | |
+ p1 = addr.r.p1; | |
+ p2 = addr.r.p2; | |
+ if(p2 > f->_.nc){ | |
+ fprint(2, "bad display addr p1=%ld p2=%ld f->_.nc=%d\n", p1, p… | |
+ p2 = f->_.nc; | |
+ } | |
+ while(p1 < p2){ | |
+ np = p2-p1; | |
+ if(np>BLOCKSIZE-1) | |
+ np = BLOCKSIZE-1; | |
+ bufread(f, p1, genbuf, np); | |
+ genbuf[np] = 0; | |
+ c = Strtoc(tmprstr(genbuf, np+1)); | |
+ if(downloaded) | |
+ termwrite(c); | |
+ else | |
+ Write(1, c, strlen(c)); | |
+ free(c); | |
+ p1 += np; | |
+ } | |
+ f->dot = addr; | |
+ return TRUE; | |
+} | |
+ | |
+void | |
+looper(File *f, Cmd *cp, int xy) | |
+{ | |
+ Posn p, op; | |
+ Range r; | |
+ | |
+ r = addr.r; | |
+ op= xy? -1 : r.p1; | |
+ nest++; | |
+ compile(cp->re); | |
+ for(p = r.p1; p<=r.p2; ){ | |
+ if(!execute(f, p, r.p2)){ /* no match, but y should still run … | |
+ if(xy || op>r.p2) | |
+ break; | |
+ f->dot.r.p1 = op, f->dot.r.p2 = r.p2; | |
+ p = r.p2+1; /* exit next loop */ | |
+ }else{ | |
+ if(sel.p[0].p1==sel.p[0].p2){ /* empty match? */ | |
+ if(sel.p[0].p1==op){ | |
+ p++; | |
+ continue; | |
+ } | |
+ p = sel.p[0].p2+1; | |
+ }else | |
+ p = sel.p[0].p2; | |
+ if(xy) | |
+ f->dot.r = sel.p[0]; | |
+ else | |
+ f->dot.r.p1 = op, f->dot.r.p2 = sel.p[0].p1; | |
+ } | |
+ op = sel.p[0].p2; | |
+ cmdexec(f, cp->ccmd); | |
+ compile(cp->re); | |
+ } | |
+ --nest; | |
+} | |
+ | |
+void | |
+linelooper(File *f, Cmd *cp) | |
+{ | |
+ Posn p; | |
+ Range r, linesel; | |
+ Address a, a3; | |
+ | |
+ nest++; | |
+ r = addr.r; | |
+ a3.f = f; | |
+ a3.r.p1 = a3.r.p2 = r.p1; | |
+ for(p = r.p1; p<r.p2; p = a3.r.p2){ | |
+ a3.r.p1 = a3.r.p2; | |
+/*pjw if(p!=r.p1 || (linesel = lineaddr((Posn)0, a3, 1)).r.p2==… | |
+ if(p!=r.p1 || (a = lineaddr((Posn)0, a3, 1), linesel = a.r, li… | |
+ a = lineaddr((Posn)1, a3, 1); | |
+ linesel = a.r; | |
+ } | |
+ if(linesel.p1 >= r.p2) | |
+ break; | |
+ if(linesel.p2 >= r.p2) | |
+ linesel.p2 = r.p2; | |
+ if(linesel.p2 > linesel.p1) | |
+ if(linesel.p1>=a3.r.p2 && linesel.p2>a3.r.p2){ | |
+ f->dot.r = linesel; | |
+ cmdexec(f, cp->ccmd); | |
+ a3.r = linesel; | |
+ continue; | |
+ } | |
+ break; | |
+ } | |
+ --nest; | |
+} | |
+ | |
+void | |
+filelooper(Cmd *cp, int XY) | |
+{ | |
+ File *f, *cur; | |
+ int i; | |
+ | |
+ if(Glooping++) | |
+ error(EnestXY); | |
+ nest++; | |
+ settempfile(); | |
+ cur = curfile; | |
+ for(i = 0; i<tempfile.nused; i++){ | |
+ f = tempfile.filepptr[i]; | |
+ if(f==cmd) | |
+ continue; | |
+ if(cp->re==0 || filematch(f, cp->re)==XY) | |
+ cmdexec(f, cp->ccmd); | |
+ } | |
+ if(cur && whichmenu(cur)>=0) /* check that cur is still a file … | |
+ current(cur); | |
+ --Glooping; | |
+ --nest; | |
+} | |
diff --git a/src/libdraw/BOT b/src/libdraw/BOT | |
diff --git a/src/libdraw/Make.Darwin-PowerMacintosh b/src/libdraw/Make.Darwin-P… | |
t@@ -0,0 +1,6 @@ | |
+CC=gcc | |
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -… | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O | |
diff --git a/src/libdraw/Make.FreeBSD-386 b/src/libdraw/Make.FreeBSD-386 | |
t@@ -0,0 +1,7 @@ | |
+CC=gcc | |
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -… | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME) | |
+NAN=nan64.$O | |
diff --git a/src/libdraw/Make.HP-UX-9000 b/src/libdraw/Make.HP-UX-9000 | |
t@@ -0,0 +1,6 @@ | |
+CC=cc | |
+CFLAGS=-O -c -Ae -I. | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O | |
diff --git a/src/libdraw/Make.Linux-386 b/src/libdraw/Make.Linux-386 | |
t@@ -0,0 +1,7 @@ | |
+CC=gcc | |
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME) | |
+NAN=nan64.$O | |
diff --git a/src/libdraw/Make.NetBSD-386 b/src/libdraw/Make.NetBSD-386 | |
t@@ -0,0 +1,7 @@ | |
+CC=gcc | |
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -… | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME) | |
+NAN=nan64.$O | |
diff --git a/src/libdraw/Make.OSF1-alpha b/src/libdraw/Make.OSF1-alpha | |
t@@ -0,0 +1,6 @@ | |
+CC=cc | |
+CFLAGS+=-g -c -I. | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O | |
diff --git a/src/libdraw/Make.SunOS-sun4u b/src/libdraw/Make.SunOS-sun4u | |
t@@ -0,0 +1,2 @@ | |
+include Make.SunOS-sun4u-$(CC) | |
+NAN=nan64.$O | |
diff --git a/src/libdraw/Make.SunOS-sun4u-cc b/src/libdraw/Make.SunOS-sun4u-cc | |
t@@ -0,0 +1,6 @@ | |
+CC=cc | |
+CFLAGS+=-g -c -I. -O | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O | |
diff --git a/src/libdraw/Make.SunOS-sun4u-gcc b/src/libdraw/Make.SunOS-sun4u-gcc | |
t@@ -0,0 +1,6 @@ | |
+CC=gcc | |
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O | |
diff --git a/src/libdraw/Makefile b/src/libdraw/Makefile | |
t@@ -0,0 +1,194 @@ | |
+ | |
+# this works in gnu make | |
+SYSNAME:=${shell uname} | |
+OBJTYPE:=${shell uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g'} | |
+ | |
+# this works in bsd make | |
+SYSNAME!=uname | |
+OBJTYPE!=uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g' | |
+ | |
+# the gnu rules will mess up bsd but not vice versa, | |
+# hence the gnu rules come first. | |
+ | |
+include Make.$(SYSNAME)-$(OBJTYPE) | |
+ | |
+PREFIX=/usr/local | |
+ | |
+NUKEFILES= | |
+ | |
+TGZFILES= | |
+ | |
+LIB=libdraw.a | |
+VERSION=2.0 | |
+PORTPLACE=devel/libdraw | |
+NAME=libdraw | |
+ | |
+# keyboard.$O\ | |
+# newwindow.$O\ | |
+OFILES=\ | |
+ alloc.$O\ | |
+ allocimagemix.$O\ | |
+ arith.$O\ | |
+ bezier.$O\ | |
+ border.$O\ | |
+ buildfont.$O\ | |
+ bytesperline.$O\ | |
+ chan.$O\ | |
+ cloadimage.$O\ | |
+ computil.$O\ | |
+ creadimage.$O\ | |
+ debug.$O\ | |
+ defont.$O\ | |
+ draw.$O\ | |
+ drawrepl.$O\ | |
+ egetrect.$O\ | |
+ ellipse.$O\ | |
+ emenuhit.$O\ | |
+ font.$O\ | |
+ freesubfont.$O\ | |
+ getdefont.$O\ | |
+ getrect.$O\ | |
+ getsubfont.$O\ | |
+ icossin.$O\ | |
+ icossin2.$O\ | |
+ init.$O\ | |
+ line.$O\ | |
+ loadimage.$O\ | |
+ menuhit.$O\ | |
+ mkfont.$O\ | |
+ openfont.$O\ | |
+ poly.$O\ | |
+ readcolmap.$O\ | |
+ readimage.$O\ | |
+ readsubfont.$O\ | |
+ rectclip.$O\ | |
+ replclipr.$O\ | |
+ rgb.$O\ | |
+ string.$O\ | |
+ stringbg.$O\ | |
+ stringsubfont.$O\ | |
+ stringwidth.$O\ | |
+ subfont.$O\ | |
+ subfontcache.$O\ | |
+ subfontname.$O\ | |
+ unloadimage.$O\ | |
+ window.$O\ | |
+ writecolmap.$O\ | |
+ writeimage.$O\ | |
+ writesubfont.$O\ | |
+ md-alloc.$O\ | |
+ md-arc.$O\ | |
+ md-cload.$O\ | |
+ md-cmap.$O\ | |
+ md-cread.$O\ | |
+ md-defont.$O\ | |
+ md-draw.$O\ | |
+ md-ellipse.$O\ | |
+ md-fillpoly.$O\ | |
+ md-hwdraw.$O\ | |
+ md-iprint.$O\ | |
+ md-line.$O\ | |
+ md-load.$O\ | |
+ md-openmemsubfont.$O\ | |
+ md-poly.$O\ | |
+ md-read.$O\ | |
+ md-string.$O\ | |
+ md-subfont.$O\ | |
+ md-unload.$O\ | |
+ md-write.$O\ | |
+ ml-draw.$O\ | |
+ ml-lalloc.$O\ | |
+ ml-layerop.$O\ | |
+ ml-ldelete.$O\ | |
+ ml-lhide.$O\ | |
+ ml-line.$O\ | |
+ ml-load.$O\ | |
+ ml-lorigin.$O\ | |
+ ml-lsetrefresh.$O\ | |
+ ml-ltofront.$O\ | |
+ ml-ltorear.$O\ | |
+ ml-unload.$O\ | |
+ x11-alloc.$O\ | |
+ x11-cload.$O\ | |
+ x11-draw.$O\ | |
+ x11-event.$O\ | |
+ x11-fill.$O\ | |
+ x11-get.$O\ | |
+ x11-init.$O\ | |
+ x11-itrans.$O\ | |
+ x11-keyboard.$O\ | |
+ x11-load.$O\ | |
+ x11-mouse.$O\ | |
+ x11-pixelbits.$O\ | |
+ x11-unload.$O\ | |
+ devdraw.$O\ | |
+ unix.$O\ | |
+ | |
+HFILES=\ | |
+ draw.h\ | |
+ memdraw.h | |
+ | |
+all: $(LIB) | |
+ | |
+install: $(LIB) | |
+ install -c -m 0644 $(LIB) $(PREFIX)/lib/$(LIB) | |
+ install -c -m 0644 draw.h $(PREFIX)/include/draw.h | |
+ install -c -m 0644 event.h $(PREFIX)/include/event.h | |
+ install -c -m 0644 cursor.h $(PREFIX)/include/cursor.h | |
+ install -c -m 0644 mouse.h $(PREFIX)/include/mouse.h | |
+ install -c -m 0644 keyboard.h $(PREFIX)/include/keyboard.h | |
+ | |
+test: test.o $(LIB) | |
+ gcc -o test test.o $(LIB) -L$(PREFIX)/lib -l9 -lfmt -lutf -L/usr/X11R6… | |
+ | |
+$(LIB): $(OFILES) | |
+ $(AR) $(ARFLAGS) $(LIB) $(OFILES) | |
+ | |
+NUKEFILES+=$(LIB) | |
+.c.$O: | |
+ $(CC) $(CFLAGS) -I/usr/X11R6/include -I../sam -I$(PREFIX)/include $*.c | |
+ | |
+%.$O: %.c | |
+ $(CC) $(CFLAGS) -I/usr/X11R6/include -I../sam -I$(PREFIX)/include $*.c | |
+ | |
+ | |
+$(OFILES): $(HFILES) | |
+ | |
+tgz: | |
+ rm -rf $(NAME)-$(VERSION) | |
+ mkdir $(NAME)-$(VERSION) | |
+ cp Makefile Make.* README LICENSE NOTICE *.[ch137] rpm.spec bundle.por… | |
+ tar cf - $(NAME)-$(VERSION) | gzip >$(NAME)-$(VERSION).tgz | |
+ rm -rf $(NAME)-$(VERSION) | |
+ | |
+clean: | |
+ rm -f $(OFILES) $(LIB) | |
+ | |
+nuke: | |
+ rm -f $(OFILES) *.tgz *.rpm $(NUKEFILES) | |
+ | |
+rpm: | |
+ make tgz | |
+ cp $(NAME)-$(VERSION).tgz /usr/src/RPM/SOURCES | |
+ rpm -ba rpm.spec | |
+ cp /usr/src/RPM/SRPMS/$(NAME)-$(VERSION)-1.src.rpm . | |
+ cp /usr/src/RPM/RPMS/i586/$(NAME)-$(VERSION)-1.i586.rpm . | |
+ scp *.rpm [email protected]:public_html/software | |
+ | |
+PORTDIR=/usr/ports/$(PORTPLACE) | |
+ | |
+ports: | |
+ make tgz | |
+ rm -rf $(PORTDIR) | |
+ mkdir $(PORTDIR) | |
+ cp $(NAME)-$(VERSION).tgz /usr/ports/distfiles | |
+ cat bundle.ports | (cd $(PORTDIR) && awk '$$1=="---" && $$3=="---" { o… | |
+ (cd $(PORTDIR); make makesum) | |
+ (cd $(PORTDIR); make) | |
+ (cd $(PORTDIR); /usr/local/bin/portlint) | |
+ rm -rf $(PORTDIR)/work | |
+ shar `find $(PORTDIR)` > ports.shar | |
+ (cd $(PORTDIR); tar cf - *) | gzip >$(NAME)-$(VERSION)-ports.tgz | |
+ scp *.tgz [email protected]:public_html/software | |
+ | |
+.phony: all clean nuke install tgz rpm ports | |
diff --git a/src/libdraw/Makefile.MID b/src/libdraw/Makefile.MID | |
t@@ -0,0 +1,123 @@ | |
+LIB=libdraw.a | |
+VERSION=2.0 | |
+PORTPLACE=devel/libdraw | |
+NAME=libdraw | |
+ | |
+# keyboard.$O\ | |
+# newwindow.$O\ | |
+OFILES=\ | |
+ alloc.$O\ | |
+ allocimagemix.$O\ | |
+ arith.$O\ | |
+ bezier.$O\ | |
+ border.$O\ | |
+ buildfont.$O\ | |
+ bytesperline.$O\ | |
+ chan.$O\ | |
+ cloadimage.$O\ | |
+ computil.$O\ | |
+ creadimage.$O\ | |
+ debug.$O\ | |
+ defont.$O\ | |
+ draw.$O\ | |
+ drawrepl.$O\ | |
+ egetrect.$O\ | |
+ ellipse.$O\ | |
+ emenuhit.$O\ | |
+ font.$O\ | |
+ freesubfont.$O\ | |
+ getdefont.$O\ | |
+ getrect.$O\ | |
+ getsubfont.$O\ | |
+ icossin.$O\ | |
+ icossin2.$O\ | |
+ init.$O\ | |
+ line.$O\ | |
+ loadimage.$O\ | |
+ menuhit.$O\ | |
+ mkfont.$O\ | |
+ openfont.$O\ | |
+ poly.$O\ | |
+ readcolmap.$O\ | |
+ readimage.$O\ | |
+ readsubfont.$O\ | |
+ rectclip.$O\ | |
+ replclipr.$O\ | |
+ rgb.$O\ | |
+ string.$O\ | |
+ stringbg.$O\ | |
+ stringsubfont.$O\ | |
+ stringwidth.$O\ | |
+ subfont.$O\ | |
+ subfontcache.$O\ | |
+ subfontname.$O\ | |
+ unloadimage.$O\ | |
+ window.$O\ | |
+ writecolmap.$O\ | |
+ writeimage.$O\ | |
+ writesubfont.$O\ | |
+ md-alloc.$O\ | |
+ md-arc.$O\ | |
+ md-cload.$O\ | |
+ md-cmap.$O\ | |
+ md-cread.$O\ | |
+ md-defont.$O\ | |
+ md-draw.$O\ | |
+ md-ellipse.$O\ | |
+ md-fillpoly.$O\ | |
+ md-hwdraw.$O\ | |
+ md-iprint.$O\ | |
+ md-line.$O\ | |
+ md-load.$O\ | |
+ md-openmemsubfont.$O\ | |
+ md-poly.$O\ | |
+ md-read.$O\ | |
+ md-string.$O\ | |
+ md-subfont.$O\ | |
+ md-unload.$O\ | |
+ md-write.$O\ | |
+ ml-draw.$O\ | |
+ ml-lalloc.$O\ | |
+ ml-layerop.$O\ | |
+ ml-ldelete.$O\ | |
+ ml-lhide.$O\ | |
+ ml-line.$O\ | |
+ ml-load.$O\ | |
+ ml-lorigin.$O\ | |
+ ml-lsetrefresh.$O\ | |
+ ml-ltofront.$O\ | |
+ ml-ltorear.$O\ | |
+ ml-unload.$O\ | |
+ x11-alloc.$O\ | |
+ x11-cload.$O\ | |
+ x11-draw.$O\ | |
+ x11-event.$O\ | |
+ x11-fill.$O\ | |
+ x11-get.$O\ | |
+ x11-init.$O\ | |
+ x11-itrans.$O\ | |
+ x11-keyboard.$O\ | |
+ x11-load.$O\ | |
+ x11-mouse.$O\ | |
+ x11-pixelbits.$O\ | |
+ x11-unload.$O\ | |
+ devdraw.$O\ | |
+ unix.$O\ | |
+ | |
+HFILES=\ | |
+ draw.h\ | |
+ memdraw.h | |
+ | |
+all: $(LIB) | |
+ | |
+install: $(LIB) | |
+ install -c -m 0644 $(LIB) $(PREFIX)/lib/$(LIB) | |
+ install -c -m 0644 draw.h $(PREFIX)/include/draw.h | |
+ install -c -m 0644 event.h $(PREFIX)/include/event.h | |
+ install -c -m 0644 cursor.h $(PREFIX)/include/cursor.h | |
+ install -c -m 0644 mouse.h $(PREFIX)/include/mouse.h | |
+ install -c -m 0644 keyboard.h $(PREFIX)/include/keyboard.h | |
+ | |
+test: test.o $(LIB) | |
+ gcc -o test test.o $(LIB) -L$(PREFIX)/lib -l9 -lfmt -lutf -L/usr/X11R6… | |
+ | |
diff --git a/src/libdraw/alloc.c b/src/libdraw/alloc.c | |
t@@ -0,0 +1,237 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+Image* | |
+allocimage(Display *d, Rectangle r, u32int chan, int repl, u32int val) | |
+{ | |
+ return _allocimage(nil, d, r, chan, repl, val, 0, 0); | |
+} | |
+ | |
+Image* | |
+_allocimage(Image *ai, Display *d, Rectangle r, u32int chan, int repl, u32int … | |
+{ | |
+ 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 hug… | |
+ 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; | |
+ u32int 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+1); | |
+ 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); | |
+ a[6+n] = 'I'; | |
+ if(flushimage(d, 0) < 0) | |
+ goto Error; | |
+ if(_drawmsgread(d, buf, sizeof buf) < 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/src/libdraw/arith.c b/src/libdraw/arith.c | |
t@@ -0,0 +1,206 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+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<s.max.x && s.min.x<r.max.x && | |
+ r.min.y<s.max.y && s.min.y<r.max.y; | |
+} | |
+ | |
+int | |
+rectinrect(Rectangle r, Rectangle s) | |
+{ | |
+ return s.min.x<=r.min.x && r.max.x<=s.max.x && s.min.y<=r.min.y && r.m… | |
+} | |
+ | |
+int | |
+ptinrect(Point p, Rectangle r) | |
+{ | |
+ return p.x>=r.min.x && p.x<r.max.x && | |
+ p.y>=r.min.y && p.y<r.max.y; | |
+} | |
+ | |
+Rectangle | |
+canonrect(Rectangle r) | |
+{ | |
+ int t; | |
+ if (r.max.x < r.min.x) { | |
+ t = r.min.x; | |
+ r.min.x = r.max.x; | |
+ r.max.x = t; | |
+ } | |
+ if (r.max.y < r.min.y) { | |
+ t = r.min.y; | |
+ r.min.y = r.max.y; | |
+ r.max.y = t; | |
+ } | |
+ return r; | |
+} | |
+ | |
+void | |
+combinerect(Rectangle *r1, Rectangle r2) | |
+{ | |
+ if(r1->min.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; | |
+} | |
+ | |
+u32int | |
+drawld2chan[] = { | |
+ GREY1, | |
+ GREY2, | |
+ GREY4, | |
+ CMAP8, | |
+}; | |
+ | |
+int log2[] = { -1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4, … | |
+ | |
+u32int | |
+setalpha(u32int 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/src/libdraw/buildfont.c b/src/libdraw/buildfont.c | |
t@@ -0,0 +1,141 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+static char* | |
+skip(char *s) | |
+{ | |
+ while(*s==' ' || *s=='\n' || *s=='\t') | |
+ s++; | |
+ return s; | |
+} | |
+ | |
+Font* | |
+buildfont(Display *d, char *buf, char *name) | |
+{ | |
+ Font *fnt; | |
+ Cachefont *c; | |
+ char *s, *t; | |
+ ulong min, max; | |
+ int offset; | |
+ char badform[] = "bad font format: number expected (char position %d)"; | |
+ | |
+ s = buf; | |
+ fnt = malloc(sizeof(Font)); | |
+ if(fnt == 0) | |
+ return 0; | |
+ memset(fnt, 0, sizeof(Font)); | |
+ fnt->display = d; | |
+ fnt->name = strdup(name); | |
+ fnt->ncache = NFCACHE+NFLOOK; | |
+ fnt->nsubf = NFSUBF; | |
+ fnt->cache = malloc(fnt->ncache * sizeof(fnt->cache[0])); | |
+ fnt->subf = malloc(fnt->nsubf * sizeof(fnt->subf[0])); | |
+ if(fnt->name==0 || fnt->cache==0 || fnt->subf==0){ | |
+ Err2: | |
+ free(fnt->name); | |
+ free(fnt->cache); | |
+ free(fnt->subf); | |
+ free(fnt->sub); | |
+ free(fnt); | |
+ return 0; | |
+ } | |
+ fnt->height = strtol(s, &s, 0); | |
+ s = skip(s); | |
+ fnt->ascent = strtol(s, &s, 0); | |
+ s = skip(s); | |
+ if(fnt->height<=0 || fnt->ascent<=0){ | |
+ werrstr("bad height or ascent in font file"); | |
+ goto Err2; | |
+ } | |
+ fnt->width = 0; | |
+ fnt->nsub = 0; | |
+ fnt->sub = 0; | |
+ | |
+ memset(fnt->subf, 0, fnt->nsubf * sizeof(fnt->subf[0])); | |
+ memset(fnt->cache, 0, fnt->ncache*sizeof(fnt->cache[0])); | |
+ fnt->age = 1; | |
+ do{ | |
+ /* must be looking at a number now */ | |
+ if(*s<'0' || '9'<*s){ | |
+ werrstr(badform, s-buf); | |
+ goto Err3; | |
+ } | |
+ min = strtol(s, &s, 0); | |
+ s = skip(s); | |
+ /* must be looking at a number now */ | |
+ if(*s<'0' || '9'<*s){ | |
+ werrstr(badform, s-buf); | |
+ goto Err3; | |
+ } | |
+ max = strtol(s, &s, 0); | |
+ s = skip(s); | |
+ if(*s==0 || min>=65536 || max>=65536 || min>max){ | |
+ werrstr("illegal subfont range"); | |
+ Err3: | |
+ freefont(fnt); | |
+ return 0; | |
+ } | |
+ t = s; | |
+ offset = strtol(s, &t, 0); | |
+ if(t>s && (*t==' ' || *t=='\t' || *t=='\n')) | |
+ s = skip(t); | |
+ else | |
+ offset = 0; | |
+ fnt->sub = realloc(fnt->sub, (fnt->nsub+1)*sizeof(Cachefont*)); | |
+ if(fnt->sub == 0){ | |
+ /* realloc manual says fnt->sub may have been destroye… | |
+ fnt->nsub = 0; | |
+ goto Err3; | |
+ } | |
+ c = malloc(sizeof(Cachefont)); | |
+ if(c == 0) | |
+ goto Err3; | |
+ fnt->sub[fnt->nsub] = c; | |
+ c->min = min; | |
+ c->max = max; | |
+ c->offset = offset; | |
+ t = s; | |
+ while(*s && *s!=' ' && *s!='\n' && *s!='\t') | |
+ s++; | |
+ *s++ = 0; | |
+ c->subfontname = 0; | |
+ c->name = strdup(t); | |
+ if(c->name == 0){ | |
+ free(c); | |
+ goto Err3; | |
+ } | |
+ s = skip(s); | |
+ fnt->nsub++; | |
+ }while(*s); | |
+ return fnt; | |
+} | |
+ | |
+void | |
+freefont(Font *f) | |
+{ | |
+ int i; | |
+ Cachefont *c; | |
+ Subfont *s; | |
+ | |
+ if(f == 0) | |
+ return; | |
+ | |
+ for(i=0; i<f->nsub; i++){ | |
+ c = f->sub[i]; | |
+ free(c->subfontname); | |
+ free(c->name); | |
+ free(c); | |
+ } | |
+ for(i=0; i<f->nsubf; i++){ | |
+ s = f->subf[i].f; | |
+ if(s && s!=display->defaultsubfont) | |
+ freesubfont(s); | |
+ } | |
+ freeimage(f->cacheimage); | |
+ free(f->name); | |
+ free(f->cache); | |
+ free(f->subf); | |
+ free(f->sub); | |
+ free(f); | |
+} | |
diff --git a/src/libdraw/bytesperline.c b/src/libdraw/bytesperline.c | |
t@@ -0,0 +1,34 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+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/src/libdraw/chan.c b/src/libdraw/chan.c | |
t@@ -0,0 +1,77 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+static char channames[] = "rgbkamx"; | |
+char* | |
+chantostr(char *buf, u32int cc) | |
+{ | |
+ u32int c, rc; | |
+ char *p; | |
+ | |
+ if(chantodepth(cc) == 0) | |
+ return nil; | |
+ | |
+ /* reverse the channel descriptor so we can easily generate the string… | |
+ 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'; | |
+} | |
+ | |
+u32int | |
+strtochan(char *s) | |
+{ | |
+ char *p, *q; | |
+ u32int 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(u32int 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/src/libdraw/creadimage.c b/src/libdraw/creadimage.c | |
t@@ -0,0 +1,113 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+Image * | |
+creadimage(Display *d, int fd, int dolock) | |
+{ | |
+ char hdr[5*12+1]; | |
+ Rectangle r; | |
+ int m, nb, miny, maxy, new, ldepth, ncblock; | |
+ uchar *buf, *a; | |
+ Image *i; | |
+ u32int chan; | |
+ | |
+ if(readn(fd, hdr, 5*12) != 5*12) | |
+ 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; | |
+ } | |
+ | |
+ if(dolock) | |
+ lockdisplay(d); | |
+ i = allocimage(d, r, chan, 0, 0); | |
+ if(dolock) | |
+ unlockdisplay(d); | |
+ 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){ | |
+ Errout: | |
+ if(dolock) | |
+ lockdisplay(d); | |
+ Erroutlock: | |
+ freeimage(i); | |
+ if(dolock) | |
+ unlockdisplay(d); | |
+ free(buf); | |
+ return nil; | |
+ } | |
+ maxy = atoi(hdr+0*12); | |
+ nb = atoi(hdr+1*12); | |
+ if(maxy<=miny || r.max.y<maxy){ | |
+ werrstr("creadimage: bad maxy %d", maxy); | |
+ goto Errout; | |
+ } | |
+ if(nb<=0 || ncblock<nb){ | |
+ werrstr("creadimage: bad count %d", nb); | |
+ goto Errout; | |
+ } | |
+ if(readn(fd, buf, nb)!=nb) | |
+ goto Errout; | |
+ if(dolock) | |
+ lockdisplay(d); | |
+ a = bufimage(i->display, 21+nb); | |
+ if(a == nil) | |
+ goto Erroutlock; | |
+ a[0] = 'Y'; | |
+ BPLONG(a+1, i->id); | |
+ BPLONG(a+5, r.min.x); | |
+ BPLONG(a+9, miny); | |
+ BPLONG(a+13, r.max.x); | |
+ BPLONG(a+17, maxy); | |
+ if(!new) /* old image: flip the data bits */ | |
+ _twiddlecompressed(buf, nb); | |
+ memmove(a+21, buf, nb); | |
+ if(dolock) | |
+ unlockdisplay(d); | |
+ miny = maxy; | |
+ } | |
+ free(buf); | |
+ return i; | |
+} | |
diff --git a/src/libdraw/cursor.h b/src/libdraw/cursor.h | |
t@@ -0,0 +1,7 @@ | |
+typedef struct Cursor Cursor; | |
+struct Cursor | |
+{ | |
+ Point offset; | |
+ uchar clr[2*16]; | |
+ uchar set[2*16]; | |
+}; | |
diff --git a/src/libdraw/devdraw.c b/src/libdraw/devdraw.c | |
t@@ -0,0 +1,1587 @@ | |
+/* | |
+ * /dev/draw simulator -- handles the messages prepared by the draw library. | |
+ * Includes all the memlayer code even though most programs don't use it. | |
+ * This whole approach is overkill, but cpu is cheap and it keeps things simpl… | |
+ */ | |
+ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include <memlayer.h> | |
+ | |
+#define NHASH (1<<5) | |
+#define HASHMASK (NHASH-1) | |
+ | |
+extern void flushmemscreen(Rectangle); | |
+ | |
+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; | |
+ | |
+struct Draw | |
+{ | |
+ QLock lk; | |
+ int clientid; | |
+ int nclient; | |
+ Client* client[1]; | |
+ int nname; | |
+ DName* name; | |
+ int vers; | |
+ int softscreen; | |
+}; | |
+ | |
+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 … | |
+ 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 Client *client0; | |
+static Memimage *screenimage; | |
+static Rectangle flushrect; | |
+static int waste; | |
+static DScreen* dscreen; | |
+static int drawuninstall(Client*, int); | |
+static Memimage* drawinstall(Client*, int, Memimage*, DScreen*); | |
+static void drawfreedimage(DImage*); | |
+ | |
+void | |
+_initdisplaymemimage(Display *d, Memimage *m) | |
+{ | |
+ screenimage = m; | |
+ client0 = mallocz(sizeof(Client), 1); | |
+ if(client0 == nil){ | |
+ fprint(2, "initdraw: allocating client0: out of memory"); | |
+ abort(); | |
+ } | |
+ client0->slot = 0; | |
+ client0->clientid = ++sdraw.clientid; | |
+ client0->op = SoverD; | |
+ sdraw.client[0] = client0; | |
+ sdraw.nclient = 1; | |
+} | |
+ | |
+void | |
+_drawreplacescreenimage(Memimage *m) | |
+{ | |
+ /* | |
+ * Replace the screen image because the screen | |
+ * was resized. | |
+ * | |
+ * In theory there should only be one reference | |
+ * to the current screen image, and that's through | |
+ * client0's image 0, installed a few lines above. | |
+ * Once the client drops the image, the underlying backing | |
+ * store freed properly. The client is being notified | |
+ * about the resize through external means, so all we | |
+ * need to do is this assignment. | |
+ */ | |
+ screenimage = m; | |
+} | |
+ | |
+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 = mallocz(sizeof(Refresh), 1); | |
+ 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*2<anbb || rectXrect(flushrect, r)){ | |
+ flushrect = nbb; | |
+ return; | |
+ } | |
+ /* emit current state */ | |
+ if(flushrect.min.x < flushrect.max.x) | |
+ flushmemscreen(flushrect); | |
+ flushrect = r; | |
+ waste = 0; | |
+} | |
+ | |
+static | |
+void | |
+dstflush(int dstid, Memimage *dst, Rectangle r) | |
+{ | |
+ Memlayer *l; | |
+ | |
+ if(dstid == 0){ | |
+ combinerect(&flushrect, r); | |
+ return; | |
+ } | |
+ /* how can this happen? -rsc, dec 12 2002 */ | |
+ if(dst == 0){ | |
+ print("nil dstflush\n"); | |
+ return; | |
+ } | |
+ l = dst->layer; | |
+ 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); | |
+} | |
+ | |
+static | |
+void | |
+drawflush(void) | |
+{ | |
+ if(flushrect.min.x < flushrect.max.x) | |
+ flushmemscreen(flushrect); | |
+ flushrect = Rect(10000, 10000, -10000, -10000); | |
+} | |
+ | |
+static | |
+int | |
+drawcmp(char *a, char *b, int n) | |
+{ | |
+ if(strlen(a) != n) | |
+ return 1; | |
+ return memcmp(a, b, n); | |
+} | |
+ | |
+static | |
+DName* | |
+drawlookupname(int n, char *str) | |
+{ | |
+ DName *name, *ename; | |
+ | |
+ name = sdraw.name; | |
+ ename = &name[sdraw.nname]; | |
+ for(; name<ename; name++) | |
+ if(drawcmp(name->name, str, n) == 0) | |
+ return name; | |
+ return 0; | |
+} | |
+ | |
+static | |
+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; | |
+} | |
+ | |
+static | |
+DImage* | |
+drawlookup(Client *client, int id, int checkname) | |
+{ | |
+ DImage *d; | |
+ | |
+ d = client->dimage[id&HASHMASK]; | |
+ while(d){ | |
+ if(d->id == id){ | |
+ /* | |
+ * BUG: should error out but too hard. | |
+ * Return 0 instead. | |
+ */ | |
+ if(checkname && !drawgoodname(d)) | |
+ return 0; | |
+ return d; | |
+ } | |
+ d = d->next; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+static | |
+DScreen* | |
+drawlookupdscreen(int id) | |
+{ | |
+ DScreen *s; | |
+ | |
+ s = dscreen; | |
+ while(s){ | |
+ if(s->id == id) | |
+ return s; | |
+ s = s->next; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+static | |
+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; | |
+ } | |
+ /* caller must check! */ | |
+ return 0; | |
+} | |
+ | |
+static | |
+Memimage* | |
+drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen) | |
+{ | |
+ DImage *d; | |
+ | |
+ d = mallocz(sizeof(DImage), 1); | |
+ 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; | |
+} | |
+ | |
+static | |
+Memscreen* | |
+drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *… | |
+{ | |
+ Memscreen *s; | |
+ CScreen *c; | |
+ | |
+ c = mallocz(sizeof(CScreen), 1); | |
+ if(dimage && dimage->image && dimage->image->chan == 0){ | |
+ print("bad image %p in drawinstallscreen", dimage->image); | |
+ abort(); | |
+ } | |
+ | |
+ if(c == 0) | |
+ return 0; | |
+ if(d == 0){ | |
+ d = mallocz(sizeof(DScreen), 1); | |
+ if(d == 0){ | |
+ free(c); | |
+ return 0; | |
+ } | |
+ s = mallocz(sizeof(Memscreen), 1); | |
+ 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; | |
+} | |
+ | |
+static | |
+void | |
+drawdelname(DName *name) | |
+{ | |
+ int i; | |
+ | |
+ i = name-sdraw.name; | |
+ memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName)); | |
+ sdraw.nname--; | |
+} | |
+ | |
+static | |
+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; | |
+ } | |
+ /* | |
+ * Should signal Enodrawimage, but too hard. | |
+ */ | |
+ return; | |
+ | |
+ Found: | |
+ if(this->dimage) | |
+ drawfreedimage(this->dimage); | |
+ if(this->dfill) | |
+ drawfreedimage(this->dfill); | |
+ free(this->screen); | |
+ free(this); | |
+} | |
+ | |
+static | |
+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; i<sdraw.nname; ) | |
+ if(sdraw.name[i].dimage == dimage) | |
+ drawdelname(sdraw.name+i); | |
+ else | |
+ i++; | |
+ if(dimage->fromname){ /* acquired by name; owned by someone els… | |
+ 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 own… | |
+ 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); | |
+} | |
+ | |
+static | |
+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; | |
+ } | |
+} | |
+ | |
+static | |
+int | |
+drawuninstall(Client *client, int id) | |
+{ | |
+ DImage *d, *next; | |
+ | |
+ d = client->dimage[id&HASHMASK]; | |
+ if(d == 0) | |
+ return -1; | |
+ if(d->id == id){ | |
+ client->dimage[id&HASHMASK] = d->next; | |
+ drawfreedimage(d); | |
+ return 0; | |
+ } | |
+ while(next = d->next){ /* assign = */ | |
+ if(next->id == id){ | |
+ d->next = next->next; | |
+ drawfreedimage(next); | |
+ return 0; | |
+ } | |
+ d = next; | |
+ } | |
+ return -1; | |
+} | |
+ | |
+static | |
+int | |
+drawaddname(Client *client, DImage *di, int n, char *str, char **err) | |
+{ | |
+ DName *name, *ename, *new, *t; | |
+ char *ns; | |
+ | |
+ name = sdraw.name; | |
+ ename = &name[sdraw.nname]; | |
+ for(; name<ename; name++) | |
+ if(drawcmp(name->name, str, n) == 0){ | |
+ *err = "image name in use"; | |
+ return -1; | |
+ } | |
+ t = mallocz((sdraw.nname+1)*sizeof(DName), 1); | |
+ ns = malloc(n+1); | |
+ if(t == nil || ns == nil){ | |
+ free(t); | |
+ free(ns); | |
+ *err = "out of memory"; | |
+ return -1; | |
+ } | |
+ memmove(t, sdraw.name, sdraw.nname*sizeof(DName)); | |
+ free(sdraw.name); | |
+ sdraw.name = t; | |
+ new = &sdraw.name[sdraw.nname++]; | |
+ new->name = ns; | |
+ memmove(new->name, str, n); | |
+ new->name[n] = 0; | |
+ new->dimage = di; | |
+ new->client = client; | |
+ new->vers = ++sdraw.vers; | |
+ return 0; | |
+} | |
+ | |
+static int | |
+drawclientop(Client *cl) | |
+{ | |
+ int op; | |
+ | |
+ op = cl->op; | |
+ cl->op = SoverD; | |
+ return op; | |
+} | |
+ | |
+static | |
+Memimage* | |
+drawimage(Client *client, uchar *a) | |
+{ | |
+ DImage *d; | |
+ | |
+ d = drawlookup(client, BGLONG(a), 1); | |
+ if(d == nil) | |
+ return nil; /* caller must check! */ | |
+ return d->image; | |
+} | |
+ | |
+static | |
+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); | |
+} | |
+ | |
+static | |
+void | |
+drawpoint(Point *p, uchar *a) | |
+{ | |
+ p->x = BGLONG(a+0*4); | |
+ p->y = BGLONG(a+1*4); | |
+} | |
+ | |
+static | |
+Point | |
+drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int i… | |
+{ | |
+ 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 | |
+uchar* | |
+drawcoord(uchar *p, uchar *maxp, int oldx, int *newx) | |
+{ | |
+ int b, x; | |
+ | |
+ if(p >= maxp) | |
+ return nil; | |
+ b = *p++; | |
+ x = b & 0x7F; | |
+ if(b & 0x80){ | |
+ if(p+1 >= maxp) | |
+ return nil; | |
+ 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; | |
+} | |
+ | |
+int | |
+_drawmsgread(Display *d, void *a, int n) | |
+{ | |
+ int inbuf; | |
+ | |
+ inbuf = d->obufp - d->obuf; | |
+ if(n > inbuf) | |
+ n = inbuf; | |
+ memmove(a, d->obuf, n); | |
+ inbuf -= n; | |
+ if(inbuf) | |
+ memmove(d->obuf, d->obufp-inbuf, inbuf); | |
+ d->obufp = d->obuf+inbuf; | |
+ return n; | |
+} | |
+ | |
+static void | |
+drawmsgsquirrel(Display *d, void *a, int n) | |
+{ | |
+ uchar *ep; | |
+ | |
+ ep = d->obuf + d->obufsize; | |
+ if(d->obufp + n > ep) | |
+ abort(); | |
+ memmove(d->obufp, a, n); | |
+ d->obufp += n; | |
+} | |
+ | |
+int | |
+_drawmsgwrite(Display *d, void *v, int n) | |
+{ | |
+ char cbuf[40], *err, ibuf[12*12+1], *s; | |
+ int c, ci, doflush, dstid, e0, e1, esize, j, m; | |
+ int ni, nw, oesize, oldn, op, ox, oy, repl, scrnid, y; | |
+ uchar *a, refresh, *u; | |
+ u32int chan, value; | |
+ Client *client; | |
+ CScreen *cs; | |
+ DImage *di, *ddst, *dsrc, *font, *ll; | |
+ DName *dn; | |
+ DScreen *dscrn; | |
+ FChar *fc; | |
+ Memimage *dst, *i, *l, **lp, *mask, *src; | |
+ Memscreen *scrn; | |
+ Point p, *pp, q, sp; | |
+ Rectangle clipr, r; | |
+ Refreshfn reffn; | |
+ Refx *refx; | |
+ | |
+ d->obufp = d->obuf; | |
+ a = v; | |
+ m = 0; | |
+ oldn = n; | |
+ client = client0; | |
+ | |
+ while((n-=m) > 0){ | |
+ a += m; | |
+//fprint(2, "msgwrite %d(%d)...", n, *a); | |
+ switch(*a){ | |
+ default: | |
+//fprint(2, "bad command %d\n", *a); | |
+ err = "bad draw command"; | |
+ goto error; | |
+ | |
+ /* allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] | |
+ R[4*4] clipR[4*4] rrggbbaa[4] | |
+ */ | |
+ case 'b': | |
+ m = 1+4+4+1+4+1+4*4+4*4+4; | |
+ if(n < m) | |
+ goto 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)) | |
+ goto Eimageexists; | |
+ if(scrnid){ | |
+ dscrn = drawlookupscreen(client, scrnid, &cs); | |
+ if(!dscrn) | |
+ goto Enodrawscreen; | |
+ scrn = dscrn->screen; | |
+ if(repl || chan!=scrn->image->chan){ | |
+ err = "image parameters incompatibile … | |
+ goto error; | |
+ } | |
+ reffn = nil; | |
+ switch(refresh){ | |
+ case Refbackup: | |
+ break; | |
+ case Refnone: | |
+ reffn = memlnorefresh; | |
+ break; | |
+ case Refmesg: | |
+ reffn = drawrefresh; | |
+ break; | |
+ default: | |
+ err = "unknown refresh method"; | |
+ goto error; | |
+ } | |
+ l = memlalloc(scrn, r, reffn, 0, value); | |
+ if(l == 0) | |
+ goto Edrawmem; | |
+ addflush(l->layer->screenr); | |
+ l->clipr = clipr; | |
+ rectclip(&l->clipr, r); | |
+ if(drawinstall(client, dstid, l, dscrn) == 0){ | |
+ memldelete(l); | |
+ goto Edrawmem; | |
+ } | |
+ dscrn->ref++; | |
+ if(reffn){ | |
+ refx = nil; | |
+ if(reffn == drawrefresh){ | |
+ refx = mallocz(sizeof(Refx), 1… | |
+ if(refx == 0){ | |
+ if(drawuninstall(clien… | |
+ goto Enodrawim… | |
+ goto Edrawmem; | |
+ } | |
+ refx->client = client; | |
+ refx->dimage = drawlookup(clie… | |
+ } | |
+ memlsetrefresh(l, reffn, refx); | |
+ } | |
+ continue; | |
+ } | |
+ i = allocmemimage(r, chan); | |
+ if(i == 0) | |
+ goto Edrawmem; | |
+ if(repl) | |
+ i->flags |= Frepl; | |
+ i->clipr = clipr; | |
+ if(!repl) | |
+ rectclip(&i->clipr, r); | |
+ if(drawinstall(client, dstid, i, 0) == 0){ | |
+ freememimage(i); | |
+ goto Edrawmem; | |
+ } | |
+ memfillcolor(i, value); | |
+ continue; | |
+ | |
+ /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */ | |
+ case 'A': | |
+ m = 1+4+4+4+1; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ dstid = BGLONG(a+1); | |
+ if(dstid == 0) | |
+ goto Ebadarg; | |
+ if(drawlookupdscreen(dstid)) | |
+ goto Escreenexists; | |
+ ddst = drawlookup(client, BGLONG(a+5), 1); | |
+ dsrc = drawlookup(client, BGLONG(a+9), 1); | |
+ if(ddst==0 || dsrc==0) | |
+ goto Enodrawimage; | |
+ if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[1… | |
+ goto Edrawmem; | |
+ continue; | |
+ | |
+ /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */ | |
+ case 'c': | |
+ m = 1+4+1+4*4; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ ddst = drawlookup(client, BGLONG(a+1), 1); | |
+ if(ddst == nil) | |
+ goto Enodrawimage; | |
+ if(ddst->name){ | |
+ err = "can't change repl/clipr of shared image… | |
+ goto error; | |
+ } | |
+ 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': | |
+ m = 1+4+4+4+4*4+2*4+2*4; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ dst = drawimage(client, a+1); | |
+ dstid = BGLONG(a+1); | |
+ src = drawimage(client, a+5); | |
+ mask = drawimage(client, a+9); | |
+ if(!dst || !src || !mask) | |
+ goto Enodrawimage; | |
+ 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': | |
+ m = 1+1; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ drawdebug = a[1]; | |
+ continue; | |
+ | |
+ /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[… | |
+ case 'e': | |
+ case 'E': | |
+ m = 1+4+4+2*4+4+4+4+2*4+2*4; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ dst = drawimage(client, a+1); | |
+ dstid = BGLONG(a+1); | |
+ src = drawimage(client, a+5); | |
+ if(!dst || !src) | |
+ goto Enodrawimage; | |
+ drawpoint(&p, a+9); | |
+ e0 = BGLONG(a+17); | |
+ e1 = BGLONG(a+21); | |
+ if(e0<0 || e1<0){ | |
+ err = "invalid ellipse semidiameter"; | |
+ goto error; | |
+ } | |
+ j = BGLONG(a+25); | |
+ if(j < 0){ | |
+ err = "negative ellipse thickness"; | |
+ goto error; | |
+ } | |
+ | |
+ 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… | |
+ continue; | |
+ | |
+ /* free: 'f' id[4] */ | |
+ case 'f': | |
+ m = 1+4; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ ll = drawlookup(client, BGLONG(a+1), 0); | |
+ if(ll && ll->dscreen && ll->dscreen->owner != client) | |
+ ll->dscreen->owner->refreshme = 1; | |
+ if(drawuninstall(client, BGLONG(a+1)) < 0) | |
+ goto Enodrawimage; | |
+ continue; | |
+ | |
+ /* free screen: 'F' id[4] */ | |
+ case 'F': | |
+ m = 1+4; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ if(!drawlookupscreen(client, BGLONG(a+1), &cs)) | |
+ goto Enodrawscreen; | |
+ drawuninstallscreen(client, cs); | |
+ continue; | |
+ | |
+ /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */ | |
+ case 'i': | |
+ m = 1+4+4+1; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ dstid = BGLONG(a+1); | |
+ if(dstid == 0){ | |
+ err = "can't use display as font"; | |
+ goto error; | |
+ } | |
+ font = drawlookup(client, dstid, 1); | |
+ if(font == 0) | |
+ goto Enodrawimage; | |
+ if(font->image->layer){ | |
+ err = "can't use window as font"; | |
+ goto error; | |
+ } | |
+ ni = BGLONG(a+5); | |
+ if(ni<=0 || ni>4096){ | |
+ err = "bad font size (4096 chars max)"; | |
+ goto error; | |
+ } | |
+ free(font->fchar); /* should we complain if non… | |
+ font->fchar = mallocz(ni*sizeof(FChar), 1); | |
+ if(font->fchar == 0){ | |
+ err = "no memory for font"; | |
+ goto error; | |
+ } | |
+ memset(font->fchar, 0, ni*sizeof(FChar)); | |
+ font->nfchar = ni; | |
+ font->ascent = a[9]; | |
+ continue; | |
+ | |
+ /* set image 0 to screen image */ | |
+ case 'J': | |
+ m = 1; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ drawinstall(client, 0, screenimage, 0); | |
+ client->infoid = 0; | |
+ continue; | |
+ | |
+ /* get image info: 'I' */ | |
+ case 'I': | |
+ m = 1; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ if(client->infoid < 0) | |
+ goto Enodrawimage; | |
+ if(client->infoid == 0){ | |
+ i = screenimage; | |
+ if(i == nil) | |
+ goto Enodrawimage; | |
+ }else{ | |
+ di = drawlookup(client, client->infoid, 1); | |
+ if(di == nil) | |
+ goto Enodrawimage; | |
+ i = di->image; | |
+ } | |
+ ni = sprint(ibuf, "%11d %11d %11s %11d %11d %11d %11d … | |
+ " %11d %11d %11d %11d ", | |
+ client->clientid, | |
+ client->infoid, | |
+ chantostr(cbuf, i->chan), | |
+ (i->flags&Frepl)==Frepl, | |
+ i->r.min.x, i->r.min.y, i->r.max.x, i-… | |
+ i->clipr.min.x, i->clipr.min.y, | |
+ i->clipr.max.x, i->clipr.max.y); | |
+ drawmsgsquirrel(d, ibuf, ni); | |
+ client->infoid = -1; | |
+ continue; | |
+ | |
+ /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*… | |
+ case 'l': | |
+ m = 1+4+4+2+4*4+2*4+1+1; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ font = drawlookup(client, BGLONG(a+1), 1); | |
+ if(font == 0) | |
+ goto Enodrawimage; | |
+ if(font->nfchar == 0) | |
+ goto Enotfont; | |
+ src = drawimage(client, a+5); | |
+ if(!src) | |
+ goto Enodrawimage; | |
+ ci = BGSHORT(a+9); | |
+ if(ci >= font->nfchar) | |
+ goto 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] rad… | |
+ case 'L': | |
+ m = 1+4+2*4+2*4+4+4+4+4+2*4; | |
+ if(n < m) | |
+ goto 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){ | |
+ err = "negative line width"; | |
+ goto error; | |
+ } | |
+ src = drawimage(client, a+33); | |
+ if(!dst || !src) | |
+ goto Enodrawimage; | |
+ 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 m… | |
+ 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': | |
+ m = 4+4; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ break; | |
+ * | |
+ */ | |
+ | |
+ /* attach to a named image: 'n' dstid[4] j[1] name[j] */ | |
+ case 'n': | |
+ m = 1+4+1; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ j = a[5]; | |
+ if(j == 0) /* give me a non-empty name please */ | |
+ goto Eshortdraw; | |
+ m += j; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ dstid = BGLONG(a+1); | |
+ if(drawlookup(client, dstid, 0)) | |
+ goto Eimageexists; | |
+ dn = drawlookupname(j, (char*)a+6); | |
+ if(dn == nil) | |
+ goto Enoname; | |
+ s = malloc(j+1); | |
+ if(s == nil) | |
+ goto Enomem; | |
+ if(drawinstall(client, dstid, dn->dimage->image, 0) ==… | |
+ goto Edrawmem; | |
+ di = drawlookup(client, dstid, 0); | |
+ if(di == 0) | |
+ goto Eoldname; | |
+ di->vers = dn->vers; | |
+ di->name = s; | |
+ 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': | |
+ m = 1+4+1+1; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ c = a[5]; | |
+ j = a[6]; | |
+ if(j == 0) /* give me a non-empty name please */ | |
+ goto Eshortdraw; | |
+ m += j; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ di = drawlookup(client, BGLONG(a+1), 0); | |
+ if(di == 0) | |
+ goto Enodrawimage; | |
+ if(di->name) | |
+ goto Enamed; | |
+ if(c) | |
+ if(drawaddname(client, di, j, (char*)a+7, &err… | |
+ goto error; | |
+ else{ | |
+ dn = drawlookupname(j, (char*)a+7); | |
+ if(dn == nil) | |
+ goto Enoname; | |
+ if(dn->dimage != di) | |
+ goto Ewrongname; | |
+ drawdelname(dn); | |
+ } | |
+ continue; | |
+ | |
+ /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */ | |
+ case 'o': | |
+ m = 1+4+2*4+2*4; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ dst = drawimage(client, a+1); | |
+ if(!dst) | |
+ goto Enodrawimage; | |
+ if(dst->layer){ | |
+ drawpoint(&p, a+5); | |
+ drawpoint(&q, a+13); | |
+ r = dst->layer->screenr; | |
+ ni = memlorigin(dst, p, q); | |
+ if(ni < 0){ | |
+ err = "image origin failed"; | |
+ goto error; | |
+ } | |
+ 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': | |
+ m = 1+1; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ client->op = a[1]; | |
+ continue; | |
+ | |
+ /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid… | |
+ /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[… | |
+ case 'p': | |
+ case 'P': | |
+ m = 1+4+2+4+4+4+4+2*4; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ dstid = BGLONG(a+1); | |
+ dst = drawimage(client, a+1); | |
+ ni = BGSHORT(a+5); | |
+ if(ni < 0){ | |
+ err = "negative cout in polygon"; | |
+ goto error; | |
+ } | |
+ e0 = BGLONG(a+7); | |
+ e1 = BGLONG(a+11); | |
+ j = 0; | |
+ if(*a == 'p'){ | |
+ j = BGLONG(a+15); | |
+ if(j < 0){ | |
+ err = "negative polygon line width"; | |
+ goto error; | |
+ } | |
+ } | |
+ src = drawimage(client, a+19); | |
+ if(!dst || !src) | |
+ goto Enodrawimage; | |
+ drawpoint(&sp, a+23); | |
+ drawpoint(&p, a+31); | |
+ ni++; | |
+ pp = mallocz(ni*sizeof(Point), 1); | |
+ if(pp == nil) | |
+ goto Enomem; | |
+ doflush = 0; | |
+ if(dstid==0 || (dst->layer && dst->layer->screen->imag… | |
+ doflush = 1; /* simplify test in loop */ | |
+ ox = oy = 0; | |
+ esize = 0; | |
+ u = a+m; | |
+ for(y=0; y<ni; y++){ | |
+ q = p; | |
+ oesize = esize; | |
+ u = drawcoord(u, a+n, ox, &p.x); | |
+ if(!u) | |
+ goto Eshortdraw; | |
+ u = drawcoord(u, a+n, oy, &p.y); | |
+ if(!u) | |
+ goto Eshortdraw; | |
+ ox = p.x; | |
+ oy = p.y; | |
+ if(doflush){ | |
+ esize = j; | |
+ if(*a == 'p'){ | |
+ if(y == 0){ | |
+ c = memlineendsize(e0); | |
+ if(c > 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-oesiz… | |
+ combinerect(&r, Rect(p.x-esize… | |
+ } | |
+ if(rectclip(&r, dst->clipr)) … | |
+ dstflush(dstid, dst, r); | |
+ } | |
+ pp[y] = p; | |
+ } | |
+ if(y == 1) | |
+ dstflush(dstid, dst, Rect(p.x-esize, p.y-esize… | |
+ 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': | |
+ m = 1+4+4*4; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ i = drawimage(client, a+1); | |
+ if(!i) | |
+ goto Enodrawimage; | |
+ drawrectangle(&r, a+5); | |
+ if(!rectinrect(r, i->r)) | |
+ goto Ereadoutside; | |
+ c = bytesperline(r, i->depth); | |
+ c *= Dy(r); | |
+ free(client->readdata); | |
+ client->readdata = mallocz(c, 0); | |
+ if(client->readdata == nil){ | |
+ err = "readimage malloc failed"; | |
+ goto error; | |
+ } | |
+ client->nreaddata = memunload(i, r, client->readdata, … | |
+ if(client->nreaddata < 0){ | |
+ free(client->readdata); | |
+ client->readdata = nil; | |
+ err = "bad readimage call"; | |
+ goto error; | |
+ } | |
+ continue; | |
+ | |
+ /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] s… | |
+ /* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4]… | |
+ case 's': | |
+ case 'x': | |
+ m = 1+4+4+4+2*4+4*4+2*4+2; | |
+ if(*a == 'x') | |
+ m += 4+2*4; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ | |
+ dst = drawimage(client, a+1); | |
+ dstid = BGLONG(a+1); | |
+ src = drawimage(client, a+5); | |
+ if(!dst || !src) | |
+ goto Enodrawimage; | |
+ font = drawlookup(client, BGLONG(a+9), 1); | |
+ if(font == 0) | |
+ goto Enodrawimage; | |
+ if(font->nfchar == 0) | |
+ goto 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) | |
+ goto Eshortdraw; | |
+ clipr = dst->clipr; | |
+ dst->clipr = r; | |
+ op = drawclientop(client); | |
+ if(*a == 'x'){ | |
+ /* paint background */ | |
+ l = drawimage(client, a+47); | |
+ if(!l) | |
+ goto Enodrawimage; | |
+ 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; | |
+ goto 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; | |
+ goto 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->… | |
+ continue; | |
+ | |
+ /* use public screen: 'S' id[4] chan[4] */ | |
+ case 'S': | |
+ m = 1+4+4; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ dstid = BGLONG(a+1); | |
+ if(dstid == 0) | |
+ goto Ebadarg; | |
+ dscrn = drawlookupdscreen(dstid); | |
+ if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=clie… | |
+ goto Enodrawscreen; | |
+ if(dscrn->screen->image->chan != BGLONG(a+5)){ | |
+ err = "inconsistent chan"; | |
+ goto error; | |
+ } | |
+ if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0) | |
+ goto Edrawmem; | |
+ continue; | |
+ | |
+ /* top or bottom windows: 't' top[1] nw[2] n*id[4] */ | |
+ case 't': | |
+ m = 1+1+2; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ nw = BGSHORT(a+2); | |
+ if(nw < 0) | |
+ goto Ebadarg; | |
+ if(nw == 0) | |
+ continue; | |
+ m += nw*4; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ lp = mallocz(nw*sizeof(Memimage*), 1); | |
+ if(lp == 0) | |
+ goto Enomem; | |
+ for(j=0; j<nw; j++){ | |
+ lp[j] = drawimage(client, a+1+1+2+j*4); | |
+ if(lp[j] == nil){ | |
+ free(lp); | |
+ goto Enodrawimage; | |
+ } | |
+ } | |
+ if(lp[0]->layer == 0){ | |
+ err = "images are not windows"; | |
+ free(lp); | |
+ goto error; | |
+ } | |
+ for(j=1; j<nw; j++) | |
+ if(lp[j]->layer->screen != lp[0]->layer->scree… | |
+ err = "images not on same screen"; | |
+ free(lp); | |
+ goto error; | |
+ } | |
+ if(a[1]) | |
+ memltofrontn(lp, nw); | |
+ else | |
+ memltorearn(lp, nw); | |
+ if(lp[0]->layer->screen->image->data == screenimage->d… | |
+ for(j=0; j<nw; j++) | |
+ addflush(lp[j]->layer->screenr); | |
+ free(lp); | |
+ ll = drawlookup(client, BGLONG(a+1+1+2), 1); | |
+ drawrefreshscreen(ll, client); | |
+ continue; | |
+ | |
+ /* visible: 'v' */ | |
+ case 'v': | |
+ 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': | |
+ m = 1+4+4*4; | |
+ if(n < m) | |
+ goto Eshortdraw; | |
+ dstid = BGLONG(a+1); | |
+ dst = drawimage(client, a+1); | |
+ if(!dst) | |
+ goto Enodrawimage; | |
+ drawrectangle(&r, a+5); | |
+ if(!rectinrect(r, dst->r)) | |
+ goto Ewriteoutside; | |
+ y = memload(dst, r, a+m, n-m, *a=='Y'); | |
+ if(y < 0){ | |
+ err = "bad writeimage call"; | |
+ goto error; | |
+ } | |
+ dstflush(dstid, dst, r); | |
+ m += y; | |
+ continue; | |
+ } | |
+ } | |
+ return oldn - n; | |
+ | |
+Enodrawimage: | |
+ err = "unknown id for draw image"; | |
+ goto error; | |
+Enodrawscreen: | |
+ err = "unknown id for draw screen"; | |
+ goto error; | |
+Eshortdraw: | |
+ err = "short draw message"; | |
+ goto error; | |
+Eshortread: | |
+ err = "draw read too short"; | |
+ goto error; | |
+Eimageexists: | |
+ err = "image id in use"; | |
+ goto error; | |
+Escreenexists: | |
+ err = "screen id in use"; | |
+ goto error; | |
+Edrawmem: | |
+ err = "image memory allocation failed"; | |
+ goto error; | |
+Ereadoutside: | |
+ err = "readimage outside image"; | |
+ goto error; | |
+Ewriteoutside: | |
+ err = "writeimage outside image"; | |
+ goto error; | |
+Enotfont: | |
+ err = "image not a font"; | |
+ goto error; | |
+Eindex: | |
+ err = "character index out of range"; | |
+ goto error; | |
+Enoclient: | |
+ err = "no such draw client"; | |
+ goto error; | |
+Edepth: | |
+ err = "image has bad depth"; | |
+ goto error; | |
+Enameused: | |
+ err = "image name in use"; | |
+ goto error; | |
+Enoname: | |
+ err = "no image with that name"; | |
+ goto error; | |
+Eoldname: | |
+ err = "named image no longer valid"; | |
+ goto error; | |
+Enamed: | |
+ err = "image already has name"; | |
+ goto error; | |
+Ewrongname: | |
+ err = "wrong name for image"; | |
+ goto error; | |
+Enomem: | |
+ err = "out of memory"; | |
+ goto error; | |
+Ebadarg: | |
+ err = "bad argument in draw message"; | |
+ goto error; | |
+ | |
+error: | |
+ drawerror(display, err); | |
+ return -1; | |
+} | |
+ | |
+ | |
diff --git a/src/libdraw/draw.h b/src/libdraw/draw.h | |
t@@ -0,0 +1,520 @@ | |
+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; | |
+ | |
+extern int Rfmt(Fmt*); | |
+extern int Pfmt(Fmt*); | |
+ | |
+enum | |
+{ | |
+ DOpaque = 0xFFFFFFFF, | |
+ DTransparent = 0x00000000, /* only useful for al… | |
+ 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(… | |
+ | |
+#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), … | |
+ XRGB32 = CHAN4(CIgnore, 8, CRed, 8, CGreen, 8, CBlue, 8), | |
+ XBGR32 = CHAN4(CIgnore, 8, CBlue, 8, CGreen, 8, CRed, 8), | |
+}; | |
+ | |
+extern char* chantostr(char*, u32int); | |
+extern u32int strtochan(char*); | |
+extern int chantodepth(u32int); | |
+ | |
+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 imageid; | |
+ int local; | |
+ void (*error)(Display*, char*); | |
+ char *devdir; | |
+ char *windir; | |
+ char oldlabel[64]; | |
+ u32int dataqid; | |
+ Image *image; | |
+ Image *white; | |
+ Image *black; | |
+ Image *opaque; | |
+ Image *transparent; | |
+ uchar *buf; | |
+ int bufsize; | |
+ uchar *bufp; | |
+ uchar *obuf; | |
+ int obufsize; | |
+ uchar *obufp; | |
+ 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 co… | |
+ Rectangle clipr; /* clipping region */ | |
+ int depth; /* number of bits per pixel */ | |
+ u32int chan; | |
+ int repl; /* flag: data replicates to ti… | |
+ Screen *screen; /* 0 if not a window */ | |
+ Image *next; /* next in list of windows */ | |
+}; | |
+ | |
+struct RGB | |
+{ | |
+ u32int red; | |
+ u32int green; | |
+ u32int 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… | |
+ 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<<LOG2NFCACHE), /* #chars cached */ | |
+ NFLOOK = 5, /* #chars to scan in cache */ | |
+ NFSUBF = 2, /* #subfonts to cache */ | |
+ /* max value */ | |
+ MAXFCACHE = 1024+NFLOOK, /* upper limit */ | |
+ MAXSUBF = 50, /* generous upper limit */ | |
+ /* deltas */ | |
+ DSUBF = 4, | |
+ /* expiry ages */ | |
+ SUBFAGE = 10000, | |
+ CACHEAGE = 10000 | |
+}; | |
+ | |
+struct Cachefont | |
+{ | |
+ Rune min; /* lowest rune value to be taken from … | |
+ Rune max; /* highest rune value+1 to be taken fr… | |
+ int offset; /* position in subfont of character … | |
+ char *name; /* stored in font */ | |
+ char *subfontname; /* to access subfont … | |
+}; | |
+ | |
+struct Cacheinfo | |
+{ | |
+ ushort x; /* left edge of bits */ | |
+ uchar width; /* width of baseline */ | |
+ schar left; /* offset of baseline */ | |
+ Rune value; /* value of character at this slot i… | |
+ ushort age; | |
+}; | |
+ | |
+struct Cachesubf | |
+{ | |
+ u32int age; /* for replacement */ | |
+ Cachefont *cf; /* font info that owns us */ | |
+ Subfont *f; /* attached subfont */ | |
+}; | |
+ | |
+struct Font | |
+{ | |
+ char *name; | |
+ Display *display; | |
+ short height; /* max height of image, interline … | |
+ short ascent; /* top of image to baseline */ | |
+ short width; /* widest so far; used in caching o… | |
+ short nsub; /* number of subfonts */ | |
+ u32int age; /* increasing counter; used for LRU … | |
+ int maxdepth; /* maximum depth of all loaded sub… | |
+ int ncache; /* size of cache */ | |
+ int nsubf; /* size of subfont list */ | |
+ Cacheinfo *cache; | |
+ Cachesubf *subf; | |
+ Cachefont **sub; /* as read from file */ | |
+ Image *cacheimage; | |
+}; | |
+ | |
+#define Dx(r) ((r).max.x-(r).min.x) | |
+#define Dy(r) ((r).max.y-(r).min.y) | |
+ | |
+/* | |
+ * Image management | |
+ */ | |
+extern Image* _allocimage(Image*, Display*, Rectangle, u32int, int, u32… | |
+extern Image* allocimage(Display*, Rectangle, u32int, int, u32int); | |
+extern uchar* bufimage(Display*, int); | |
+extern int bytesperline(Rectangle, int); | |
+extern void closedisplay(Display*); | |
+extern void drawerror(Display*, char*); | |
+extern int flushimage(Display*, int); | |
+extern int freeimage(Image*); | |
+extern int _freeimage1(Image*); | |
+extern int geninitdraw(char*, void(*)(Display*, char*), char*, char*, c… | |
+extern int initdraw(void(*)(Display*, char*), char*, char*); | |
+extern int newwindow(char*); | |
+extern int loadimage(Image*, Rectangle, uchar*, int); | |
+extern int cloadimage(Image*, Rectangle, uchar*, int); | |
+extern int getwindow(Display*, int); | |
+extern int gengetwindow(Display*, char*, Image**, Screen**, int); | |
+extern Image* readimage(Display*, int, int); | |
+extern Image* creadimage(Display*, int, int); | |
+extern int unloadimage(Image*, Rectangle, uchar*, int); | |
+extern int wordsperline(Rectangle, int); | |
+extern int writeimage(int, Image*, int); | |
+extern Image* namedimage(Display*, char*); | |
+extern int nameimage(Image*, char*, int); | |
+extern Image* allocimagemix(Display*, u32int, u32int); | |
+ | |
+/* | |
+ * Colors | |
+ */ | |
+extern void readcolmap(Display*, RGB*); | |
+extern void writecolmap(Display*, RGB*); | |
+extern u32int setalpha(u32int, uchar); | |
+ | |
+/* | |
+ * Windows | |
+ */ | |
+extern Screen* allocscreen(Image*, Image*, int); | |
+extern Image* _allocwindow(Image*, Screen*, Rectangle, int, u32int); | |
+extern Image* allocwindow(Screen*, Rectangle, int, u32int); | |
+extern void bottomnwindows(Image**, int); | |
+extern void bottomwindow(Image*); | |
+extern int freescreen(Screen*); | |
+extern Screen* publicscreen(Display*, int, u32int); | |
+extern void topnwindows(Image**, int); | |
+extern void topwindow(Image*); | |
+extern int originwindow(Image*, Point, Point); | |
+ | |
+/* | |
+ * Geometry | |
+ */ | |
+extern Point Pt(int, int); | |
+extern Rectangle Rect(int, int, int, int); | |
+extern Rectangle Rpt(Point, Point); | |
+extern Point addpt(Point, Point); | |
+extern Point subpt(Point, Point); | |
+extern Point divpt(Point, int); | |
+extern Point mulpt(Point, int); | |
+extern int eqpt(Point, Point); | |
+extern int eqrect(Rectangle, Rectangle); | |
+extern Rectangle insetrect(Rectangle, int); | |
+extern Rectangle rectaddpt(Rectangle, Point); | |
+extern Rectangle rectsubpt(Rectangle, Point); | |
+extern Rectangle canonrect(Rectangle); | |
+extern int rectXrect(Rectangle, Rectangle); | |
+extern int rectinrect(Rectangle, Rectangle); | |
+extern void combinerect(Rectangle*, Rectangle); | |
+extern int rectclip(Rectangle*, Rectangle); | |
+extern int ptinrect(Point, Rectangle); | |
+extern void replclipr(Image*, int, Rectangle); | |
+extern int drawreplxy(int, int, int); /* used to be draw… | |
+extern Point drawrepl(Rectangle, Point); | |
+extern int rgb2cmap(int, int, int); | |
+extern int cmap2rgb(int); | |
+extern int cmap2rgba(int); | |
+extern void icossin(int, int*, int*); | |
+extern void icossin2(int, int, int*, int*); | |
+ | |
+/* | |
+ * Graphics | |
+ */ | |
+extern void draw(Image*, Rectangle, Image*, Image*, Point); | |
+extern void drawop(Image*, Rectangle, Image*, Image*, Point, Drawop); | |
+extern void gendraw(Image*, Rectangle, Image*, Point, Image*, Point); | |
+extern void gendrawop(Image*, Rectangle, Image*, Point, Image*, Point, … | |
+extern void line(Image*, Point, Point, int, int, int, Image*, Point); | |
+extern void lineop(Image*, Point, Point, int, int, int, Image*, Point, … | |
+extern void poly(Image*, Point*, int, int, int, int, Image*, Point); | |
+extern void polyop(Image*, Point*, int, int, int, int, Image*, Point, D… | |
+extern void fillpoly(Image*, Point*, int, int, Image*, Point); | |
+extern void fillpolyop(Image*, Point*, int, int, Image*, Point, Drawop); | |
+extern Point string(Image*, Point, Image*, Point, Font*, char*); | |
+extern Point stringop(Image*, Point, Image*, Point, Font*, char*, Drawo… | |
+extern Point stringn(Image*, Point, Image*, Point, Font*, char*, int); | |
+extern Point stringnop(Image*, Point, Image*, Point, Font*, char*, int,… | |
+extern Point runestring(Image*, Point, Image*, Point, Font*, Rune*); | |
+extern Point runestringop(Image*, Point, Image*, Point, Font*, Rune*, D… | |
+extern Point runestringn(Image*, Point, Image*, Point, Font*, Rune*, in… | |
+extern Point runestringnop(Image*, Point, Image*, Point, Font*, Rune*, … | |
+extern Point stringbg(Image*, Point, Image*, Point, Font*, char*, Image… | |
+extern Point stringbgop(Image*, Point, Image*, Point, Font*, char*, Ima… | |
+extern Point stringnbg(Image*, Point, Image*, Point, Font*, char*, int,… | |
+extern Point stringnbgop(Image*, Point, Image*, Point, Font*, char*, in… | |
+extern Point runestringbg(Image*, Point, Image*, Point, Font*, Rune*, I… | |
+extern Point runestringbgop(Image*, Point, Image*, Point, Font*, Rune*,… | |
+extern Point runestringnbg(Image*, Point, Image*, Point, Font*, Rune*, … | |
+extern Point runestringnbgop(Image*, Point, Image*, Point, Font*, Rune*… | |
+extern Point _string(Image*, Point, Image*, Point, Font*, char*, Rune*,… | |
+extern Point stringsubfont(Image*, Point, Image*, Subfont*, char*); | |
+extern int bezier(Image*, Point, Point, Point, Point, int, int,… | |
+extern int bezierop(Image*, Point, Point, Point, Point, int, in… | |
+extern int bezspline(Image*, Point*, int, int, int, int, Image*… | |
+extern int bezsplineop(Image*, Point*, int, int, int, int, Imag… | |
+extern int bezsplinepts(Point*, int, Point**); | |
+extern int fillbezier(Image*, Point, Point, Point, Point, int, … | |
+extern int fillbezierop(Image*, Point, Point, Point, Point, int… | |
+extern int fillbezspline(Image*, Point*, int, int, Image*, Poin… | |
+extern int fillbezsplineop(Image*, Point*, int, int, Image*, Po… | |
+extern void ellipse(Image*, Point, int, int, int, Image*, Point); | |
+extern void ellipseop(Image*, Point, int, int, int, Image*, Point, Draw… | |
+extern void fillellipse(Image*, Point, int, int, Image*, Point); | |
+extern void fillellipseop(Image*, Point, int, int, Image*, Point, Drawo… | |
+extern void arc(Image*, Point, int, int, int, Image*, Point, int, int); | |
+extern void arcop(Image*, Point, int, int, int, Image*, Point, int, int… | |
+extern void fillarc(Image*, Point, int, int, Image*, Point, int, int); | |
+extern void fillarcop(Image*, Point, int, int, Image*, Point, int, int,… | |
+extern void border(Image*, Rectangle, int, Image*, Point); | |
+extern void borderop(Image*, Rectangle, int, Image*, Point, Drawop); | |
+ | |
+/* | |
+ * Font management | |
+ */ | |
+extern Font* openfont(Display*, char*); | |
+extern Font* buildfont(Display*, char*, char*); | |
+extern void freefont(Font*); | |
+extern Font* mkfont(Subfont*, Rune); | |
+extern int cachechars(Font*, char**, Rune**, ushort*, int, int*, char**… | |
+extern void agefont(Font*); | |
+extern Subfont* allocsubfont(char*, int, int, int, Fontchar*, Image*); | |
+extern Subfont* lookupsubfont(Display*, char*); | |
+extern void installsubfont(char*, Subfont*); | |
+extern void uninstallsubfont(Subfont*); | |
+extern void freesubfont(Subfont*); | |
+extern Subfont* readsubfont(Display*, char*, int, int); | |
+extern Subfont* readsubfonti(Display*, char*, int, Image*, int); | |
+extern int writesubfont(int, Subfont*); | |
+extern void _unpackinfo(Fontchar*, uchar*, int); | |
+extern Point stringsize(Font*, char*); | |
+extern int stringwidth(Font*, char*); | |
+extern int stringnwidth(Font*, char*, int); | |
+extern Point runestringsize(Font*, Rune*); | |
+extern int runestringwidth(Font*, Rune*); | |
+extern int runestringnwidth(Font*, Rune*, int); | |
+extern Point strsubfontwidth(Subfont*, char*); | |
+extern int loadchar(Font*, Rune, Cacheinfo*, int, int, char**); | |
+extern char* subfontname(char*, char*, int); | |
+extern Subfont* _getsubfont(Display*, char*); | |
+extern Subfont* getdefont(Display*); | |
+extern void lockdisplay(Display*); | |
+extern void unlockdisplay(Display*); | |
+extern int drawlsetrefresh(u32int, int, void*, void*); | |
+ | |
+/* | |
+ * Predefined | |
+ */ | |
+extern uchar defontdata[]; | |
+extern int sizeofdefont; | |
+extern Point ZP; | |
+extern Rectangle ZR; | |
+ | |
+/* | |
+ * Set up by initdraw() | |
+ */ | |
+extern Display *display; | |
+extern Font *font; | |
+extern Image *screen; | |
+extern Screen *_screen; | |
+extern int _cursorfd; | |
+extern int _drawdebug; /* set to 1 to see errors from flu… | |
+extern void _setdrawop(Display*, Drawop); | |
+extern Display *_initdisplay(void(*)(Display*,char*), char*); | |
+ | |
+#define BGSHORT(p) (((p)[0]<<0) | ((p)[1]<<8)) | |
+#define BGLONG(p) ((BGSHORT(p)<<0) | (BGSHORT(p+2)<<16)) | |
+#define BPSHORT(p, v) ((p)[0]=(v), (p)[1]=((v)>>8)) | |
+#define BPLONG(p, v) (BPSHORT(p, (v)), BPSHORT(p+2, (v)>… | |
+ | |
+/* | |
+ * 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 u32int drawld2chan[]; | |
+extern void drawsetdebug(int); | |
+ | |
+/* | |
+ * Port magic. | |
+ */ | |
+int _drawmsgread(Display*, void*, int); | |
+int _drawmsgwrite(Display*, void*, int); | |
diff --git a/src/libdraw/ellipse.c b/src/libdraw/ellipse.c | |
t@@ -0,0 +1,82 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+static | |
+void | |
+doellipse(int cmd, Image *dst, Point *c, int xr, int yr, int thick, Image *src… | |
+{ | |
+ uchar *a; | |
+ | |
+ _setdrawop(dst->display, op); | |
+ | |
+ a = bufimage(dst->display, 1+4+4+2*4+4+4+4+2*4+2*4); | |
+ if(a == 0){ | |
+ fprint(2, "image ellipse: %r\n"); | |
+ return; | |
+ } | |
+ a[0] = cmd; | |
+ BPLONG(a+1, dst->id); | |
+ BPLONG(a+5, src->id); | |
+ BPLONG(a+9, c->x); | |
+ BPLONG(a+13, c->y); | |
+ BPLONG(a+17, xr); | |
+ BPLONG(a+21, yr); | |
+ BPLONG(a+25, thick); | |
+ BPLONG(a+29, sp->x); | |
+ BPLONG(a+33, sp->y); | |
+ BPLONG(a+37, alpha); | |
+ BPLONG(a+41, phi); | |
+} | |
+ | |
+void | |
+ellipse(Image *dst, Point c, int a, int b, int thick, Image *src, Point sp) | |
+{ | |
+ doellipse('e', dst, &c, a, b, thick, src, &sp, 0, 0, SoverD); | |
+} | |
+ | |
+void | |
+ellipseop(Image *dst, Point c, int a, int b, int thick, Image *src, Point sp, … | |
+{ | |
+ doellipse('e', dst, &c, a, b, thick, src, &sp, 0, 0, op); | |
+} | |
+ | |
+void | |
+fillellipse(Image *dst, Point c, int a, int b, Image *src, Point sp) | |
+{ | |
+ doellipse('E', dst, &c, a, b, 0, src, &sp, 0, 0, SoverD); | |
+} | |
+ | |
+void | |
+fillellipseop(Image *dst, Point c, int a, int b, Image *src, Point sp, Drawop … | |
+{ | |
+ doellipse('E', dst, &c, a, b, 0, src, &sp, 0, 0, op); | |
+} | |
+ | |
+void | |
+arc(Image *dst, Point c, int a, int b, int thick, Image *src, Point sp, int al… | |
+{ | |
+ alpha |= 1<<31; | |
+ doellipse('e', dst, &c, a, b, thick, src, &sp, alpha, phi, SoverD); | |
+} | |
+ | |
+void | |
+arcop(Image *dst, Point c, int a, int b, int thick, Image *src, Point sp, int … | |
+{ | |
+ alpha |= 1<<31; | |
+ doellipse('e', dst, &c, a, b, thick, src, &sp, alpha, phi, op); | |
+} | |
+ | |
+void | |
+fillarc(Image *dst, Point c, int a, int b, Image *src, Point sp, int alpha, in… | |
+{ | |
+ alpha |= 1<<31; | |
+ doellipse('E', dst, &c, a, b, 0, src, &sp, alpha, phi, SoverD); | |
+} | |
+ | |
+void | |
+fillarcop(Image *dst, Point c, int a, int b, Image *src, Point sp, int alpha, … | |
+{ | |
+ alpha |= 1<<31; | |
+ doellipse('E', dst, &c, a, b, 0, src, &sp, alpha, phi, op); | |
+} | |
diff --git a/src/libdraw/emenuhit.c b/src/libdraw/emenuhit.c | |
t@@ -0,0 +1,271 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <event.h> | |
+ | |
+enum | |
+{ | |
+ Margin = 4, /* outside to text */ | |
+ Border = 2, /* outside to selection boxes */ | |
+ Blackborder = 2, /* width of outlining border */ | |
+ Vspacing = 2, /* extra spacing between lines of text */ | |
+ Maxunscroll = 25, /* maximum #entries before scrolling turns on… | |
+ Nscroll = 20, /* number entries in scrolling part */ | |
+ Scrollwid = 14, /* width of scroll bar */ | |
+ Gap = 4, /* between text and scroll bar */ | |
+}; | |
+ | |
+static Image *menutxt; | |
+static Image *back; | |
+static Image *high; | |
+static Image *bord; | |
+static Image *text; | |
+static Image *htext; | |
+ | |
+static | |
+void | |
+menucolors(void) | |
+{ | |
+ /* Main tone is greenish, with negative selection */ | |
+ back = allocimagemix(display, DPalegreen, DWhite); | |
+ high = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DDarkgreen); … | |
+ bord = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedgreen); … | |
+ if(back==nil || high==nil || bord==nil) | |
+ goto Error; | |
+ text = display->black; | |
+ htext = back; | |
+ return; | |
+ | |
+ Error: | |
+ freeimage(back); | |
+ freeimage(high); | |
+ freeimage(bord); | |
+ back = display->white; | |
+ high = display->black; | |
+ bord = display->black; | |
+ text = display->black; | |
+ htext = display->white; | |
+} | |
+ | |
+/* | |
+ * r is a rectangle holding the text elements. | |
+ * return the rectangle, including its black edge, holding element i. | |
+ */ | |
+static Rectangle | |
+menurect(Rectangle r, int i) | |
+{ | |
+ if(i < 0) | |
+ return Rect(0, 0, 0, 0); | |
+ r.min.y += (font->height+Vspacing)*i; | |
+ r.max.y = r.min.y+font->height+Vspacing; | |
+ return insetrect(r, Border-Margin); | |
+} | |
+ | |
+/* | |
+ * r is a rectangle holding the text elements. | |
+ * return the element number containing p. | |
+ */ | |
+static int | |
+menusel(Rectangle r, Point p) | |
+{ | |
+ r = insetrect(r, Margin); | |
+ if(!ptinrect(p, r)) | |
+ return -1; | |
+ return (p.y-r.min.y)/(font->height+Vspacing); | |
+} | |
+ | |
+static | |
+void | |
+paintitem(Menu *menu, Rectangle textr, int off, int i, int highlight, Image *s… | |
+{ | |
+ char *item; | |
+ Rectangle r; | |
+ Point pt; | |
+ | |
+ if(i < 0) | |
+ return; | |
+ r = menurect(textr, i); | |
+ if(restore){ | |
+ draw(screen, r, restore, nil, restore->r.min); | |
+ return; | |
+ } | |
+ if(save) | |
+ draw(save, save->r, screen, nil, r.min); | |
+ item = menu->item? menu->item[i+off] : (*menu->gen)(i+off); | |
+ pt.x = (textr.min.x+textr.max.x-stringwidth(font, item))/2; | |
+ pt.y = textr.min.y+i*(font->height+Vspacing); | |
+ draw(screen, r, highlight? high : back, nil, pt); | |
+ string(screen, pt, highlight? htext : text, pt, font, item); | |
+} | |
+ | |
+/* | |
+ * menur is a rectangle holding all the highlightable text elements. | |
+ * track mouse while inside the box, return what's selected when button | |
+ * is raised, -1 as soon as it leaves box. | |
+ * invariant: nothing is highlighted on entry or exit. | |
+ */ | |
+static int | |
+menuscan(Menu *menu, int but, Mouse *m, Rectangle textr, int off, int lasti, I… | |
+{ | |
+ int i; | |
+ | |
+ paintitem(menu, textr, off, lasti, 1, save, nil); | |
+ flushimage(display, 1); /* in case display->locking is set */ | |
+ *m = emouse(); | |
+ while(m->buttons & (1<<(but-1))){ | |
+ flushimage(display, 1); /* in case display->locking is … | |
+ *m = emouse(); | |
+ i = menusel(textr, m->xy); | |
+ if(i != -1 && i == lasti) | |
+ continue; | |
+ paintitem(menu, textr, off, lasti, 0, nil, save); | |
+ if(i == -1) | |
+ return i; | |
+ lasti = i; | |
+ paintitem(menu, textr, off, lasti, 1, save, nil); | |
+ } | |
+ return lasti; | |
+} | |
+ | |
+static void | |
+menupaint(Menu *menu, Rectangle textr, int off, int nitemdrawn) | |
+{ | |
+ int i; | |
+ | |
+ draw(screen, insetrect(textr, Border-Margin), back, nil, ZP); | |
+ for(i = 0; i<nitemdrawn; i++) | |
+ paintitem(menu, textr, off, i, 0, nil, nil); | |
+} | |
+ | |
+static void | |
+menuscrollpaint(Rectangle scrollr, int off, int nitem, int nitemdrawn) | |
+{ | |
+ Rectangle r; | |
+ | |
+ draw(screen, scrollr, back, nil, ZP); | |
+ r.min.x = scrollr.min.x; | |
+ r.max.x = scrollr.max.x; | |
+ r.min.y = scrollr.min.y + (Dy(scrollr)*off)/nitem; | |
+ r.max.y = scrollr.min.y + (Dy(scrollr)*(off+nitemdrawn))/nitem; | |
+ if(r.max.y < r.min.y+2) | |
+ r.max.y = r.min.y+2; | |
+ border(screen, r, 1, bord, ZP); | |
+ if(menutxt == 0) | |
+ menutxt = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, DDar… | |
+ if(menutxt) | |
+ draw(screen, insetrect(r, 1), menutxt, nil, ZP); | |
+} | |
+ | |
+int | |
+emenuhit(int but, Mouse *m, Menu *menu) | |
+{ | |
+ int i, nitem, nitemdrawn, maxwid, lasti, off, noff, wid, screenitem; | |
+ int scrolling; | |
+ Rectangle r, menur, sc, textr, scrollr; | |
+ Image *b, *save; | |
+ Point pt; | |
+ char *item; | |
+ | |
+ if(back == nil) | |
+ menucolors(); | |
+ sc = screen->clipr; | |
+ replclipr(screen, 0, screen->r); | |
+ maxwid = 0; | |
+ for(nitem = 0; | |
+ item = menu->item? menu->item[nitem] : (*menu->gen)(nitem); | |
+ nitem++){ | |
+ i = stringwidth(font, item); | |
+ if(i > maxwid) | |
+ maxwid = i; | |
+ } | |
+ if(menu->lasthit<0 || menu->lasthit>=nitem) | |
+ menu->lasthit = 0; | |
+ screenitem = (Dy(screen->r)-10)/(font->height+Vspacing); | |
+ if(nitem>Maxunscroll || nitem>screenitem){ | |
+ scrolling = 1; | |
+ nitemdrawn = Nscroll; | |
+ if(nitemdrawn > screenitem) | |
+ nitemdrawn = screenitem; | |
+ wid = maxwid + Gap + Scrollwid; | |
+ off = menu->lasthit - nitemdrawn/2; | |
+ if(off < 0) | |
+ off = 0; | |
+ if(off > nitem-nitemdrawn) | |
+ off = nitem-nitemdrawn; | |
+ lasti = menu->lasthit-off; | |
+ }else{ | |
+ scrolling = 0; | |
+ nitemdrawn = nitem; | |
+ wid = maxwid; | |
+ off = 0; | |
+ lasti = menu->lasthit; | |
+ } | |
+ r = insetrect(Rect(0, 0, wid, nitemdrawn*(font->height+Vspacing)), -Ma… | |
+ r = rectsubpt(r, Pt(wid/2, lasti*(font->height+Vspacing)+font->height/… | |
+ r = rectaddpt(r, m->xy); | |
+ pt = ZP; | |
+ if(r.max.x>screen->r.max.x) | |
+ pt.x = screen->r.max.x-r.max.x; | |
+ if(r.max.y>screen->r.max.y) | |
+ pt.y = screen->r.max.y-r.max.y; | |
+ if(r.min.x<screen->r.min.x) | |
+ pt.x = screen->r.min.x-r.min.x; | |
+ if(r.min.y<screen->r.min.y) | |
+ pt.y = screen->r.min.y-r.min.y; | |
+ menur = rectaddpt(r, pt); | |
+ textr.max.x = menur.max.x-Margin; | |
+ textr.min.x = textr.max.x-maxwid; | |
+ textr.min.y = menur.min.y+Margin; | |
+ textr.max.y = textr.min.y + nitemdrawn*(font->height+Vspacing); | |
+ if(scrolling){ | |
+ scrollr = insetrect(menur, Border); | |
+ scrollr.max.x = scrollr.min.x+Scrollwid; | |
+ }else | |
+ scrollr = Rect(0, 0, 0, 0); | |
+ | |
+ b = allocimage(display, menur, screen->chan, 0, 0); | |
+ if(b == 0) | |
+ b = screen; | |
+ draw(b, menur, screen, nil, menur.min); | |
+ draw(screen, menur, back, nil, ZP); | |
+ border(screen, menur, Blackborder, bord, ZP); | |
+ save = allocimage(display, menurect(textr, 0), screen->chan, 0, -1); | |
+ r = menurect(textr, lasti); | |
+ emoveto(divpt(addpt(r.min, r.max), 2)); | |
+ menupaint(menu, textr, off, nitemdrawn); | |
+ if(scrolling) | |
+ menuscrollpaint(scrollr, off, nitem, nitemdrawn); | |
+ while(m->buttons & (1<<(but-1))){ | |
+ lasti = menuscan(menu, but, m, textr, off, lasti, save); | |
+ if(lasti >= 0) | |
+ break; | |
+ while(!ptinrect(m->xy, textr) && (m->buttons & (1<<(but-1)))){ | |
+ if(scrolling && ptinrect(m->xy, scrollr)){ | |
+ noff = ((m->xy.y-scrollr.min.y)*nitem)/Dy(scro… | |
+ noff -= nitemdrawn/2; | |
+ if(noff < 0) | |
+ noff = 0; | |
+ if(noff > nitem-nitemdrawn) | |
+ noff = nitem-nitemdrawn; | |
+ if(noff != off){ | |
+ off = noff; | |
+ menupaint(menu, textr, off, nitemdrawn… | |
+ menuscrollpaint(scrollr, off, nitem, n… | |
+ } | |
+ } | |
+ flushimage(display, 1); /* in case display->loc… | |
+ *m = emouse(); | |
+ } | |
+ } | |
+ draw(screen, menur, b, nil, menur.min); | |
+ if(b != screen) | |
+ freeimage(b); | |
+ freeimage(save); | |
+ replclipr(screen, 0, sc); | |
+ flushimage(display, 1); | |
+ if(lasti >= 0){ | |
+ menu->lasthit = lasti+off; | |
+ return menu->lasthit; | |
+ } | |
+ return -1; | |
+} | |
diff --git a/src/libdraw/event.c b/src/libdraw/event.c | |
t@@ -0,0 +1,486 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <cursor.h> | |
+#include <event.h> | |
+ | |
+typedef struct Slave Slave; | |
+typedef struct Ebuf Ebuf; | |
+ | |
+struct Slave | |
+{ | |
+ int pid; | |
+ Ebuf *head; /* ueue of messages for this descrip… | |
+ Ebuf *tail; | |
+ int (*fn)(int, Event*, uchar*, int); | |
+}; | |
+ | |
+struct Ebuf | |
+{ | |
+ Ebuf *next; | |
+ int n; /* number of bytes in buf */ | |
+ uchar buf[EMAXMSG]; | |
+}; | |
+ | |
+static Slave eslave[MAXSLAVE]; | |
+static int Skeyboard = -1; | |
+static int Smouse = -1; | |
+static int Stimer = -1; | |
+static int logfid; | |
+ | |
+static int nslave; | |
+static int parentpid; | |
+static int epipe[2]; | |
+static int eforkslave(ulong); | |
+static void extract(void); | |
+static void ekill(void); | |
+static int enote(void *, char *); | |
+static int mousefd; | |
+static int cursorfd; | |
+ | |
+static | |
+Ebuf* | |
+ebread(Slave *s) | |
+{ | |
+ Ebuf *eb; | |
+ Dir *d; | |
+ ulong l; | |
+ | |
+ for(;;){ | |
+ d = dirfstat(epipe[0]); | |
+ if(d == nil) | |
+ drawerror(display, "events: eread stat error"); | |
+ l = d->length; | |
+ free(d); | |
+ if(s->head && l==0) | |
+ break; | |
+ extract(); | |
+ } | |
+ eb = s->head; | |
+ s->head = s->head->next; | |
+ if(s->head == 0) | |
+ s->tail = 0; | |
+ return eb; | |
+} | |
+ | |
+ulong | |
+event(Event *e) | |
+{ | |
+ return eread(~0UL, e); | |
+} | |
+ | |
+ulong | |
+eread(ulong keys, Event *e) | |
+{ | |
+ Ebuf *eb; | |
+ int i, id; | |
+ | |
+ if(keys == 0) | |
+ return 0; | |
+ for(;;){ | |
+ for(i=0; i<nslave; i++) | |
+ if((keys & (1<<i)) && eslave[i].head){ | |
+ id = 1<<i; | |
+ if(i == Smouse) | |
+ e->mouse = emouse(); | |
+ else if(i == Skeyboard) | |
+ e->kbdc = ekbd(); | |
+ else if(i == Stimer) | |
+ eslave[i].head = 0; | |
+ else{ | |
+ eb = ebread(&eslave[i]); | |
+ e->n = eb->n; | |
+ if(eslave[i].fn) | |
+ id = (*eslave[i].fn)(id, e, eb… | |
+ else | |
+ memmove(e->data, eb->buf, eb->… | |
+ free(eb); | |
+ } | |
+ return id; | |
+ } | |
+ extract(); | |
+ } | |
+ return 0; | |
+} | |
+ | |
+int | |
+ecanmouse(void) | |
+{ | |
+ if(Smouse < 0) | |
+ drawerror(display, "events: mouse not initialized"); | |
+ return ecanread(Emouse); | |
+} | |
+ | |
+int | |
+ecankbd(void) | |
+{ | |
+ if(Skeyboard < 0) | |
+ drawerror(display, "events: keyboard not initialzed"); | |
+ return ecanread(Ekeyboard); | |
+} | |
+ | |
+int | |
+ecanread(ulong keys) | |
+{ | |
+ Dir *d; | |
+ int i; | |
+ ulong l; | |
+ | |
+ for(;;){ | |
+ for(i=0; i<nslave; i++) | |
+ if((keys & (1<<i)) && eslave[i].head) | |
+ return 1; | |
+ d = dirfstat(epipe[0]); | |
+ if(d == nil) | |
+ drawerror(display, "events: ecanread stat error"); | |
+ l = d->length; | |
+ free(d); | |
+ if(l == 0) | |
+ return 0; | |
+ extract(); | |
+ } | |
+ return -1; | |
+} | |
+ | |
+ulong | |
+estartfn(ulong key, int fd, int n, int (*fn)(int, Event*, uchar*, int)) | |
+{ | |
+ char buf[EMAXMSG+1]; | |
+ int i, r; | |
+ | |
+ if(fd < 0) | |
+ drawerror(display, "events: bad file descriptor"); | |
+ if(n <= 0 || n > EMAXMSG) | |
+ n = EMAXMSG; | |
+ i = eforkslave(key); | |
+ if(i < MAXSLAVE){ | |
+ eslave[i].fn = fn; | |
+ return 1<<i; | |
+ } | |
+ buf[0] = i - MAXSLAVE; | |
+ while((r = read(fd, buf+1, n))>0) | |
+ if(write(epipe[1], buf, r+1)!=r+1) | |
+ break; | |
+ buf[0] = MAXSLAVE; | |
+ write(epipe[1], buf, 1); | |
+ _exits(0); | |
+ return 0; | |
+} | |
+ | |
+ulong | |
+estart(ulong key, int fd, int n) | |
+{ | |
+ return estartfn(key, fd, n, nil); | |
+} | |
+ | |
+ulong | |
+etimer(ulong key, int n) | |
+{ | |
+ char t[2]; | |
+ | |
+ if(Stimer != -1) | |
+ drawerror(display, "events: timer started twice"); | |
+ Stimer = eforkslave(key); | |
+ if(Stimer < MAXSLAVE) | |
+ return 1<<Stimer; | |
+ if(n <= 0) | |
+ n = 1000; | |
+ t[0] = t[1] = Stimer - MAXSLAVE; | |
+ do | |
+ sleep(n); | |
+ while(write(epipe[1], t, 2) == 2); | |
+ t[0] = MAXSLAVE; | |
+ write(epipe[1], t, 1); | |
+ _exits(0); | |
+ return 0; | |
+} | |
+ | |
+static void | |
+ekeyslave(int fd) | |
+{ | |
+ Rune r; | |
+ char t[3], k[10]; | |
+ int kr, kn, w; | |
+ | |
+ if(eforkslave(Ekeyboard) < MAXSLAVE) | |
+ return; | |
+ kn = 0; | |
+ t[0] = Skeyboard; | |
+ for(;;){ | |
+ while(!fullrune(k, kn)){ | |
+ kr = read(fd, k+kn, sizeof k - kn); | |
+ if(kr <= 0) | |
+ goto breakout; | |
+ kn += kr; | |
+ } | |
+ w = chartorune(&r, k); | |
+ kn -= w; | |
+ memmove(k, &k[w], kn); | |
+ t[1] = r; | |
+ t[2] = r>>8; | |
+ if(write(epipe[1], t, 3) != 3) | |
+ break; | |
+ } | |
+breakout:; | |
+ t[0] = MAXSLAVE; | |
+ write(epipe[1], t, 1); | |
+ _exits(0); | |
+} | |
+ | |
+void | |
+einit(ulong keys) | |
+{ | |
+ int ctl, fd; | |
+ char buf[256]; | |
+ | |
+ parentpid = getpid(); | |
+ if(pipe(epipe) < 0) | |
+ drawerror(display, "events: einit pipe"); | |
+ atexit(ekill); | |
+ atnotify(enote, 1); | |
+ snprint(buf, sizeof buf, "%s/mouse", display->devdir); | |
+ mousefd = open(buf, ORDWR|OCEXEC); | |
+ if(mousefd < 0) | |
+ drawerror(display, "einit: can't open mouse\n"); | |
+ snprint(buf, sizeof buf, "%s/cursor", display->devdir); | |
+ cursorfd = open(buf, ORDWR|OCEXEC); | |
+ if(cursorfd < 0) | |
+ drawerror(display, "einit: can't open cursor\n"); | |
+ if(keys&Ekeyboard){ | |
+ snprint(buf, sizeof buf, "%s/cons", display->devdir); | |
+ fd = open(buf, OREAD); | |
+ if(fd < 0) | |
+ drawerror(display, "events: can't open console"); | |
+ snprint(buf, sizeof buf, "%s/consctl", display->devdir); | |
+ ctl = open("/dev/consctl", OWRITE|OCEXEC); | |
+ if(ctl < 0) | |
+ drawerror(display, "events: can't open consctl"); | |
+ write(ctl, "rawon", 5); | |
+ for(Skeyboard=0; Ekeyboard & ~(1<<Skeyboard); Skeyboard++) | |
+ ; | |
+ ekeyslave(fd); | |
+ } | |
+ if(keys&Emouse){ | |
+ estart(Emouse, mousefd, 1+4*12); | |
+ for(Smouse=0; Emouse & ~(1<<Smouse); Smouse++) | |
+ ; | |
+ } | |
+} | |
+ | |
+static void | |
+extract(void) | |
+{ | |
+ Slave *s; | |
+ Ebuf *eb; | |
+ int i, n; | |
+ uchar ebuf[EMAXMSG+1]; | |
+ | |
+ /* avoid generating a message if there's nothing to show. */ | |
+ /* this test isn't perfect, though; could do flushimage(display, 0) th… | |
+ /* also: make sure we don't interfere if we're multiprocessing the dis… | |
+ if(display->locking){ | |
+ /* if locking is being done by program, this means it can't de… | |
+ if(canqlock(&display->qlock)){ | |
+ if(display->bufp > display->buf) | |
+ flushimage(display, 1); | |
+ unlockdisplay(display); | |
+ } | |
+ }else | |
+ if(display->bufp > display->buf) | |
+ flushimage(display, 1); | |
+loop: | |
+ if((n=read(epipe[0], ebuf, EMAXMSG+1)) < 0 | |
+ || ebuf[0] >= MAXSLAVE) | |
+ drawerror(display, "eof on event pipe"); | |
+ if(n == 0) | |
+ goto loop; | |
+ i = ebuf[0]; | |
+ if(i >= nslave || n <= 1) | |
+ drawerror(display, "events: protocol error: short read"); | |
+ s = &eslave[i]; | |
+ if(i == Stimer){ | |
+ s->head = (Ebuf *)1; | |
+ return; | |
+ } | |
+ if(i == Skeyboard && n != 3) | |
+ drawerror(display, "events: protocol error: keyboard"); | |
+ if(i == Smouse){ | |
+ if(n < 1+1+2*12) | |
+ drawerror(display, "events: protocol error: mouse"); | |
+ if(ebuf[1] == 'r') | |
+ eresized(1); | |
+ /* squash extraneous mouse events */ | |
+ if((eb=s->tail) && memcmp(eb->buf+1+2*12, ebuf+1+1+2*12, 12)==… | |
+ memmove(eb->buf, &ebuf[1], n - 1); | |
+ return; | |
+ } | |
+ } | |
+ /* try to save space by only allocating as much buffer as we need */ | |
+ eb = malloc(sizeof(*eb) - sizeof(eb->buf) + n - 1); | |
+ if(eb == 0) | |
+ drawerror(display, "events: protocol error 4"); | |
+ eb->n = n - 1; | |
+ memmove(eb->buf, &ebuf[1], n - 1); | |
+ eb->next = 0; | |
+ if(s->head) | |
+ s->tail = s->tail->next = eb; | |
+ else | |
+ s->head = s->tail = eb; | |
+} | |
+ | |
+static int | |
+eforkslave(ulong key) | |
+{ | |
+ int i, pid; | |
+ | |
+ for(i=0; i<MAXSLAVE; i++) | |
+ if((key & ~(1<<i)) == 0 && eslave[i].pid == 0){ | |
+ if(nslave <= i) | |
+ nslave = i + 1; | |
+ /* | |
+ * share the file descriptors so the last child | |
+ * out closes all connections to the window server. | |
+ */ | |
+ switch(pid = rfork(RFPROC)){ | |
+ case 0: | |
+ return MAXSLAVE+i; | |
+ case -1: | |
+ fprint(2, "events: fork error\n"); | |
+ exits("fork"); | |
+ } | |
+ eslave[i].pid = pid; | |
+ eslave[i].head = eslave[i].tail = 0; | |
+ return i; | |
+ } | |
+ drawerror(display, "events: bad slave assignment"); | |
+ return 0; | |
+} | |
+ | |
+static int | |
+enote(void *v, char *s) | |
+{ | |
+ char t[1]; | |
+ int i, pid; | |
+ | |
+ USED(v, s); | |
+ pid = getpid(); | |
+ if(pid != parentpid){ | |
+ for(i=0; i<nslave; i++){ | |
+ if(pid == eslave[i].pid){ | |
+ t[0] = MAXSLAVE; | |
+ write(epipe[1], t, 1); | |
+ break; | |
+ } | |
+ } | |
+ return 0; | |
+ } | |
+ close(epipe[0]); | |
+ epipe[0] = -1; | |
+ close(epipe[1]); | |
+ epipe[1] = -1; | |
+ for(i=0; i<nslave; i++){ | |
+ if(pid == eslave[i].pid) | |
+ continue; /* don't kill myself */ | |
+ postnote(PNPROC, eslave[i].pid, "die"); | |
+ } | |
+ return 0; | |
+} | |
+ | |
+static void | |
+ekill(void) | |
+{ | |
+ enote(0, 0); | |
+} | |
+ | |
+Mouse | |
+emouse(void) | |
+{ | |
+ Mouse m; | |
+ Ebuf *eb; | |
+ static but[2]; | |
+ int b; | |
+ | |
+ if(Smouse < 0) | |
+ drawerror(display, "events: mouse not initialized"); | |
+ eb = ebread(&eslave[Smouse]); | |
+ m.xy.x = atoi((char*)eb->buf+1+0*12); | |
+ m.xy.y = atoi((char*)eb->buf+1+1*12); | |
+ b = atoi((char*)eb->buf+1+2*12); | |
+ m.buttons = b&7; | |
+ m.msec = atoi((char*)eb->buf+1+3*12); | |
+ if (logfid) | |
+ fprint(logfid, "b: %d xy: %P\n", m.buttons, m.xy); | |
+ free(eb); | |
+ return m; | |
+} | |
+ | |
+int | |
+ekbd(void) | |
+{ | |
+ Ebuf *eb; | |
+ int c; | |
+ | |
+ if(Skeyboard < 0) | |
+ drawerror(display, "events: keyboard not initialzed"); | |
+ eb = ebread(&eslave[Skeyboard]); | |
+ c = eb->buf[0] + (eb->buf[1]<<8); | |
+ free(eb); | |
+ return c; | |
+} | |
+ | |
+void | |
+emoveto(Point pt) | |
+{ | |
+ char buf[2*12+2]; | |
+ int n; | |
+ | |
+ n = sprint(buf, "m%d %d", pt.x, pt.y); | |
+ write(mousefd, buf, n); | |
+} | |
+ | |
+void | |
+esetcursor(Cursor *c) | |
+{ | |
+ uchar curs[2*4+2*2*16]; | |
+ | |
+ if(c == 0) | |
+ write(cursorfd, curs, 0); | |
+ else{ | |
+ BPLONG(curs+0*4, c->offset.x); | |
+ BPLONG(curs+1*4, c->offset.y); | |
+ memmove(curs+2*4, c->clr, 2*2*16); | |
+ write(cursorfd, curs, sizeof curs); | |
+ } | |
+} | |
+ | |
+int | |
+ereadmouse(Mouse *m) | |
+{ | |
+ int n; | |
+ char buf[128]; | |
+ | |
+ do{ | |
+ n = read(mousefd, buf, sizeof(buf)); | |
+ if(n < 0) /* probably interrupted */ | |
+ return -1; | |
+ n = eatomouse(m, buf, n); | |
+ }while(n == 0); | |
+ return n; | |
+} | |
+ | |
+int | |
+eatomouse(Mouse *m, char *buf, int n) | |
+{ | |
+ if(n != 1+4*12){ | |
+ werrstr("atomouse: bad count"); | |
+ return -1; | |
+ } | |
+ | |
+ if(buf[0] == 'r') | |
+ eresized(1); | |
+ m->xy.x = atoi(buf+1+0*12); | |
+ m->xy.y = atoi(buf+1+1*12); | |
+ m->buttons = atoi(buf+1+2*12); | |
+ m->msec = atoi(buf+1+3*12); | |
+ return n; | |
+} | |
diff --git a/src/libdraw/event.h b/src/libdraw/event.h | |
t@@ -0,0 +1,63 @@ | |
+typedef struct Event Event; | |
+typedef struct Menu Menu; | |
+ | |
+enum | |
+{ | |
+ Emouse = 1, | |
+ Ekeyboard = 2, | |
+}; | |
+ | |
+enum | |
+{ | |
+ MAXSLAVE = 32, | |
+ EMAXMSG = 128+8192, /* size of 9p header+data */ | |
+}; | |
+ | |
+struct Mouse | |
+{ | |
+ int buttons; /* bit array: LMR=124 */ | |
+ Point xy; | |
+ ulong msec; | |
+}; | |
+ | |
+struct Event | |
+{ | |
+ int kbdc; | |
+ Mouse mouse; | |
+ int n; /* number of characters in message */ | |
+ void *v; /* data unpacked by general event-handl… | |
+ uchar data[EMAXMSG]; /* message from an arbitrary file d… | |
+}; | |
+ | |
+struct Menu | |
+{ | |
+ char **item; | |
+ char *(*gen)(int); | |
+ int lasthit; | |
+}; | |
+ | |
+/* | |
+ * Events | |
+ */ | |
+extern void einit(ulong); | |
+extern ulong estart(ulong, int, int); | |
+extern ulong estartfn(ulong, int, int, int (*fn)(int, Event*, uchar*, … | |
+extern ulong etimer(ulong, int); | |
+extern ulong event(Event*); | |
+extern ulong eread(ulong, Event*); | |
+extern Mouse emouse(void); | |
+extern int ekbd(void); | |
+extern int ecanread(ulong); | |
+extern int ecanmouse(void); | |
+extern int ecankbd(void); | |
+extern void eresized(int); /* supplied by user */ | |
+extern int emenuhit(int, Mouse*, Menu*); | |
+extern int eatomouse(Mouse*, char*, int); | |
+extern Rectangle getrect(int, Mouse*); | |
+struct Cursor; | |
+extern void esetcursor(struct Cursor*); | |
+extern void emoveto(Point); | |
+extern Rectangle egetrect(int, Mouse*); | |
+extern void edrawgetrect(Rectangle, int); | |
+extern int ereadmouse(Mouse*); | |
+extern int eatomouse(Mouse*, char*, int); | |
diff --git a/src/libdraw/font.c b/src/libdraw/font.c | |
t@@ -0,0 +1,401 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+static int fontresize(Font*, int, int, int); | |
+#if 0 | |
+static int freeup(Font*); | |
+#endif | |
+ | |
+#define PJW 0 /* use NUL==pjw for invisible characters */ | |
+ | |
+static Rune empty[] = { 0 }; | |
+int | |
+cachechars(Font *f, char **ss, Rune **rr, ushort *cp, int max, int *wp, char *… | |
+{ | |
+ int i, th, sh, h, ld, w, rw, wid, nc; | |
+ char *sp; | |
+ Rune r, *rp, vr; | |
+ ulong a; | |
+ Cacheinfo *c, *tc, *ec; | |
+ | |
+ if(ss){ | |
+ sp = *ss; | |
+ rp = empty; | |
+ }else{ | |
+ sp = ""; | |
+ rp = *rr; | |
+ } | |
+ wid = 0; | |
+ *subfontname = 0; | |
+ for(i=0; (*sp || *rp) && i<max; sp+=w, rp+=rw){ | |
+ if(ss){ | |
+ r = *(uchar*)sp; | |
+ if(r < Runeself) | |
+ w = 1; | |
+ else{ | |
+ w = chartorune(&vr, sp); | |
+ r = vr; | |
+ } | |
+ rw = 0; | |
+ }else{ | |
+ r = *rp; | |
+ w = 0; | |
+ rw = 1; | |
+ } | |
+ | |
+ sh = (17 * (uint)r) & (f->ncache-NFLOOK-1); | |
+ c = &f->cache[sh]; | |
+ ec = c+NFLOOK; | |
+ h = sh; | |
+ while(c < ec){ | |
+ if(c->value==r && c->age) | |
+ goto Found; | |
+ c++; | |
+ h++; | |
+ } | |
+ | |
+ /* | |
+ * Not found; toss out oldest entry | |
+ */ | |
+ a = ~0; | |
+ th = sh; | |
+ tc = &f->cache[th]; | |
+ while(tc < ec){ | |
+ if(tc->age < a){ | |
+ a = tc->age; | |
+ h = th; | |
+ c = tc; | |
+ } | |
+ tc++; | |
+ th++; | |
+ } | |
+ | |
+ if(a && (f->age-a)<500){ /* kicking out too recent; res… | |
+ nc = 2*(f->ncache-NFLOOK) + NFLOOK; | |
+ if(nc <= MAXFCACHE){ | |
+ if(i == 0) | |
+ fontresize(f, f->width, nc, f->maxdept… | |
+ /* else flush first; retry will resize */ | |
+ break; | |
+ } | |
+ } | |
+ | |
+ if(c->age == f->age) /* flush pending string output */ | |
+ break; | |
+ | |
+ ld = loadchar(f, r, c, h, i, subfontname); | |
+ if(ld <= 0){ | |
+ if(ld == 0) | |
+ continue; | |
+ break; | |
+ } | |
+ c = &f->cache[h]; /* may have reallocated f->cache */ | |
+ | |
+ Found: | |
+ wid += c->width; | |
+ c->age = f->age; | |
+ cp[i] = h; | |
+ i++; | |
+ } | |
+ if(ss) | |
+ *ss = sp; | |
+ else | |
+ *rr = rp; | |
+ *wp = wid; | |
+ return i; | |
+} | |
+ | |
+void | |
+agefont(Font *f) | |
+{ | |
+ Cacheinfo *c, *ec; | |
+ Cachesubf *s, *es; | |
+ | |
+ f->age++; | |
+ if(f->age == 65536){ | |
+ /* | |
+ * Renormalize ages | |
+ */ | |
+ c = f->cache; | |
+ ec = c+f->ncache; | |
+ while(c < ec){ | |
+ if(c->age){ | |
+ c->age >>= 2; | |
+ c->age++; | |
+ } | |
+ c++; | |
+ } | |
+ s = f->subf; | |
+ es = s+f->nsubf; | |
+ while(s < es){ | |
+ if(s->age){ | |
+ if(s->age<SUBFAGE && s->cf->name != nil){ | |
+ /* clean up */ | |
+ if(s->f != display->defaultsubfont) | |
+ freesubfont(s->f); | |
+ s->cf = nil; | |
+ s->f = nil; | |
+ s->age = 0; | |
+ }else{ | |
+ s->age >>= 2; | |
+ s->age++; | |
+ } | |
+ } | |
+ s++; | |
+ } | |
+ f->age = (65536>>2) + 1; | |
+ } | |
+} | |
+ | |
+static Subfont* | |
+cf2subfont(Cachefont *cf, Font *f) | |
+{ | |
+ int depth; | |
+ char *name; | |
+ Subfont *sf; | |
+ | |
+ name = cf->subfontname; | |
+ if(name == nil){ | |
+ depth = 0; | |
+ if(f->display){ | |
+ if(f->display->screenimage) | |
+ depth = f->display->screenimage->depth; | |
+ } | |
+ name = subfontname(cf->name, f->name, depth); | |
+ if(name == nil) | |
+ return nil; | |
+ cf->subfontname = name; | |
+ } | |
+ sf = lookupsubfont(f->display, name); | |
+ return sf; | |
+} | |
+ | |
+/* return 1 if load succeeded, 0 if failed, -1 if must retry */ | |
+int | |
+loadchar(Font *f, Rune r, Cacheinfo *c, int h, int noflush, char **subfontname) | |
+{ | |
+ int i, oi, wid, top, bottom; | |
+ Rune pic; | |
+ Fontchar *fi; | |
+ Cachefont *cf; | |
+ Cachesubf *subf, *of; | |
+ uchar *b; | |
+ | |
+ pic = r; | |
+ Again: | |
+ for(i=0; i<f->nsub; i++){ | |
+ cf = f->sub[i]; | |
+ if(cf->min<=pic && pic<=cf->max) | |
+ goto Found; | |
+ } | |
+ TryPJW: | |
+ if(pic != PJW){ | |
+ pic = PJW; | |
+ goto Again; | |
+ } | |
+ return 0; | |
+ | |
+ Found: | |
+ /* | |
+ * Choose exact or oldest | |
+ */ | |
+ oi = 0; | |
+ subf = &f->subf[0]; | |
+ for(i=0; i<f->nsubf; i++){ | |
+ if(cf == subf->cf) | |
+ goto Found2; | |
+ if(subf->age < f->subf[oi].age) | |
+ oi = i; | |
+ subf++; | |
+ } | |
+ subf = &f->subf[oi]; | |
+ | |
+ if(subf->f){ | |
+ if(f->age-subf->age>SUBFAGE || f->nsubf>MAXSUBF){ | |
+ Toss: | |
+ /* ancient data; toss */ | |
+ freesubfont(subf->f); | |
+ subf->cf = nil; | |
+ subf->f = nil; | |
+ subf->age = 0; | |
+ }else{ /* too recent; grow inst… | |
+ of = f->subf; | |
+ f->subf = malloc((f->nsubf+DSUBF)*sizeof *subf); | |
+ if(f->subf == nil){ | |
+ f->subf = of; | |
+ goto Toss; | |
+ } | |
+ memmove(f->subf, of, (f->nsubf+DSUBF)*sizeof *subf); | |
+ memset(f->subf+f->nsubf, 0, DSUBF*sizeof *subf); | |
+ subf = &f->subf[f->nsubf]; | |
+ f->nsubf += DSUBF; | |
+ free(of); | |
+ } | |
+ } | |
+ subf->age = 0; | |
+ subf->cf = nil; | |
+ subf->f = cf2subfont(cf, f); | |
+ if(subf->f == nil){ | |
+ if(cf->subfontname == nil) | |
+ goto TryPJW; | |
+ *subfontname = cf->subfontname; | |
+ return -1; | |
+ } | |
+ | |
+ subf->cf = cf; | |
+ if(subf->f->ascent > f->ascent){ | |
+ /* should print something? this is a mistake in the font file … | |
+ /* must prevent c->top from going negative when loading cache … | |
+ Image *b; | |
+ int d, t; | |
+ d = subf->f->ascent - f->ascent; | |
+ b = subf->f->bits; | |
+ draw(b, b->r, b, nil, addpt(b->r.min, Pt(0, d))); | |
+ draw(b, Rect(b->r.min.x, b->r.max.y-d, b->r.max.x, b->r.max.y)… | |
+ for(i=0; i<subf->f->n; i++){ | |
+ t = subf->f->info[i].top-d; | |
+ if(t < 0) | |
+ t = 0; | |
+ subf->f->info[i].top = t; | |
+ t = subf->f->info[i].bottom-d; | |
+ if(t < 0) | |
+ t = 0; | |
+ subf->f->info[i].bottom = t; | |
+ } | |
+ subf->f->ascent = f->ascent; | |
+ } | |
+ | |
+ Found2: | |
+ subf->age = f->age; | |
+ | |
+ pic += cf->offset; | |
+ if(pic-cf->min >= subf->f->n) | |
+ goto TryPJW; | |
+ fi = &subf->f->info[pic - cf->min]; | |
+ if(fi->width == 0) | |
+ goto TryPJW; | |
+ wid = (fi+1)->x - fi->x; | |
+ if(f->width < wid || f->width == 0 || f->maxdepth < subf->f->bits->dep… | |
+ /* | |
+ * Flush, free, reload (easier than reformatting f->b) | |
+ */ | |
+ if(noflush) | |
+ return -1; | |
+ if(f->width < wid) | |
+ f->width = wid; | |
+ if(f->maxdepth < subf->f->bits->depth) | |
+ f->maxdepth = subf->f->bits->depth; | |
+ i = fontresize(f, f->width, f->ncache, f->maxdepth); | |
+ if(i <= 0) | |
+ return i; | |
+ /* c is still valid as didn't reallocate f->cache */ | |
+ } | |
+ c->value = r; | |
+ top = fi->top + (f->ascent-subf->f->ascent); | |
+ bottom = fi->bottom + (f->ascent-subf->f->ascent); | |
+ c->width = fi->width; | |
+ c->x = h*f->width; | |
+ c->left = fi->left; | |
+ flushimage(f->display, 0); /* flush any pending errors */ | |
+ b = bufimage(f->display, 37); | |
+ if(b == 0) | |
+ return 0; | |
+ b[0] = 'l'; | |
+ BPLONG(b+1, f->cacheimage->id); | |
+ BPLONG(b+5, subf->f->bits->id); | |
+ BPSHORT(b+9, c-f->cache); | |
+ BPLONG(b+11, c->x); | |
+ BPLONG(b+15, top); | |
+ BPLONG(b+19, c->x+((fi+1)->x-fi->x)); | |
+ BPLONG(b+23, bottom); | |
+ BPLONG(b+27, fi->x); | |
+ BPLONG(b+31, fi->top); | |
+ b[35] = fi->left; | |
+ b[36] = fi->width; | |
+ return 1; | |
+} | |
+ | |
+/* release all subfonts, return number freed */ | |
+#if 0 | |
+static | |
+int | |
+freeup(Font *f) | |
+{ | |
+ Cachesubf *s, *es; | |
+ int nf; | |
+ | |
+ if(f->sub[0]->name == nil) /* font from mkfont; don't free */ | |
+ return 0; | |
+ s = f->subf; | |
+ es = s+f->nsubf; | |
+ nf = 0; | |
+ while(s < es){ | |
+ if(s->age){ | |
+ freesubfont(s->f); | |
+ s->cf = nil; | |
+ s->f = nil; | |
+ s->age = 0; | |
+ nf++; | |
+ } | |
+ s++; | |
+ } | |
+ return nf; | |
+} | |
+#endif | |
+ | |
+/* return whether resize succeeded && f->cache is unchanged */ | |
+static int | |
+fontresize(Font *f, int wid, int ncache, int depth) | |
+{ | |
+ Cacheinfo *i; | |
+ int ret; | |
+ Image *new; | |
+ uchar *b; | |
+ Display *d; | |
+ | |
+ ret = 0; | |
+ d = f->display; | |
+ if(depth <= 0) | |
+ depth = 1; | |
+ | |
+ new = allocimage(d, Rect(0, 0, ncache*wid, f->height), CHAN1(CGrey, de… | |
+ if(new == nil){ | |
+ fprint(2, "font cache resize failed: %r\n"); | |
+ abort(); | |
+ goto Return; | |
+ } | |
+ flushimage(d, 0); /* flush any pending errors */ | |
+ b = bufimage(d, 1+4+4+1); | |
+ if(b == 0){ | |
+ freeimage(new); | |
+ goto Return; | |
+ } | |
+ b[0] = 'i'; | |
+ BPLONG(b+1, new->id); | |
+ BPLONG(b+5, ncache); | |
+ b[9] = f->ascent; | |
+ if(flushimage(d, 0) < 0){ | |
+ fprint(2, "resize: init failed: %r\n"); | |
+ freeimage(new); | |
+ goto Return; | |
+ } | |
+ freeimage(f->cacheimage); | |
+ f->cacheimage = new; | |
+ f->width = wid; | |
+ f->maxdepth = depth; | |
+ ret = 1; | |
+ if(f->ncache != ncache){ | |
+ i = malloc(ncache*sizeof f->cache[0]); | |
+ if(i != nil){ | |
+ ret = 0; | |
+ free(f->cache); | |
+ f->ncache = ncache; | |
+ f->cache = i; | |
+ } | |
+ /* else just wipe the cache clean and things will be ok */ | |
+ } | |
+ Return: | |
+ memset(f->cache, 0, f->ncache*sizeof f->cache[0]); | |
+ return ret; | |
+} | |
diff --git a/src/libdraw/getsubfont.c b/src/libdraw/getsubfont.c | |
t@@ -0,0 +1,36 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+/* | |
+ * Default version: treat as file name | |
+ */ | |
+ | |
+Subfont* | |
+_getsubfont(Display *d, char *name) | |
+{ | |
+ int fd; | |
+ Subfont *f; | |
+ | |
+ fd = open(name, OREAD); | |
+ | |
+ if(fd < 0){ | |
+ fprint(2, "getsubfont: can't open %s: %r\n", name); | |
+ return 0; | |
+ } | |
+ /* | |
+ * unlock display so i/o happens with display released, unless | |
+ * user is doing his own locking, in which case this could break thing… | |
+ * _getsubfont is called only from string.c and stringwidth.c, | |
+ * which are known to be safe to have this done. | |
+ */ | |
+ if(d->locking == 0) | |
+ unlockdisplay(d); | |
+ f = readsubfont(d, name, fd, d->locking==0); | |
+ if(d->locking == 0) | |
+ lockdisplay(d); | |
+ if(f == 0) | |
+ fprint(2, "getsubfont: can't read %s: %r\n", name); | |
+ close(fd); | |
+ return f; | |
+} | |
diff --git a/src/libdraw/init.c b/src/libdraw/init.c | |
t@@ -0,0 +1,203 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+Display *display; | |
+Font *font; | |
+Image *screen; | |
+int _drawdebug; | |
+ | |
+static char deffontname[] = "*default*"; | |
+Screen *_screen; | |
+ | |
+int debuglockdisplay = 0; | |
+ | |
+static void | |
+drawshutdown(void) | |
+{ | |
+ Display *d; | |
+ | |
+ d = display; | |
+ if(d){ | |
+ display = nil; | |
+ closedisplay(d); | |
+ } | |
+} | |
+ | |
+int | |
+initdraw(void (*error)(Display*, char*), char *fontname, char *label) | |
+{ | |
+ Subfont *df; | |
+ char buf[128]; | |
+ | |
+ display = _initdisplay(error, label); /* sets screen too */ | |
+ if(display == nil) | |
+ return -1; | |
+ | |
+ display->image = display->screenimage; | |
+ screen = display->screenimage; | |
+ | |
+ /* | |
+ * Set up default font | |
+ */ | |
+ df = getdefont(display); | |
+ display->defaultsubfont = df; | |
+ if(df == nil){ | |
+ fprint(2, "imageinit: can't open default subfont: %r\n"); | |
+ Error: | |
+ closedisplay(display); | |
+ display = nil; | |
+ return -1; | |
+ } | |
+ if(fontname == nil) | |
+ fontname = getenv("font"); /* leak */ | |
+ | |
+ /* | |
+ * Build fonts with caches==depth of screen, for speed. | |
+ * If conversion were faster, we'd use 0 and save memory. | |
+ */ | |
+ if(fontname == nil){ | |
+ snprint(buf, sizeof buf, "%d %d\n0 %d\t%s\n", df->height, df->… | |
+ df->n-1, deffontname); | |
+//BUG: Need something better for this installsubfont("*default*", df); | |
+ font = buildfont(display, buf, deffontname); | |
+ if(font == nil){ | |
+ fprint(2, "initdraw: can't open default font: %r\n"); | |
+ goto Error; | |
+ } | |
+ }else{ | |
+ font = openfont(display, fontname); /* BUG: grey fonts … | |
+ if(font == nil){ | |
+ fprint(2, "initdraw: can't open font %s: %r\n", fontna… | |
+ goto Error; | |
+ } | |
+ } | |
+ display->defaultfont = font; | |
+ | |
+ display->white = allocimage(display, Rect(0,0,1,1), GREY1, 1, DWhite); | |
+ display->black = allocimage(display, Rect(0,0,1,1), GREY1, 1, DBlack); | |
+ if(display->white == nil || display->black == nil){ | |
+ fprint(2, "initdraw: can't allocate white and black"); | |
+ goto Error; | |
+ } | |
+ display->opaque = display->white; | |
+ display->transparent = display->black; | |
+ atexit(drawshutdown); | |
+ return 1; | |
+} | |
+ | |
+/* | |
+ * Call with d unlocked. | |
+ * Note that disp->defaultfont and defaultsubfont are not freed here. | |
+ */ | |
+void | |
+closedisplay(Display *disp) | |
+{ | |
+ int fd; | |
+ char buf[128]; | |
+ | |
+ if(disp == nil) | |
+ return; | |
+ if(disp == display) | |
+ display = nil; | |
+ if(disp->oldlabel[0]){ | |
+ snprint(buf, sizeof buf, "%s/label", disp->windir); | |
+ fd = open(buf, OWRITE); | |
+ if(fd >= 0){ | |
+ write(fd, disp->oldlabel, strlen(disp->oldlabel)); | |
+ close(fd); | |
+ } | |
+ } | |
+ | |
+ free(disp->devdir); | |
+ free(disp->windir); | |
+ freeimage(disp->white); | |
+ freeimage(disp->black); | |
+ qunlock(&disp->qlock); | |
+ free(disp); | |
+} | |
+ | |
+void | |
+lockdisplay(Display *disp) | |
+{ | |
+ if(debuglockdisplay){ | |
+ /* avoid busy looping; it's rare we collide anyway */ | |
+ while(!canqlock(&disp->qlock)){ | |
+ fprint(1, "proc %d waiting for display lock...\n", get… | |
+ sleep(1000); | |
+ } | |
+ }else | |
+ qlock(&disp->qlock); | |
+} | |
+ | |
+void | |
+unlockdisplay(Display *disp) | |
+{ | |
+ qunlock(&disp->qlock); | |
+} | |
+ | |
+void | |
+drawerror(Display *d, char *s) | |
+{ | |
+ char err[ERRMAX]; | |
+ | |
+ if(d->error) | |
+ d->error(d, s); | |
+ else{ | |
+ errstr(err, sizeof err); | |
+ fprint(2, "draw: %s: %s\n", s, err); | |
+ exits(s); | |
+ } | |
+} | |
+ | |
+static | |
+int | |
+doflush(Display *d) | |
+{ | |
+ int n; | |
+ | |
+ n = d->bufp-d->buf; | |
+ if(n <= 0) | |
+ return 1; | |
+ | |
+ if(_drawmsgwrite(d, d->buf, n) != n){ | |
+ if(_drawdebug) | |
+ fprint(2, "flushimage fail: d=%p: %r\n", d); /**/ | |
+ d->bufp = d->buf; /* might as well; chance of continuin… | |
+ return -1; | |
+ } | |
+ d->bufp = d->buf; | |
+ return 1; | |
+} | |
+ | |
+int | |
+flushimage(Display *d, int visible) | |
+{ | |
+ if(visible){ | |
+ *d->bufp++ = 'v'; /* five bytes always reserved for thi… | |
+ if(d->_isnewdisplay){ | |
+ BPLONG(d->bufp, d->screenimage->id); | |
+ d->bufp += 4; | |
+ } | |
+ } | |
+ return doflush(d); | |
+} | |
+ | |
+uchar* | |
+bufimage(Display *d, int n) | |
+{ | |
+ uchar *p; | |
+ | |
+ if(n<0 || n>d->bufsize){ | |
+abort(); | |
+ werrstr("bad count in bufimage"); | |
+ return 0; | |
+ } | |
+ if(d->bufp+n > d->buf+d->bufsize) | |
+ if(doflush(d) < 0) | |
+ return 0; | |
+ p = d->bufp; | |
+ d->bufp += n; | |
+ return p; | |
+} | |
+ | |
diff --git a/src/libdraw/keyboard.c b/src/libdraw/keyboard.c | |
t@@ -0,0 +1,102 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <thread.h> | |
+#include <keyboard.h> | |
+ | |
+ | |
+void | |
+closekeyboard(Keyboardctl *kc) | |
+{ | |
+ if(kc == nil) | |
+ return; | |
+ | |
+ postnote(PNPROC, kc->pid, "kill"); | |
+ | |
+#ifdef BUG | |
+ /* Drain the channel */ | |
+ while(?kc->c) | |
+ <-kc->c; | |
+#endif | |
+ | |
+ close(kc->ctlfd); | |
+ close(kc->consfd); | |
+ free(kc->file); | |
+ free(kc->c); | |
+ free(kc); | |
+} | |
+ | |
+static | |
+void | |
+_ioproc(void *arg) | |
+{ | |
+ int m, n; | |
+ char buf[20]; | |
+ Rune r; | |
+ Keyboardctl *kc; | |
+ | |
+ kc = arg; | |
+ threadsetname("kbdproc"); | |
+ kc->pid = getpid(); | |
+ n = 0; | |
+ for(;;){ | |
+ while(n>0 && fullrune(buf, n)){ | |
+ m = chartorune(&r, buf); | |
+ n -= m; | |
+ memmove(buf, buf+m, n); | |
+ send(kc->c, &r); | |
+ } | |
+ m = read(kc->consfd, buf+n, sizeof buf-n); | |
+ if(m <= 0){ | |
+ yield(); /* if error is due to exiting, we'll e… | |
+ fprint(2, "keyboard read error: %r\n"); | |
+ threadexits("error"); | |
+ } | |
+ n += m; | |
+ } | |
+} | |
+ | |
+Keyboardctl* | |
+initkeyboard(char *file) | |
+{ | |
+ Keyboardctl *kc; | |
+ char *t; | |
+ | |
+ kc = mallocz(sizeof(Keyboardctl), 1); | |
+ if(kc == nil) | |
+ return nil; | |
+ if(file == nil) | |
+ file = "/dev/cons"; | |
+ kc->file = strdup(file); | |
+ kc->consfd = open(file, ORDWR|OCEXEC); | |
+ t = malloc(strlen(file)+16); | |
+ if(kc->consfd<0 || t==nil){ | |
+Error1: | |
+ free(kc); | |
+ return nil; | |
+ } | |
+ sprint(t, "%sctl", file); | |
+ kc->ctlfd = open(t, OWRITE|OCEXEC); | |
+ if(kc->ctlfd < 0){ | |
+ fprint(2, "initkeyboard: can't open %s: %r\n", t); | |
+Error2: | |
+ close(kc->consfd); | |
+ free(t); | |
+ goto Error1; | |
+ } | |
+ if(ctlkeyboard(kc, "rawon") < 0){ | |
+ fprint(2, "initkeyboard: can't turn on raw mode on %s: %r\n", … | |
+ close(kc->ctlfd); | |
+ goto Error2; | |
+ } | |
+ free(t); | |
+ kc->c = chancreate(sizeof(Rune), 20); | |
+ proccreate(_ioproc, kc, 4096); | |
+ return kc; | |
+} | |
+ | |
+int | |
+ctlkeyboard(Keyboardctl *kc, char *m) | |
+{ | |
+ return write(kc->ctlfd, m, strlen(m)); | |
+} | |
diff --git a/src/libdraw/keyboard.h b/src/libdraw/keyboard.h | |
t@@ -0,0 +1,36 @@ | |
+typedef struct Keyboardctl Keyboardctl; | |
+ | |
+struct Keyboardctl | |
+{ | |
+ struct 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 … | |
+ /* 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= 0x80, | |
+ Kview= 0x80, | |
+ Kpgdown= KF|0x13, | |
+ Kins= KF|0x14, | |
+ Kend= '\r', /* [sic] */ | |
+ | |
+ Kalt= KF|0x15, | |
+ Kshift= KF|0x16, | |
+ Kctl= KF|0x17, | |
+}; | |
diff --git a/src/libdraw/libdraw.x b/src/libdraw/libdraw.x | |
t@@ -0,0 +1 @@ | |
+!<arch> | |
diff --git a/src/libdraw/md-alloc.c b/src/libdraw/md-alloc.c | |
t@@ -0,0 +1,200 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+#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, u32int chan, Memdata *md, void *X) | |
+{ | |
+ int d; | |
+ u32int 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(u32int)*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, u32int chan) | |
+{ | |
+ int d; | |
+ u32int 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(u32int)); | |
+ if(md->base == nil){ | |
+ free(md); | |
+ return nil; | |
+ } | |
+ | |
+ md->base[0] = (u32int)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. | |
+ */ | |
+u32int* | |
+wordaddr(Memimage *i, Point p) | |
+{ | |
+ return (u32int*) ((u32int)byteaddr(i, p) & ~(sizeof(u32int)-1)); | |
+} | |
+ | |
+uchar* | |
+byteaddr(Memimage *i, Point p) | |
+{ | |
+ uchar *a; | |
+ | |
+ a = i->data->bdata+i->zero+sizeof(u32int)*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, u32int chan) | |
+{ | |
+ int d; | |
+ int t, j, k; | |
+ u32int 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(cc))-1; | |
+ i->nbits[t] = NBITS(cc); | |
+ if(NBITS(cc) != 8) | |
+ bytes = 0; | |
+ } | |
+ i->nchan = k; | |
+ if(bytes) | |
+ i->flags |= Fbytes; | |
+ return 0; | |
+} | |
diff --git a/src/libdraw/md-arc.c b/src/libdraw/md-arc.c | |
t@@ -0,0 +1,116 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+/* | |
+ * 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, i… | |
+{ | |
+ 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/src/libdraw/md-arctest.c b/src/libdraw/md-arctest.c | |
t@@ -0,0 +1,61 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+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<n; i++) | |
+ memarc(x, c, a, b, thick, memblack, sp, alpha, phi, SoverD); | |
+ t1 = nsec(); | |
+ print("%lld %lld\n", t1-t0-del, del); | |
+} | |
+ | |
+int drawdebug = 0; | |
+ | |
+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; | |
+} | |
+ | |
diff --git a/src/libdraw/md-cload.c b/src/libdraw/md-cload.c | |
t@@ -0,0 +1,68 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+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 smal… | |
+ 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/src/libdraw/md-cmap.c b/src/libdraw/md-cmap.c | |
t@@ -0,0 +1,320 @@ | |
+/* | |
+ * generated by mkcmap.c | |
+ */ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+static Memcmap def = { | |
+/* cmap2rgb */ { | |
+ 0x00,0x00,0x00,0x00,0x00,0x44,0x00,0x00,0x88,0x00,0x00,0xcc,0x00,0x44,… | |
+ 0x44,0x44,0x00,0x44,0x88,0x00,0x44,0xcc,0x00,0x88,0x00,0x00,0x88,0x44,… | |
+ 0x88,0x00,0x88,0xcc,0x00,0xcc,0x00,0x00,0xcc,0x44,0x00,0xcc,0x88,0x00,… | |
+ 0x00,0xdd,0xdd,0x11,0x11,0x11,0x00,0x00,0x55,0x00,0x00,0x99,0x00,0x00,… | |
+ 0x55,0x00,0x00,0x55,0x55,0x00,0x4c,0x99,0x00,0x49,0xdd,0x00,0x99,0x00,… | |
+ 0x4c,0x00,0x99,0x99,0x00,0x93,0xdd,0x00,0xdd,0x00,0x00,0xdd,0x49,0x00,… | |
+ 0x00,0xee,0x9e,0x00,0xee,0xee,0x22,0x22,0x22,0x00,0x00,0x66,0x00,0x00,… | |
+ 0x00,0xee,0x00,0x66,0x00,0x00,0x66,0x66,0x00,0x55,0xaa,0x00,0x4f,0xee,… | |
+ 0x00,0x00,0xaa,0x55,0x00,0xaa,0xaa,0x00,0x9e,0xee,0x00,0xee,0x00,0x00,… | |
+ 0x00,0xff,0x55,0x00,0xff,0xaa,0x00,0xff,0xff,0x33,0x33,0x33,0x00,0x00,… | |
+ 0x00,0xbb,0x00,0x00,0xff,0x00,0x77,0x00,0x00,0x77,0x77,0x00,0x5d,0xbb,… | |
+ 0xff,0x00,0xbb,0x00,0x00,0xbb,0x5d,0x00,0xbb,0xbb,0x00,0xaa,0xff,0x00,… | |
+ 0x44,0x00,0x44,0x44,0x00,0x88,0x44,0x00,0xcc,0x44,0x44,0x00,0x44,0x44,… | |
+ 0x44,0x88,0x44,0x44,0xcc,0x44,0x88,0x00,0x44,0x88,0x44,0x44,0x88,0x88,… | |
+ 0xcc,0x44,0xcc,0x00,0x44,0xcc,0x44,0x44,0xcc,0x88,0x44,0xcc,0xcc,0x44,… | |
+ 0x55,0x00,0x00,0x55,0x00,0x55,0x4c,0x00,0x99,0x49,0x00,0xdd,0x55,0x55,… | |
+ 0x55,0x55,0x4c,0x4c,0x99,0x49,0x49,0xdd,0x4c,0x99,0x00,0x4c,0x99,0x4c,… | |
+ 0x99,0x49,0x93,0xdd,0x49,0xdd,0x00,0x49,0xdd,0x49,0x49,0xdd,0x93,0x49,… | |
+ 0x4f,0xee,0xee,0x66,0x00,0x00,0x66,0x00,0x66,0x55,0x00,0xaa,0x4f,0x00,… | |
+ 0x66,0x00,0x66,0x66,0x66,0x55,0x55,0xaa,0x4f,0x4f,0xee,0x55,0xaa,0x00,… | |
+ 0x55,0x55,0xaa,0xaa,0x4f,0x9e,0xee,0x4f,0xee,0x00,0x4f,0xee,0x4f,0x4f,… | |
+ 0x55,0xff,0xaa,0x55,0xff,0xff,0x77,0x00,0x00,0x77,0x00,0x77,0x5d,0x00,… | |
+ 0x00,0xff,0x77,0x77,0x00,0x77,0x77,0x77,0x5d,0x5d,0xbb,0x55,0x55,0xff,… | |
+ 0x00,0x5d,0xbb,0x5d,0x5d,0xbb,0xbb,0x55,0xaa,0xff,0x55,0xff,0x00,0x55,… | |
+ 0x88,0x00,0x88,0x88,0x00,0xcc,0x88,0x44,0x00,0x88,0x44,0x44,0x88,0x44,… | |
+ 0x44,0xcc,0x88,0x88,0x00,0x88,0x88,0x44,0x88,0x88,0x88,0x88,0x88,0xcc,… | |
+ 0x00,0x88,0xcc,0x44,0x88,0xcc,0x88,0x88,0xcc,0xcc,0x88,0x00,0x00,0x88,… | |
+ 0x99,0x00,0x4c,0x99,0x00,0x99,0x93,0x00,0xdd,0x99,0x4c,0x00,0x99,0x4c,… | |
+ 0x4c,0x99,0x93,0x49,0xdd,0x99,0x99,0x00,0x99,0x99,0x4c,0x99,0x99,0x99,… | |
+ 0xdd,0x93,0xdd,0x00,0x93,0xdd,0x49,0x93,0xdd,0x93,0x93,0xdd,0xdd,0x99,… | |
+ 0xaa,0x00,0x00,0xaa,0x00,0x55,0xaa,0x00,0xaa,0x9e,0x00,0xee,0xaa,0x55,… | |
+ 0x55,0x55,0xaa,0x55,0xaa,0x9e,0x4f,0xee,0xaa,0xaa,0x00,0xaa,0xaa,0x55,… | |
+ 0xaa,0x9e,0x9e,0xee,0x9e,0xee,0x00,0x9e,0xee,0x4f,0x9e,0xee,0x9e,0x9e,… | |
+ 0xaa,0xff,0xff,0xbb,0x00,0x00,0xbb,0x00,0x5d,0xbb,0x00,0xbb,0xaa,0x00,… | |
+ 0x5d,0x00,0xbb,0x5d,0x5d,0xbb,0x5d,0xbb,0xaa,0x55,0xff,0xbb,0xbb,0x00,… | |
+ 0x5d,0xbb,0xbb,0xbb,0xaa,0xaa,0xff,0xaa,0xff,0x00,0xaa,0xff,0x55,0xaa,… | |
+ 0xcc,0x00,0xcc,0xcc,0x44,0x00,0xcc,0x44,0x44,0xcc,0x44,0x88,0xcc,0x44,… | |
+ 0x88,0x00,0xcc,0x88,0x44,0xcc,0x88,0x88,0xcc,0x88,0xcc,0xcc,0xcc,0x00,… | |
+ 0x44,0xcc,0xcc,0x88,0xcc,0xcc,0xcc,0xcc,0x00,0x00,0xcc,0x00,0x44,0xcc,… | |
+ 0xdd,0x00,0x93,0xdd,0x00,0xdd,0xdd,0x49,0x00,0xdd,0x49,0x49,0xdd,0x49,… | |
+ 0x49,0xdd,0xdd,0x93,0x00,0xdd,0x93,0x49,0xdd,0x93,0x93,0xdd,0x93,0xdd,… | |
+ 0x00,0xdd,0xdd,0x49,0xdd,0xdd,0x93,0xdd,0xdd,0xdd,0xdd,0x00,0x00,0xdd,… | |
+ 0xee,0x00,0x4f,0xee,0x00,0x9e,0xee,0x00,0xee,0xee,0x4f,0x00,0xee,0x4f,… | |
+ 0x4f,0x9e,0xee,0x4f,0xee,0xee,0x9e,0x00,0xee,0x9e,0x4f,0xee,0x9e,0x9e,… | |
+ 0xee,0xee,0xee,0x00,0xee,0xee,0x4f,0xee,0xee,0x9e,0xee,0xee,0xee,0xee,… | |
+ 0xff,0x00,0x00,0xff,0x00,0x55,0xff,0x00,0xaa,0xff,0x00,0xff,0xff,0x55,… | |
+ 0x55,0x55,0xff,0x55,0xaa,0xff,0x55,0xff,0xff,0xaa,0x00,0xff,0xaa,0x55,… | |
+ 0xaa,0xff,0xaa,0xff,0xff,0xff,0x00,0xff,0xff,0x55,0xff,0xff,0xaa,0xff,… | |
+}, | |
+/* rgb2cmap */ { | |
+ 0x00,0x00,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,… | |
+ 0x00,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,… | |
+ 0x11,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,… | |
+ 0x04,0x04,0x04,0x05,0x05,0x05,0x05,0x06,0x06,0x06,0x17,0x07,0x07,0x18,… | |
+ 0x04,0x04,0x04,0x05,0x05,0x05,0x16,0x06,0x06,0x17,0x28,0x07,0x07,0x18,… | |
+ 0x15,0x15,0x15,0x05,0x05,0x16,0x16,0x06,0x06,0x17,0x28,0x39,0x07,0x18,… | |
+ 0x26,0x26,0x26,0x05,0x16,0x16,0x27,0x27,0x38,0x28,0x28,0x39,0x39,0x29,… | |
+ 0x37,0x37,0x37,0x09,0x09,0x09,0x27,0x38,0x0a,0x0a,0x39,0x0b,0x0b,0x0b,… | |
+ 0x08,0x08,0x08,0x09,0x09,0x09,0x38,0x0a,0x0a,0x0a,0x1b,0x0b,0x0b,0x1c,… | |
+ 0x19,0x19,0x19,0x09,0x1a,0x1a,0x2b,0x0a,0x0a,0x1b,0x1b,0x0b,0x0b,0x1c,… | |
+ 0x2a,0x2a,0x2a,0x1a,0x2b,0x2b,0x2b,0x3c,0x1b,0x1b,0x2c,0x2c,0x3d,0x2d,… | |
+ 0x3b,0x3b,0x3b,0x0d,0x0d,0x3c,0x3c,0x0e,0x0e,0x0e,0x2c,0x3d,0x0f,0x0f,… | |
+ 0x0c,0x0c,0x0c,0x0d,0x0d,0x0d,0x3c,0x0e,0x0e,0x0e,0x3d,0x0f,0x0f,0x0f,… | |
+ 0x1d,0x1d,0x1d,0x1e,0x1e,0x1e,0x2f,0x0e,0x1f,0x1f,0x20,0x0f,0x0f,0x10,… | |
+ 0x2e,0x2e,0x2e,0x1e,0x2f,0x2f,0x2f,0x1f,0x1f,0x20,0x20,0x31,0x10,0x10,… | |
+ 0x3f,0x3f,0x3f,0x2f,0x30,0x30,0x30,0x30,0x20,0x31,0x31,0x31,0x31,0x21,… | |
+ 0x00,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,… | |
+ 0x11,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,… | |
+ 0x11,0x11,0x22,0x22,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,… | |
+ 0x04,0x04,0x22,0x05,0x05,0x05,0x05,0x06,0x06,0x06,0x17,0x07,0x07,0x18,… | |
+ 0x04,0x04,0x04,0x05,0x05,0x05,0x16,0x06,0x06,0x17,0x28,0x07,0x07,0x18,… | |
+ 0x15,0x15,0x15,0x05,0x05,0x16,0x16,0x06,0x06,0x17,0x28,0x39,0x07,0x18,… | |
+ 0x26,0x26,0x26,0x05,0x16,0x16,0x27,0x27,0x38,0x28,0x28,0x39,0x39,0x29,… | |
+ 0x37,0x37,0x37,0x09,0x09,0x09,0x27,0x38,0x0a,0x0a,0x39,0x0b,0x0b,0x0b,… | |
+ 0x08,0x08,0x08,0x09,0x09,0x09,0x38,0x0a,0x0a,0x0a,0x1b,0x0b,0x0b,0x1c,… | |
+ 0x19,0x19,0x19,0x09,0x1a,0x1a,0x2b,0x0a,0x0a,0x1b,0x1b,0x0b,0x0b,0x1c,… | |
+ 0x2a,0x2a,0x2a,0x1a,0x2b,0x2b,0x2b,0x3c,0x1b,0x1b,0x2c,0x2c,0x3d,0x2d,… | |
+ 0x3b,0x3b,0x3b,0x0d,0x0d,0x3c,0x3c,0x0e,0x0e,0x0e,0x2c,0x3d,0x0f,0x0f,… | |
+ 0x0c,0x0c,0x0c,0x0d,0x0d,0x0d,0x3c,0x0e,0x0e,0x0e,0x3d,0x0f,0x0f,0x0f,… | |
+ 0x1d,0x1d,0x1d,0x1e,0x1e,0x1e,0x2f,0x0e,0x1f,0x1f,0x20,0x0f,0x0f,0x10,… | |
+ 0x2e,0x2e,0x2e,0x1e,0x2f,0x2f,0x2f,0x1f,0x1f,0x20,0x20,0x31,0x10,0x10,… | |
+ 0x3f,0x3f,0x3f,0x2f,0x30,0x30,0x30,0x30,0x20,0x31,0x31,0x31,0x31,0x21,… | |
+ 0x11,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,… | |
+ 0x11,0x11,0x22,0x22,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,… | |
+ 0x11,0x22,0x22,0x22,0x33,0x33,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,… | |
+ 0x04,0x22,0x22,0x33,0x33,0x33,0x05,0x06,0x06,0x06,0x17,0x07,0x07,0x18,… | |
+ 0x04,0x04,0x33,0x33,0x33,0x05,0x16,0x06,0x06,0x17,0x28,0x07,0x07,0x18,… | |
+ 0x15,0x15,0x33,0x33,0x05,0x16,0x16,0x06,0x06,0x17,0x28,0x39,0x07,0x18,… | |
+ 0x26,0x26,0x26,0x05,0x16,0x16,0x27,0x27,0x38,0x28,0x28,0x39,0x39,0x29,… | |
+ 0x37,0x37,0x37,0x09,0x09,0x09,0x27,0x38,0x0a,0x0a,0x39,0x0b,0x0b,0x0b,… | |
+ 0x08,0x08,0x08,0x09,0x09,0x09,0x38,0x0a,0x0a,0x0a,0x1b,0x0b,0x0b,0x1c,… | |
+ 0x19,0x19,0x19,0x09,0x1a,0x1a,0x2b,0x0a,0x0a,0x1b,0x1b,0x0b,0x0b,0x1c,… | |
+ 0x2a,0x2a,0x2a,0x1a,0x2b,0x2b,0x2b,0x3c,0x1b,0x1b,0x2c,0x2c,0x3d,0x2d,… | |
+ 0x3b,0x3b,0x3b,0x0d,0x0d,0x3c,0x3c,0x0e,0x0e,0x0e,0x2c,0x3d,0x0f,0x0f,… | |
+ 0x0c,0x0c,0x0c,0x0d,0x0d,0x0d,0x3c,0x0e,0x0e,0x0e,0x3d,0x0f,0x0f,0x0f,… | |
+ 0x1d,0x1d,0x1d,0x1e,0x1e,0x1e,0x2f,0x0e,0x1f,0x1f,0x20,0x0f,0x0f,0x10,… | |
+ 0x2e,0x2e,0x2e,0x1e,0x2f,0x2f,0x2f,0x1f,0x1f,0x20,0x20,0x31,0x10,0x10,… | |
+ 0x3f,0x3f,0x3f,0x2f,0x30,0x30,0x30,0x30,0x20,0x31,0x31,0x31,0x31,0x21,… | |
+ 0x4f,0x4f,0x22,0x40,0x40,0x40,0x40,0x41,0x41,0x41,0x52,0x42,0x42,0x53,… | |
+ 0x4f,0x22,0x22,0x22,0x40,0x40,0x40,0x41,0x41,0x41,0x52,0x42,0x42,0x53,… | |
+ 0x22,0x22,0x22,0x33,0x33,0x33,0x40,0x41,0x41,0x41,0x52,0x42,0x42,0x53,… | |
+ 0x43,0x22,0x33,0x33,0x33,0x44,0x44,0x45,0x45,0x45,0x56,0x46,0x46,0x46,… | |
+ 0x43,0x43,0x33,0x33,0x44,0x44,0x44,0x45,0x45,0x45,0x56,0x46,0x46,0x57,… | |
+ 0x43,0x43,0x33,0x44,0x44,0x44,0x55,0x45,0x45,0x56,0x56,0x46,0x46,0x57,… | |
+ 0x43,0x43,0x43,0x44,0x44,0x55,0x55,0x45,0x45,0x56,0x67,0x46,0x46,0x57,… | |
+ 0x47,0x47,0x47,0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x49,0x4a,0x4a,0x4a,… | |
+ 0x47,0x47,0x47,0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x5a,0x4a,0x4a,0x4a,… | |
+ 0x47,0x47,0x47,0x48,0x48,0x59,0x59,0x49,0x49,0x5a,0x5a,0x4a,0x4a,0x5b,… | |
+ 0x58,0x58,0x58,0x59,0x59,0x59,0x6a,0x49,0x5a,0x5a,0x6b,0x6b,0x5b,0x5b,… | |
+ 0x4b,0x4b,0x4b,0x4c,0x4c,0x4c,0x4c,0x4d,0x4d,0x4d,0x6b,0x4e,0x4e,0x4e,… | |
+ 0x4b,0x4b,0x4b,0x4c,0x4c,0x4c,0x4c,0x4d,0x4d,0x4d,0x5e,0x4e,0x4e,0x4e,… | |
+ 0x5c,0x5c,0x5c,0x4c,0x5d,0x5d,0x5d,0x4d,0x4d,0x5e,0x5e,0x4e,0x4e,0x5f,… | |
+ 0x5c,0x5c,0x5c,0x5d,0x5d,0x6e,0x6e,0x5e,0x5e,0x5e,0x6f,0x6f,0x5f,0x5f,… | |
+ 0x6d,0x6d,0x6d,0x6e,0x6e,0x6e,0x7f,0x7f,0x6f,0x6f,0x70,0x70,0x5f,0x60,… | |
+ 0x4f,0x4f,0x40,0x40,0x40,0x40,0x51,0x41,0x41,0x52,0x63,0x42,0x42,0x53,… | |
+ 0x4f,0x4f,0x22,0x40,0x40,0x40,0x51,0x41,0x41,0x52,0x63,0x42,0x42,0x53,… | |
+ 0x43,0x22,0x33,0x33,0x33,0x40,0x51,0x41,0x41,0x52,0x63,0x42,0x42,0x53,… | |
+ 0x43,0x43,0x33,0x33,0x44,0x44,0x44,0x45,0x45,0x45,0x56,0x46,0x46,0x57,… | |
+ 0x43,0x43,0x33,0x44,0x44,0x44,0x55,0x45,0x45,0x56,0x56,0x46,0x46,0x57,… | |
+ 0x43,0x43,0x43,0x44,0x44,0x55,0x55,0x45,0x45,0x56,0x67,0x46,0x46,0x57,… | |
+ 0x54,0x54,0x54,0x44,0x55,0x55,0x55,0x45,0x56,0x56,0x67,0x78,0x78,0x57,… | |
+ 0x47,0x47,0x47,0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x49,0x4a,0x4a,0x4a,… | |
+ 0x47,0x47,0x47,0x48,0x48,0x48,0x59,0x49,0x49,0x49,0x5a,0x4a,0x4a,0x5b,… | |
+ 0x58,0x58,0x58,0x48,0x59,0x59,0x59,0x49,0x49,0x5a,0x5a,0x4a,0x4a,0x5b,… | |
+ 0x69,0x69,0x69,0x59,0x59,0x6a,0x6a,0x49,0x5a,0x5a,0x6b,0x6b,0x5b,0x5b,… | |
+ 0x4b,0x4b,0x4b,0x4c,0x4c,0x4c,0x7b,0x4d,0x4d,0x4d,0x6b,0x4e,0x4e,0x4e,… | |
+ 0x4b,0x4b,0x4b,0x4c,0x4c,0x4c,0x7b,0x4d,0x4d,0x4d,0x5e,0x4e,0x4e,0x4e,… | |
+ 0x5c,0x5c,0x5c,0x5d,0x5d,0x5d,0x5d,0x4d,0x5e,0x5e,0x5e,0x4e,0x4e,0x5f,… | |
+ 0x6d,0x6d,0x6d,0x5d,0x6e,0x6e,0x6e,0x5e,0x5e,0x6f,0x6f,0x70,0x5f,0x5f,… | |
+ 0x7e,0x7e,0x7e,0x6e,0x6e,0x7f,0x7f,0x7f,0x6f,0x6f,0x70,0x70,0x70,0x60,… | |
+ 0x50,0x50,0x50,0x40,0x40,0x51,0x51,0x41,0x41,0x52,0x63,0x74,0x42,0x53,… | |
+ 0x50,0x50,0x50,0x40,0x40,0x51,0x51,0x41,0x41,0x52,0x63,0x74,0x42,0x53,… | |
+ 0x50,0x50,0x33,0x33,0x40,0x51,0x51,0x41,0x41,0x52,0x63,0x74,0x42,0x53,… | |
+ 0x43,0x43,0x33,0x44,0x44,0x44,0x55,0x45,0x45,0x56,0x56,0x46,0x46,0x57,… | |
+ 0x43,0x43,0x43,0x44,0x44,0x55,0x55,0x45,0x45,0x56,0x67,0x46,0x46,0x57,… | |
+ 0x54,0x54,0x54,0x44,0x55,0x55,0x55,0x45,0x56,0x56,0x67,0x78,0x78,0x57,… | |
+ 0x54,0x54,0x54,0x55,0x55,0x55,0x66,0x66,0x56,0x67,0x67,0x78,0x78,0x68,… | |
+ 0x47,0x47,0x47,0x48,0x48,0x48,0x66,0x49,0x49,0x49,0x78,0x78,0x4a,0x4a,… | |
+ 0x47,0x47,0x47,0x48,0x48,0x59,0x59,0x49,0x49,0x5a,0x5a,0x4a,0x4a,0x5b,… | |
+ 0x58,0x58,0x58,0x59,0x59,0x59,0x6a,0x49,0x5a,0x5a,0x6b,0x6b,0x5b,0x5b,… | |
+ 0x69,0x69,0x69,0x59,0x6a,0x6a,0x6a,0x7b,0x5a,0x6b,0x6b,0x6b,0x7c,0x6c,… | |
+ 0x7a,0x7a,0x7a,0x4c,0x4c,0x7b,0x7b,0x7b,0x4d,0x6b,0x6b,0x7c,0x7c,0x4e,… | |
+ 0x4b,0x4b,0x4b,0x4c,0x4c,0x7b,0x7b,0x4d,0x4d,0x5e,0x7c,0x7c,0x4e,0x5f,… | |
+ 0x5c,0x5c,0x5c,0x5d,0x5d,0x5d,0x6e,0x4d,0x5e,0x5e,0x6f,0x4e,0x5f,0x5f,… | |
+ 0x6d,0x6d,0x6d,0x6e,0x6e,0x6e,0x6e,0x5e,0x6f,0x6f,0x6f,0x70,0x5f,0x60,… | |
+ 0x7e,0x7e,0x7e,0x6e,0x7f,0x7f,0x7f,0x7f,0x6f,0x70,0x70,0x70,0x70,0x60,… | |
+ 0x61,0x61,0x61,0x40,0x51,0x51,0x62,0x62,0x73,0x63,0x63,0x74,0x74,0x64,… | |
+ 0x61,0x61,0x61,0x40,0x51,0x51,0x62,0x62,0x73,0x63,0x63,0x74,0x74,0x64,… | |
+ 0x61,0x61,0x61,0x40,0x51,0x51,0x62,0x62,0x73,0x63,0x63,0x74,0x74,0x64,… | |
+ 0x43,0x43,0x43,0x44,0x44,0x55,0x55,0x45,0x45,0x56,0x67,0x46,0x46,0x57,… | |
+ 0x54,0x54,0x54,0x44,0x55,0x55,0x55,0x45,0x56,0x56,0x67,0x78,0x78,0x57,… | |
+ 0x54,0x54,0x54,0x55,0x55,0x55,0x66,0x66,0x56,0x67,0x67,0x78,0x78,0x68,… | |
+ 0x65,0x65,0x65,0x55,0x55,0x66,0x66,0x66,0x77,0x67,0x78,0x78,0x78,0x78,… | |
+ 0x65,0x65,0x65,0x48,0x48,0x66,0x66,0x77,0x77,0x77,0x78,0x78,0x78,0x5b,… | |
+ 0x76,0x76,0x76,0x48,0x59,0x59,0x77,0x77,0x77,0x5a,0x5a,0x4a,0x4a,0x5b,… | |
+ 0x69,0x69,0x69,0x59,0x59,0x6a,0x6a,0x77,0x5a,0x5a,0x6b,0x6b,0x5b,0x6c,… | |
+ 0x69,0x69,0x69,0x6a,0x6a,0x6a,0x7b,0x7b,0x5a,0x6b,0x6b,0x7c,0x7c,0x6c,… | |
+ 0x7a,0x7a,0x7a,0x4c,0x7b,0x7b,0x7b,0x7b,0x4d,0x6b,0x7c,0x7c,0x7c,0x7c,… | |
+ 0x7a,0x7a,0x7a,0x4c,0x7b,0x7b,0x7b,0x7b,0x4d,0x5e,0x7c,0x7c,0x7c,0x5f,… | |
+ 0x6d,0x6d,0x6d,0x5d,0x5d,0x6e,0x7b,0x5e,0x5e,0x6f,0x6f,0x7c,0x5f,0x5f,… | |
+ 0x6d,0x6d,0x6d,0x6e,0x6e,0x6e,0x7f,0x7f,0x6f,0x6f,0x70,0x70,0x5f,0x60,… | |
+ 0x7e,0x7e,0x7e,0x7f,0x7f,0x7f,0x7f,0x7f,0x6f,0x70,0x70,0x70,0x70,0x60,… | |
+ 0x72,0x72,0x72,0x8f,0x8f,0x62,0x62,0x73,0x73,0x80,0x74,0x81,0x81,0x81,… | |
+ 0x72,0x72,0x72,0x8f,0x8f,0x62,0x62,0x73,0x73,0x80,0x74,0x81,0x81,0x81,… | |
+ 0x72,0x72,0x72,0x83,0x83,0x62,0x62,0x73,0x73,0x80,0x74,0x81,0x81,0x81,… | |
+ 0x82,0x82,0x82,0x83,0x83,0x83,0x83,0x84,0x84,0x84,0x84,0x85,0x85,0x85,… | |
+ 0x82,0x82,0x82,0x83,0x83,0x83,0x66,0x84,0x84,0x84,0x67,0x85,0x85,0x85,… | |
+ 0x65,0x65,0x65,0x83,0x83,0x66,0x66,0x66,0x84,0x84,0x78,0x78,0x85,0x85,… | |
+ 0x65,0x65,0x65,0x83,0x66,0x66,0x66,0x77,0x77,0x77,0x78,0x78,0x78,0x96,… | |
+ 0x76,0x76,0x76,0x87,0x87,0x66,0x77,0x77,0x77,0x88,0x78,0x89,0x89,0x89,… | |
+ 0x76,0x76,0x76,0x87,0x87,0x87,0x77,0x77,0x88,0x88,0x88,0x89,0x89,0x89,… | |
+ 0x86,0x86,0x86,0x87,0x87,0x87,0x77,0x88,0x88,0x88,0x6b,0x89,0x89,0x9a,… | |
+ 0x7a,0x7a,0x7a,0x87,0x6a,0x7b,0x7b,0x7b,0x88,0x6b,0x6b,0x7c,0x7c,0x9a,… | |
+ 0x8a,0x8a,0x8a,0x8b,0x8b,0x7b,0x7b,0x8c,0x8c,0x8c,0x7c,0x7c,0x8d,0x8d,… | |
+ 0x8a,0x8a,0x8a,0x8b,0x8b,0x8b,0x7b,0x8c,0x8c,0x8c,0x7c,0x8d,0x8d,0x8d,… | |
+ 0x8a,0x8a,0x8a,0x8b,0x8b,0x8b,0x9c,0x8c,0x8c,0x9d,0x9d,0x8d,0x8d,0x9e,… | |
+ 0x9b,0x9b,0x9b,0x9c,0x9c,0x9c,0x7f,0x8c,0x9d,0x9d,0x70,0x70,0x9e,0x9e,… | |
+ 0x7e,0x7e,0x7e,0x7f,0x7f,0x7f,0x7f,0x7f,0x9d,0x70,0x70,0x70,0x9e,0x9e,… | |
+ 0x8e,0x8e,0x8e,0x8f,0x8f,0x8f,0x73,0x73,0x80,0x80,0x91,0x81,0x81,0x92,… | |
+ 0x8e,0x8e,0x8e,0x8f,0x8f,0x8f,0x73,0x73,0x80,0x80,0x91,0x81,0x81,0x92,… | |
+ 0x82,0x82,0x82,0x83,0x83,0x83,0x73,0x73,0x80,0x80,0x91,0x81,0x81,0x92,… | |
+ 0x82,0x82,0x82,0x83,0x83,0x83,0x83,0x84,0x84,0x84,0x95,0x85,0x85,0x85,… | |
+ 0x82,0x82,0x82,0x83,0x83,0x83,0x94,0x84,0x84,0x84,0x95,0x85,0x85,0x96,… | |
+ 0x82,0x82,0x82,0x83,0x83,0x94,0x94,0x84,0x84,0x95,0x95,0x85,0x85,0x96,… | |
+ 0x76,0x76,0x76,0x83,0x94,0x94,0x77,0x77,0x77,0x95,0x95,0x85,0x85,0x96,… | |
+ 0x76,0x76,0x76,0x87,0x87,0x87,0x77,0x77,0x88,0x88,0x88,0x89,0x89,0x89,… | |
+ 0x86,0x86,0x86,0x87,0x87,0x87,0x77,0x88,0x88,0x88,0x99,0x89,0x89,0x9a,… | |
+ 0x86,0x86,0x86,0x87,0x87,0x98,0x98,0x88,0x88,0x99,0x99,0x89,0x89,0x9a,… | |
+ 0x97,0x97,0x97,0x98,0x98,0x98,0x98,0x88,0x99,0x99,0x99,0x89,0x9a,0x9a,… | |
+ 0x8a,0x8a,0x8a,0x8b,0x8b,0x8b,0x8b,0x8c,0x8c,0x8c,0x8c,0x8d,0x8d,0x8d,… | |
+ 0x8a,0x8a,0x8a,0x8b,0x8b,0x8b,0x8b,0x8c,0x8c,0x8c,0x9d,0x8d,0x8d,0x8d,… | |
+ 0x9b,0x9b,0x9b,0x8b,0x9c,0x9c,0x9c,0x8c,0x9d,0x9d,0x9d,0x8d,0x8d,0x9e,… | |
+ 0x9b,0x9b,0x9b,0x9c,0x9c,0xad,0xad,0x9d,0x9d,0x9d,0xae,0xae,0x9e,0x9e,… | |
+ 0xac,0xac,0xac,0xad,0xad,0xad,0xad,0x9d,0xae,0xae,0xae,0xbf,0x9e,0xaf,… | |
+ 0x9f,0x9f,0x9f,0x8f,0x90,0x90,0xa1,0x80,0x80,0x91,0x91,0x81,0x81,0x92,… | |
+ 0x9f,0x9f,0x9f,0x8f,0x90,0x90,0xa1,0x80,0x80,0x91,0x91,0x81,0x81,0x92,… | |
+ 0x9f,0x9f,0x9f,0x83,0x90,0x90,0xa1,0x80,0x80,0x91,0x91,0x81,0x81,0x92,… | |
+ 0x82,0x82,0x82,0x83,0x83,0x94,0x94,0x84,0x84,0x95,0x95,0x85,0x85,0x96,… | |
+ 0x93,0x93,0x93,0x83,0x94,0x94,0x94,0x84,0x84,0x95,0x95,0x85,0x85,0x96,… | |
+ 0x93,0x93,0x93,0x94,0x94,0x94,0xa5,0x84,0x95,0x95,0xa6,0xa6,0x96,0x96,… | |
+ 0xa4,0xa4,0xa4,0x94,0x94,0xa5,0xa5,0x77,0x95,0x95,0xa6,0xa6,0x96,0xa7,… | |
+ 0x86,0x86,0x86,0x87,0x87,0x87,0x77,0x88,0x88,0x88,0x99,0x89,0x89,0x9a,… | |
+ 0x86,0x86,0x86,0x87,0x87,0x98,0x98,0x88,0x88,0x99,0x99,0x89,0x89,0x9a,… | |
+ 0x97,0x97,0x97,0x98,0x98,0x98,0x98,0x88,0x99,0x99,0x99,0x89,0x9a,0x9a,… | |
+ 0x97,0x97,0x97,0x98,0x98,0xa9,0xa9,0x99,0x99,0x99,0xaa,0xaa,0x9a,0xab,… | |
+ 0x8a,0x8a,0x8a,0x8b,0x8b,0xa9,0xa9,0x8c,0x8c,0x8c,0xaa,0x8d,0x8d,0x8d,… | |
+ 0x8a,0x8a,0x8a,0x8b,0x8b,0x9c,0x9c,0x8c,0x8c,0x9d,0x9d,0x8d,0x8d,0x9e,… | |
+ 0x9b,0x9b,0x9b,0x9c,0x9c,0x9c,0xad,0x9d,0x9d,0x9d,0xae,0x8d,0x9e,0x9e,… | |
+ 0xac,0xac,0xac,0x9c,0xad,0xad,0xad,0x9d,0x9d,0xae,0xae,0xae,0x9e,0xaf,… | |
+ 0xbd,0xbd,0xbd,0xad,0xad,0xbe,0xbe,0xbe,0xae,0xae,0xbf,0xbf,0xbf,0xaf,… | |
+ 0xa0,0xa0,0xa0,0x90,0xa1,0xa1,0xa1,0xb2,0x91,0x91,0xa2,0xa2,0xb3,0xa3,… | |
+ 0xa0,0xa0,0xa0,0x90,0xa1,0xa1,0xa1,0xb2,0x91,0x91,0xa2,0xa2,0xb3,0xa3,… | |
+ 0xa0,0xa0,0xa0,0x90,0xa1,0xa1,0xa1,0xb2,0x91,0x91,0xa2,0xa2,0xb3,0xa3,… | |
+ 0x93,0x93,0x93,0x94,0x94,0x94,0xa5,0x84,0x95,0x95,0xa6,0xa6,0x96,0x96,… | |
+ 0xa4,0xa4,0xa4,0x94,0x94,0xa5,0xa5,0x84,0x95,0x95,0xa6,0xa6,0x96,0x96,… | |
+ 0xa4,0xa4,0xa4,0x94,0xa5,0xa5,0xa5,0xb6,0x95,0xa6,0xa6,0xa6,0xb7,0xa7,… | |
+ 0xa4,0xa4,0xa4,0xa5,0xa5,0xa5,0xb6,0xb6,0x95,0xa6,0xa6,0xb7,0xb7,0xa7,… | |
+ 0xb5,0xb5,0xb5,0x87,0x87,0xb6,0xb6,0xb6,0x88,0x99,0xa6,0xb7,0xb7,0x9a,… | |
+ 0x97,0x97,0x97,0x98,0x98,0x98,0x98,0x88,0x99,0x99,0x99,0x89,0x9a,0x9a,… | |
+ 0x97,0x97,0x97,0x98,0x98,0xa9,0xa9,0x99,0x99,0x99,0xaa,0xaa,0x9a,0xab,… | |
+ 0xa8,0xa8,0xa8,0xa9,0xa9,0xa9,0xa9,0xa9,0x99,0xaa,0xaa,0xaa,0xbb,0xab,… | |
+ 0xa8,0xa8,0xa8,0xa9,0xa9,0xa9,0xba,0xba,0x8c,0xaa,0xaa,0xbb,0xbb,0xab,… | |
+ 0xb9,0xb9,0xb9,0x9c,0x9c,0xba,0xba,0xba,0x9d,0x9d,0xbb,0xbb,0xbb,0x9e,… | |
+ 0xac,0xac,0xac,0x9c,0x9c,0xad,0xad,0x9d,0x9d,0xae,0xae,0xae,0x9e,0x9e,… | |
+ 0xac,0xac,0xac,0xad,0xad,0xad,0xbe,0xbe,0xae,0xae,0xae,0xbf,0x9e,0xaf,… | |
+ 0xbd,0xbd,0xbd,0xbe,0xbe,0xbe,0xbe,0xbe,0xae,0xbf,0xbf,0xbf,0xbf,0xaf,… | |
+ 0xb1,0xb1,0xb1,0xce,0xce,0xb2,0xb2,0xcf,0xcf,0xa2,0xa2,0xb3,0xb3,0xc0,… | |
+ 0xb1,0xb1,0xb1,0xce,0xce,0xb2,0xb2,0xcf,0xcf,0xa2,0xa2,0xb3,0xb3,0xc0,… | |
+ 0xb1,0xb1,0xb1,0xc2,0xc2,0xb2,0xb2,0xc3,0xc3,0xa2,0xa2,0xb3,0xb3,0xc0,… | |
+ 0xc1,0xc1,0xc1,0xc2,0xc2,0xc2,0xa5,0xc3,0xc3,0xc3,0xa6,0xc4,0xc4,0xc4,… | |
+ 0xc1,0xc1,0xc1,0xc2,0xc2,0xa5,0xb6,0xc3,0xc3,0xc3,0xa6,0xc4,0xc4,0xc4,… | |
+ 0xb5,0xb5,0xb5,0xc2,0xa5,0xb6,0xb6,0xb6,0xc3,0xa6,0xa6,0xb7,0xb7,0xc4,… | |
+ 0xb5,0xb5,0xb5,0xa5,0xb6,0xb6,0xb6,0xb6,0xc3,0xa6,0xb7,0xb7,0xb7,0xb7,… | |
+ 0xc5,0xc5,0xc5,0xc6,0xc6,0xb6,0xb6,0xc7,0xc7,0xc7,0xb7,0xb7,0xc8,0xc8,… | |
+ 0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xc6,0xc7,0xc7,0xc7,0xaa,0xc8,0xc8,0xc8,… | |
+ 0xa8,0xa8,0xa8,0xc6,0xc6,0xa9,0xa9,0xc7,0xc7,0xaa,0xaa,0xaa,0xc8,0xc8,… | |
+ 0xa8,0xa8,0xa8,0xa9,0xa9,0xa9,0xba,0xba,0xaa,0xaa,0xaa,0xbb,0xbb,0xab,… | |
+ 0xb9,0xb9,0xb9,0xca,0xca,0xba,0xba,0xba,0xcb,0xaa,0xbb,0xbb,0xbb,0xcc,… | |
+ 0xb9,0xb9,0xb9,0xca,0xca,0xba,0xba,0xcb,0xcb,0xcb,0xbb,0xbb,0xcc,0xcc,… | |
+ 0xc9,0xc9,0xc9,0xca,0xca,0xca,0xba,0xcb,0xcb,0xcb,0xae,0xcc,0xcc,0xcc,… | |
+ 0xbd,0xbd,0xbd,0xad,0xbe,0xbe,0xbe,0xbe,0xae,0xae,0xbf,0xbf,0xcc,0xaf,… | |
+ 0xbd,0xbd,0xbd,0xbe,0xbe,0xbe,0xbe,0xbe,0xbf,0xbf,0xbf,0xbf,0xbf,0xaf,… | |
+ 0xcd,0xcd,0xcd,0xce,0xce,0xce,0xb2,0xcf,0xcf,0xcf,0xb3,0xb3,0xc0,0xc0,… | |
+ 0xcd,0xcd,0xcd,0xce,0xce,0xce,0xb2,0xcf,0xcf,0xcf,0xb3,0xb3,0xc0,0xc0,… | |
+ 0xc1,0xc1,0xc1,0xc2,0xc2,0xc2,0xb2,0xc3,0xc3,0xc3,0xb3,0xb3,0xc0,0xc0,… | |
+ 0xc1,0xc1,0xc1,0xc2,0xc2,0xc2,0xc2,0xc3,0xc3,0xc3,0xd4,0xc4,0xc4,0xc4,… | |
+ 0xc1,0xc1,0xc1,0xc2,0xc2,0xc2,0xb6,0xc3,0xc3,0xc3,0xd4,0xc4,0xc4,0xc4,… | |
+ 0xc1,0xc1,0xc1,0xc2,0xc2,0xb6,0xb6,0xc3,0xc3,0xd4,0xb7,0xb7,0xc4,0xd5,… | |
+ 0xb5,0xb5,0xb5,0xc2,0xb6,0xb6,0xb6,0xb6,0xc3,0xd4,0xb7,0xb7,0xb7,0xd5,… | |
+ 0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xb6,0xc7,0xc7,0xc7,0xb7,0xc8,0xc8,0xc8,… | |
+ 0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xc6,0xc7,0xc7,0xc7,0xd8,0xc8,0xc8,0xc8,… | |
+ 0xc5,0xc5,0xc5,0xc6,0xc6,0xd7,0xd7,0xc7,0xc7,0xd8,0xd8,0xc8,0xc8,0xd9,… | |
+ 0xb9,0xb9,0xb9,0xd7,0xd7,0xba,0xba,0xba,0xd8,0xd8,0xbb,0xbb,0xbb,0xd9,… | |
+ 0xb9,0xb9,0xb9,0xca,0xca,0xba,0xba,0xcb,0xcb,0xcb,0xbb,0xbb,0xcc,0xcc,… | |
+ 0xc9,0xc9,0xc9,0xca,0xca,0xca,0xba,0xcb,0xcb,0xcb,0xbb,0xcc,0xcc,0xcc,… | |
+ 0xc9,0xc9,0xc9,0xca,0xca,0xdb,0xdb,0xcb,0xcb,0xdc,0xdc,0xcc,0xcc,0xdd,… | |
+ 0xda,0xda,0xda,0xdb,0xdb,0xdb,0xdb,0xdc,0xdc,0xdc,0xdc,0xcc,0xdd,0xdd,… | |
+ 0xbd,0xbd,0xbd,0xdb,0xbe,0xbe,0xbe,0xdc,0xdc,0xbf,0xbf,0xbf,0xdd,0xdd,… | |
+ 0xde,0xde,0xde,0xdf,0xdf,0xdf,0xe0,0xcf,0xd0,0xd0,0xe1,0xc0,0xc0,0xd1,… | |
+ 0xde,0xde,0xde,0xdf,0xdf,0xdf,0xe0,0xcf,0xd0,0xd0,0xe1,0xc0,0xc0,0xd1,… | |
+ 0xde,0xde,0xde,0xdf,0xdf,0xdf,0xe0,0xc3,0xd0,0xd0,0xe1,0xc0,0xc0,0xd1,… | |
+ 0xd2,0xd2,0xd2,0xc2,0xd3,0xd3,0xd3,0xc3,0xc3,0xd4,0xd4,0xc4,0xc4,0xd5,… | |
+ 0xd2,0xd2,0xd2,0xd3,0xd3,0xd3,0xd3,0xc3,0xd4,0xd4,0xd4,0xc4,0xc4,0xd5,… | |
+ 0xd2,0xd2,0xd2,0xd3,0xd3,0xd3,0xe4,0xc3,0xd4,0xd4,0xe5,0xc4,0xd5,0xd5,… | |
+ 0xe3,0xe3,0xe3,0xd3,0xd3,0xe4,0xb6,0xd4,0xd4,0xe5,0xe5,0xb7,0xd5,0xd5,… | |
+ 0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xd7,0xc7,0xc7,0xd8,0xd8,0xc8,0xc8,0xd9,… | |
+ 0xd6,0xd6,0xd6,0xc6,0xd7,0xd7,0xd7,0xc7,0xd8,0xd8,0xd8,0xc8,0xc8,0xd9,… | |
+ 0xd6,0xd6,0xd6,0xd7,0xd7,0xd7,0xe8,0xd8,0xd8,0xd8,0xe9,0xc8,0xd9,0xd9,… | |
+ 0xe7,0xe7,0xe7,0xd7,0xd7,0xe8,0xe8,0xd8,0xd8,0xe9,0xe9,0xe9,0xd9,0xd9,… | |
+ 0xc9,0xc9,0xc9,0xca,0xca,0xca,0xba,0xcb,0xcb,0xcb,0xe9,0xcc,0xcc,0xcc,… | |
+ 0xc9,0xc9,0xc9,0xca,0xca,0xdb,0xdb,0xcb,0xcb,0xdc,0xdc,0xcc,0xcc,0xdd,… | |
+ 0xda,0xda,0xda,0xdb,0xdb,0xdb,0xdb,0xdc,0xdc,0xdc,0xdc,0xcc,0xdd,0xdd,… | |
+ 0xda,0xda,0xda,0xdb,0xdb,0xec,0xec,0xdc,0xdc,0xed,0xed,0xed,0xdd,0xdd,… | |
+ 0xeb,0xeb,0xeb,0xec,0xec,0xec,0xec,0xdc,0xed,0xed,0xed,0xed,0xdd,0xee,… | |
+ 0xef,0xef,0xef,0xdf,0xe0,0xe0,0xe0,0xd0,0xd0,0xe1,0xe1,0xf2,0xd1,0xd1,… | |
+ 0xef,0xef,0xef,0xdf,0xe0,0xe0,0xe0,0xd0,0xd0,0xe1,0xe1,0xf2,0xd1,0xd1,… | |
+ 0xef,0xef,0xef,0xdf,0xe0,0xe0,0xe0,0xd0,0xd0,0xe1,0xe1,0xf2,0xd1,0xd1,… | |
+ 0xd2,0xd2,0xd2,0xd3,0xd3,0xe4,0xe4,0xd4,0xd4,0xd4,0xe5,0xe5,0xd5,0xd5,… | |
+ 0xe3,0xe3,0xe3,0xd3,0xe4,0xe4,0xe4,0xd4,0xd4,0xe5,0xe5,0xf6,0xd5,0xd5,… | |
+ 0xe3,0xe3,0xe3,0xe4,0xe4,0xe4,0xe4,0xd4,0xe5,0xe5,0xe5,0xf6,0xd5,0xe6,… | |
+ 0xe3,0xe3,0xe3,0xe4,0xe4,0xe4,0xf5,0xf5,0xe5,0xe5,0xf6,0xf6,0xd5,0xe6,… | |
+ 0xd6,0xd6,0xd6,0xd7,0xd7,0xd7,0xf5,0xc7,0xd8,0xd8,0xf6,0xc8,0xd9,0xd9,… | |
+ 0xd6,0xd6,0xd6,0xd7,0xd7,0xe8,0xe8,0xd8,0xd8,0xd8,0xe9,0xe9,0xd9,0xd9,… | |
+ 0xe7,0xe7,0xe7,0xd7,0xe8,0xe8,0xe8,0xd8,0xd8,0xe9,0xe9,0xe9,0xd9,0xea,… | |
+ 0xe7,0xe7,0xe7,0xe8,0xe8,0xe8,0xf9,0xf9,0xe9,0xe9,0xe9,0xfa,0xd9,0xea,… | |
+ 0xf8,0xf8,0xf8,0xe8,0xf9,0xf9,0xf9,0xcb,0xe9,0xe9,0xfa,0xfa,0xcc,0xea,… | |
+ 0xda,0xda,0xda,0xdb,0xdb,0xdb,0xdb,0xdc,0xdc,0xdc,0xdc,0xcc,0xdd,0xdd,… | |
+ 0xda,0xda,0xda,0xdb,0xdb,0xec,0xec,0xdc,0xdc,0xed,0xed,0xed,0xdd,0xdd,… | |
+ 0xeb,0xeb,0xeb,0xec,0xec,0xec,0xec,0xdc,0xed,0xed,0xed,0xed,0xdd,0xee,… | |
+ 0xeb,0xeb,0xeb,0xec,0xec,0xfd,0xfd,0xfd,0xed,0xed,0xfe,0xfe,0xee,0xee,… | |
+ 0xf0,0xf0,0xf0,0xe0,0xf1,0xf1,0xf1,0xf1,0xe1,0xf2,0xf2,0xf2,0xf2,0xe2,… | |
+ 0xf0,0xf0,0xf0,0xe0,0xf1,0xf1,0xf1,0xf1,0xe1,0xf2,0xf2,0xf2,0xf2,0xe2,… | |
+ 0xf0,0xf0,0xf0,0xe0,0xf1,0xf1,0xf1,0xf1,0xe1,0xf2,0xf2,0xf2,0xf2,0xe2,… | |
+ 0xe3,0xe3,0xe3,0xe4,0xe4,0xe4,0xf5,0xf5,0xe5,0xe5,0xf6,0xf6,0xd5,0xe6,… | |
+ 0xf4,0xf4,0xf4,0xe4,0xe4,0xf5,0xf5,0xf5,0xe5,0xe5,0xf6,0xf6,0xf6,0xe6,… | |
+ 0xf4,0xf4,0xf4,0xe4,0xf5,0xf5,0xf5,0xf5,0xe5,0xf6,0xf6,0xf6,0xf6,0xe6,… | |
+ 0xf4,0xf4,0xf4,0xf5,0xf5,0xf5,0xf5,0xf5,0xe5,0xf6,0xf6,0xf6,0xf6,0xe6,… | |
+ 0xf4,0xf4,0xf4,0xf5,0xf5,0xf5,0xf5,0xf5,0xd8,0xf6,0xf6,0xf6,0xd9,0xd9,… | |
+ 0xe7,0xe7,0xe7,0xe8,0xe8,0xe8,0xe8,0xd8,0xe9,0xe9,0xe9,0xfa,0xd9,0xea,… | |
+ 0xf8,0xf8,0xf8,0xe8,0xe8,0xf9,0xf9,0xf9,0xe9,0xe9,0xfa,0xfa,0xfa,0xea,… | |
+ 0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xf9,0xf9,0xe9,0xfa,0xfa,0xfa,0xfa,0xea,… | |
+ 0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xf9,0xf9,0xfa,0xfa,0xfa,0xfa,0xfa,0xea,… | |
+ 0xf8,0xf8,0xf8,0xdb,0xf9,0xf9,0xf9,0xdc,0xdc,0xfa,0xfa,0xfa,0xdd,0xdd,… | |
+ 0xeb,0xeb,0xeb,0xec,0xec,0xec,0xec,0xdc,0xed,0xed,0xed,0xed,0xdd,0xee,… | |
+ 0xeb,0xeb,0xeb,0xec,0xec,0xfd,0xfd,0xfd,0xed,0xed,0xfe,0xfe,0xee,0xee,… | |
+ 0xfc,0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfd,0xed,0xfe,0xfe,0xfe,0xfe,0xee,… | |
+} | |
+}; | |
+Memcmap *memdefcmap = &def; | |
+void _memmkcmap(void){} | |
diff --git a/src/libdraw/md-cread.c b/src/libdraw/md-cread.c | |
t@@ -0,0 +1,96 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+Memimage* | |
+creadmemimage(int fd) | |
+{ | |
+ char hdr[5*12+1]; | |
+ Rectangle r; | |
+ int m, nb, miny, maxy, new, ldepth, ncblock; | |
+ uchar *buf; | |
+ Memimage *i; | |
+ u32int 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<maxy){ | |
+ werrstr("readimage: bad maxy %d", maxy); | |
+ goto Errout; | |
+ } | |
+ if(nb<=0 || ncblock<nb){ | |
+ werrstr("readimage: bad count %d", nb); | |
+ goto Errout; | |
+ } | |
+ if(readn(fd, buf, nb)!=nb) | |
+ goto Shortread; | |
+ if(!new) /* old image: flip the data bits */ | |
+ _twiddlecompressed(buf, nb); | |
+ cloadmemimage(i, Rect(r.min.x, miny, r.max.x, maxy), buf, nb); | |
+ miny = maxy; | |
+ } | |
+ free(buf); | |
+ return i; | |
+} | |
diff --git a/src/libdraw/md-defont.c b/src/libdraw/md-defont.c | |
t@@ -0,0 +1,68 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+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 u32int*. | |
+ */ | |
+ p = (char*)defontdata; | |
+ n = (u32int)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(u32int); | |
+ 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/src/libdraw/md-draw.c b/src/libdraw/md-draw.c | |
t@@ -0,0 +1,2487 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+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 approximatio… | |
+ * the last one is perfect for all up to 1<<16, avoids a multiply, but require… | |
+ */ | |
+/* #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>… | |
+#define MUL02(a, x, t) (t = (a)*(((x)&MASK02)>>0)+128, ((t+((t>… | |
+#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 */); /* R… | |
+ | |
+ memwhite = memones; | |
+ memblack = memzeros; | |
+ memopaque = memones; | |
+ memtransparent = memzeros; | |
+} | |
+ | |
+u32int _imgtorgba(Memimage*, u32int); | |
+u32int _rgbatoimg(Memimage*, u32int); | |
+u32int _pixelbits(Memimage*, Point); | |
+ | |
+#define DBG if(0) | |
+static Memdrawparam par; | |
+ | |
+Memdrawparam* | |
+_memimagedrawsetup(Memimage *dst, Rectangle r, Memimage *src, Point p0, Memima… | |
+{ | |
+ if(mask == nil) | |
+ mask = memopaque; | |
+ | |
+DBG print("memimagedraw %p/%luX %R @ %p %p/%luX %P %p/%luX %P... ", dst… | |
+ | |
+ 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… | |
+ return nil; /* no-op successfully handl… | |
+ } | |
+ } | |
+ } | |
+ | |
+ 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 handl… | |
+ } | |
+ 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) | |
+{ | |
+ /* | |
+ * 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… | |
+ * to handle us. If the sub-drawing routine returns zero, it means it… | |
+ * 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… | |
+ */ | |
+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… | |
+ */ | |
+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 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) - … | |
+ delta.y = drawreplxy(src->r.min.y, src->r.max.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 replica… | |
+static uchar conv18[256][8]; /* conv18[x][y] is the yth pixel i… | |
+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<<j)-1); | |
+ replbit[j][i] = (small*replmul[j])>>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; | |
+ u32int *rgba; | |
+ int delta; /* number of bytes to add to pointer to get n… | |
+ | |
+ /* used by boolcalc* for mask data */ | |
+ uchar *m; /* ptr to mask data r.min byte; like p… | |
+ int mskip; /* no. of left bits to skip in *m */ | |
+ uchar *bm; /* ptr to mask data img->r.min byte; … | |
+ int bmskip; /* no. of left bits to skip in *bm */ | |
+ uchar *em; /* ptr to mask data img->r.max.x byte… | |
+ 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… | |
+ uchar *bytermin; /* byteaddr(Pt(r.min.x, img->r.min.y)) … | |
+ uchar *bytey0e; /* byteaddr(Pt(img->r.max.x, img… | |
+ int bwidth; | |
+ | |
+ int replcache; /* if set, cache buffers */ | |
+ Buffer bcache[MAXBCACHE]; | |
+ u32int 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, al… | |
+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 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(u32int)*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; i<n; i++){ | |
+ print(" "); | |
+ if(p=b.grey){ | |
+ print(" k%.2uX", *p); | |
+ b.grey += b.delta; | |
+ }else{ | |
+ if(p=b.red){ | |
+ print(" r%.2uX", *p); | |
+ b.red += b.delta; | |
+ } | |
+ if(p=b.grn){ | |
+ print(" g%.2uX", *p); | |
+ b.grn += b.delta; | |
+ } | |
+ if(p=b.blu){ | |
+ print(" b%.2uX", *p); | |
+ b.blu += b.delta; | |
+ } | |
+ } | |
+ if((p=b.alpha) != &ones){ | |
+ print(" α%.2uX", *p); | |
+ b.alpha += b.delta; | |
+ } | |
+ } | |
+ print("\n"); | |
+} | |
+ | |
+/* | |
+ * For each scan line, we expand the pixels from source, mask, and destination | |
+ * into byte-aligned red, green, blue, alpha, and grey channels. If buffering… | |
+ * needed and the channels were already byte-aligned (grey8, rgb24, rgba32, rg… | |
+ * the readers need not copy the data: they can simply return pointers to the … | |
+ * If the destination image is grey and the source is not, it is converted usi… | |
+ * formula. | |
+ * | |
+ * Once we have all the channels, we call either rgbcalc or greycalc, dependin… | |
+ * whether the destination image is color. This is allowed to overwrite the d… | |
+ * the actual data, perhaps a copy) with its result. It should only overwrite… | |
+ * with the same format (i.e. red bytes with red bytes, etc.) A new buffer is… | |
+ * the calculator, and that buffer is passed to a function to write it to the … | |
+ * If the buffer is already pointing at the destination, the writing function … | |
+ */ | |
+#define DBG if(0) | |
+static int | |
+alphadraw(Memdrawparam *par) | |
+{ | |
+ int isgrey, starty, endy, op; | |
+ int needbuf, dsty, srcy, masky; | |
+ int y, dir, dx, dy; | |
+ Buffer bsrc, bdst, bmask; | |
+ Readfn *rdsrc, *rdmask, *rddst; | |
+ Calcfn *calc; | |
+ Writefn *wrdst; | |
+ Memimage *src, *mask, *dst; | |
+ Rectangle r, sr, mr; | |
+ | |
+ r = par->r; | |
+ 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 no… | |
+ * necessary. There are stronger conditions we could use. We could | |
+ * check to see if the rectangles intersect, and if simply moving in t… | |
+ * 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 :… | |
+ 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… | |
+ * This requires a boolean (1-bit grey) mask and lack of a source alph… | |
+ * | |
+ * The computation is accomplished by assigning the function pointers … | |
+ * 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 the… | |
+ * but it avoids a fair amount of code duplication to make this a case… | |
+ * rather than have a separate booldraw. | |
+ */ | |
+//if(drawdebug) iprint("flag %lud mchan %lux=?%x dd %d\n", src->flags&Falpha, … | |
+ if(!(src->flags&Falpha) && mask->chan == GREY1 && dst->depth >= 8 && o… | |
+//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 … | |
+ * to deal with bit alignment, and I haven't written t… | |
+ * | |
+ * This is a common case for things like ellipse drawi… | |
+ * When there's no alpha involved and the mask is bool… | |
+ * 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; | |
+ u32int s, t; | |
+ | |
+ obdst = bdst; | |
+ sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta; | |
+ q = bsrc.delta == 4 && bdst.delta == 4; | |
+ | |
+ for(i=0; i<dx; i++){ | |
+ sa = *bsrc.alpha; | |
+ ma = *bmask.alpha; | |
+ fd = MUL(sa, ma, t); | |
+ if(op == DoutS) | |
+ fd = 255-fd; | |
+ | |
+ if(grey){ | |
+ *bdst.grey = MUL(fd, *bdst.grey, t); | |
+ bsrc.grey += bsrc.delta; | |
+ bdst.grey += bdst.delta; | |
+ }else{ | |
+ if(q){ | |
+ *bdst.rgba = MUL0123(fd, *bdst.rgba, s, t); | |
+ bsrc.rgba++; | |
+ bdst.rgba++; | |
+ bsrc.alpha += sadelta; | |
+ bmask.alpha += bmask.delta; | |
+ continue; | |
+ } | |
+ *bdst.red = MUL(fd, *bdst.red, t); | |
+ *bdst.grn = MUL(fd, *bdst.grn, t); | |
+ *bdst.blu = MUL(fd, *bdst.blu, t); | |
+ bsrc.red += bsrc.delta; | |
+ bsrc.blu += bsrc.delta; | |
+ bsrc.grn += bsrc.delta; | |
+ bdst.red += bdst.delta; | |
+ bdst.blu += bdst.delta; | |
+ bdst.grn += bdst.delta; | |
+ } | |
+ if(bdst.alpha != &ones){ | |
+ *bdst.alpha = MUL(fd, *bdst.alpha, t); | |
+ bdst.alpha += bdst.delta; | |
+ } | |
+ bmask.alpha += bmask.delta; | |
+ bsrc.alpha += sadelta; | |
+ } | |
+ return obdst; | |
+} | |
+ | |
+static Buffer | |
+alphacalc2810(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op) | |
+{ | |
+ Buffer obdst; | |
+ int fs, sadelta; | |
+ int i, ma, da, q; | |
+ u32int s, t; | |
+ | |
+ obdst = bdst; | |
+ sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta; | |
+ q = bsrc.delta == 4 && bdst.delta == 4; | |
+ | |
+ for(i=0; i<dx; i++){ | |
+ ma = *bmask.alpha; | |
+ da = *bdst.alpha; | |
+ if(op == SoutD) | |
+ da = 255-da; | |
+ fs = ma; | |
+ if(op != S) | |
+ fs = MUL(fs, da, t); | |
+ | |
+ if(grey){ | |
+ *bdst.grey = MUL(fs, *bsrc.grey, t); | |
+ bsrc.grey += bsrc.delta; | |
+ bdst.grey += bdst.delta; | |
+ }else{ | |
+ if(q){ | |
+ *bdst.rgba = MUL0123(fs, *bsrc.rgba, s, t); | |
+ bsrc.rgba++; | |
+ bdst.rgba++; | |
+ bmask.alpha += bmask.delta; | |
+ bdst.alpha += bdst.delta; | |
+ continue; | |
+ } | |
+ *bdst.red = MUL(fs, *bsrc.red, t); | |
+ *bdst.grn = MUL(fs, *bsrc.grn, t); | |
+ *bdst.blu = MUL(fs, *bsrc.blu, t); | |
+ bsrc.red += bsrc.delta; | |
+ bsrc.blu += bsrc.delta; | |
+ bsrc.grn += bsrc.delta; | |
+ bdst.red += bdst.delta; | |
+ bdst.blu += bdst.delta; | |
+ bdst.grn += bdst.delta; | |
+ } | |
+ if(bdst.alpha != &ones){ | |
+ *bdst.alpha = MUL(fs, *bsrc.alpha, t); | |
+ bdst.alpha += bdst.delta; | |
+ } | |
+ bmask.alpha += bmask.delta; | |
+ bsrc.alpha += sadelta; | |
+ } | |
+ return obdst; | |
+} | |
+ | |
+static Buffer | |
+alphacalc3679(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op) | |
+{ | |
+ Buffer obdst; | |
+ int fs, fd, sadelta; | |
+ int i, sa, ma, da, q; | |
+ u32int s, t, u, v; | |
+ | |
+ obdst = bdst; | |
+ sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta; | |
+ q = bsrc.delta == 4 && bdst.delta == 4; | |
+ | |
+ for(i=0; i<dx; i++){ | |
+ sa = *bsrc.alpha; | |
+ ma = *bmask.alpha; | |
+ da = *bdst.alpha; | |
+ if(op == SatopD) | |
+ fs = MUL(ma, da, t); | |
+ else | |
+ fs = MUL(ma, 255-da, t); | |
+ if(op == DoverS) | |
+ fd = 255; | |
+ else{ | |
+ fd = MUL(sa, ma, t); | |
+ if(op != DatopS) | |
+ fd = 255-fd; | |
+ } | |
+ | |
+ if(grey){ | |
+ *bdst.grey = MUL(fs, *bsrc.grey, s)+MUL(fd, *bdst.grey… | |
+ bsrc.grey += bsrc.delta; | |
+ bdst.grey += bdst.delta; | |
+ }else{ | |
+ if(q){ | |
+ *bdst.rgba = MUL0123(fs, *bsrc.rgba, s, t)+MUL… | |
+ bsrc.rgba++; | |
+ bdst.rgba++; | |
+ bsrc.alpha += sadelta; | |
+ bmask.alpha += bmask.delta; | |
+ bdst.alpha += bdst.delta; | |
+ continue; | |
+ } | |
+ *bdst.red = MUL(fs, *bsrc.red, s)+MUL(fd, *bdst.red, t… | |
+ *bdst.grn = MUL(fs, *bsrc.grn, s)+MUL(fd, *bdst.grn, t… | |
+ *bdst.blu = MUL(fs, *bsrc.blu, s)+MUL(fd, *bdst.blu, t… | |
+ bsrc.red += bsrc.delta; | |
+ bsrc.blu += bsrc.delta; | |
+ bsrc.grn += bsrc.delta; | |
+ bdst.red += bdst.delta; | |
+ bdst.blu += bdst.delta; | |
+ bdst.grn += bdst.delta; | |
+ } | |
+ if(bdst.alpha != &ones){ | |
+ *bdst.alpha = MUL(fs, sa, s)+MUL(fd, da, t); | |
+ bdst.alpha += bdst.delta; | |
+ } | |
+ bmask.alpha += bmask.delta; | |
+ bsrc.alpha += sadelta; | |
+ } | |
+ return obdst; | |
+} | |
+ | |
+static Buffer | |
+alphacalc5(Buffer bdst, Buffer b1, Buffer b2, int dx, int grey, int op) | |
+{ | |
+ USED(dx); | |
+ USED(grey); | |
+ USED(op); | |
+ return bdst; | |
+} | |
+ | |
+static Buffer | |
+alphacalc11(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op) | |
+{ | |
+ Buffer obdst; | |
+ int fd, sadelta; | |
+ int i, sa, ma, q; | |
+ u32int s, t, u, v; | |
+ | |
+ USED(op); | |
+ obdst = bdst; | |
+ sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta; | |
+ q = bsrc.delta == 4 && bdst.delta == 4; | |
+ | |
+ for(i=0; i<dx; i++){ | |
+ sa = *bsrc.alpha; | |
+ ma = *bmask.alpha; | |
+ fd = 255-MUL(sa, ma, t); | |
+ | |
+ if(grey){ | |
+ *bdst.grey = MUL(ma, *bsrc.grey, s)+MUL(fd, *bdst.grey… | |
+ bsrc.grey += bsrc.delta; | |
+ bdst.grey += bdst.delta; | |
+ }else{ | |
+ if(q){ | |
+ *bdst.rgba = MUL0123(ma, *bsrc.rgba, s, t)+MUL… | |
+ bsrc.rgba++; | |
+ bdst.rgba++; | |
+ bsrc.alpha += sadelta; | |
+ bmask.alpha += bmask.delta; | |
+ continue; | |
+ } | |
+ *bdst.red = MUL(ma, *bsrc.red, s)+MUL(fd, *bdst.red, t… | |
+ *bdst.grn = MUL(ma, *bsrc.grn, s)+MUL(fd, *bdst.grn, t… | |
+ *bdst.blu = MUL(ma, *bsrc.blu, s)+MUL(fd, *bdst.blu, t… | |
+ bsrc.red += bsrc.delta; | |
+ bsrc.blu += bsrc.delta; | |
+ bsrc.grn += bsrc.delta; | |
+ bdst.red += bdst.delta; | |
+ bdst.blu += bdst.delta; | |
+ bdst.grn += bdst.delta; | |
+ } | |
+ if(bdst.alpha != &ones){ | |
+ *bdst.alpha = MUL(ma, sa, s)+MUL(fd, *bdst.alpha, t); | |
+ bdst.alpha += bdst.delta; | |
+ } | |
+ bmask.alpha += bmask.delta; | |
+ bsrc.alpha += sadelta; | |
+ } | |
+ return obdst; | |
+} | |
+ | |
+/* | |
+not used yet | |
+source and mask alpha 1 | |
+static Buffer | |
+alphacalcS0(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op) | |
+{ | |
+ Buffer obdst; | |
+ int i; | |
+ | |
+ USED(op); | |
+ obdst = bdst; | |
+ if(bsrc.delta == bdst.delta){ | |
+ memmove(bdst.rgba, bsrc.rgba, dx*bdst.delta); | |
+ return obdst; | |
+ } | |
+ for(i=0; i<dx; i++){ | |
+ if(grey){ | |
+ *bdst.grey = *bsrc.grey; | |
+ bsrc.grey += bsrc.delta; | |
+ bdst.grey += bdst.delta; | |
+ }else{ | |
+ *bdst.red = *bsrc.red; | |
+ *bdst.grn = *bsrc.grn; | |
+ *bdst.blu = *bsrc.blu; | |
+ bsrc.red += bsrc.delta; | |
+ bsrc.blu += bsrc.delta; | |
+ bsrc.grn += bsrc.delta; | |
+ bdst.red += bdst.delta; | |
+ bdst.blu += bdst.delta; | |
+ bdst.grn += bdst.delta; | |
+ } | |
+ if(bdst.alpha != &ones){ | |
+ *bdst.alpha = 255; | |
+ bdst.alpha += bdst.delta; | |
+ } | |
+ } | |
+ return obdst; | |
+} | |
+*/ | |
+ | |
+/* source alpha 1 */ | |
+static Buffer | |
+alphacalcS(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op) | |
+{ | |
+ Buffer obdst; | |
+ int fd; | |
+ int i, ma; | |
+ u32int s, t; | |
+ | |
+ USED(op); | |
+ obdst = bdst; | |
+ | |
+ for(i=0; i<dx; i++){ | |
+ ma = *bmask.alpha; | |
+ fd = 255-ma; | |
+ | |
+ if(grey){ | |
+ *bdst.grey = MUL(ma, *bsrc.grey, s)+MUL(fd, *bdst.grey… | |
+ bsrc.grey += bsrc.delta; | |
+ bdst.grey += bdst.delta; | |
+ }else{ | |
+ *bdst.red = MUL(ma, *bsrc.red, s)+MUL(fd, *bdst.red, t… | |
+ *bdst.grn = MUL(ma, *bsrc.grn, s)+MUL(fd, *bdst.grn, t… | |
+ *bdst.blu = MUL(ma, *bsrc.blu, s)+MUL(fd, *bdst.blu, t… | |
+ bsrc.red += bsrc.delta; | |
+ bsrc.blu += bsrc.delta; | |
+ bsrc.grn += bsrc.delta; | |
+ bdst.red += bdst.delta; | |
+ bdst.blu += bdst.delta; | |
+ bdst.grn += bdst.delta; | |
+ } | |
+ if(bdst.alpha != &ones){ | |
+ *bdst.alpha = ma+MUL(fd, *bdst.alpha, t); | |
+ bdst.alpha += bdst.delta; | |
+ } | |
+ bmask.alpha += bmask.delta; | |
+ } | |
+ return obdst; | |
+} | |
+ | |
+static Buffer | |
+boolcalc14(Buffer bdst, Buffer b1, Buffer bmask, int dx, int grey, int op) | |
+{ | |
+ Buffer obdst; | |
+ int i, ma, zero; | |
+ | |
+ obdst = bdst; | |
+ | |
+ for(i=0; i<dx; i++){ | |
+ ma = *bmask.alpha; | |
+ zero = ma ? op == DoutS : op == DinS; | |
+ | |
+ if(grey){ | |
+ if(zero) | |
+ *bdst.grey = 0; | |
+ bdst.grey += bdst.delta; | |
+ }else{ | |
+ if(zero) | |
+ *bdst.red = *bdst.grn = *bdst.blu = 0; | |
+ bdst.red += bdst.delta; | |
+ bdst.blu += bdst.delta; | |
+ bdst.grn += bdst.delta; | |
+ } | |
+ bmask.alpha += bmask.delta; | |
+ if(bdst.alpha != &ones){ | |
+ if(zero) | |
+ *bdst.alpha = 0; | |
+ bdst.alpha += bdst.delta; | |
+ } | |
+ } | |
+ return obdst; | |
+} | |
+ | |
+static Buffer | |
+boolcalc236789(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int o… | |
+{ | |
+ Buffer obdst; | |
+ int fs, fd; | |
+ int i, ma, da, zero; | |
+ u32int s, t; | |
+ | |
+ obdst = bdst; | |
+ zero = !(op&1); | |
+ | |
+ for(i=0; i<dx; i++){ | |
+ ma = *bmask.alpha; | |
+ da = *bdst.alpha; | |
+ fs = da; | |
+ if(op&2) | |
+ fs = 255-da; | |
+ fd = 0; | |
+ if(op&4) | |
+ fd = 255; | |
+ | |
+ if(grey){ | |
+ if(ma) | |
+ *bdst.grey = MUL(fs, *bsrc.grey, s)+MUL(fd, *b… | |
+ else if(zero) | |
+ *bdst.grey = 0; | |
+ bsrc.grey += bsrc.delta; | |
+ bdst.grey += bdst.delta; | |
+ }else{ | |
+ if(ma){ | |
+ *bdst.red = MUL(fs, *bsrc.red, s)+MUL(fd, *bds… | |
+ *bdst.grn = MUL(fs, *bsrc.grn, s)+MUL(fd, *bds… | |
+ *bdst.blu = MUL(fs, *bsrc.blu, s)+MUL(fd, *bds… | |
+ } | |
+ else if(zero) | |
+ *bdst.red = *bdst.grn = *bdst.blu = 0; | |
+ bsrc.red += bsrc.delta; | |
+ bsrc.blu += bsrc.delta; | |
+ bsrc.grn += bsrc.delta; | |
+ bdst.red += bdst.delta; | |
+ bdst.blu += bdst.delta; | |
+ bdst.grn += bdst.delta; | |
+ } | |
+ bmask.alpha += bmask.delta; | |
+ if(bdst.alpha != &ones){ | |
+ if(ma) | |
+ *bdst.alpha = fs+MUL(fd, da, t); | |
+ else if(zero) | |
+ *bdst.alpha = 0; | |
+ bdst.alpha += bdst.delta; | |
+ } | |
+ } | |
+ return obdst; | |
+} | |
+ | |
+static Buffer | |
+boolcalc1011(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op) | |
+{ | |
+ Buffer obdst; | |
+ int i, ma, zero; | |
+ | |
+ obdst = bdst; | |
+ zero = !(op&1); | |
+ | |
+ for(i=0; i<dx; i++){ | |
+ ma = *bmask.alpha; | |
+ | |
+ if(grey){ | |
+ if(ma) | |
+ *bdst.grey = *bsrc.grey; | |
+ else if(zero) | |
+ *bdst.grey = 0; | |
+ bsrc.grey += bsrc.delta; | |
+ bdst.grey += bdst.delta; | |
+ }else{ | |
+ if(ma){ | |
+ *bdst.red = *bsrc.red; | |
+ *bdst.grn = *bsrc.grn; | |
+ *bdst.blu = *bsrc.blu; | |
+ } | |
+ else if(zero) | |
+ *bdst.red = *bdst.grn = *bdst.blu = 0; | |
+ bsrc.red += bsrc.delta; | |
+ bsrc.blu += bsrc.delta; | |
+ bsrc.grn += bsrc.delta; | |
+ bdst.red += bdst.delta; | |
+ bdst.blu += bdst.delta; | |
+ bdst.grn += bdst.delta; | |
+ } | |
+ bmask.alpha += bmask.delta; | |
+ if(bdst.alpha != &ones){ | |
+ if(ma) | |
+ *bdst.alpha = 255; | |
+ else if(zero) | |
+ *bdst.alpha = 0; | |
+ bdst.alpha += bdst.delta; | |
+ } | |
+ } | |
+ return obdst; | |
+} | |
+/* | |
+ * Replicated cached scan line read. Call the function listed in the Param, | |
+ * but cache the result so that for replicated images we only do the work once. | |
+ */ | |
+static Buffer | |
+replread(Param *p, uchar *s, int y) | |
+{ | |
+ Buffer *b; | |
+ | |
+ USED(s); | |
+ b = &p->bcache[y]; | |
+ if((p->bfilled & (1<<y)) == 0){ | |
+ p->bfilled |= 1<<y; | |
+ *b = p->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 = (u32int*)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… | |
+ bits = *r++; | |
+ nbits = 8; | |
+ if(i=x&(npack-1)){ | |
+DBG print("throwaway %d...", i); | |
+ bits <<= depth*i; | |
+ nbits -= depth*i; | |
+ } | |
+ for(i=0; i<n; i++){ | |
+ if(nbits == 0){ | |
+DBG print("(%.2ux)...", *r); | |
+ bits = *r++; | |
+ nbits = 8; | |
+ } | |
+ *w++ = repl[bits>>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<n; i++){ | |
+ if(nbits == 0){ | |
+ bits = *r++; | |
+ nbits = 8; | |
+ } | |
+ *w++ = repl[bits>>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 i… | |
+ 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; | |
+ u32int 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<ex; x++){ | |
+ bits <<= depth; | |
+DBG print(" %x", *r); | |
+ bits |= (*r++ >> sh); | |
+ nbits += depth; | |
+ if(nbits == 8){ | |
+ *w++ = bits; | |
+ nbits = 0; | |
+ } | |
+ } | |
+ | |
+ if(nbits){ | |
+ sh = 8-nbits; | |
+ bits <<= sh; | |
+ bits |= *w & ((1<<sh)-1); | |
+ *w = bits; | |
+ } | |
+DBG print("\n"); | |
+ return; | |
+} | |
+#undef DBG | |
+ | |
+static Buffer | |
+readcmap(Param *p, uchar *buf, int y) | |
+{ | |
+ Buffer b; | |
+ int a, convgrey, copyalpha, dx, i, m; | |
+ uchar *q, *cmap, *begin, *end, *r, *w; | |
+ | |
+ begin = p->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; i<dx; i++){ | |
+ *w++ = r[a]; | |
+ q = cmap+r[m]*3; | |
+ r += 2; | |
+ if(r == end) | |
+ r = begin; | |
+ if(convgrey){ | |
+ *w++ = RGB2K(q[0], q[1], q[2]); | |
+ }else{ | |
+ *w++ = q[2]; /* blue */ | |
+ *w++ = q[1]; /* green */ | |
+ *w++ = q[0]; /* red */ | |
+ } | |
+ } | |
+ }else{ | |
+ b.alpha = &ones; | |
+ for(i=0; i<dx; i++){ | |
+ q = cmap+*r++*3; | |
+ if(r == end) | |
+ r = begin; | |
+ if(convgrey){ | |
+ *w++ = RGB2K(q[0], q[1], q[2]); | |
+ }else{ | |
+ *w++ = q[2]; /* blue */ | |
+ *w++ = q[1]; /* green */ | |
+ *w++ = q[0]; /* red */ | |
+ } | |
+ } | |
+ } | |
+ | |
+ b.rgba = (u32int*)(buf-copyalpha); | |
+ | |
+ if(convgrey){ | |
+ b.grey = buf; | |
+ b.red = b.blu = b.grn = buf; | |
+ b.delta = 1+copyalpha; | |
+ }else{ | |
+ b.blu = buf; | |
+ b.grn = buf+1; | |
+ b.red = buf+2; | |
+ b.grey = nil; | |
+ b.delta = 3+copyalpha; | |
+ } | |
+ return b; | |
+} | |
+ | |
+static void | |
+writecmap(Param *p, uchar *w, Buffer src) | |
+{ | |
+ uchar *cmap, *red, *grn, *blu; | |
+ int i, dx, delta; | |
+ | |
+ cmap = p->img->cmap->rgb2cmap; | |
+ | |
+ delta = src.delta; | |
+ red= src.red; | |
+ grn = src.grn; | |
+ blu = src.blu; | |
+ | |
+ dx = p->dx; | |
+ for(i=0; i<dx; i++, red+=delta, grn+=delta, blu+=delta) | |
+ *w++ = cmap[(*red>>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; | |
+ u32int 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, alph… | |
+ /* 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 = (u32int*)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<dx; i++){ | |
+ u = r[0] | (r[1]<<8) | (r[2]<<16) | (r[3]<<24); | |
+ if(copyalpha) { | |
+ *w++ = arepl[(u>>img->shift[CAlpha]) & img->mask[CAlph… | |
+DBG print("a %x\n", w[-1]); | |
+ } | |
+ | |
+ if(isgrey) | |
+ *w++ = krepl[(u >> img->shift[CGrey]) & img->mask[CGre… | |
+ else if(!alphaonly){ | |
+ ured = rrepl[(u >> img->shift[CRed]) & img->mask[CRed]… | |
+ ugrn = grepl[(u >> img->shift[CGreen]) & img->mask[CGr… | |
+ ublu = brepl[(u >> img->shift[CBlue]) & img->mask[CBlu… | |
+ 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->m… | |
+ *w++ = grepl[(u >> img->shift[CGreen]) & img->… | |
+ *w++ = rrepl[(u >> img->shift[CRed]) & img->ma… | |
+ } | |
+ } | |
+ r += nb; | |
+ if(r == end) | |
+ r = begin; | |
+ } | |
+ | |
+ b.alpha = copyalpha ? buf : &ones; | |
+ b.rgba = (u32int*)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; | |
+ u32int 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<<img->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<dx; i++){ | |
+ u = w[0] | (w[1]<<8) | (w[2]<<16) | (w[3]<<24); | |
+DBG print("u %.8lux...", u); | |
+ u &= mask; | |
+DBG print("&mask %.8lux...", u); | |
+ if(isgrey){ | |
+ u |= ((*grey >> (8-img->nbits[CGrey])) & img->mask[CGr… | |
+DBG print("|grey %.8lux...", u); | |
+ grey += delta; | |
+ }else{ | |
+ u |= ((*red >> (8-img->nbits[CRed])) & img->mask[CRed]… | |
+ u |= ((*grn >> (8-img->nbits[CGreen])) & img->mask[CGr… | |
+ u |= ((*blu >> (8-img->nbits[CBlue])) & img->mask[CBlu… | |
+ red += delta; | |
+ grn += delta; | |
+ blu += delta; | |
+DBG print("|rgb %.8lux...", u); | |
+ } | |
+ | |
+ if(isalpha){ | |
+ u |= ((*alpha >> (8-img->nbits[CAlpha])) & img->mask[C… | |
+ 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 = (u32int*)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; | |
+ u32int *r, *w, *ew; | |
+ | |
+ USED(i); | |
+ USED(o); | |
+ m = bmask.grey; | |
+ w = (u32int*)bdst.red; | |
+ r = (u32int*)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(w<ew) | |
+ *w++ = *r++; | |
+ } | |
+ | |
+ b.red = buf; | |
+ b.blu = b.grn = b.grey = b.alpha = nil; | |
+ b.rgba = (u32int*)buf; | |
+ b.delta = 0; | |
+ | |
+ return b; | |
+} | |
+ | |
+static Readfn* | |
+convfn(Memimage *dst, Param *dpar, Memimage *src, Param *spar) | |
+{ | |
+ if(dst->chan == src->chan && !(src->flags&Frepl)){ | |
+//if(drawdebug) iprint("readptr..."); | |
+ return readptr; | |
+ } | |
+ | |
+ if(dst->chan==CMAP8 && (src->chan==GREY1||src->chan==GREY2||src->chan=… | |
+ /* cheat because we know the replicated value is exactly the c… | |
+//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; | |
+} | |
+ | |
+/* | |
+ * Do NOT call this directly. pixelbits is a wrapper | |
+ * around this that fetches the bits from the X server | |
+ * when necessary. | |
+ */ | |
+u32int | |
+_pixelbits(Memimage *i, Point pt) | |
+{ | |
+ uchar *p; | |
+ u32int 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<<bpp)-1; | |
+ break; | |
+ case 8: | |
+ val = p[0]; | |
+ break; | |
+ case 16: | |
+ val = p[0]|(p[1]<<8); | |
+ break; | |
+ case 24: | |
+ val = p[0]|(p[1]<<8)|(p[2]<<16); | |
+ break; | |
+ case 32: | |
+ val = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24); | |
+ break; | |
+ } | |
+ while(bpp<32){ | |
+ val |= val<<bpp; | |
+ bpp *= 2; | |
+ } | |
+ return val; | |
+} | |
+ | |
+static Calcfn* | |
+boolcopyfn(Memimage *img, Memimage *mask) | |
+{ | |
+ if(mask->flags&Frepl && Dx(mask->r)==1 && Dy(mask->r)==1 && pixelbits(… | |
+ 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 | |
+memsets(void *vp, ushort val, int n) | |
+{ | |
+ ushort *p, *ep; | |
+ | |
+ p = vp; | |
+ ep = p+n; | |
+ while(p<ep) | |
+ *p++ = val; | |
+} | |
+ | |
+static void | |
+memsetl(void *vp, u32int val, int n) | |
+{ | |
+ u32int *p, *ep; | |
+ | |
+ p = vp; | |
+ ep = p+n; | |
+ while(p<ep) | |
+ *p++ = val; | |
+} | |
+ | |
+static void | |
+memset24(void *vp, u32int val, int n) | |
+{ | |
+ uchar *p, *ep; | |
+ uchar a,b,c; | |
+ | |
+ p = vp; | |
+ ep = p+3*n; | |
+ a = val; | |
+ b = val>>8; | |
+ c = val>>16; | |
+ while(p<ep){ | |
+ *p++ = a; | |
+ *p++ = b; | |
+ *p++ = c; | |
+ } | |
+} | |
+ | |
+u32int | |
+_imgtorgba(Memimage *img, u32int val) | |
+{ | |
+ uchar r, g, b, a; | |
+ int nb, ov, v; | |
+ u32int chan; | |
+ uchar *p; | |
+ | |
+ a = 0xFF; | |
+ r = g = b = 0xAA; /* garbage */ | |
+ for(chan=img->chan; chan; chan>>=8){ | |
+ nb = NBITS(chan); | |
+ ov = v = val&((1<<nb)-1); | |
+ val >>= nb; | |
+ | |
+ while(nb < 8){ | |
+ v |= v<<nb; | |
+ nb *= 2; | |
+ } | |
+ 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; | |
+} | |
+ | |
+u32int | |
+_rgbatoimg(Memimage *img, u32int rgba) | |
+{ | |
+ u32int chan; | |
+ int d, nb; | |
+ u32int 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))<<d; | |
+ break; | |
+ case CGreen: | |
+ v |= (g>>(8-nb))<<d; | |
+ break; | |
+ case CBlue: | |
+ v |= (b>>(8-nb))<<d; | |
+ break; | |
+ case CAlpha: | |
+ v |= (a>>(8-nb))<<d; | |
+ break; | |
+ case CMap: | |
+ p = img->cmap->rgb2cmap; | |
+ m = p[(r>>4)*256+(g>>4)*16+(b>>4)]; | |
+ v |= (m>>(8-nb))<<d; | |
+ break; | |
+ case CGrey: | |
+ m = RGB2K(r,g,b); | |
+ v |= (m>>(8-nb))<<d; | |
+ break; | |
+ } | |
+ d += nb; | |
+ } | |
+// print("rgba2img %.8lux = %.*lux\n", rgba, 2*d/8, v); | |
+ return v; | |
+} | |
+ | |
+#define DBG if(0) | |
+static int | |
+memoptdraw(Memdrawparam *par) | |
+{ | |
+ int m, y, dy, dx, op; | |
+ u32int v; | |
+ Memimage *src; | |
+ Memimage *dst; | |
+ | |
+ dx = Dx(par->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 con… | |
+ * destination format and just replicate with memset. | |
+ */ | |
+ m = Simplesrc|Simplemask|Fullmask; | |
+ if((par->state&m)==m && (par->srgba&0xFF) == 0xFF && (op ==S || op == … | |
+ 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(u32int); | |
+ 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<<d); | |
+ ppb = 8/dst->depth; /* pixels per byte */ | |
+ m = ppb-1; | |
+ /* left edge */ | |
+ np = par->r.min.x&m; /* no. pixels unus… | |
+ dx -= (ppb-np); | |
+ nb = 8 - np * dst->depth; /* no. bits u… | |
+ lm = (1<<nb)-1; | |
+DBG print("np %d x %d nb %d lm %ux ppb %d m %ux\n", np, par->r.min.x, nb, lm, … | |
+ | |
+ /* right edge */ | |
+ np = par->r.max.x&m; /* no. pixels used on left… | |
+ dx -= np; | |
+ nb = 8 - np * dst->depth; /* no. bits u… | |
+ rm = ~((1<<nb)-1); | |
+DBG print("np %d x %d nb %d rm %ux ppb %d m %ux\n", np, par->r.max.x, nb, rm, … | |
+ | |
+DBG print("dx %d Dx %d\n", dx, Dx(par->r)); | |
+ /* lm, rm are masks that are 1 where we should touch t… | |
+ if(dx < 0){ /* just one byte */ | |
+ lm &= rm; | |
+ for(y=0; y<dy; y++, dp+=dwid) | |
+ *dp ^= (v ^ *dp) & lm; | |
+ }else if(dx == 0){ /* no full bytes */ | |
+ if(lm) | |
+ dwid--; | |
+ | |
+ for(y=0; y<dy; y++, dp+=dwid){ | |
+ if(lm){ | |
+DBG print("dp %p v %lux lm %ux (v ^ *dp) & lm %lux\n", dp, v, lm, (v^*dp)&lm); | |
+ *dp ^= (v ^ *dp) & lm; | |
+ dp++; | |
+ } | |
+ *dp ^= (v ^ *dp) & rm; | |
+ } | |
+ }else{ /* full bytes in middle */ | |
+ dx /= ppb; | |
+ if(lm) | |
+ dwid--; | |
+ dwid -= dx; | |
+ | |
+ for(y=0; y<dy; y++, dp+=dwid){ | |
+ if(lm){ | |
+ *dp ^= (v ^ *dp) & lm; | |
+ dp++; | |
+ } | |
+ memset(dp, v, dx); | |
+ dp += dx; | |
+ *dp ^= (v ^ *dp) & rm; | |
+ } | |
+ } | |
+ return 1; | |
+ case 8: | |
+ for(y=0; y<dy; y++, dp+=dwid) | |
+ memset(dp, v, dx); | |
+ return 1; | |
+ case 16: | |
+ p[0] = v; /* make little endian */ | |
+ p[1] = v>>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<dy; y++, dp+=dwid) | |
+ memsets(dp, v, dx); | |
+ return 1; | |
+ case 24: | |
+ for(y=0; y<dy; y++, dp+=dwid) | |
+ memset24(dp, v, dx); | |
+ return 1; | |
+ case 32: | |
+ p[0] = v; /* make little endian */ | |
+ p[1] = v>>8; | |
+ p[2] = v>>16; | |
+ p[3] = v>>24; | |
+ v = *(u32int*)p; | |
+ for(y=0; y<dy; y++, dp+=dwid) | |
+ memsetl(dp, v, dx); | |
+ return 1; | |
+ default: | |
+ assert(0 /* bad dest depth in memoptdraw */); | |
+ } | |
+ } | |
+ | |
+ /* | |
+ * 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, memmove suffices. | |
+ */ | |
+ m = Simplemask|Fullmask; | |
+ if((par->state&(m|Replsrc))==m && src->depth >= 8 | |
+ && src->chan == dst->chan && !(src->flags&Falpha) && (op == S || op ==… | |
+ uchar *sp, *dp; | |
+ long swid, dwid, nb; | |
+ int dir; | |
+ | |
+ if(src->data == dst->data && byteaddr(dst, par->r.min) > bytea… | |
+ dir = -1; | |
+ else | |
+ dir = 1; | |
+ | |
+ swid = src->width*sizeof(u32int); | |
+ dwid = dst->width*sizeof(u32int); | |
+ 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; y<dy; y++, sp+=swid, dp+=dwid) | |
+ memmove(dp, sp, nb); | |
+ return 1; | |
+ } | |
+ | |
+ /* | |
+ * If we have a 1-bit mask, 1-bit source, and 1-bit destination, and | |
+ * they're all bit aligned, we can just use bit operators. This happe… | |
+ * when we're manipulating boolean masks, e.g. in the arc code. | |
+ */ | |
+ if((par->state&(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.m… | |
+ 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(u32int); | |
+ dwid = dst->width*sizeof(u32int); | |
+ mwid = par->mask->width*sizeof(u32int); | |
+ | |
+ if(src->data == dst->data && byteaddr(dst, par->r.min) > bytea… | |
+ 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; y<dy; y++){ | |
+ *dp ^= (*dp ^ *sp) & *mp & lm; | |
+ dp += dwid; | |
+ sp += swid; | |
+ mp += mwid; | |
+ } | |
+ return 1; | |
+ } | |
+ | |
+ dx /= 8; | |
+ if(dir == 1){ | |
+ i = (lm!=0)+dx+(rm!=0); | |
+ mwid -= i; | |
+ swid -= i; | |
+ dwid -= i; | |
+ for(y=0; y<dy; y++, dp+=dwid, sp+=swid, mp+=mwid){ | |
+ if(lm){ | |
+ *dp ^= (*dp ^ *sp++) & *mp++ & lm; | |
+ dp++; | |
+ } | |
+ for(x=0; x<dx; x++){ | |
+ *dp ^= (*dp ^ *sp++) & *mp++; | |
+ dp++; | |
+ } | |
+ if(rm){ | |
+ *dp ^= (*dp ^ *sp++) & *mp++ & rm; | |
+ dp++; | |
+ } | |
+ } | |
+ return 1; | |
+ }else{ | |
+ /* dir == -1 */ | |
+ i = (lm!=0)+dx+(rm!=0); | |
+ dp += dwid*(dy-1)+i-1; | |
+ sp += swid*(dy-1)+i-1; | |
+ mp += mwid*(dy-1)+i-1; | |
+ dwid = -dwid+i; | |
+ swid = -swid+i; | |
+ mwid = -mwid+i; | |
+ for(y=0; y<dy; y++, dp+=dwid, sp+=swid, mp+=mwid){ | |
+ if(rm){ | |
+ *dp ^= (*dp ^ *sp--) & *mp-- & rm; | |
+ dp--; | |
+ } | |
+ for(x=0; x<dx; x++){ | |
+ *dp ^= (*dp ^ *sp--) & *mp--; | |
+ dp--; | |
+ } | |
+ if(lm){ | |
+ *dp ^= (*dp ^ *sp--) & *mp-- & lm; | |
+ dp--; | |
+ } | |
+ } | |
+ } | |
+ return 1; | |
+ } | |
+ return 0; | |
+} | |
+#undef DBG | |
+ | |
+/* | |
+ * Boolean character drawing. | |
+ * Solid opaque color through a 1-bit greyscale mask. | |
+ */ | |
+#define DBG if(0) | |
+static int | |
+chardraw(Memdrawparam *par) | |
+{ | |
+ u32int bits; | |
+ int i, ddepth, dy, dx, x, bx, ex, y, npack, bsh, depth, op; | |
+ u32int v, maskwid, dstwid; | |
+ uchar *wp, *rp, *q, *wc; | |
+ ushort *ws; | |
+ u32int *wl; | |
+ uchar sp[4]; | |
+ Rectangle r, mr; | |
+ Memimage *mask, *src, *dst; | |
+ | |
+if(0) if(drawdebug) iprint("chardraw? mf %lux md %d sf %lux dxs %d dys %d dd %… | |
+ par->mask->flags, par->mask->depth, par->src->flags, | |
+ Dx(par->src->r), Dy(par->src->r), par->dst->depth, par->dst->d… | |
+ | |
+ 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=… | |
+ || op != SoverD) | |
+ return 0; | |
+ | |
+//if(drawdebug) iprint("chardraw..."); | |
+ | |
+ depth = mask->depth; | |
+ maskwid = mask->width*sizeof(u32int); | |
+ rp = byteaddr(mask, mr.min); | |
+ npack = 8/depth; | |
+ bsh = (mr.min.x % npack) * depth; | |
+ | |
+ wp = byteaddr(dst, r.min); | |
+ dstwid = dst->width*sizeof(u32int); | |
+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; y<dy; y++, rp+=maskwid, wp+=dstwid){ | |
+ q = rp; | |
+ if(bsh) | |
+ bits = *q++; | |
+ switch(ddepth){ | |
+ case 8: | |
+//if(drawdebug) iprint("8loop..."); | |
+ wc = wp; | |
+ for(x=bx; x>ex; 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 = (u32int*)wp; | |
+ v = *(u32int*)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. | |
+ * | |
+u32int | |
+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, u32int val) | |
+{ | |
+ u32int 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; y<i->r.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 = *(u32int*)p; | |
+ memsetl(wordaddr(i, i->r.min), bits, i->width*Dy(i->r)); | |
+ break; | |
+ } | |
+} | |
+ | |
diff --git a/src/libdraw/md-drawtest.c b/src/libdraw/md-drawtest.c | |
t@@ -0,0 +1,1004 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <bio.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+#define DBG if(0) | |
+#define RGB2K(r,g,b) ((299*((u32int)(r))+587*((u32int)(g))+114*((u32int… | |
+ | |
+/* | |
+ * 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, u32int nv); | |
+u32int 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; | |
+u32int *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, … | |
+ 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, … | |
+ 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(u32int)*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*, u32int 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*, u32int 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*, u32int); | |
+ int npr, x, y, nb, bpp; | |
+ u32int v, mask; | |
+ Rectangle r; | |
+ | |
+ fmt = nil; | |
+ arg = nil; | |
+ switch(img->depth){ | |
+ case 1: | |
+ case 2: | |
+ case 4: | |
+ fmt = (void(*)(Biobuf*,char*,u32int))Bprint; | |
+ arg = "%.1ux"; | |
+ break; | |
+ case 8: | |
+ fmt = (void(*)(Biobuf*,char*,u32int))Bprint; | |
+ arg = "%.2ux"; | |
+ break; | |
+ case 16: | |
+ arg = nil; | |
+ if(img->chan == RGB16) | |
+ fmt = Bprintr5g6b5; | |
+ else{ | |
+ fmt = (void(*)(Biobuf*,char*,u32int))Bprint; | |
+ arg = "%.4ux"; | |
+ } | |
+ break; | |
+ case 24: | |
+ fmt = (void(*)(Biobuf*,char*,u32int))Bprint; | |
+ arg = "%.6lux"; | |
+ break; | |
+ case 32: | |
+ fmt = (void(*)(Biobuf*,char*,u32int))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.… | |
+ mask = (1ULL<<bpp)-1; | |
+// for(y=r.min.y; y<r.max.y; y++){ | |
+ for(y=0; y<Yrange; y++){ | |
+ nb = 0; | |
+ v = 0; | |
+ p = data+(byteaddr(img, Pt(0,y))-(uchar*)img->data->bdata); | |
+ Bprint(&b, "%-4d\t", y); | |
+// for(x=r.min.x; x<r.max.x; x++){ | |
+ for(x=0; x<Xrange; x++){ | |
+ if(x==0) | |
+ Bprint(&b, "\t"); | |
+ | |
+ if(x != 0 && (x%8)==0) | |
+ Bprint(&b, " "); | |
+ | |
+ npr = 0; | |
+ if(x==labelpt.x && y==labelpt.y){ | |
+ Bprint(&b, "*"); | |
+ npr++; | |
+ } | |
+ if(npr == 0) | |
+ Bprint(&b, " "); | |
+ | |
+ while(nb < bpp){ | |
+ v &= (1<<nb)-1; | |
+ v |= (u32int)(*p++) << nb; | |
+ nb += 8; | |
+ } | |
+ nb -= bpp; | |
+// print("bpp %d v %.8lux mask %.8lux nb %d\n", bpp, v,… | |
+ fmt(&b, arg, (v>>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 … | |
+ fprint(2, " %.2ux %.2ux %.2ux %.2ux should be %.2ux %.2ux %.2u… | |
+ dp[0], dp[1], dp[2], dp[3], sdp[0], sdp[1], sdp[2], sd… | |
+ fprint(2, "addresses dst %p src %p mask %p\n", dp, byteaddr(sr… | |
+ 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 *mt… | |
+{ | |
+ u32int *dp; | |
+ int nb; | |
+ u32int *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, save… | |
+ 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<nbytes/2; i++) | |
+ *up++ = lrand() >> 7; | |
+ if(i+i != nbytes) | |
+ *(uchar*)up = lrand() >> 7; | |
+ }else{ | |
+ data = img->data->bdata; | |
+ img->data->bdata = ucbits; | |
+ | |
+ for(x=img->r.min.x; x<img->r.max.x; x++) | |
+ for(y=img->r.min.y; y<img->r.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(u32int)*Yrange); | |
+ memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange); | |
+ memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*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(u32int)*Yrange… | |
+ memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrang… | |
+ | |
+ memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); | |
+ memimagedraw(dst, Rect(dp.x, dp.y, dp.x+1, dp.y+1), src, sp, mask, mp,… | |
+ memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*Yrange… | |
+ | |
+ checkone(dp, sp, mp); | |
+} | |
+ | |
+void | |
+verifyone(void) | |
+{ | |
+ int i; | |
+ | |
+ /* mask all zeros */ | |
+ memset(maskbits, 0, nbytes); | |
+ for(i=0; i<niters; i++) | |
+ verifyonemask(); | |
+ | |
+ /* mask all ones */ | |
+ memset(maskbits, 0xFF, nbytes); | |
+ for(i=0; i<niters; i++) | |
+ verifyonemask(); | |
+ | |
+ /* random mask */ | |
+ for(i=0; i<niters; i++){ | |
+ fill(mask, maskbits); | |
+ verifyonemask(); | |
+ } | |
+} | |
+ | |
+/* | |
+ * Mask is preset; do the rest | |
+ */ | |
+void | |
+verifylinemask(void) | |
+{ | |
+ Point sp, mp, tp, up; | |
+ Rectangle dr; | |
+ int x; | |
+ | |
+ fill(dst, dstbits); | |
+ fill(src, srcbits); | |
+ memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); | |
+ memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange); | |
+ memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*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; x<dr.max.x && tp.x<Xrange && up.x<Xrange; x++,tp.x++,u… | |
+ memimagedraw(dst, Rect(x, dr.min.y, x+1, dr.min.y+1), src, tp,… | |
+ memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrang… | |
+ | |
+ memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); | |
+ | |
+ memimagedraw(dst, dr, src, sp, mask, mp, SoverD); | |
+ checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), dr.min.y, n… | |
+} | |
+ | |
+void | |
+verifyline(void) | |
+{ | |
+ int i; | |
+ | |
+ /* mask all ones */ | |
+ memset(maskbits, 0xFF, nbytes); | |
+ for(i=0; i<niters; i++) | |
+ verifylinemask(); | |
+ | |
+ /* mask all zeros */ | |
+ memset(maskbits, 0, nbytes); | |
+ for(i=0; i<niters; i++) | |
+ verifylinemask(); | |
+ | |
+ /* random mask */ | |
+ for(i=0; i<niters; i++){ | |
+ fill(mask, maskbits); | |
+ verifylinemask(); | |
+ } | |
+} | |
+ | |
+/* | |
+ * Mask is preset; do the rest | |
+ */ | |
+void | |
+verifyrectmask(void) | |
+{ | |
+ Point sp, mp, tp, up; | |
+ Rectangle dr; | |
+ int x, y; | |
+ | |
+ fill(dst, dstbits); | |
+ fill(src, srcbits); | |
+ memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); | |
+ memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange); | |
+ memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*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; y<dr.max.y && tp.y<Yrange && up.y<Yrange; y++,tp.y++,u… | |
+ for(x=dr.min.x; x<dr.max.x && tp.x<Xrange && up.x<Xrange; x++,… | |
+ memimagedraw(dst, Rect(x, y, x+1, y+1), src, tp, mask,… | |
+ tp.x = sp.x; | |
+ up.x = mp.x; | |
+ } | |
+ memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrang… | |
+ | |
+ memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); | |
+ | |
+ memimagedraw(dst, dr, src, sp, mask, mp, SoverD); | |
+ for(y=0; y<Yrange; y++) | |
+ checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), y, … | |
+} | |
+ | |
+void | |
+verifyrect(void) | |
+{ | |
+ int i; | |
+ | |
+ /* mask all zeros */ | |
+ memset(maskbits, 0, nbytes); | |
+ for(i=0; i<niters; i++) | |
+ verifyrectmask(); | |
+ | |
+ /* mask all ones */ | |
+ memset(maskbits, 0xFF, nbytes); | |
+ for(i=0; i<niters; i++) | |
+ verifyrectmask(); | |
+ | |
+ /* random mask */ | |
+ for(i=0; i<niters; i++){ | |
+ fill(mask, maskbits); | |
+ verifyrectmask(); | |
+ } | |
+} | |
+ | |
+Rectangle | |
+randrect(void) | |
+{ | |
+ Rectangle r; | |
+ | |
+ r.min.x = nrand(Xrange-1); | |
+ r.min.y = nrand(Yrange-1); | |
+ r.max.x = r.min.x + 1 + nrand(Xrange-1-r.min.x); | |
+ r.max.y = r.min.y + 1 + nrand(Yrange-1-r.min.y); | |
+ return r; | |
+} | |
+ | |
+/* | |
+ * Return coordinate corresponding to x withing range [minx, maxx) | |
+ */ | |
+int | |
+tilexy(int minx, int maxx, int x) | |
+{ | |
+ int sx; | |
+ | |
+ sx = (x-minx) % (maxx-minx); | |
+ if(sx < 0) | |
+ sx += maxx-minx; | |
+ return sx+minx; | |
+} | |
+ | |
+void | |
+replicate(Memimage *i, Memimage *tmp) | |
+{ | |
+ Rectangle r, r1; | |
+ int x, y, nb; | |
+ | |
+ /* choose the replication window (i->r) */ | |
+ 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(u32int)*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(; x<Xrange; x+=Dx(r)){ | |
+ y = -(tilexy(r.min.y, r.max.y, 0)-r.min.y); | |
+ for(; y<Yrange; y+=Dy(r)){ | |
+ /* set r1 to instance of tile by translation */ | |
+ r1.min.x = x; | |
+ r1.min.y = y; | |
+ r1.max.x = r1.min.x+Dx(r); | |
+ r1.max.y = r1.min.y+Dy(r); | |
+ memimagedraw(tmp, r1, i, r.min, ones, r.min, SoverD); | |
+ } | |
+ } | |
+ i->flags |= Frepl; | |
+ i->r = r; | |
+ i->clipr = randrect(); | |
+// fprint(2, "replicate [[%d %d] [%d %d]] [[%d %d][%d %d]]\n", r.min.x,… | |
+// i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max… | |
+ 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(u32int)*Yrange); | |
+ memmove(src->data->bdata, srcbits, src->width*sizeof(u32int)*Yrange); | |
+ memmove(mask->data->bdata, maskbits, mask->width*sizeof(u32int)*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; y<dr.max.y && tp.y<Yrange && up.y<… | |
+ for(tp.x=sp.x,up.x=mp.x,x=dr.min.x; x<dr.max.x && tp.x<Xrange … | |
+ memimagedraw(dst, Rect(x, y, x+1, y+1), s, tp, m, up, … | |
+ memmove(savedstbits, dst->data->bdata, dst->width*sizeof(u32int)*Yrang… | |
+ | |
+ memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); | |
+ | |
+DBG print("bigdraw\n"); | |
+ memimagedraw(dst, dr, src, sp, mask, mp, SoverD); | |
+ for(y=0; y<Yrange; y++) | |
+ checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), y, … | |
+} | |
+ | |
+void | |
+verifyrectrepl(int srcrepl, int maskrepl) | |
+{ | |
+ int i; | |
+ | |
+ /* mask all ones */ | |
+ memset(maskbits, 0xFF, nbytes); | |
+ for(i=0; i<niters; i++) | |
+ verifyrectmaskrepl(srcrepl, maskrepl); | |
+ | |
+ /* mask all zeros */ | |
+ memset(maskbits, 0, nbytes); | |
+ for(i=0; i<niters; i++) | |
+ verifyrectmaskrepl(srcrepl, maskrepl); | |
+ | |
+ /* random mask */ | |
+ for(i=0; i<niters; i++){ | |
+ fill(mask, maskbits); | |
+ verifyrectmaskrepl(srcrepl, maskrepl); | |
+ } | |
+} | |
+ | |
+/* | |
+ * Trivial draw implementation. | |
+ * Color values are passed around as u32ints containing ααRRGGBB | |
+ */ | |
+ | |
+/* | |
+ * Convert v, which is nhave bits wide, into its nwant bits wide equivalent. | |
+ * Replicates to widen the value, truncates to narrow it. | |
+ */ | |
+u32int | |
+replbits(u32int v, int nhave, int nwant) | |
+{ | |
+ v &= (1<<nhave)-1; | |
+ for(; nhave<nwant; nhave*=2) | |
+ v |= v<<nhave; | |
+ v >>= (nhave-nwant); | |
+ return v & ((1<<nwant)-1); | |
+} | |
+ | |
+/* | |
+ * Decode a pixel into the uchar* values. | |
+ */ | |
+void | |
+pixtorgba(u32int v, uchar *r, uchar *g, uchar *b, uchar *a) | |
+{ | |
+ *a = v>>24; | |
+ *r = v>>16; | |
+ *g = v>>8; | |
+ *b = v; | |
+} | |
+ | |
+/* | |
+ * Convert uchar channels into u32int pixel. | |
+ */ | |
+u32int | |
+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. | |
+ */ | |
+u32int | |
+getpixel(Memimage *img, Point pt) | |
+{ | |
+ uchar r, g, b, a, *p; | |
+ int nbits, npack, bpp; | |
+ u32int 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 us… | |
+ * in the bottom byte of v. This madness is due to having big… | |
+ * but little endian bytes. | |
+ */ | |
+ npack = 8/bpp; | |
+ v >>= 8 - bpp*(pt.x%npack+1); | |
+ v &= (1<<bpp)-1; | |
+ r = g = b = replbits(v, bpp, 8); | |
+ }else{ | |
+ /* | |
+ * General case. We need to parse the channel descriptor and … | |
+ * In all channels but the color map, we replicate to 8 bits b… | |
+ * precision that all calculations are done at. | |
+ * | |
+ * In the case of the color map, we leave the bits alone, in c… | |
+ * with less than 8 bits of index is used. This is currently … | |
+ * sort of silly. | |
+ */ | |
+ | |
+ for(c=img->chan; c; c>>=8){ | |
+ nbits = NBITS(c); | |
+ bits = v & ((1<<nbits)-1); | |
+ rbits = replbits(bits, nbits, 8); | |
+ v >>= 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(… | |
+ 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, u32int nv) | |
+{ | |
+ uchar r, g, b, a, *p, *q; | |
+ u32int 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… | |
+ * which is equivalent to skipping the rightmost npack - pt.x%… | |
+ */ | |
+ 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<<bpp)-1; | |
+DBG print("bits %lux mask %lux sh %d...", bits, mask, sh); | |
+ mask <<= sh; | |
+ bits <<= sh; | |
+DBG print("(%lux & %lux) | (%lux & %lux)", v, ~mask, bits, mask); | |
+ v = (v & ~mask) | (bits & mask); | |
+ } else { | |
+ /* | |
+ * General case. We need to parse the channel descriptor agai… | |
+ */ | |
+ sh = 0; | |
+ for(c=img->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(… | |
+ 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<<nbits)-1; | |
+DBG print("bits %lux mask %lux sh %d...", bits, mask, sh); | |
+ bits <<= sh; | |
+ mask <<= sh; | |
+ v = (v & ~mask) | (bits & mask); | |
+ sh += nbits; | |
+ } | |
+ } | |
+DBG print("v %.8lux\n", v); | |
+ p[0] = v; | |
+ p[1] = v>>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,… | |
+{ | |
+ 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,… | |
+ if(dst->flags&Fgrey){ | |
+ /* | |
+ * We need to do the conversion to grey before the alpha calcu… | |
+ * because the draw operator does this, and we need to be oper… | |
+ * 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… | |
+ * the same. It might have been nice to use an array, but oh … | |
+ */ | |
+ 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/src/libdraw/md-ellipse.c b/src/libdraw/md-ellipse.c | |
t@@ -0,0 +1,247 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+/* | |
+ * 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<a: t/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 s… | |
+{ | |
+ 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(b<a && u>b*b || a<b && -u>a*a) { | |
+/* if(b<a&&(t<<1)>b*b/a || a<b&&(t<<1)>a*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.mi… | |
+} | |
+ | |
+/* | |
+ * 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/src/libdraw/md-fillpoly.c b/src/libdraw/md-fillpoly.c | |
t@@ -0,0 +1,524 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+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(const void*, const void*); | |
+static int xcompare(const void*, const void*); | |
+static int zcompare(const void*, const void*); | |
+static void xscan(Memimage *dst, Seg **seg, Seg *segtab, int nse… | |
+static void yscan(Memimage *dst, Seg **seg, Seg *segtab, int nse… | |
+ | |
+#if 0 | |
+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); | |
+} | |
+#endif | |
+ | |
+static void | |
+fillline(Memimage *dst, int left, int right, int y, Memimage *src, Point p, in… | |
+{ | |
+ 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… | |
+{ | |
+ _memfillpolysc(dst, vert, nvert, w, src, sp, 0, 0, 0, op); | |
+} | |
+ | |
+void | |
+_memfillpolysc(Memimage *dst, Point *vert, int nvert, int w, Memimage *src, Po… | |
+{ | |
+ 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, … | |
+ 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)(((u32int)z)^((u32int)y)) > 0 || z == 0) | |
+ return z; | |
+ return z + y; | |
+} | |
+ | |
+static long | |
+sdiv(long x, long y) | |
+{ | |
+ if((long)(((u32int)x)^((u32int)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… | |
+{ | |
+ 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 && … | |
+ fill = fillcolor; | |
+ sp.x = membyteval(src); | |
+ } | |
+ * | |
+ */ | |
+ USED(clipped); | |
+ | |
+ | |
+ for(i=0, s=segtab, p=seg; i<nseg; i++, s++) { | |
+ *p = s; | |
+ if(s->p0.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(y<maxy) { | |
+ for(q = p = seg; p < ep; p++) { | |
+ s = *p; | |
+ if(s->p1.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… | |
+ } | |
+ *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->ze… | |
+ if(s->zerr < 0 || s->zerr >= s->den) | |
+ print("bad ratzerr2: %ld den %ld ratdzrem %ld\… | |
+ *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]->de… | |
+ x++; | |
+ ix = (x + x2) >> (fixshift+1); | |
+ ix2 = ix+1; | |
+ } | |
+ (*fill)(dst, ix, ix2, iy, src, sp, op); | |
+ } | |
+ y += (1<<fixshift); | |
+ iy++; | |
+ } | |
+} | |
+ | |
+static void | |
+yscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src… | |
+{ | |
+ long x, maxx, y, y2, yerr, yden, onehalf; | |
+ Seg **ep, **next, **p, **q, *s; | |
+ int n, i, ix, cnt, iy, iy2, miny, maxy; | |
+ Point pt; | |
+ | |
+ for(i=0, s=segtab, p=seg; i<nseg; i++, s++) { | |
+ *p = s; | |
+ if(s->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(x<maxx) { | |
+ for(q = p = seg; p < ep; p++) { | |
+ s = *p; | |
+ if(s->p1.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 ratdz… | |
+ } | |
+ *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->ze… | |
+ if(s->zerr < 0 || s->zerr >= s->den) | |
+ print("bad ratzerr2: %ld den %ld ratdzrem %ld\… | |
+ *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]->de… | |
+ y++; | |
+ iy = (y + y2) >> (fixshift+1); | |
+ fillpoint(dst, ix, iy, src, sp, op); | |
+ } | |
+ } | |
+ x += (1<<fixshift); | |
+ ix++; | |
+ } | |
+} | |
+ | |
+static void | |
+zsort(Seg **seg, Seg **ep) | |
+{ | |
+ int done; | |
+ Seg **q, **p, *s; | |
+ | |
+ if(ep-seg < 20) { | |
+ /* bubble sort by z - they should be almost sorted already */ | |
+ q = ep; | |
+ do { | |
+ done = 1; | |
+ q--; | |
+ for(p = seg; p < q; p++) { | |
+ if(p[0]->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(const void *a, const void *b) | |
+{ | |
+ Seg **s0, **s1; | |
+ long y0, y1; | |
+ | |
+ s0 = (Seg**)a; | |
+ s1 = (Seg**)b; | |
+ y0 = (*s0)->p0.y; | |
+ y1 = (*s1)->p0.y; | |
+ | |
+ if(y0 < y1) | |
+ return -1; | |
+ if(y0 == y1) | |
+ return 0; | |
+ return 1; | |
+} | |
+ | |
+static int | |
+xcompare(const void *a, const void *b) | |
+{ | |
+ Seg **s0, **s1; | |
+ long x0, x1; | |
+ | |
+ s0 = (Seg**)a; | |
+ s1 = (Seg**)b; | |
+ x0 = (*s0)->p0.x; | |
+ x1 = (*s1)->p0.x; | |
+ | |
+ if(x0 < x1) | |
+ return -1; | |
+ if(x0 == x1) | |
+ return 0; | |
+ return 1; | |
+} | |
+ | |
+static int | |
+zcompare(const void *a, const void *b) | |
+{ | |
+ Seg **s0, **s1; | |
+ long z0, z1; | |
+ | |
+ s0 = (Seg**)a; | |
+ s1 = (Seg**)b; | |
+ z0 = (*s0)->z; | |
+ z1 = (*s1)->z; | |
+ | |
+ if(z0 < z1) | |
+ return -1; | |
+ if(z0 == z1) | |
+ return 0; | |
+ return 1; | |
+} | |
diff --git a/src/libdraw/md-hwdraw.c b/src/libdraw/md-hwdraw.c | |
t@@ -0,0 +1,12 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+int | |
+hwdraw(Memdrawparam *p) | |
+{ | |
+ USED(p); | |
+ return 0; /* could not satisfy request */ | |
+} | |
+ | |
diff --git a/src/libdraw/md-iprint.c b/src/libdraw/md-iprint.c | |
t@@ -0,0 +1,12 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+int | |
+iprint(char *fmt,...) | |
+{ | |
+ USED(fmt); | |
+ return -1; | |
+} | |
+ | |
diff --git a/src/libdraw/md-line.c b/src/libdraw/md-line.c | |
t@@ -0,0 +1,484 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+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(u32int); | |
+ 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<clipr.max.y)) | |
+ *d ^= (*d^srcval) & m; | |
+ if(e > 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<clipr.max.x)) | |
+ *d ^= (*d^srcval) & m; | |
+ if(e > 0){ | |
+ x += dd; | |
+ d += dd; | |
+ e += deltay; | |
+ }else | |
+ e += deltax; | |
+ d += dst->width*sizeof(u32int); | |
+ m >>= bpp; | |
+ if(m == 0) | |
+ m = m0; | |
+ } | |
+} | |
+ | |
+static | |
+void | |
+horliner(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectang… | |
+{ | |
+ 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 && y<clipr.max.y){ | |
+ d = byteaddr(dst, Pt(x, y)); | |
+ sy = drawreplxy(src->r.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, Rectang… | |
+{ | |
+ 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<clipr.max.x){ | |
+ m = m0 >> (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, Rectangl… | |
+{ | |
+ 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<clipr.max.y){ | |
+ d = byteaddr(dst, Pt(x, y)); | |
+ s = byteaddr(src, addpt(dsrc, Pt(x, y))); | |
+ *d ^= (*d^*s) & m; | |
+ } | |
+ m >>= bpp; | |
+ if(m == 0) | |
+ m = m0; | |
+ } | |
+} | |
+ | |
+static | |
+void | |
+verline(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangl… | |
+{ | |
+ 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<clipr.max.x){ | |
+ m = m0 >> (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), memo… | |
+ if(brush != nil){ | |
+ memfillcolor(brush, DTransparent); /* zeros */ | |
+ memellipse(brush, Pt(radius, radius), radius, radius, … | |
+ } | |
+ 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 … | |
+ x2 = (end>>14) & 0x1FF; /* distance along line from bar… | |
+ x3 = (end>>23) & 0x1FF; /* distance perpendicular from … | |
+ } | |
+ | |
+ /* comments follow track of right-facing arrowhead */ | |
+ pp->x = tip.x+((2*radius+1)*sin/2-x1*cos); /* upper sid… | |
+ 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); /* uppe… | |
+ 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 si… | |
+ pp->y = tip.y+((2*radius+1)*cos/2-x1*sin); | |
+} | |
+ | |
+void | |
+_memimageline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radiu… | |
+{ | |
+ /* | |
+ * 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 routi… | |
+ * 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… | |
+ 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, IC… | |
+ 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, ICO… | |
+ pp[1] = pp[4]; | |
+ pp += 2; | |
+ } | |
+ _memfillpolysc(dst, pts, pp-pts, ~0, src, addpt(pts[0], mulpt(d, ICOSS… | |
+ dst->clipr = oclipr; | |
+ return; | |
+} | |
+ | |
+void | |
+memimageline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius… | |
+{ | |
+ _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/src/libdraw/md-load.c b/src/libdraw/md-load.c | |
t@@ -0,0 +1,72 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+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; y<r.max.y; y++){ | |
+ *q ^= (*data^*q) & m; | |
+ q += i->width*sizeof(u32int); | |
+ data++; | |
+ } | |
+ return ndata; | |
+ } | |
+ if(lpart==0 && rpart==0){ /* easy case */ | |
+ for(y=r.min.y; y<r.max.y; y++){ | |
+ memmove(q, data, l); | |
+ q += i->width*sizeof(u32int); | |
+ data += l; | |
+ } | |
+ return ndata; | |
+ } | |
+ mr = 0xFF ^ (0xFF >> rpart); | |
+ if(lpart!=0 && rpart==0){ | |
+ for(y=r.min.y; y<r.max.y; y++){ | |
+ *q ^= (*data^*q) & m; | |
+ if(l > 1) | |
+ memmove(q+1, data+1, l-1); | |
+ q += i->width*sizeof(u32int); | |
+ data += l; | |
+ } | |
+ return ndata; | |
+ } | |
+ if(lpart==0 && rpart!=0){ | |
+ for(y=r.min.y; y<r.max.y; y++){ | |
+ if(l > 1) | |
+ memmove(q, data, l-1); | |
+ q[l-1] ^= (data[l-1]^q[l-1]) & mr; | |
+ q += i->width*sizeof(u32int); | |
+ data += l; | |
+ } | |
+ return ndata; | |
+ } | |
+ for(y=r.min.y; y<r.max.y; y++){ | |
+ *q ^= (*data^*q) & m; | |
+ if(l > 2) | |
+ memmove(q+1, data+1, l-2); | |
+ q[l-1] ^= (data[l-1]^q[l-1]) & mr; | |
+ q += i->width*sizeof(u32int); | |
+ data += l; | |
+ } | |
+ return ndata; | |
+} | |
diff --git a/src/libdraw/md-mkcmap.c b/src/libdraw/md-mkcmap.c | |
t@@ -0,0 +1,79 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+/* | |
+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 <u.h>\n#include <libc.h>\n"); | |
+ else | |
+ print("#include \"lib9.h\"\n"); | |
+ print("#include <draw.h>\n"); | |
+ print("#include <memdraw.h>\n\n"); | |
+ print("static Memcmap def = {\n"); | |
+ print("/* cmap2rgb */ {\n"); | |
+ for(i=0; i<sizeof(c->cmap2rgb); ){ | |
+ 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; i<sizeof(c->rgb2cmap);){ | |
+ 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/src/libdraw/md-openmemsubfont.c b/src/libdraw/md-openmemsubfont.c | |
t@@ -0,0 +1,53 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+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/src/libdraw/md-poly.c b/src/libdraw/md-poly.c | |
t@@ -0,0 +1,23 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+void | |
+mempoly(Memimage *dst, Point *vert, int nvert, int end0, int end1, int radius,… | |
+{ | |
+ int i, e0, e1; | |
+ Point d; | |
+ | |
+ if(nvert < 2) | |
+ return; | |
+ d = subpt(sp, vert[0]); | |
+ for(i=1; i<nvert; i++){ | |
+ e0 = e1 = Enddisc; | |
+ if(i == 1) | |
+ e0 = end0; | |
+ if(i == nvert-1) | |
+ e1 = end1; | |
+ memline(dst, vert[i-1], vert[i], e0, e1, radius, src, addpt(d,… | |
+ } | |
+} | |
diff --git a/src/libdraw/md-read.c b/src/libdraw/md-read.c | |
t@@ -0,0 +1,111 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+Memimage* | |
+readmemimage(int fd) | |
+{ | |
+ char hdr[5*12+1]; | |
+ int dy; | |
+ u32int 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<chunk; j++) | |
+ tmp[j] ^= 0xFF; | |
+ | |
+ if(loadmemimage(i, Rect(r.min.x, miny, r.max.x, miny+dy), tmp,… | |
+ goto Err; | |
+ miny += dy; | |
+ } | |
+ free(tmp); | |
+ return i; | |
+} | |
diff --git a/src/libdraw/md-string.c b/src/libdraw/md-string.c | |
t@@ -0,0 +1,66 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+Point | |
+memimagestring(Memimage *b, Point p, Memimage *color, Point cp, Memsubfont *f,… | |
+{ | |
+ 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; | |
+ width = i->width; | |
+ memdraw(b, Rect(p.x+i->left, p.y+i->top, p.x+i->left+(i[1].x-i… | |
+ 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/src/libdraw/md-subfont.c b/src/libdraw/md-subfont.c | |
t@@ -0,0 +1,34 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+Memsubfont* | |
+allocmemsubfont(char *name, int n, int height, int ascent, Fontchar *info, Mem… | |
+{ | |
+ 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/src/libdraw/md-unload.c b/src/libdraw/md-unload.c | |
t@@ -0,0 +1,25 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+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; y<r.max.y; y++){ | |
+ memmove(data, q, l); | |
+ q += i->width*sizeof(u32int); | |
+ data += l; | |
+ } | |
+ return ndata; | |
+} | |
diff --git a/src/libdraw/md-write.c b/src/libdraw/md-write.c | |
t@@ -0,0 +1,183 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+ | |
+#define CHUNK 8000 | |
+ | |
+#define HSHIFT 3 /* HSHIFT==5 runs slightly faster, but h… | |
+#define NHASH (1<<(HSHIFT*NMATCH)) | |
+#define HMASK (NHASH-1) | |
+#define hupdate(h, c) ((((h)<<HSHIFT)^(c))&HMASK) | |
+typedef struct Hlist Hlist; | |
+struct Hlist{ | |
+ uchar *s; | |
+ Hlist *next, *prev; | |
+}; | |
+ | |
+int | |
+writememimage(int fd, Memimage *i) | |
+{ | |
+ uchar *outbuf, *outp, *eout; /* encoded data, pointer, … | |
+ uchar *loutp; /* start of encoded line … | |
+ Hlist *hash; /* heads of hash chains of… | |
+ Hlist *chain, *hp; /* hash chain members, point… | |
+ Hlist *cp; /* next Hlist to fall out of… | |
+ int h; /* hash value */ | |
+ uchar *line, *eline; /* input line, end pointer… | |
+ uchar *data, *edata; /* input buffer, end point… | |
+ u32int n; /* length of input buffer */ | |
+ u32int nb; /* # of bytes returned by un… | |
+ int bpl; /* input line length */ | |
+ int offs, runlen; /* offset, length of consumed… | |
+ uchar dumpbuf[NDUMP]; /* dump accumulator */ | |
+ int ndump; /* length of dump accumulato… | |
+ int miny, dy; /* y values while unloadi… | |
+ int ncblock; /* size of compressed bloc… | |
+ Rectangle r; | |
+ uchar *p, *q, *s, *es, *t; | |
+ char hdr[11+5*12+1]; | |
+ char cbuf[20]; | |
+ | |
+ r = i->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… | |
+ *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/src/libdraw/memdraw.h b/src/libdraw/memdraw.h | |
t@@ -0,0 +1,209 @@ | |
+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 | |
+{ | |
+ u32int *base; /* allocated data pointer */ | |
+ uchar *bdata; /* pointer to first byte of actual data; w… | |
+ 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 pixe… | |
+ int nchan; /* number of channels */ | |
+ u32int chan; /* channel descriptions */ | |
+ Memcmap *cmap; | |
+ | |
+ Memdata *data; /* pointer to data; shared by win… | |
+ int zero; /* data->bdata+zero==&byte containing … | |
+ u32int width; /* width in words of a single scan… | |
+ Memlayer *layer; /* nil if not a layer*/ | |
+ u32int flags; | |
+ void *X; | |
+ | |
+ int shift[NChan]; | |
+ int mask[NChan]; | |
+ int nbits[NChan]; | |
+}; | |
+ | |
+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 baselin… | |
+ 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; | |
+ | |
+ u32int state; | |
+ u32int mval; /* if Simplemask, the mask pixel in mask format */ | |
+ u32int mrgba; /* mval in rgba */ | |
+ u32int sval; /* if Simplesrc, the source pixel in src format */ | |
+ u32int srgba; /* sval in rgba */ | |
+ u32int sdval; /* sval in dst format */ | |
+}; | |
+ | |
+/* | |
+ * Memimage management | |
+ */ | |
+ | |
+extern Memimage* allocmemimage(Rectangle, u32int); | |
+extern Memimage* allocmemimaged(Rectangle, u32int, 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 u32int* wordaddr(Memimage*, Point); | |
+extern uchar* byteaddr(Memimage*, Point); | |
+extern int drawclip(Memimage*, Rectangle*, Memimage*, Point*, | |
+ Memimage*, Point*, Rectangle*, Rectangle*); | |
+extern void memfillcolor(Memimage*, u32int); | |
+extern int memsetchan(Memimage*, u32int); | |
+extern u32int pixelbits(Memimage*, Point); | |
+ | |
+/* | |
+ * 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 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*, Mem… | |
+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*, ...); | |
+extern int drawdebug; | |
+ | |
+/* | |
+ * For other implementations, like x11. | |
+ */ | |
+extern void _memfillcolor(Memimage*, u32int); | |
+extern Memimage* _allocmemimage(Rectangle, u32int); | |
+extern int _cloadmemimage(Memimage*, Rectangle, uchar*, int); | |
+extern int _loadmemimage(Memimage*, Rectangle, uchar*, int); | |
+extern void _freememimage(Memimage*); | |
+extern u32int _rgbatoimg(Memimage*, u32int); | |
+extern u32int _imgtorgba(Memimage*, u32int); | |
+extern u32int _pixelbits(Memimage*, Point); | |
+extern int _unloadmemimage(Memimage*, Rectangle, uchar*, int); | |
+extern Memdrawparam* _memimagedrawsetup(Memimage*, | |
+ Rectangle, Memimage*, Point, Memimage*, | |
+ Point, int); | |
+extern void _memimagedraw(Memdrawparam*); | |
+extern void _drawreplacescreenimage(Memimage*); | |
diff --git a/src/libdraw/memlayer.h b/src/libdraw/memlayer.h | |
t@@ -0,0 +1,48 @@ | |
+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 dr… | |
+ Memimage *fill; /* if non-zero, picture … | |
+}; | |
+ | |
+struct Memlayer | |
+{ | |
+ Rectangle screenr; /* true position of layer on … | |
+ Point delta; /* add delta to go from ima… | |
+ 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 refr… | |
+ 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, Recta… | |
+Memimage* memlalloc(Memscreen*, Rectangle, Refreshfn, void*, u32int); | |
+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/src/libdraw/menuhit.c b/src/libdraw/menuhit.c | |
t@@ -0,0 +1,277 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <thread.h> | |
+#include <mouse.h> | |
+ | |
+enum | |
+{ | |
+ Margin = 4, /* outside to text */ | |
+ Border = 2, /* outside to selection boxes */ | |
+ Blackborder = 2, /* width of outlining border */ | |
+ Vspacing = 2, /* extra spacing between lines of text */ | |
+ Maxunscroll = 25, /* maximum #entries before scrolling turns on… | |
+ Nscroll = 20, /* number entries in scrolling part */ | |
+ Scrollwid = 14, /* width of scroll bar */ | |
+ Gap = 4, /* between text and scroll bar */ | |
+}; | |
+ | |
+static Image *menutxt; | |
+static Image *back; | |
+static Image *high; | |
+static Image *bord; | |
+static Image *text; | |
+static Image *htext; | |
+ | |
+static | |
+void | |
+menucolors(void) | |
+{ | |
+ /* Main tone is greenish, with negative selection */ | |
+ back = allocimagemix(display, DPalegreen, DWhite); | |
+ high = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkgreen)… | |
+ bord = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedgreen);… | |
+ if(back==nil || high==nil || bord==nil) | |
+ goto Error; | |
+ text = display->black; | |
+ htext = back; | |
+ return; | |
+ | |
+ Error: | |
+ freeimage(back); | |
+ freeimage(high); | |
+ freeimage(bord); | |
+ back = display->white; | |
+ high = display->black; | |
+ bord = display->black; | |
+ text = display->black; | |
+ htext = display->white; | |
+} | |
+ | |
+/* | |
+ * r is a rectangle holding the text elements. | |
+ * return the rectangle, including its black edge, holding element i. | |
+ */ | |
+static Rectangle | |
+menurect(Rectangle r, int i) | |
+{ | |
+ if(i < 0) | |
+ return Rect(0, 0, 0, 0); | |
+ r.min.y += (font->height+Vspacing)*i; | |
+ r.max.y = r.min.y+font->height+Vspacing; | |
+ return insetrect(r, Border-Margin); | |
+} | |
+ | |
+/* | |
+ * r is a rectangle holding the text elements. | |
+ * return the element number containing p. | |
+ */ | |
+static int | |
+menusel(Rectangle r, Point p) | |
+{ | |
+ r = insetrect(r, Margin); | |
+ if(!ptinrect(p, r)) | |
+ return -1; | |
+ return (p.y-r.min.y)/(font->height+Vspacing); | |
+} | |
+ | |
+static | |
+void | |
+paintitem(Image *m, Menu *menu, Rectangle textr, int off, int i, int highlight… | |
+{ | |
+ char *item; | |
+ Rectangle r; | |
+ Point pt; | |
+ | |
+ if(i < 0) | |
+ return; | |
+ r = menurect(textr, i); | |
+ if(restore){ | |
+ draw(m, r, restore, nil, restore->r.min); | |
+ return; | |
+ } | |
+ if(save) | |
+ draw(save, save->r, m, nil, r.min); | |
+ item = menu->item? menu->item[i+off] : (*menu->gen)(i+off); | |
+ pt.x = (textr.min.x+textr.max.x-stringwidth(font, item))/2; | |
+ pt.y = textr.min.y+i*(font->height+Vspacing); | |
+ draw(m, r, highlight? high : back, nil, pt); | |
+ string(m, pt, highlight? htext : text, pt, font, item); | |
+} | |
+ | |
+/* | |
+ * menur is a rectangle holding all the highlightable text elements. | |
+ * track mouse while inside the box, return what's selected when button | |
+ * is raised, -1 as soon as it leaves box. | |
+ * invariant: nothing is highlighted on entry or exit. | |
+ */ | |
+static int | |
+menuscan(Image *m, Menu *menu, int but, Mousectl *mc, Rectangle textr, int off… | |
+{ | |
+ int i; | |
+ | |
+ paintitem(m, menu, textr, off, lasti, 1, save, nil); | |
+ for(readmouse(mc); mc->m.buttons & (1<<(but-1)); readmouse(mc)){ | |
+ i = menusel(textr, mc->m.xy); | |
+ if(i != -1 && i == lasti) | |
+ continue; | |
+ paintitem(m, menu, textr, off, lasti, 0, nil, save); | |
+ if(i == -1) | |
+ return i; | |
+ lasti = i; | |
+ paintitem(m, menu, textr, off, lasti, 1, save, nil); | |
+ } | |
+ return lasti; | |
+} | |
+ | |
+static void | |
+menupaint(Image *m, Menu *menu, Rectangle textr, int off, int nitemdrawn) | |
+{ | |
+ int i; | |
+ | |
+ draw(m, insetrect(textr, Border-Margin), back, nil, ZP); | |
+ for(i = 0; i<nitemdrawn; i++) | |
+ paintitem(m, menu, textr, off, i, 0, nil, nil); | |
+} | |
+ | |
+static void | |
+menuscrollpaint(Image *m, Rectangle scrollr, int off, int nitem, int nitemdraw… | |
+{ | |
+ Rectangle r; | |
+ | |
+ draw(m, scrollr, back, nil, ZP); | |
+ r.min.x = scrollr.min.x; | |
+ r.max.x = scrollr.max.x; | |
+ r.min.y = scrollr.min.y + (Dy(scrollr)*off)/nitem; | |
+ r.max.y = scrollr.min.y + (Dy(scrollr)*(off+nitemdrawn))/nitem; | |
+ if(r.max.y < r.min.y+2) | |
+ r.max.y = r.min.y+2; | |
+ border(m, r, 1, bord, ZP); | |
+ if(menutxt == 0) | |
+ menutxt = allocimage(display, Rect(0, 0, 1, 1), screen->chan, … | |
+ if(menutxt) | |
+ draw(m, insetrect(r, 1), menutxt, nil, ZP); | |
+} | |
+ | |
+int | |
+menuhit(int but, Mousectl *mc, Menu *menu, Screen *scr) | |
+{ | |
+ int i, nitem, nitemdrawn, maxwid, lasti, off, noff, wid, screenitem; | |
+ int scrolling; | |
+ Rectangle r, menur, sc, textr, scrollr; | |
+ Image *b, *save, *backup; | |
+ Point pt; | |
+ char *item; | |
+ | |
+ if(back == nil) | |
+ menucolors(); | |
+ sc = screen->clipr; | |
+ replclipr(screen, 0, screen->r); | |
+ maxwid = 0; | |
+ for(nitem = 0; | |
+ item = menu->item? menu->item[nitem] : (*menu->gen)(nitem); | |
+ nitem++){ | |
+ i = stringwidth(font, item); | |
+ if(i > maxwid) | |
+ maxwid = i; | |
+ } | |
+ if(menu->lasthit<0 || menu->lasthit>=nitem) | |
+ menu->lasthit = 0; | |
+ screenitem = (Dy(screen->r)-10)/(font->height+Vspacing); | |
+ if(nitem>Maxunscroll || nitem>screenitem){ | |
+ scrolling = 1; | |
+ nitemdrawn = Nscroll; | |
+ if(nitemdrawn > screenitem) | |
+ nitemdrawn = screenitem; | |
+ wid = maxwid + Gap + Scrollwid; | |
+ off = menu->lasthit - nitemdrawn/2; | |
+ if(off < 0) | |
+ off = 0; | |
+ if(off > nitem-nitemdrawn) | |
+ off = nitem-nitemdrawn; | |
+ lasti = menu->lasthit-off; | |
+ }else{ | |
+ scrolling = 0; | |
+ nitemdrawn = nitem; | |
+ wid = maxwid; | |
+ off = 0; | |
+ lasti = menu->lasthit; | |
+ } | |
+ r = insetrect(Rect(0, 0, wid, nitemdrawn*(font->height+Vspacing)), -Ma… | |
+ r = rectsubpt(r, Pt(wid/2, lasti*(font->height+Vspacing)+font->height/… | |
+ r = rectaddpt(r, mc->m.xy); | |
+ pt = ZP; | |
+ if(r.max.x>screen->r.max.x) | |
+ pt.x = screen->r.max.x-r.max.x; | |
+ if(r.max.y>screen->r.max.y) | |
+ pt.y = screen->r.max.y-r.max.y; | |
+ if(r.min.x<screen->r.min.x) | |
+ pt.x = screen->r.min.x-r.min.x; | |
+ if(r.min.y<screen->r.min.y) | |
+ pt.y = screen->r.min.y-r.min.y; | |
+ menur = rectaddpt(r, pt); | |
+ textr.max.x = menur.max.x-Margin; | |
+ textr.min.x = textr.max.x-maxwid; | |
+ textr.min.y = menur.min.y+Margin; | |
+ textr.max.y = textr.min.y + nitemdrawn*(font->height+Vspacing); | |
+ if(scrolling){ | |
+ scrollr = insetrect(menur, Border); | |
+ scrollr.max.x = scrollr.min.x+Scrollwid; | |
+ }else | |
+ scrollr = Rect(0, 0, 0, 0); | |
+ | |
+ if(scr){ | |
+ b = allocwindow(scr, menur, Refbackup, DWhite); | |
+ if(b == nil) | |
+ b = screen; | |
+ backup = nil; | |
+ }else{ | |
+ b = screen; | |
+ backup = allocimage(display, menur, screen->chan, 0, -1); | |
+ if(backup) | |
+ draw(backup, menur, screen, nil, menur.min); | |
+ } | |
+ draw(b, menur, back, nil, ZP); | |
+ border(b, menur, Blackborder, bord, ZP); | |
+ save = allocimage(display, menurect(textr, 0), screen->chan, 0, -1); | |
+ r = menurect(textr, lasti); | |
+ moveto(mc, divpt(addpt(r.min, r.max), 2)); | |
+ menupaint(b, menu, textr, off, nitemdrawn); | |
+ if(scrolling) | |
+ menuscrollpaint(b, scrollr, off, nitem, nitemdrawn); | |
+ while(mc->m.buttons & (1<<(but-1))){ | |
+ lasti = menuscan(b, menu, but, mc, textr, off, lasti, save); | |
+ if(lasti >= 0) | |
+ break; | |
+ while(!ptinrect(mc->m.xy, textr) && (mc->m.buttons & (1<<(but-… | |
+ if(scrolling && ptinrect(mc->m.xy, scrollr)){ | |
+ noff = ((mc->m.xy.y-scrollr.min.y)*nitem)/Dy(s… | |
+ noff -= nitemdrawn/2; | |
+ if(noff < 0) | |
+ noff = 0; | |
+ if(noff > nitem-nitemdrawn) | |
+ noff = nitem-nitemdrawn; | |
+ if(noff != off){ | |
+ off = noff; | |
+ menupaint(b, menu, textr, off, nitemdr… | |
+ menuscrollpaint(b, scrollr, off, nitem… | |
+ } | |
+ } | |
+ readmouse(mc); | |
+ } | |
+ } | |
+ if(b != screen) | |
+ freeimage(b); | |
+ if(backup){ | |
+ draw(screen, menur, backup, nil, menur.min); | |
+ freeimage(backup); | |
+ } | |
+ freeimage(save); | |
+ replclipr(screen, 0, sc); | |
+ flushimage(display, 1); | |
+ if(lasti >= 0){ | |
+ menu->lasthit = lasti+off; | |
+ return menu->lasthit; | |
+ } | |
+ return -1; | |
+} | |
diff --git a/src/libdraw/mkfile b/src/libdraw/mkfile | |
t@@ -0,0 +1 @@ | |
+<../libutf/mkfile | |
diff --git a/src/libdraw/ml-draw.c b/src/libdraw/ml-draw.c | |
t@@ -0,0 +1,192 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include <memlayer.h> | |
+ | |
+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 insa… | |
+{ | |
+ 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 necessar… | |
+ 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, P… | |
+{ | |
+ 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, p… | |
+ | |
+ 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->cli… | |
+ 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 expos… | |
+ */ | |
+ if(dl!=nil && dst==src){ | |
+ if(dl->save == nil) | |
+ return; /* refresh function makes this case unw… | |
+ 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 unw… | |
+ 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/src/libdraw/ml-lalloc.c b/src/libdraw/ml-lalloc.c | |
t@@ -0,0 +1,79 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include <memlayer.h> | |
+ | |
+Memimage* | |
+memlalloc(Memscreen *s, Rectangle screenr, Refreshfn refreshfn, void *refreshp… | |
+{ | |
+ 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, 0x3FFFF… | |
+ } | |
+ | |
+ n = allocmemimaged(screenr, s->image->chan, s->image->data, nil); | |
+ 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 a… | |
+ 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 ri… | |
+ * if this window has backing store, but just painting the whole thing… | |
+ */ | |
+ 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/src/libdraw/ml-layerop.c b/src/libdraw/ml-layerop.c | |
t@@ -0,0 +1,112 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include <memlayer.h> | |
+ | |
+#define RECUR(a,b,c,d) _layerop(fn, i, Rect(a.x, b.y, c.x, d.y),… | |
+ | |
+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 && fro… | |
+ 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 cl… | |
+ */ | |
+void | |
+_memlayerop( | |
+ void (*fn)(Memimage*, Rectangle, Rectangle, void*, int), | |
+ Memimage *i, | |
+ Rectangle screenr, /* clipped to window boundaries */ | |
+ Rectangle clipr, /* clipped also to clipping rectangles… | |
+ 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,… | |
+ 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,… | |
+ 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,… | |
+ 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,… | |
+ } | |
+} | |
diff --git a/src/libdraw/ml-ldelete.c b/src/libdraw/ml-ldelete.c | |
t@@ -0,0 +1,67 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include <memlayer.h> | |
+ | |
+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 dealloc… | |
+ 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/src/libdraw/ml-lhide.c b/src/libdraw/ml-lhide.c | |
t@@ -0,0 +1,67 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include <memlayer.h> | |
+ | |
+/* | |
+ * Hide puts that portion of screenr now on the screen into the window's save … | |
+ * 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 the… | |
+ * 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 insa… | |
+{ | |
+ Rectangle r; | |
+ Memlayer *l; | |
+ | |
+ USED(clipr.min.x); | |
+ USED(insave); | |
+ l = etc; | |
+ if(src != l->save){ /* do nothing if src is already in save are… | |
+ 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 in… | |
+{ | |
+ 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/src/libdraw/ml-line.c b/src/libdraw/ml-line.c | |
t@@ -0,0 +1,122 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include <memlayer.h> | |
+ | |
+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, Me… | |
+{ | |
+ 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,… | |
+ 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,… | |
+ 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 insa… | |
+{ | |
+ 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,… | |
+} | |
+ | |
+void | |
+memline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Mem… | |
+{ | |
+ _memline(dst, p0, p1, end0, end1, radius, src, sp, dst->clipr, op); | |
+} | |
diff --git a/src/libdraw/ml-load.c b/src/libdraw/ml-load.c | |
t@@ -0,0 +1,55 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include <memlayer.h> | |
+ | |
+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/src/libdraw/ml-lorigin.c b/src/libdraw/ml-lorigin.c | |
t@@ -0,0 +1,107 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include <memlayer.h> | |
+ | |
+/* | |
+ * 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… | |
+ 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,… | |
+ 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 i… | |
+ * push it behind this one, and (later) delete it. Because the refres… | |
+ * for this fake window is a no-op, this will cause no graphics action… | |
+ * 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 windo… | |
+ */ | |
+ 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/src/libdraw/ml-lsetrefresh.c b/src/libdraw/ml-lsetrefresh.c | |
t@@ -0,0 +1,35 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include <memlayer.h> | |
+ | |
+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/src/libdraw/ml-ltofront.c b/src/libdraw/ml-ltofront.c | |
t@@ -0,0 +1,80 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include <memlayer.h> | |
+ | |
+/* | |
+ * 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/src/libdraw/ml-ltorear.c b/src/libdraw/ml-ltorear.c | |
t@@ -0,0 +1,69 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include <memlayer.h> | |
+ | |
+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/src/libdraw/ml-unload.c b/src/libdraw/ml-unload.c | |
t@@ -0,0 +1,52 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include <memlayer.h> | |
+ | |
+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 R… | |
+ 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/src/libdraw/mouse.c b/src/libdraw/mouse.c | |
t@@ -0,0 +1,139 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <thread.h> | |
+#include <cursor.h> | |
+#include <mouse.h> | |
+ | |
+void | |
+moveto(Mousectl *m, Point pt) | |
+{ | |
+ fprint(m->mfd, "m%d %d", pt.x, pt.y); | |
+ m->xy = pt; | |
+} | |
+ | |
+void | |
+closemouse(Mousectl *mc) | |
+{ | |
+ if(mc == nil) | |
+ return; | |
+ | |
+ postnote(PNPROC, mc->pid, "kill"); | |
+ | |
+ do; while(nbrecv(mc->c, &mc->Mouse) > 0); | |
+ | |
+ close(mc->mfd); | |
+ close(mc->cfd); | |
+ free(mc->file); | |
+ free(mc->c); | |
+ free(mc->resizec); | |
+ free(mc); | |
+} | |
+ | |
+int | |
+readmouse(Mousectl *mc) | |
+{ | |
+ if(mc->image) | |
+ flushimage(mc->image->display, 1); | |
+ if(recv(mc->c, &mc->Mouse) < 0){ | |
+ fprint(2, "readmouse: %r\n"); | |
+ return -1; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+static | |
+void | |
+_ioproc(void *arg) | |
+{ | |
+ int n, nerr, one; | |
+ char buf[1+5*12]; | |
+ Mouse m; | |
+ Mousectl *mc; | |
+ | |
+ mc = arg; | |
+ threadsetname("mouseproc"); | |
+ one = 1; | |
+ memset(&m, 0, sizeof m); | |
+ mc->pid = getpid(); | |
+ nerr = 0; | |
+ for(;;){ | |
+ n = read(mc->mfd, buf, sizeof buf); | |
+ if(n != 1+4*12){ | |
+ yield(); /* if error is due to exiting, we'll e… | |
+ fprint(2, "mouse: bad count %d not 49: %r\n", n); | |
+ if(n<0 || ++nerr>10) | |
+ threadexits("read error"); | |
+ continue; | |
+ } | |
+ nerr = 0; | |
+ switch(buf[0]){ | |
+ case 'r': | |
+ send(mc->resizec, &one); | |
+ /* fall through */ | |
+ case 'm': | |
+ m.xy.x = atoi(buf+1+0*12); | |
+ m.xy.y = atoi(buf+1+1*12); | |
+ m.buttons = atoi(buf+1+2*12); | |
+ m.msec = atoi(buf+1+3*12); | |
+ send(mc->c, &m); | |
+ /* | |
+ * mc->Mouse is updated after send so it doesn't have … | |
+ * This means that programs should receive into mc->Mo… | |
+ * they want full synchrony. | |
+ */ | |
+ mc->Mouse = m; | |
+ break; | |
+ } | |
+ } | |
+} | |
+ | |
+Mousectl* | |
+initmouse(char *file, Image *i) | |
+{ | |
+ Mousectl *mc; | |
+ char *t, *sl; | |
+ | |
+ mc = mallocz(sizeof(Mousectl), 1); | |
+ if(file == nil) | |
+ file = "/dev/mouse"; | |
+ mc->file = strdup(file); | |
+ mc->mfd = open(file, ORDWR|OCEXEC); | |
+ if(mc->mfd<0 && strcmp(file, "/dev/mouse")==0){ | |
+ bind("#m", "/dev", MAFTER); | |
+ mc->mfd = open(file, ORDWR|OCEXEC); | |
+ } | |
+ if(mc->mfd < 0){ | |
+ free(mc); | |
+ return nil; | |
+ } | |
+ t = malloc(strlen(file)+16); | |
+ strcpy(t, file); | |
+ sl = utfrrune(t, '/'); | |
+ if(sl) | |
+ strcpy(sl, "/cursor"); | |
+ else | |
+ strcpy(t, "/dev/cursor"); | |
+ mc->cfd = open(t, ORDWR|OCEXEC); | |
+ free(t); | |
+ mc->image = i; | |
+ mc->c = chancreate(sizeof(Mouse), 0); | |
+ mc->resizec = chancreate(sizeof(int), 2); | |
+ proccreate(_ioproc, mc, 4096); | |
+ return mc; | |
+} | |
+ | |
+void | |
+setcursor(Mousectl *mc, Cursor *c) | |
+{ | |
+ char curs[2*4+2*2*16]; | |
+ | |
+ if(c == nil) | |
+ write(mc->cfd, curs, 0); | |
+ else{ | |
+ BPLONG(curs+0*4, c->offset.x); | |
+ BPLONG(curs+1*4, c->offset.y); | |
+ memmove(curs+2*4, c->clr, 2*2*16); | |
+ write(mc->cfd, curs, sizeof curs); | |
+ } | |
+} | |
diff --git a/src/libdraw/mouse.h b/src/libdraw/mouse.h | |
t@@ -0,0 +1,44 @@ | |
+typedef struct Menu Menu; | |
+typedef struct Mousectl Mousectl; | |
+ | |
+struct Mouse | |
+{ | |
+ int buttons; /* bit array: LMR=124 */ | |
+ Point xy; | |
+ ulong msec; | |
+}; | |
+ | |
+struct Mousectl | |
+{ | |
+ Mouse m; | |
+ struct Channel *c; /* chan(Mouse) */ | |
+ struct Channel *resizec; /* chan(int)[2] */ | |
+ /* buffered in case client is waiting for a mouse acti… | |
+ | |
+ char *file; | |
+ int mfd; /* to mouse file */ | |
+ int cfd; /* to cursor file */ | |
+ int pid; /* of slave proc */ | |
+ Display *display; | |
+ /*Image* image; / * of associated window/display */ | |
+}; | |
+ | |
+struct Menu | |
+{ | |
+ char **item; | |
+ char *(*gen)(int); | |
+ int lasthit; | |
+}; | |
+ | |
+/* | |
+ * Mouse | |
+ */ | |
+extern Mousectl* initmouse(char*, Image*); | |
+extern void moveto(Mousectl*, Point); | |
+extern int readmouse(Mousectl*); | |
+extern void closemouse(Mousectl*); | |
+struct Cursor; | |
+extern void setcursor(Mousectl*, struct Cursor*); | |
+extern void drawgetrect(Rectangle, int); | |
+extern Rectangle getrect(int, Mousectl*); | |
+extern int menuhit(int, Mousectl*, Menu*, Screen*); | |
diff --git a/src/libdraw/openfont.c b/src/libdraw/openfont.c | |
t@@ -0,0 +1,32 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+Font* | |
+openfont(Display *d, char *name) | |
+{ | |
+ Font *fnt; | |
+ int fd, i, n; | |
+ char *buf; | |
+ | |
+ fd = open(name, OREAD); | |
+ if(fd < 0) | |
+ return 0; | |
+ | |
+ n = flength(fd); | |
+ buf = malloc(n+1); | |
+ if(buf == 0){ | |
+ close(fd); | |
+ return 0; | |
+ } | |
+ buf[n] = 0; | |
+ i = read(fd, buf, n); | |
+ close(fd); | |
+ if(i != n){ | |
+ free(buf); | |
+ return 0; | |
+ } | |
+ fnt = buildfont(d, buf, name); | |
+ free(buf); | |
+ return fnt; | |
+} | |
diff --git a/src/libdraw/readcolmap.c b/src/libdraw/readcolmap.c | |
t@@ -0,0 +1,49 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <bio.h> | |
+ | |
+static ulong | |
+getval(char **p) | |
+{ | |
+ ulong v; | |
+ char *q; | |
+ | |
+ v = strtoul(*p, &q, 0); | |
+ v |= v<<8; | |
+ v |= v<<16; | |
+ *p = q; | |
+ return v; | |
+} | |
+ | |
+void | |
+readcolmap(Display *d, RGB *colmap) | |
+{ | |
+ int i; | |
+ char *p, *q; | |
+ Biobuf *b; | |
+ char buf[128]; | |
+ | |
+ USED(screen); | |
+ | |
+ sprint(buf, "/dev/draw/%d/colormap", d->dirno); | |
+ b = Bopen(buf, OREAD); | |
+ if(b == 0) | |
+ drawerror(d, "rdcolmap: can't open colormap device"); | |
+ | |
+ for(;;) { | |
+ p = Brdline(b, '\n'); | |
+ if(p == 0) | |
+ break; | |
+ i = strtoul(p, &q, 0); | |
+ if(i < 0 || i > 255) { | |
+ fprint(2, "rdcolmap: bad index\n"); | |
+ exits("bad"); | |
+ } | |
+ p = q; | |
+ colmap[255-i].red = getval(&p); | |
+ colmap[255-i].green = getval(&p); | |
+ colmap[255-i].blue = getval(&p); | |
+ } | |
+ Bterm(b); | |
+} | |
diff --git a/src/libdraw/readimage.c b/src/libdraw/readimage.c | |
t@@ -0,0 +1,118 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+Image* | |
+readimage(Display *d, int fd, int dolock) | |
+{ | |
+ char hdr[5*12+1]; | |
+ int dy; | |
+ int new; | |
+ uint l, n; | |
+ int m, j, chunk; | |
+ int miny, maxy; | |
+ Rectangle r; | |
+ int ldepth; | |
+ u32int chan; | |
+ uchar *tmp; | |
+ Image *i; | |
+ | |
+ if(readn(fd, hdr, 11) != 11) | |
+ return nil; | |
+ if(memcmp(hdr, "compressed\n", 11) == 0) | |
+ return creadimage(d, fd, dolock); | |
+ if(readn(fd, hdr+11, 5*12-11) != 5*12-11) | |
+ return nil; | |
+ chunk = d->bufsize - 32; /* a little room for header */ | |
+ | |
+ /* | |
+ * 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)); | |
+ if(dolock) | |
+ lockdisplay(d); | |
+ i = allocimage(d, r, chan, 0, -1); | |
+ if(dolock) | |
+ unlockdisplay(d); | |
+ if(i == nil) | |
+ return nil; | |
+ 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("readimage: image too wide for buffer"); | |
+ goto Err; | |
+ } | |
+ n = dy*l; | |
+ m = readn(fd, tmp, n); | |
+ if(m != n){ | |
+ werrstr("readimage: read count %d not %d: %r", m, n); | |
+ Err: | |
+ if(dolock) | |
+ lockdisplay(d); | |
+ Err1: | |
+ freeimage(i); | |
+ if(dolock) | |
+ unlockdisplay(d); | |
+ free(tmp); | |
+ return nil; | |
+ } | |
+ if(!new) /* an old image: must flip all the bits */ | |
+ for(j=0; j<chunk; j++) | |
+ tmp[j] ^= 0xFF; | |
+ | |
+ if(dolock) | |
+ lockdisplay(d); | |
+ if(loadimage(i, Rect(r.min.x, miny, r.max.x, miny+dy), tmp, ch… | |
+ goto Err1; | |
+ if(dolock) | |
+ unlockdisplay(d); | |
+ miny += dy; | |
+ } | |
+ free(tmp); | |
+ return i; | |
+} | |
diff --git a/src/libdraw/readsubfont.c b/src/libdraw/readsubfont.c | |
t@@ -0,0 +1,58 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+Subfont* | |
+readsubfonti(Display*d, char *name, int fd, Image *ai, int dolock) | |
+{ | |
+ char hdr[3*12+4+1]; | |
+ int n; | |
+ uchar *p; | |
+ Fontchar *fc; | |
+ Subfont *f; | |
+ Image *i; | |
+ | |
+ i = ai; | |
+ if(i == nil){ | |
+ i = readimage(d, fd, dolock); | |
+ if(i == nil) | |
+ return nil; | |
+ } | |
+ if(read(fd, hdr, 3*12) != 3*12){ | |
+ if(ai == nil) | |
+ freeimage(i); | |
+ werrstr("rdsubfonfile: header read error: %r"); | |
+ return nil; | |
+ } | |
+ n = atoi(hdr); | |
+ p = malloc(6*(n+1)); | |
+ if(p == nil) | |
+ return nil; | |
+ if(read(fd, p, 6*(n+1)) != 6*(n+1)){ | |
+ werrstr("rdsubfonfile: fontchar read error: %r"); | |
+ Err: | |
+ free(p); | |
+ return nil; | |
+ } | |
+ fc = malloc(sizeof(Fontchar)*(n+1)); | |
+ if(fc == nil) | |
+ goto Err; | |
+ _unpackinfo(fc, p, n); | |
+ if(dolock) | |
+ lockdisplay(d); | |
+ f = allocsubfont(name, n, atoi(hdr+12), atoi(hdr+24), fc, i); | |
+ if(dolock) | |
+ unlockdisplay(d); | |
+ if(f == nil){ | |
+ free(fc); | |
+ goto Err; | |
+ } | |
+ free(p); | |
+ return f; | |
+} | |
+ | |
+Subfont* | |
+readsubfont(Display*d, char *name, int fd, int dolock) | |
+{ | |
+ return readsubfonti(d, name, fd, nil, dolock); | |
+} | |
diff --git a/src/libdraw/string.c b/src/libdraw/string.c | |
t@@ -0,0 +1,137 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+enum | |
+{ | |
+ Max = 100 | |
+}; | |
+ | |
+Point | |
+string(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s) | |
+{ | |
+ return _string(dst, pt, src, sp, f, s, nil, 1<<24, dst->clipr, nil, ZP… | |
+} | |
+ | |
+Point | |
+stringop(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, Drawop … | |
+{ | |
+ return _string(dst, pt, src, sp, f, s, nil, 1<<24, dst->clipr, nil, ZP… | |
+} | |
+ | |
+Point | |
+stringn(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, int len) | |
+{ | |
+ return _string(dst, pt, src, sp, f, s, nil, len, dst->clipr, nil, ZP, … | |
+} | |
+ | |
+Point | |
+stringnop(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, int le… | |
+{ | |
+ return _string(dst, pt, src, sp, f, s, nil, len, dst->clipr, nil, ZP, … | |
+} | |
+ | |
+Point | |
+runestring(Image *dst, Point pt, Image *src, Point sp, Font *f, Rune *r) | |
+{ | |
+ return _string(dst, pt, src, sp, f, nil, r, 1<<24, dst->clipr, nil, ZP… | |
+} | |
+ | |
+Point | |
+runestringop(Image *dst, Point pt, Image *src, Point sp, Font *f, Rune *r, Dra… | |
+{ | |
+ return _string(dst, pt, src, sp, f, nil, r, 1<<24, dst->clipr, nil, ZP… | |
+} | |
+ | |
+Point | |
+runestringn(Image *dst, Point pt, Image *src, Point sp, Font *f, Rune *r, int … | |
+{ | |
+ return _string(dst, pt, src, sp, f, nil, r, len, dst->clipr, nil, ZP, … | |
+} | |
+ | |
+Point | |
+runestringnop(Image *dst, Point pt, Image *src, Point sp, Font *f, Rune *r, in… | |
+{ | |
+ return _string(dst, pt, src, sp, f, nil, r, len, dst->clipr, nil, ZP, … | |
+} | |
+ | |
+Point | |
+_string(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, Rune *r,… | |
+{ | |
+ int m, n, wid, max; | |
+ ushort cbuf[Max], *c, *ec; | |
+ uchar *b; | |
+ char *subfontname; | |
+ char **sptr; | |
+ Rune **rptr; | |
+ Font *def; | |
+ | |
+ if(s == nil){ | |
+ s = ""; | |
+ sptr = nil; | |
+ }else | |
+ sptr = &s; | |
+ if(r == nil){ | |
+ r = (Rune*) L""; | |
+ rptr = nil; | |
+ }else | |
+ rptr = &r; | |
+ while((*s || *r) && len){ | |
+ max = Max; | |
+ if(len < max) | |
+ max = len; | |
+ n = cachechars(f, sptr, rptr, cbuf, max, &wid, &subfontname); | |
+ if(n > 0){ | |
+ _setdrawop(dst->display, op); | |
+ | |
+ m = 47+2*n; | |
+ if(bg) | |
+ m += 4+2*4; | |
+ b = bufimage(dst->display, m); | |
+ if(b == 0){ | |
+ fprint(2, "string: %r\n"); | |
+ break; | |
+ } | |
+ if(bg) | |
+ b[0] = 'x'; | |
+ else | |
+ b[0] = 's'; | |
+ BPLONG(b+1, dst->id); | |
+ BPLONG(b+5, src->id); | |
+ BPLONG(b+9, f->cacheimage->id); | |
+ BPLONG(b+13, pt.x); | |
+ BPLONG(b+17, pt.y+f->ascent); | |
+ BPLONG(b+21, clipr.min.x); | |
+ BPLONG(b+25, clipr.min.y); | |
+ BPLONG(b+29, clipr.max.x); | |
+ BPLONG(b+33, clipr.max.y); | |
+ BPLONG(b+37, sp.x); | |
+ BPLONG(b+41, sp.y); | |
+ BPSHORT(b+45, n); | |
+ b += 47; | |
+ if(bg){ | |
+ BPLONG(b, bg->id); | |
+ BPLONG(b+4, bgp.x); | |
+ BPLONG(b+8, bgp.y); | |
+ b += 12; | |
+ } | |
+ ec = &cbuf[n]; | |
+ for(c=cbuf; c<ec; c++, b+=2) | |
+ BPSHORT(b, *c); | |
+ pt.x += wid; | |
+ bgp.x += wid; | |
+ agefont(f); | |
+ len -= n; | |
+ } | |
+ if(subfontname){ | |
+ if(_getsubfont(f->display, subfontname) == 0){ | |
+ def = f->display->defaultfont; | |
+ if(def && f!=def) | |
+ f = def; | |
+ else | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ return pt; | |
+} | |
diff --git a/src/libdraw/stringwidth.c b/src/libdraw/stringwidth.c | |
t@@ -0,0 +1,97 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+static Rune empty[] = { 0 }; | |
+int | |
+_stringnwidth(Font *f, char *s, Rune *r, int len) | |
+{ | |
+ int wid, twid, n, max, l; | |
+ char *name; | |
+ enum { Max = 64 }; | |
+ ushort cbuf[Max]; | |
+ Rune rune, **rptr; | |
+ char *subfontname, **sptr; | |
+ Font *def; | |
+ | |
+ if(s == nil){ | |
+ s = ""; | |
+ sptr = nil; | |
+ }else | |
+ sptr = &s; | |
+ if(r == nil){ | |
+ r = empty; | |
+ rptr = nil; | |
+ }else | |
+ rptr = &r; | |
+ twid = 0; | |
+ while((*s || *r) && len){ | |
+ max = Max; | |
+ if(len < max) | |
+ max = len; | |
+ n = 0; | |
+ while((l = cachechars(f, sptr, rptr, cbuf, max, &wid, &subfont… | |
+ if(++n > 10){ | |
+ if(*r) | |
+ rune = *r; | |
+ else | |
+ chartorune(&rune, s); | |
+ if(f->name != nil) | |
+ name = f->name; | |
+ else | |
+ name = "unnamed font"; | |
+ fprint(2, "stringwidth: bad character set for … | |
+ return twid; | |
+ } | |
+ if(subfontname){ | |
+ if(_getsubfont(f->display, subfontname) == 0){ | |
+ def = f->display->defaultfont; | |
+ if(def && f!=def) | |
+ f = def; | |
+ else | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ agefont(f); | |
+ twid += wid; | |
+ len -= l; | |
+ } | |
+ return twid; | |
+} | |
+ | |
+int | |
+stringnwidth(Font *f, char *s, int len) | |
+{ | |
+ return _stringnwidth(f, s, nil, len); | |
+} | |
+ | |
+int | |
+stringwidth(Font *f, char *s) | |
+{ | |
+ return _stringnwidth(f, s, nil, 1<<24); | |
+} | |
+ | |
+Point | |
+stringsize(Font *f, char *s) | |
+{ | |
+ return Pt(_stringnwidth(f, s, nil, 1<<24), f->height); | |
+} | |
+ | |
+int | |
+runestringnwidth(Font *f, Rune *r, int len) | |
+{ | |
+ return _stringnwidth(f, nil, r, len); | |
+} | |
+ | |
+int | |
+runestringwidth(Font *f, Rune *r) | |
+{ | |
+ return _stringnwidth(f, nil, r, 1<<24); | |
+} | |
+ | |
+Point | |
+runestringsize(Font *f, Rune *r) | |
+{ | |
+ return Pt(_stringnwidth(f, nil, r, 1<<24), f->height); | |
+} | |
diff --git a/src/libdraw/subfont.c b/src/libdraw/subfont.c | |
t@@ -0,0 +1,28 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+Subfont* | |
+allocsubfont(char *name, int n, int height, int ascent, Fontchar *info, Image … | |
+{ | |
+ Subfont *f; | |
+ | |
+ assert(height != 0 /* allocsubfont */); | |
+ | |
+ f = malloc(sizeof(Subfont)); | |
+ if(f == 0) | |
+ return 0; | |
+ f->n = n; | |
+ f->height = height; | |
+ f->ascent = ascent; | |
+ f->info = info; | |
+ f->bits = i; | |
+ f->ref = 1; | |
+ if(name){ | |
+ f->name = strdup(name); | |
+ if(lookupsubfont(i->display, name) == 0) | |
+ installsubfont(name, f); | |
+ }else | |
+ f->name = 0; | |
+ return f; | |
+} | |
diff --git a/src/libdraw/subfontcache.c b/src/libdraw/subfontcache.c | |
t@@ -0,0 +1,39 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+/* | |
+ * Easy versions of the cache routines; may be substituted by fancier ones for… | |
+ */ | |
+ | |
+static char *lastname; | |
+Subfont *lastsubfont; | |
+ | |
+Subfont* | |
+lookupsubfont(Display *d, char *name) | |
+{ | |
+ if(strcmp(name, "*default*") == 0) | |
+ return d->defaultsubfont; | |
+ if(lastname && strcmp(name, lastname)==0 && d==lastsubfont->bits->disp… | |
+ lastsubfont->ref++; | |
+ return lastsubfont; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+void | |
+installsubfont(char *name, Subfont *subfont) | |
+{ | |
+ free(lastname); | |
+ lastname = strdup(name); | |
+ lastsubfont = subfont; /* notice we don't free the old one; tha… | |
+} | |
+ | |
+void | |
+uninstallsubfont(Subfont *subfont) | |
+{ | |
+ if(subfont == lastsubfont){ | |
+ lastname = 0; | |
+ lastsubfont = 0; | |
+ } | |
+} | |
diff --git a/src/libdraw/subfontname.c b/src/libdraw/subfontname.c | |
t@@ -0,0 +1,44 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+/* | |
+ * Default version: convert to file name | |
+ */ | |
+ | |
+char* | |
+subfontname(char *cfname, char *fname, int maxdepth) | |
+{ | |
+ char *t, *u, tmp1[64], tmp2[64]; | |
+ int i; | |
+ | |
+ if(strcmp(cfname, "*default*") == 0) | |
+ return strdup(cfname); | |
+ t = cfname; | |
+ if(t[0] != '/'){ | |
+ snprint(tmp2, sizeof tmp2, "%s", fname); | |
+ u = utfrrune(tmp2, '/'); | |
+ if(u) | |
+ u[0] = 0; | |
+ else | |
+ strcpy(tmp2, "."); | |
+ snprint(tmp1, sizeof tmp1, "%s/%s", tmp2, t); | |
+ t = tmp1; | |
+ } | |
+ | |
+ if(maxdepth > 8) | |
+ maxdepth = 8; | |
+ | |
+ for(i=log2[maxdepth]; i>=0; i--){ | |
+ /* try i-bit grey */ | |
+ snprint(tmp2, sizeof tmp2, "%s.%d", t, i); | |
+ if(access(tmp2, AREAD) == 0) | |
+ return strdup(tmp2); | |
+ } | |
+ | |
+ /* try default */ | |
+ if(access(t, AREAD) == 0) | |
+ return strdup(t); | |
+ | |
+ return nil; | |
+} | |
diff --git a/src/libdraw/unix.c b/src/libdraw/unix.c | |
t@@ -0,0 +1,16 @@ | |
+#include <sys/stat.h> | |
+ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+vlong | |
+flength(int fd) | |
+{ | |
+ struct stat s; | |
+ | |
+ if(fstat(fd, &s) < 0) | |
+ return -1; | |
+ return s.st_size; | |
+} | |
+ | |
diff --git a/src/libdraw/unloadimage.c b/src/libdraw/unloadimage.c | |
t@@ -0,0 +1,53 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+int | |
+unloadimage(Image *i, Rectangle r, uchar *data, int ndata) | |
+{ | |
+ int bpl, n, ntot, dy; | |
+ uchar *a; | |
+ Display *d; | |
+ | |
+ if(!rectinrect(r, i->r)){ | |
+ werrstr("unloadimage: bad rectangle"); | |
+ return -1; | |
+ } | |
+ bpl = bytesperline(r, i->depth); | |
+ if(ndata < bpl*Dy(r)){ | |
+ werrstr("unloadimage: buffer too small"); | |
+ return -1; | |
+ } | |
+ | |
+ d = i->display; | |
+ flushimage(d, 0); /* make sure subsequent flush is for us only … | |
+ ntot = 0; | |
+ while(r.min.y < r.max.y){ | |
+ a = bufimage(d, 1+4+4*4); | |
+ if(a == 0){ | |
+ werrstr("unloadimage: %r"); | |
+ return -1; | |
+ } | |
+ dy = 8000/bpl; | |
+ if(dy <= 0){ | |
+ werrstr("unloadimage: image too wide"); | |
+ return -1; | |
+ } | |
+ if(dy > Dy(r)) | |
+ dy = Dy(r); | |
+ a[0] = 'r'; | |
+ BPLONG(a+1, i->id); | |
+ BPLONG(a+5, r.min.x); | |
+ BPLONG(a+9, r.min.y); | |
+ BPLONG(a+13, r.max.x); | |
+ BPLONG(a+17, r.min.y+dy); | |
+ if(flushimage(d, 0) < 0) | |
+ return -1; | |
+ n = _drawmsgread(d, data+ntot, ndata-ntot); | |
+ if(n < 0) | |
+ return n; | |
+ ntot += n; | |
+ r.min.y += dy; | |
+ } | |
+ return ntot; | |
+} | |
diff --git a/src/libdraw/writecolmap.c b/src/libdraw/writecolmap.c | |
t@@ -0,0 +1,35 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+ | |
+/* | |
+ * This code (and the devdraw interface) will have to change | |
+ * if we ever get bitmaps with ldepth > 3, because the | |
+ * colormap will have to be written in chunks | |
+ */ | |
+ | |
+void | |
+writecolmap(Display *d, RGB *m) | |
+{ | |
+ int i, n, fd; | |
+ char buf[64], *t; | |
+ ulong r, g, b; | |
+ | |
+ sprint(buf, "/dev/draw/%d/colormap", d->dirno); | |
+ fd = open(buf, OWRITE); | |
+ if(fd < 0) | |
+ drawerror(d, "wrcolmap: open colormap failed"); | |
+ t = malloc(8192); | |
+ n = 0; | |
+ for(i = 0; i < 256; i++) { | |
+ r = m[i].red>>24; | |
+ g = m[i].green>>24; | |
+ b = m[i].blue>>24; | |
+ n += sprint(t+n, "%d %lud %lud %lud\n", 255-i, r, g, b); | |
+ } | |
+ i = write(fd, t, n); | |
+ free(t); | |
+ close(fd); | |
+ if(i != n) | |
+ drawerror(d, "wrcolmap: bad write"); | |
+} | |
diff --git a/src/libdraw/x11-alloc.c b/src/libdraw/x11-alloc.c | |
t@@ -0,0 +1,120 @@ | |
+#include "x11-inc.h" | |
+ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include "x11-memdraw.h" | |
+ | |
+/* | |
+ * Allocate a Memimage with an optional pixmap backing on the X server. | |
+ */ | |
+Memimage* | |
+xallocmemimage(Rectangle r, u32int chan, int pixmap) | |
+{ | |
+ int d, offset; | |
+ Memimage *m; | |
+ Xmem *xm; | |
+ XImage *xi; | |
+ | |
+ m = _allocmemimage(r, chan); | |
+ if(chan != GREY1 && chan != _x.chan) | |
+ return m; | |
+ | |
+ /* | |
+ * For bootstrapping, don't bother storing 1x1 images | |
+ * on the X server. Memimageinit needs to allocate these | |
+ * and we memimageinit before we do the rest of the X stuff. | |
+ * Of course, 1x1 images on the server are useless anyway. | |
+ */ | |
+ if(Dx(r)==1 && Dy(r)==1) | |
+ return m; | |
+ | |
+ xm = mallocz(sizeof(Xmem), 1); | |
+ if(xm == nil){ | |
+ freememimage(m); | |
+ return nil; | |
+ } | |
+ | |
+ /* | |
+ * Allocate backing store. What we call a 32-bit image | |
+ * the X server calls a 24-bit image. | |
+ */ | |
+ d = m->depth; | |
+ if(pixmap != PMundef) | |
+ xm->pixmap = pixmap; | |
+ else | |
+ xm->pixmap = XCreatePixmap(_x.display, _x.drawable, | |
+ Dx(r), Dy(r), d==32 ? 24 : d); | |
+ | |
+ /* | |
+ * We want to align pixels on word boundaries. | |
+ */ | |
+ if(d == 24) | |
+ offset = r.min.x&3; | |
+ else | |
+ offset = r.min.x&(31/m->depth); | |
+ r.min.x -= offset; | |
+ assert(wordsperline(r, m->depth) <= m->width); | |
+ | |
+ /* | |
+ * Wrap our data in an XImage structure. | |
+ */ | |
+ xi = XCreateImage(_x.display, _x.vis, d==32 ? 24 : d, | |
+ ZPixmap, 0, (char*)m->data->bdata, Dx(r), Dy(r), | |
+ 32, m->width*sizeof(u32int)); | |
+ if(xi == nil){ | |
+ freememimage(m); | |
+ if(xm->pixmap != pixmap) | |
+ XFreePixmap(_x.display, xm->pixmap); | |
+ return nil; | |
+ } | |
+ | |
+ xm->xi = xi; | |
+ xm->r = r; | |
+ | |
+ /* | |
+ * Set the XImage parameters so that it looks exactly like | |
+ * a Memimage -- we're using the same data. | |
+ */ | |
+ if(m->depth < 8 || m->depth == 24) | |
+ xi->bitmap_unit = 8; | |
+ else | |
+ xi->bitmap_unit = m->depth; | |
+ xi->byte_order = LSBFirst; | |
+ xi->bitmap_bit_order = MSBFirst; | |
+ xi->bitmap_pad = 32; | |
+ XInitImage(xi); | |
+ XFlush(_x.display); | |
+ | |
+ m->X = xm; | |
+ return m; | |
+} | |
+ | |
+Memimage* | |
+allocmemimage(Rectangle r, u32int chan) | |
+{ | |
+ return xallocmemimage(r, chan, PMundef); | |
+} | |
+ | |
+void | |
+freememimage(Memimage *m) | |
+{ | |
+ Xmem *xm; | |
+ | |
+ if(m == nil) | |
+ return; | |
+ | |
+ xm = m->X; | |
+ if(xm && m->data->ref == 1){ | |
+ if(xm->xi){ | |
+ xm->xi->data = nil; | |
+ XFree(xm->xi); | |
+ } | |
+ XFreePixmap(_x.display, xm->pixmap); | |
+ free(xm); | |
+ m->X = nil; | |
+ } | |
+ _freememimage(m); | |
+} | |
+ | |
diff --git a/src/libdraw/x11-cload.c b/src/libdraw/x11-cload.c | |
t@@ -0,0 +1,19 @@ | |
+#include "x11-inc.h" | |
+ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include "x11-memdraw.h" | |
+ | |
+int | |
+cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) | |
+{ | |
+ int n; | |
+ | |
+ n = _cloadmemimage(i, r, data, ndata); | |
+ if(n > 0 && i->X) | |
+ xputxdata(i, r); | |
+ return n; | |
+} | |
+ | |
diff --git a/src/libdraw/x11-draw.c b/src/libdraw/x11-draw.c | |
t@@ -0,0 +1,143 @@ | |
+#include "x11-inc.h" | |
+ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include "x11-memdraw.h" | |
+ | |
+static int xdraw(Memdrawparam*); | |
+ | |
+/* | |
+ * The X acceleration doesn't fit into the standard hwaccel | |
+ * model because we have the extra steps of pulling the image | |
+ * data off the server and putting it back when we're done. | |
+ */ | |
+void | |
+memimagedraw(Memimage *dst, Rectangle r, Memimage *src, Point sp, | |
+ Memimage *mask, Point mp, int op) | |
+{ | |
+ Memdrawparam *par; | |
+ | |
+ if((par = _memimagedrawsetup(dst, r, src, sp, mask, mp, op)) == nil) | |
+ return; | |
+ | |
+ if(xdraw(par)) | |
+ return; | |
+ | |
+ /* only fetch dst data if we need it */ | |
+ if((par->state&(Simplemask|Fullmask)) != (Simplemask|Fullmask)) | |
+ xgetxdata(dst, par->r); | |
+ | |
+ /* always fetch source and mask */ | |
+ xgetxdata(src, par->sr); | |
+ xgetxdata(mask, par->mr); | |
+ | |
+ /* now can run memimagedraw on the in-memory bits */ | |
+ _memimagedraw(par); | |
+ | |
+ /* put bits back on x server */ | |
+ xputxdata(dst, par->r); | |
+} | |
+ | |
+static int | |
+xdraw(Memdrawparam *par) | |
+{ | |
+ u32int sdval; | |
+ uint m, state; | |
+ Memimage *src, *dst, *mask; | |
+ Point dp, mp, sp; | |
+ Rectangle r; | |
+ Xmem *xdst, *xmask, *xsrc; | |
+ XGC gc; | |
+ | |
+ if(par->dst->X == nil) | |
+ return 0; | |
+ | |
+ dst = par->dst; | |
+ mask = par->mask; | |
+ r = par->r; | |
+ src = par->src; | |
+ state = par->state; | |
+ | |
+ /* | |
+ * 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((state&m) == m){ | |
+ xfillcolor(dst, r, par->sdval); | |
+ xdirtyxdata(dst, r); | |
+ return 1; | |
+ } | |
+ | |
+ /* | |
+ * If no source alpha and 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 works. | |
+ */ | |
+ m = Simplemask|Fullmask; | |
+ if((state&(m|Replsrc))==m && src->chan==dst->chan && src->X){ | |
+ xdst = dst->X; | |
+ xsrc = src->X; | |
+ dp = subpt(r.min, dst->r.min); | |
+ sp = subpt(par->sr.min, src->r.min); | |
+ gc = dst->chan==GREY1 ? _x.gccopy0 : _x.gccopy; | |
+ | |
+ XCopyArea(_x.display, xsrc->pixmap, xdst->pixmap, gc, | |
+ sp.x, sp.y, Dx(r), Dy(r), dp.x, dp.y); | |
+ xdirtyxdata(dst, r); | |
+ return 1; | |
+ } | |
+ | |
+ /* | |
+ * If no source alpha, a 1-bit mask, and a simple source, | |
+ * we can copy through the mask onto the destination. | |
+ */ | |
+ if(dst->X && mask->X && !(mask->flags&Frepl) | |
+ && mask->chan==GREY1 && (state&Simplesrc)){ | |
+ xdst = dst->X; | |
+ xmask = mask->X; | |
+ sdval = par->sdval; | |
+ | |
+ dp = subpt(r.min, dst->r.min); | |
+ mp = subpt(r.min, subpt(par->mr.min, mask->r.min)); | |
+ | |
+ if(dst->chan == GREY1){ | |
+ gc = _x.gcsimplesrc0; | |
+ if(_x.gcsimplesrc0color != sdval){ | |
+ XSetForeground(_x.display, gc, sdval); | |
+ _x.gcsimplesrc0color = sdval; | |
+ } | |
+ if(_x.gcsimplesrc0pixmap != xmask->pixmap){ | |
+ XSetStipple(_x.display, gc, xmask->pixmap); | |
+ _x.gcsimplesrc0pixmap = xmask->pixmap; | |
+ } | |
+ }else{ | |
+ /* this doesn't work on rob's mac? */ | |
+ gc = _x.gcsimplesrc; | |
+ if(dst->chan == CMAP8 && _x.usetable) | |
+ sdval = _x.tox11[sdval]; | |
+ | |
+ if(_x.gcsimplesrccolor != sdval){ | |
+ XSetForeground(_x.display, gc, sdval); | |
+ _x.gcsimplesrccolor = sdval; | |
+ } | |
+ if(_x.gcsimplesrcpixmap != xmask->pixmap){ | |
+ XSetStipple(_x.display, gc, xmask->pixmap); | |
+ _x.gcsimplesrcpixmap = xmask->pixmap; | |
+ } | |
+ } | |
+ XSetTSOrigin(_x.display, gc, mp.x, mp.y); | |
+ XFillRectangle(_x.display, xdst->pixmap, gc, dp.x, dp.y, | |
+ Dx(r), Dy(r)); | |
+ xdirtyxdata(dst, r); | |
+ return 1; | |
+ } | |
+ | |
+ /* | |
+ * Can't accelerate. | |
+ */ | |
+ return 0; | |
+} | |
+ | |
diff --git a/src/libdraw/x11-event.c b/src/libdraw/x11-event.c | |
t@@ -0,0 +1,136 @@ | |
+#include "x11-inc.h" | |
+ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <cursor.h> | |
+#include <event.h> | |
+ | |
+#include <memdraw.h> | |
+#include "x11-memdraw.h" | |
+ | |
+ulong | |
+event(Event *e) | |
+{ | |
+ return eread(~0UL, e); | |
+} | |
+ | |
+ulong | |
+eread(ulong keys, Event *e) | |
+{ | |
+ ulong xmask; | |
+ XEvent xevent; | |
+ | |
+ xmask = ExposureMask; | |
+ | |
+ if(keys&Emouse) | |
+ xmask |= MouseMask|StructureNotifyMask; | |
+ if(keys&Ekeyboard) | |
+ xmask |= KeyPressMask; | |
+ | |
+ XSelectInput(_x.display, _x.drawable, xmask); | |
+again: | |
+ XWindowEvent(_x.display, _x.drawable, xmask, &xevent); | |
+ | |
+ switch(xevent.type){ | |
+ case Expose: | |
+ xexpose(&xevent, _x.display); | |
+ goto again; | |
+ case ConfigureNotify: | |
+ if(xconfigure(&xevent, _x.display)) | |
+ eresized(1); | |
+ goto again; | |
+ case ButtonPress: | |
+ case ButtonRelease: | |
+ case MotionNotify: | |
+ if(xtoplan9mouse(&xevent, &e->mouse) < 0) | |
+ goto again; | |
+ return Emouse; | |
+ case KeyPress: | |
+ e->kbdc = xtoplan9kbd(&xevent); | |
+ if(e->kbdc == -1) | |
+ goto again; | |
+ return Ekeyboard; | |
+ default: | |
+ return 0; | |
+ } | |
+} | |
+ | |
+void | |
+einit(ulong keys) | |
+{ | |
+ keys &= ~(Emouse|Ekeyboard); | |
+ if(keys){ | |
+ fprint(2, "unknown keys in einit\n"); | |
+ abort(); | |
+ } | |
+} | |
+ | |
+int | |
+ekbd(void) | |
+{ | |
+ Event e; | |
+ | |
+ eread(Ekeyboard, &e); | |
+ return e.kbdc; | |
+} | |
+ | |
+Mouse | |
+emouse(void) | |
+{ | |
+ Event e; | |
+ | |
+ eread(Emouse, &e); | |
+ return e.mouse; | |
+} | |
+ | |
+int | |
+ecanread(ulong keys) | |
+{ | |
+ int can; | |
+ | |
+ can = 0; | |
+ if(keys&Emouse) | |
+ can |= ecanmouse(); | |
+ if(keys&Ekeyboard) | |
+ can |= ecankbd(); | |
+ return can; | |
+} | |
+ | |
+int | |
+ecanmouse(void) | |
+{ | |
+ XEvent xe; | |
+ Mouse m; | |
+ | |
+again: | |
+ if(XCheckWindowEvent(_x.display, _x.drawable, MouseMask, &xe)){ | |
+ if(xtoplan9mouse(&xe, &m) < 0) | |
+ goto again; | |
+ XPutBackEvent(_x.display, &xe); | |
+ return 1; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+int | |
+ecankbd(void) | |
+{ | |
+ XEvent xe; | |
+ | |
+again: | |
+ if(XCheckWindowEvent(_x.display, _x.drawable, KeyPressMask, &xe)){ | |
+ if(xtoplan9kbd(&xe) == -1) | |
+ goto again; | |
+ XPutBackEvent(_x.display, &xe); | |
+ return 1; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+void | |
+emoveto(Point p) | |
+{ | |
+ xmoveto(p); | |
+} | |
+ | |
diff --git a/src/libdraw/x11-fill.c b/src/libdraw/x11-fill.c | |
t@@ -0,0 +1,57 @@ | |
+#include "x11-inc.h" | |
+ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include "x11-memdraw.h" | |
+ | |
+void | |
+memfillcolor(Memimage *m, u32int val) | |
+{ | |
+ _memfillcolor(m, val); | |
+ if(m->X == nil) | |
+ return; | |
+ if((val & 0xFF) == 0xFF) /* full alpha */ | |
+ xfillcolor(m, m->r, _rgbatoimg(m, val)); | |
+ else | |
+ xputxdata(m, m->r); | |
+} | |
+ | |
+void | |
+xfillcolor(Memimage *m, Rectangle r, u32int v) | |
+{ | |
+ Point p; | |
+ Xmem *xm; | |
+ XGC gc; | |
+ | |
+ xm = m->X; | |
+ assert(xm != nil); | |
+ | |
+ /* | |
+ * Set up fill context appropriately. | |
+ */ | |
+ if(m->chan == GREY1){ | |
+ gc = _x.gcfill0; | |
+ if(_x.gcfill0color != v){ | |
+ XSetForeground(_x.display, gc, v); | |
+ _x.gcfill0color = v; | |
+ } | |
+ }else{ | |
+ if(m->chan == CMAP8 && _x.usetable) | |
+ v = _x.tox11[v]; | |
+ gc = _x.gcfill; | |
+ if(_x.gcfillcolor != v){ | |
+ XSetForeground(_x.display, gc, v); | |
+ _x.gcfillcolor = v; | |
+ } | |
+ } | |
+ | |
+ /* | |
+ * XFillRectangle takes coordinates relative to image rectangle. | |
+ */ | |
+ p = subpt(r.min, m->r.min); | |
+ XFillRectangle(_x.display, xm->pixmap, gc, p.x, p.y, Dx(r), Dy(r)); | |
+} | |
+ | |
+ | |
diff --git a/src/libdraw/x11-get.c b/src/libdraw/x11-get.c | |
t@@ -0,0 +1,110 @@ | |
+#include "x11-inc.h" | |
+ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include "x11-memdraw.h" | |
+ | |
+static void | |
+addrect(Rectangle *rp, Rectangle r) | |
+{ | |
+ if(rp->min.x >= rp->max.x) | |
+ *rp = r; | |
+ else | |
+ combinerect(rp, r); | |
+} | |
+ | |
+XImage* | |
+xgetxdata(Memimage *m, Rectangle r) | |
+{ | |
+ int x, y; | |
+ uchar *p; | |
+ Point tp, xdelta, delta; | |
+ Xmem *xm; | |
+ | |
+ xm = m->X; | |
+ if(xm == nil) | |
+ return 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; /* need temp for Digital UNIX */ | |
+ xdelta = subpt(r.min, tp); | |
+ | |
+ XGetSubImage(_x.display, xm->pixmap, delta.x, delta.y, Dx(r), Dy(r), | |
+ AllPlanes, ZPixmap, xm->xi, xdelta.x, delta.y); | |
+ | |
+ if(_x.usetable && m->chan==CMAP8){ | |
+ for(y=r.min.y; y<r.max.y; y++) | |
+ for(x=r.min.x, p=byteaddr(m, Pt(x,y)); x<r.max.x; x++, p++) | |
+ *p = _x.toplan9[*p]; | |
+ } | |
+ xm->dirty = 0; | |
+ xm->dirtyr = Rect(0,0,0,0); | |
+ return xm->xi; | |
+} | |
+ | |
+void | |
+xputxdata(Memimage *m, Rectangle r) | |
+{ | |
+ int offset, x, y; | |
+ uchar *p; | |
+ Point tp, xdelta, delta; | |
+ Xmem *xm; | |
+ XGC gc; | |
+ XImage *xi; | |
+ | |
+ xm = m->X; | |
+ if(xm == nil) | |
+ return; | |
+ | |
+ xi = xm->xi; | |
+ gc = m->chan==GREY1 ? _x.gccopy0 : _x.gccopy; | |
+ if(m->depth == 24) | |
+ offset = r.min.x & 3; | |
+ else | |
+ offset = r.min.x & (31/m->depth); | |
+ | |
+ delta = subpt(r.min, m->r.min); | |
+ | |
+ tp = xm->r.min; /* need temporary on Digital UNIX */ | |
+ xdelta = subpt(r.min, tp); | |
+ | |
+ if(_x.usetable && m->chan==CMAP8){ | |
+ for(y=r.min.y; y<r.max.y; y++) | |
+ for(x=r.min.x, p=byteaddr(m, Pt(x,y)); x<r.max.x; x++, p++) | |
+ *p = _x.tox11[*p]; | |
+ } | |
+ | |
+ XPutImage(_x.display, xm->pixmap, gc, xi, xdelta.x, xdelta.y, delta.x,… | |
+ Dx(r), Dy(r)); | |
+ | |
+ if(_x.usetable && m->chan==CMAP8){ | |
+ for(y=r.min.y; y<r.max.y; y++) | |
+ for(x=r.min.x, p=byteaddr(m, Pt(x,y)); x<r.max.x; x++, p++) | |
+ *p = _x.toplan9[*p]; | |
+ } | |
+} | |
+ | |
+void | |
+xdirtyxdata(Memimage *m, Rectangle r) | |
+{ | |
+ Xmem *xm; | |
+ | |
+ xm = m->X; | |
+ if(xm == nil) | |
+ return; | |
+ xm->dirty = 1; | |
+ addrect(&xm->dirtyr, r); | |
+} | |
+ | |
+ | |
+ | |
diff --git a/src/libdraw/x11-inc.h b/src/libdraw/x11-inc.h | |
t@@ -0,0 +1,31 @@ | |
+#define Colormap XColormap | |
+#define Cursor XCursor | |
+#define Display XDisplay | |
+#define Drawable XDrawable | |
+#define Font XFont | |
+#define GC XGC | |
+#define Point XPoint | |
+#define Rectangle XRectangle | |
+#define Screen XScreen | |
+#define Visual XVisual | |
+#define Window XWindow | |
+ | |
+#include <X11/Xlib.h> | |
+#include <X11/Xatom.h> | |
+#include <X11/Xutil.h> | |
+#include <X11/keysym.h> | |
+#include <X11/IntrinsicP.h> | |
+#include <X11/StringDefs.h> | |
+ | |
+#undef Colormap | |
+#undef Cursor | |
+#undef Display | |
+#undef Drawable | |
+#undef Font | |
+#undef GC | |
+#undef Point | |
+#undef Rectangle | |
+#undef Screen | |
+#undef Visual | |
+#undef Window | |
+ | |
diff --git a/src/libdraw/x11-init.c b/src/libdraw/x11-init.c | |
t@@ -0,0 +1,584 @@ | |
+/* | |
+ * Some of the stuff in this file is not X-dependent and should be elsewhere. | |
+ */ | |
+#include "x11-inc.h" | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include <keyboard.h> | |
+#include <mouse.h> | |
+#include <cursor.h> | |
+#include "x11-memdraw.h" | |
+ | |
+static Memimage *xattach(char*); | |
+static void plan9cmap(void); | |
+static int setupcmap(XWindow); | |
+static XGC xgc(XDrawable, int, int); | |
+static Image *getimage0(Display*); | |
+ | |
+Xprivate _x; | |
+ | |
+Display* | |
+_initdisplay(void (*error)(Display*, char*), char *label) | |
+{ | |
+ Display *d; | |
+ Memimage *m; | |
+ | |
+ memimageinit(); | |
+ | |
+ d = mallocz(sizeof(Display), 1); | |
+ if(d == nil) | |
+ return nil; | |
+ | |
+ d->buf = malloc(16000+5); | |
+ d->obuf = malloc(16000); | |
+ if(d->buf == nil || d->obuf == nil){ | |
+ free(d->buf); | |
+ free(d->obuf); | |
+ free(d); | |
+ return nil; | |
+ } | |
+ d->bufsize = 16000; | |
+ d->obufsize = 16000; | |
+ d->bufp = d->buf; | |
+ d->obufp = d->obuf; | |
+ | |
+ m = xattach(label); | |
+ if(m == nil){ | |
+ free(d); | |
+ return nil; | |
+ } | |
+ | |
+ d->error = error; | |
+ _initdisplaymemimage(d, m); | |
+ d->screenimage = getimage0(d); | |
+ return d; | |
+} | |
+ | |
+static Image* | |
+getimage0(Display *d) | |
+{ | |
+ char *a, info[12*12+1]; | |
+ int n; | |
+ Image *image; | |
+ | |
+ a = bufimage(d, 2); | |
+ a[0] = 'J'; | |
+ a[1] = 'I'; | |
+ if(flushimage(d, 0) < 0){ | |
+ fprint(2, "cannot read screen info: %r\n"); | |
+ abort(); | |
+ } | |
+ | |
+ n = _drawmsgread(d, info, sizeof info); | |
+ if(n != 12*12){ | |
+ fprint(2, "short screen info\n"); | |
+ abort(); | |
+ } | |
+ | |
+ image = mallocz(sizeof(Image), 1); | |
+ image->display = d; | |
+ image->id = 0; | |
+ image->chan = strtochan(info+2*12); | |
+ image->depth = chantodepth(image->chan); | |
+ image->repl = atoi(info+3*12); | |
+ image->r.min.x = atoi(info+4*12); | |
+ image->r.min.y = atoi(info+5*12); | |
+ image->r.max.x = atoi(info+6*12); | |
+ image->r.max.y = atoi(info+7*12); | |
+ image->clipr.min.x = atoi(info+8*12); | |
+ image->clipr.min.y = atoi(info+9*12); | |
+ image->clipr.max.x = atoi(info+10*12); | |
+ image->clipr.max.y = atoi(info+11*12); | |
+ return image; | |
+} | |
+ | |
+int | |
+getwindow(Display *d, int ref) | |
+{ | |
+ Image *i; | |
+ | |
+ freeimage(d->screenimage); | |
+ i = getimage0(d); | |
+ screen = d->screenimage = d->image = i; | |
+ return 0; | |
+} | |
+ | |
+static int | |
+xerror(XDisplay *d, XErrorEvent *e) | |
+{ | |
+ char buf[200]; | |
+ | |
+ print("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); | |
+ print("%s\n", buf); | |
+ return 0; | |
+} | |
+ | |
+static int | |
+xioerror(XDisplay *d) | |
+{ | |
+ print("X I/O error\n"); | |
+ exit(1); | |
+ return -1; | |
+} | |
+ | |
+ | |
+static Memimage* | |
+xattach(char *label) | |
+{ | |
+ char *argv[2], *disp; | |
+ int i, n, xrootid; | |
+ Rectangle r; | |
+ XClassHint classhint; | |
+ XDrawable pmid; | |
+ XPixmapFormatValues *pfmt; | |
+ XScreen *xscreen; | |
+ XSetWindowAttributes attr; | |
+ XSizeHints normalhint; | |
+ XTextProperty name; | |
+ XVisualInfo xvi; | |
+ XWindow xrootwin; | |
+ XWMHints hint; | |
+ | |
+ /* | |
+ * Connect to X server. | |
+ */ | |
+ _x.display = XOpenDisplay(NULL); | |
+ if(_x.display == nil){ | |
+ disp = getenv("DISPLAY"); | |
+ werrstr("XOpenDisplay %s: %r", disp ? disp : ":0"); | |
+ free(disp); | |
+ return nil; | |
+ } | |
+ XSetErrorHandler(xerror); | |
+ XSetIOErrorHandler(xioerror); | |
+ xrootid = DefaultScreen(_x.display); | |
+ xrootwin = DefaultRootWindow(_x.display); | |
+ | |
+ /* | |
+ * Figure out underlying screen format. | |
+ */ | |
+ _x.depth = DefaultDepth(_x.display, xrootid); | |
+ if(XMatchVisualInfo(_x.display, xrootid, 16, TrueColor, &xvi) | |
+ || XMatchVisualInfo(_x.display, xrootid, 16, DirectColor, &xvi)){ | |
+ _x.vis = xvi.visual; | |
+ _x.depth = 16; | |
+ _x.usetable = 1; | |
+ } | |
+ else | |
+ if(XMatchVisualInfo(_x.display, xrootid, 24, TrueColor, &xvi) | |
+ || XMatchVisualInfo(_x.display, xrootid, 24, DirectColor, &xvi)){ | |
+ _x.vis = xvi.visual; | |
+ _x.depth = 24; | |
+ _x.usetable = 1; | |
+ } | |
+ else | |
+ if(XMatchVisualInfo(_x.display, xrootid, 8, PseudoColor, &xvi) | |
+ || XMatchVisualInfo(_x.display, xrootid, 8, StaticColor, &xvi)){ | |
+ if(_x.depth > 8){ | |
+ werrstr("can't deal with colormapped depth %d screens", | |
+ _x.depth); | |
+ goto err0; | |
+ } | |
+ _x.vis = xvi.visual; | |
+ _x.depth = 8; | |
+ } | |
+ else{ | |
+ if(_x.depth != 8){ | |
+ werrstr("can't understand depth %d screen", _x.depth); | |
+ goto err0; | |
+ } | |
+ _x.vis = DefaultVisual(_x.display, xrootid); | |
+ } | |
+ | |
+ /* | |
+ * _x.depth is only the number of significant pixel bits, | |
+ * not the total number of pixel bits. We need to walk the | |
+ * display list to find how many actual bits are used | |
+ * per pixel. | |
+ */ | |
+ _x.chan = 0; | |
+ pfmt = XListPixmapFormats(_x.display, &n); | |
+ for(i=0; i<n; i++){ | |
+ if(pfmt[i].depth == _x.depth){ | |
+ switch(pfmt[i].bits_per_pixel){ | |
+ case 1: /* untested */ | |
+ _x.chan = GREY1; | |
+ break; | |
+ case 2: /* untested */ | |
+ _x.chan = GREY2; | |
+ break; | |
+ case 4: /* untested */ | |
+ _x.chan = GREY4; | |
+ break; | |
+ case 8: | |
+ _x.chan = CMAP8; | |
+ break; | |
+ case 16: /* how to tell RGB15? */ | |
+ _x.chan = RGB16; | |
+ break; | |
+ case 24: /* untested (impossible?) */ | |
+ _x.chan = RGB24; | |
+ break; | |
+ case 32: | |
+ _x.chan = XRGB32; | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ if(_x.chan == 0){ | |
+ werrstr("could not determine screen pixel format"); | |
+ goto err0; | |
+ } | |
+ | |
+ /* | |
+ * Set up color map if necessary. | |
+ */ | |
+ xscreen = DefaultScreenOfDisplay(_x.display); | |
+ _x.cmap = DefaultColormapOfScreen(xscreen); | |
+ if(_x.vis->class != StaticColor){ | |
+ plan9cmap(); | |
+ setupcmap(xrootwin); | |
+ } | |
+ | |
+ /* | |
+ * We get to choose the initial rectangle size. | |
+ * This is arbitrary. In theory we should read the | |
+ * command line and allow the traditional X options. | |
+ */ | |
+ r = Rect(0, 0, WidthOfScreen(xscreen)*3/4, | |
+ HeightOfScreen(xscreen)*3/4); | |
+ | |
+ memset(&attr, 0, sizeof attr); | |
+ attr.colormap = _x.cmap; | |
+ attr.background_pixel = 0; | |
+ attr.border_pixel = 0; | |
+ _x.drawable = XCreateWindow( | |
+ _x.display, /* display */ | |
+ xrootwin, /* parent */ | |
+ 0, /* x */ | |
+ 0, /* y */ | |
+ Dx(r), /* width */ | |
+ Dy(r), /* height */ | |
+ 0, /* border width */ | |
+ _x.depth, /* depth */ | |
+ InputOutput, /* class */ | |
+ _x.vis, /* visual */ | |
+ /* valuemask */ | |
+ CWBackPixel|CWBorderPixel|CWColormap, | |
+ &attr /* attributes (the above aren't?!) */ | |
+ ); | |
+ | |
+ /* | |
+ * Label and other properties required by ICCCCM. | |
+ */ | |
+ memset(&name, 0, sizeof name); | |
+ if(label == nil) | |
+ label = "pjw-face-here"; | |
+ name.value = (uchar*)label; | |
+ name.encoding = XA_STRING; | |
+ name.format = 8; | |
+ name.nitems = strlen(name.value); | |
+ | |
+ memset(&normalhint, 0, sizeof normalhint); | |
+ normalhint.flags = USSize|PMaxSize; | |
+ normalhint.max_width = WidthOfScreen(xscreen); | |
+ normalhint.max_height = HeightOfScreen(xscreen); | |
+ | |
+ memset(&hint, 0, sizeof hint); | |
+ hint.flags = InputHint|StateHint; | |
+ hint.input = 1; | |
+ hint.initial_state = NormalState; | |
+ | |
+ memset(&classhint, 0, sizeof classhint); | |
+ classhint.res_name = label; | |
+ classhint.res_class = label; | |
+ | |
+ argv[0] = label; | |
+ argv[1] = nil; | |
+ | |
+ XSetWMProperties( | |
+ _x.display, /* display */ | |
+ _x.drawable, /* window */ | |
+ &name, /* XA_WM_NAME property */ | |
+ &name, /* XA_WM_ICON_NAME property */ | |
+ argv, /* XA_WM_COMMAND */ | |
+ 1, /* argc */ | |
+ &normalhint, /* XA_WM_NORMAL_HINTS */ | |
+ &hint, /* XA_WM_HINTS */ | |
+ &classhint /* XA_WM_CLASSHINTS */ | |
+ ); | |
+ XFlush(_x.display); | |
+ | |
+ /* | |
+ * Allocate our local backing store. | |
+ */ | |
+ _x.screenr = r; | |
+ _x.screenpm = XCreatePixmap(_x.display, _x.drawable, Dx(r), Dy(r), _x.… | |
+ _x.screenimage = xallocmemimage(r, _x.chan, _x.screenpm); | |
+ | |
+ /* | |
+ * Allocate some useful graphics contexts for the future. | |
+ */ | |
+ _x.gcfill = xgc(_x.screenpm, FillSolid, -1); | |
+ _x.gccopy = xgc(_x.screenpm, -1, -1); | |
+ _x.gcsimplesrc = xgc(_x.screenpm, FillStippled, -1); | |
+ _x.gczero = xgc(_x.screenpm, -1, -1); | |
+ _x.gcreplsrc = xgc(_x.screenpm, FillTiled, -1); | |
+ | |
+ pmid = XCreatePixmap(_x.display, _x.drawable, 1, 1, 1); | |
+ _x.gcfill0 = xgc(pmid, FillSolid, 0); | |
+ _x.gccopy0 = xgc(pmid, -1, -1); | |
+ _x.gcsimplesrc0 = xgc(pmid, FillStippled, -1); | |
+ _x.gczero0 = xgc(pmid, -1, -1); | |
+ _x.gcreplsrc0 = xgc(pmid, FillTiled, -1); | |
+ XFreePixmap(_x.display, pmid); | |
+ | |
+ /* | |
+ * Put the window on the screen. | |
+ */ | |
+ XMapWindow(_x.display, _x.drawable); | |
+ XFlush(_x.display); | |
+ | |
+ /* | |
+ * Lots of display connections for various threads. | |
+ */ | |
+ _x.kbdcon = XOpenDisplay(NULL); | |
+ _x.mousecon = XOpenDisplay(NULL); | |
+ _x.snarfcon = XOpenDisplay(NULL); | |
+ | |
+ _x.black = xscreen->black_pixel; | |
+ _x.white = xscreen->white_pixel; | |
+ | |
+ return _x.screenimage; | |
+ | |
+err0: | |
+ /* | |
+ * Should do a better job of cleaning up here. | |
+ */ | |
+ XCloseDisplay(_x.display); | |
+ return nil; | |
+} | |
+ | |
+/* | |
+ * Create a GC with a particular fill style and XXX. | |
+ * Disable generation of GraphicsExpose/NoExpose events in the GC. | |
+ */ | |
+static XGC | |
+xgc(XDrawable d, int fillstyle, int foreground) | |
+{ | |
+ XGC gc; | |
+ XGCValues v; | |
+ | |
+ memset(&v, 0, sizeof v); | |
+ v.function = GXcopy; | |
+ v.graphics_exposures = False; | |
+ gc = XCreateGC(_x.display, d, GCFunction|GCGraphicsExposures, &v); | |
+ if(fillstyle != -1) | |
+ XSetFillStyle(_x.display, gc, fillstyle); | |
+ if(foreground != -1) | |
+ XSetForeground(_x.display, gc, 0); | |
+ return gc; | |
+} | |
+ | |
+ | |
+/* | |
+ * Initialize map with the Plan 9 rgbv color map. | |
+ */ | |
+static void | |
+plan9cmap(void) | |
+{ | |
+ int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7; | |
+ static int once; | |
+ | |
+ if(once) | |
+ return; | |
+ once = 1; | |
+ | |
+ 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); | |
+ _x.map[idx].red = cr*0x0101; | |
+ _x.map[idx].green = cg*0x0101; | |
+ _x.map[idx].blue = cb*0x0101; | |
+ _x.map[idx].pixel = idx; | |
+ _x.map[idx].flags = DoRed|DoGreen|DoBlue; | |
+ | |
+ v7 = v >> 1; | |
+ idx7 = r*32 + v7*16 + g*4 + b; | |
+ if((v & 1) == v7){ | |
+ _x.map7to8[idx7][0] = idx; | |
+ if(den == 0) { /* divide check -- pick… | |
+ 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; | |
+ } | |
+ _x.map7[idx7].red = cr*0x0101; | |
+ _x.map7[idx7].green = cg*0x0101; | |
+ _x.map7[idx7].blue = cb*0x0101; | |
+ _x.map7[idx7].pixel = idx7; | |
+ _x.map7[idx7].flags = DoRed|DoGreen|DoBlue; | |
+ } | |
+ else | |
+ _x.map7to8[idx7][1] = idx; | |
+ } | |
+} | |
+ | |
+/* | |
+ * Initialize and install the rgbv color map as a private color map | |
+ * for this application. It gets the best colors when it has the | |
+ * cursor focus. | |
+ */ | |
+static int | |
+setupcmap(XWindow w) | |
+{ | |
+ char buf[30]; | |
+ int i; | |
+ u32int p, pp; | |
+ XColor c; | |
+ | |
+ if(_x.depth <= 1) | |
+ return 0; | |
+ | |
+ if(_x.depth >= 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 _x.vis->byte_order but | |
+ * some displays say MSB even though they run on LSB. | |
+ * Besides, this is more anal. | |
+ */ | |
+ | |
+ c = _x.map[19]; /* known to have different R, G, B valu… | |
+ if(!XAllocColor(_x.display, _x.cmap, &c)){ | |
+ werrstr("XAllocColor: %r"); | |
+ return -1; | |
+ } | |
+ p = c.pixel; | |
+ pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff); | |
+ if(pp != _x.map[19].pixel) { | |
+ /* check if endian is other way */ | |
+ pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff); | |
+ if(pp != _x.map[19].pixel){ | |
+ werrstr("cannot detect X server byte order"); | |
+ return -1; | |
+ } | |
+ | |
+ switch(_x.chan){ | |
+ case RGB24: | |
+ _x.chan = BGR24; | |
+ break; | |
+ case XRGB32: | |
+ _x.chan = XBGR32; | |
+ break; | |
+ default: | |
+ werrstr("cannot byteswap channel %s", | |
+ chantostr(buf, _x.chan)); | |
+ break; | |
+ } | |
+ } | |
+ }else if(_x.vis->class == TrueColor || _x.vis->class == DirectColor){ | |
+ /* | |
+ * Do nothing. We have no way to express a | |
+ * mixed-endian 16-bit screen, so pretend they don't exist. | |
+ */ | |
+ }else if(_x.vis->class == PseudoColor){ | |
+ if(_x.usetable == 0){ | |
+ _x.cmap = XCreateColormap(_x.display, w, _x.vis, Alloc… | |
+ XStoreColors(_x.display, _x.cmap, _x.map, 256); | |
+ for(i = 0; i < 256; i++){ | |
+ _x.tox11[i] = i; | |
+ _x.toplan9[i] = i; | |
+ } | |
+ }else{ | |
+ for(i = 0; i < 128; i++){ | |
+ c = _x.map7[i]; | |
+ if(!XAllocColor(_x.display, _x.cmap, &c)){ | |
+ werrstr("can't allocate colors in 7-bi… | |
+ return -1; | |
+ } | |
+ _x.tox11[_x.map7to8[i][0]] = c.pixel; | |
+ _x.tox11[_x.map7to8[i][1]] = c.pixel; | |
+ _x.toplan9[c.pixel] = _x.map7to8[i][0]; | |
+ } | |
+ } | |
+ }else{ | |
+ werrstr("unsupported visual class %d", _x.vis->class); | |
+ return -1; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+void | |
+flushmemscreen(Rectangle r) | |
+{ | |
+ if(r.min.x >= r.max.x || r.min.y >= r.max.y) | |
+ return; | |
+ XCopyArea(_x.display, _x.screenpm, _x.drawable, _x.gccopy, r.min.x, r.… | |
+ Dx(r), Dy(r), r.min.x, r.min.y); | |
+ XFlush(_x.display); | |
+} | |
+ | |
+void | |
+xexpose(XEvent *e, XDisplay *xd) | |
+{ | |
+ XExposeEvent *xe; | |
+ Rectangle r; | |
+ | |
+ 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; | |
+ XCopyArea(xd, _x.screenpm, _x.drawable, _x.gccopy, r.min.x, r.min.y, | |
+ Dx(r), Dy(r), r.min.x, r.min.y); | |
+ XFlush(xd); | |
+} | |
+ | |
+int | |
+xconfigure(XEvent *e, XDisplay *xd) | |
+{ | |
+ Memimage *m; | |
+ XConfigureEvent *xe = (XConfigureEvent*)e; | |
+ XDrawable pixmap; | |
+ | |
+ if(xe->width == Dx(_x.screenr) && xe->height == Dy(_x.screenr)) | |
+ return 0; | |
+ | |
+ pixmap = XCreatePixmap(xd, _x.drawable, xe->width, xe->height, _x.dept… | |
+ m = xallocmemimage(Rect(0, 0, xe->width, xe->height), _x.chan, pixmap); | |
+ _x.screenpm = pixmap; | |
+ _x.screenr = Rect(0, 0, xe->width, xe->height); | |
+ _drawreplacescreenimage(m); | |
+ return 1; | |
+} | |
+ | |
diff --git a/src/libdraw/x11-itrans.c b/src/libdraw/x11-itrans.c | |
t@@ -0,0 +1,258 @@ | |
+/* input event and data structure translation */ | |
+ | |
+#include "x11-inc.h" | |
+ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include <mouse.h> | |
+#include <cursor.h> | |
+#include <keyboard.h> | |
+#include "x11-memdraw.h" | |
+ | |
+int | |
+xtoplan9kbd(XEvent *e) | |
+{ | |
+ int ind, k, md; | |
+ | |
+ md = e->xkey.state; | |
+ ind = 0; | |
+ if(md & ShiftMask) | |
+ ind = 1; | |
+ | |
+ k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, ind); | |
+ if(k == XK_Multi_key || k == NoSymbol) | |
+ return -1; | |
+ | |
+ 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; | |
+ default: /* not ISO-1 or tty control */ | |
+ return -1; | |
+ } | |
+ } | |
+ | |
+ /* 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 -1; | |
+ } | |
+ | |
+ /* BUG: could/should do Alt translation here! */ | |
+ return k; | |
+} | |
+ | |
+int | |
+xtoplan9mouse(XEvent *e, Mouse *m) | |
+{ | |
+ int s; | |
+ XButtonEvent *be; | |
+ XMotionEvent *me; | |
+ | |
+ switch(e->type){ | |
+ case ButtonPress: | |
+ be = (XButtonEvent*)e; | |
+ /* BUG? on mac need to inherit these from elsewhere? */ | |
+ m->xy.x = be->x; | |
+ m->xy.y = be->y; | |
+ s = be->state; | |
+ m->msec = be->time; | |
+ switch(be->button){ | |
+ case 1: | |
+ s |= Button1Mask; | |
+ break; | |
+ case 2: | |
+ s |= Button2Mask; | |
+ break; | |
+ case 3: | |
+ s |= Button3Mask; | |
+ break; | |
+ } | |
+ break; | |
+ case ButtonRelease: | |
+ be = (XButtonEvent*)e; | |
+ m->xy.x = be->x; | |
+ m->xy.y = be->y; | |
+ s = be->state; | |
+ m->msec = be->time; | |
+ switch(be->button){ | |
+ case 1: | |
+ s &= ~Button1Mask; | |
+ break; | |
+ case 2: | |
+ s &= ~Button2Mask; | |
+ break; | |
+ case 3: | |
+ s &= ~Button3Mask; | |
+ break; | |
+ } | |
+ break; | |
+ | |
+ case MotionNotify: | |
+ me = (XMotionEvent*)e; | |
+ s = me->state; | |
+ m->xy.x = me->x; | |
+ m->xy.y = me->y; | |
+ m->msec = me->time; | |
+ break; | |
+ | |
+ default: | |
+ return -1; | |
+ } | |
+ | |
+ m->buttons = 0; | |
+ if(s & Button1Mask) | |
+ m->buttons |= 1; | |
+ if(s & Button2Mask) | |
+ m->buttons |= 2; | |
+ if(s & Button3Mask) | |
+ m->buttons |= 4; | |
+ | |
+ return 0; | |
+} | |
+ | |
+void | |
+xmoveto(Point p) | |
+{ | |
+ XWarpPointer(_x.display, None, _x.drawable, 0, 0, 0, 0, p.x, p.y); | |
+ XFlush(_x.display); | |
+} | |
+ | |
+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; | |
+} | |
+ | |
+static void | |
+xcursorarrow(void) | |
+{ | |
+ if(_x.cursor != 0){ | |
+ XFreeCursor(_x.display, _x.cursor); | |
+ _x.cursor = 0; | |
+ } | |
+ XUndefineCursor(_x.display, _x.drawable); | |
+ XFlush(_x.display); | |
+} | |
+ | |
+ | |
+void | |
+xsetcursor(Cursor *c) | |
+{ | |
+ XColor fg, bg; | |
+ XCursor xc; | |
+ Pixmap xsrc, xmask; | |
+ int i; | |
+ uchar src[2*16], mask[2*16]; | |
+ | |
+ if(c == nil){ | |
+ xcursorarrow(); | |
+ return; | |
+ } | |
+ for(i=0; i<2*16; i++){ | |
+ src[i] = revbyte(c->set[i]); | |
+ mask[i] = revbyte(c->set[i] | c->clr[i]); | |
+ } | |
+ | |
+ fg = _x.map[0]; | |
+ bg = _x.map[255]; | |
+ xsrc = XCreateBitmapFromData(_x.display, _x.drawable, src, 16, 16); | |
+ xmask = XCreateBitmapFromData(_x.display, _x.drawable, mask, 16, 16); | |
+ xc = XCreatePixmapCursor(_x.display, xsrc, xmask, &fg, &bg, -c->offset… | |
+ if(xc != 0) { | |
+ XDefineCursor(_x.display, _x.drawable, xc); | |
+ if(_x.cursor != 0) | |
+ XFreeCursor(_x.display, _x.cursor); | |
+ _x.cursor = xc; | |
+ } | |
+ XFreePixmap(_x.display, xsrc); | |
+ XFreePixmap(_x.display, xmask); | |
+ XFlush(_x.display); | |
+} | |
+ | |
diff --git a/src/libdraw/x11-keyboard.c b/src/libdraw/x11-keyboard.c | |
t@@ -0,0 +1,71 @@ | |
+#include "x11-inc.h" | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <thread.h> | |
+#include <memdraw.h> | |
+#include <keyboard.h> | |
+#include "x11-memdraw.h" | |
+ | |
+void | |
+closekeyboard(Keyboardctl *kc) | |
+{ | |
+ if(kc == nil) | |
+ return; | |
+ | |
+/* postnote(PNPROC, kc->pid, "kill"); | |
+*/ | |
+ | |
+#ifdef BUG | |
+ /* Drain the channel */ | |
+ while(?kc->c) | |
+ <-kc->c; | |
+#endif | |
+ | |
+ close(kc->ctlfd); | |
+ close(kc->consfd); | |
+ free(kc->file); | |
+ free(kc->c); | |
+ free(kc); | |
+} | |
+ | |
+static | |
+void | |
+_ioproc(void *arg) | |
+{ | |
+ int i; | |
+ Keyboardctl *kc; | |
+ Rune r; | |
+ XEvent xevent; | |
+ | |
+ kc = arg; | |
+ threadsetname("kbdproc"); | |
+ kc->pid = getpid(); | |
+ for(;;){ | |
+ XSelectInput(_x.kbdcon, _x.drawable, KeyPressMask); | |
+ XWindowEvent(_x.kbdcon, _x.drawable, KeyPressMask, &xevent); | |
+ switch(xevent.type){ | |
+ case KeyPress: | |
+ i = xtoplan9kbd(&xevent); | |
+ if(i == -1) | |
+ continue; | |
+ r = i; | |
+ send(kc->c, &r); | |
+ break; | |
+ } | |
+ } | |
+} | |
+ | |
+Keyboardctl* | |
+initkeyboard(char *file) | |
+{ | |
+ Keyboardctl *kc; | |
+ | |
+ kc = mallocz(sizeof(Keyboardctl), 1); | |
+ if(kc == nil) | |
+ return nil; | |
+ kc->c = chancreate(sizeof(Rune), 20); | |
+ proccreate(_ioproc, kc, 4096); | |
+ return kc; | |
+} | |
+ | |
diff --git a/src/libdraw/x11-load.c b/src/libdraw/x11-load.c | |
t@@ -0,0 +1,19 @@ | |
+#include "x11-inc.h" | |
+ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include "x11-memdraw.h" | |
+ | |
+int | |
+loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) | |
+{ | |
+ int n; | |
+ | |
+ n = _loadmemimage(i, r, data, ndata); | |
+ if(n > 0 && i->X) | |
+ xputxdata(i, r); | |
+ return n; | |
+} | |
+ | |
diff --git a/src/libdraw/x11-memdraw.h b/src/libdraw/x11-memdraw.h | |
t@@ -0,0 +1,93 @@ | |
+/* | |
+ * Structure pointed to by X field of Memimage | |
+ */ | |
+ | |
+typedef struct Xmem Xmem; | |
+typedef struct Xprivate Xprivate; | |
+ | |
+enum | |
+{ | |
+ PMundef = ~0 | |
+}; | |
+ | |
+struct Xmem | |
+{ | |
+ int pixmap; /* pixmap id */ | |
+ XImage *xi; /* local image */ | |
+ int dirty; /* is the X server ahead of us? */ | |
+ Rectangle dirtyr; /* which pixels? */ | |
+ Rectangle r; /* size of image */ | |
+}; | |
+ | |
+struct Xprivate { | |
+ u32int black; | |
+ u32int chan; | |
+ XColormap cmap; | |
+ XCursor cursor; | |
+ XDisplay *display; | |
+ int depth; /* of screen … | |
+ XDrawable drawable; | |
+ XColor map[256]; | |
+ XColor map7[128]; | |
+ uchar map7to8[128][2]; | |
+ XGC gccopy; | |
+ XGC gccopy0; | |
+ XGC gcfill; | |
+ u32int gcfillcolor; | |
+ XGC gcfill0; | |
+ u32int gcfill0color; | |
+ XGC gcreplsrc; | |
+ u32int gcreplsrctile; | |
+ XGC gcreplsrc0; | |
+ u32int gcreplsrc0tile; | |
+ XGC gcsimplesrc; | |
+ u32int gcsimplesrccolor; | |
+ u32int gcsimplesrcpixmap; | |
+ XGC gcsimplesrc0; | |
+ u32int gcsimplesrc0color; | |
+ u32int gcsimplesrc0pixmap; | |
+ XGC gczero; | |
+ u32int gczeropixmap; | |
+ XGC gczero0; | |
+ u32int gczero0pixmap; | |
+ XDisplay *kbdcon; | |
+ XDisplay *mousecon; | |
+ Memimage* screenimage; | |
+ XDrawable screenpm; | |
+ Rectangle screenr; | |
+ XDisplay *snarfcon; | |
+ int toplan9[256]; | |
+ int tox11[256]; | |
+ int usetable; | |
+ XVisual *vis; | |
+ u32int white; | |
+}; | |
+ | |
+extern Xprivate _x; | |
+ | |
+extern Memimage *xallocmemimage(Rectangle, u32int, int); | |
+extern XImage *xallocxdata(Memimage*, Rectangle); | |
+extern void xdirtyxdata(Memimage*, Rectangle); | |
+extern void xfillcolor(Memimage*, Rectangle, u32int); | |
+extern void xfreexdata(Memimage*); | |
+extern XImage *xgetxdata(Memimage*, Rectangle); | |
+extern void xputxdata(Memimage*, Rectangle); | |
+ | |
+struct Mouse; | |
+extern int xtoplan9mouse(XEvent*, struct Mouse*); | |
+extern int xtoplan9kbd(XEvent*); | |
+extern void xexpose(XEvent*, XDisplay*); | |
+extern int xconfigure(XEvent*, XDisplay*); | |
+extern void flushmemscreen(Rectangle); | |
+extern void xmoveto(Point); | |
+struct Cursor; | |
+extern void xsetcursor(struct Cursor*); | |
+ | |
+#define MouseMask (\ | |
+ ButtonPressMask|\ | |
+ ButtonReleaseMask|\ | |
+ PointerMotionMask|\ | |
+ Button1MotionMask|\ | |
+ Button2MotionMask|\ | |
+ Button3MotionMask) | |
+ | |
diff --git a/src/libdraw/x11-mouse.c b/src/libdraw/x11-mouse.c | |
t@@ -0,0 +1,107 @@ | |
+#include "x11-inc.h" | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <thread.h> | |
+#include <cursor.h> | |
+#include <mouse.h> | |
+#include <memdraw.h> | |
+#include "x11-memdraw.h" | |
+ | |
+void | |
+moveto(Mousectl *m, Point pt) | |
+{ | |
+ xmoveto(pt); | |
+} | |
+ | |
+void | |
+closemouse(Mousectl *mc) | |
+{ | |
+ if(mc == nil) | |
+ return; | |
+ | |
+/* postnote(PNPROC, mc->pid, "kill"); | |
+*/ | |
+ do; while(nbrecv(mc->c, &mc->m) > 0); | |
+ close(mc->mfd); | |
+ close(mc->cfd); | |
+ free(mc->file); | |
+ chanfree(mc->c); | |
+ chanfree(mc->resizec); | |
+ free(mc); | |
+} | |
+ | |
+int | |
+readmouse(Mousectl *mc) | |
+{ | |
+ if(mc->display) | |
+ flushimage(mc->display, 1); | |
+ if(recv(mc->c, &mc->m) < 0){ | |
+ fprint(2, "readmouse: %r\n"); | |
+ return -1; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+static | |
+void | |
+_ioproc(void *arg) | |
+{ | |
+ int one; | |
+ Mouse m; | |
+ Mousectl *mc; | |
+ XEvent xevent; | |
+ | |
+ one = 1; | |
+ mc = arg; | |
+ threadsetname("mouseproc"); | |
+ memset(&m, 0, sizeof m); | |
+ mc->pid = getpid(); | |
+ for(;;){ | |
+ XSelectInput(_x.mousecon, _x.drawable, MouseMask|ExposureMask|… | |
+ XWindowEvent(_x.mousecon, _x.drawable, MouseMask|ExposureMask|… | |
+ switch(xevent.type){ | |
+ case Expose: | |
+ xexpose(&xevent, _x.mousecon); | |
+ continue; | |
+ case ConfigureNotify: | |
+ if(xconfigure(&xevent, _x.mousecon)) | |
+ nbsend(mc->resizec, &one); | |
+ continue; | |
+ case ButtonPress: | |
+ case ButtonRelease: | |
+ case MotionNotify: | |
+ if(xtoplan9mouse(&xevent, &m) < 0) | |
+ continue; | |
+ send(mc->c, &m); | |
+ /* | |
+ * mc->Mouse is updated after send so it doesn't have … | |
+ * This means that programs should receive into mc->Mo… | |
+ * they want full synchrony. | |
+ */ | |
+ mc->m = m; | |
+ break; | |
+ } | |
+ } | |
+} | |
+ | |
+Mousectl* | |
+initmouse(char *file, Image *i) | |
+{ | |
+ Mousectl *mc; | |
+ | |
+ mc = mallocz(sizeof(Mousectl), 1); | |
+ if(i) | |
+ mc->display = i->display; | |
+ mc->c = chancreate(sizeof(Mouse), 0); | |
+ mc->resizec = chancreate(sizeof(int), 2); | |
+ proccreate(_ioproc, mc, 16384); | |
+ return mc; | |
+} | |
+ | |
+void | |
+setcursor(Mousectl *mc, Cursor *c) | |
+{ | |
+ xsetcursor(c); | |
+} | |
+ | |
diff --git a/src/libdraw/x11-pixelbits.c b/src/libdraw/x11-pixelbits.c | |
t@@ -0,0 +1,17 @@ | |
+#include "x11-inc.h" | |
+ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include "x11-memdraw.h" | |
+ | |
+u32int | |
+pixelbits(Memimage *m, Point p) | |
+{ | |
+ if(m->X) | |
+ xgetxdata(m, Rect(p.x, p.y, p.x+1, p.y+1)); | |
+ return _pixelbits(m, p); | |
+} | |
+ | |
+ | |
diff --git a/src/libdraw/x11-unload.c b/src/libdraw/x11-unload.c | |
t@@ -0,0 +1,16 @@ | |
+#include "x11-inc.h" | |
+ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <draw.h> | |
+#include <memdraw.h> | |
+#include "x11-memdraw.h" | |
+ | |
+int | |
+unloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) | |
+{ | |
+ if(i->X) | |
+ xgetxdata(i, r); | |
+ return _unloadmemimage(i, r, data, ndata); | |
+} | |
+ | |
diff --git a/src/libthread/386.c b/src/libthread/386.c | |
t@@ -0,0 +1,21 @@ | |
+#include "threadimpl.h" | |
+ | |
+static void | |
+launcher386(void (*f)(void *arg), void *arg) | |
+{ | |
+ (*f)(arg); | |
+ threadexits(nil); | |
+} | |
+ | |
+void | |
+_threadinitstack(Thread *t, void (*f)(void*), void *arg) | |
+{ | |
+ ulong *tos; | |
+ | |
+ tos = (ulong*)&t->stk[t->stksize&~7]; | |
+ *--tos = (ulong)arg; | |
+ *--tos = (ulong)f; | |
+ t->sched.pc = (ulong)launcher386; | |
+ t->sched.sp = (ulong)tos - 8; /* old PC and new PC */ | |
+} | |
+ | |
diff --git a/src/libthread/FreeBSD-386.s b/src/libthread/FreeBSD-386.s | |
t@@ -0,0 +1,18 @@ | |
+ | |
+.globl _xinc | |
+_xinc: | |
+ movl 4(%esp), %eax | |
+ lock incl 0(%eax) | |
+ ret | |
+ | |
+.globl _xdec | |
+_xdec: | |
+ movl 4(%esp), %eax | |
+ lock decl 0(%eax) | |
+ jz iszero | |
+ movl %eax, 1 | |
+ ret | |
+iszero: | |
+ movl %eax, 0 | |
+ ret | |
+ | |
diff --git a/src/libthread/LICENSE b/src/libthread/LICENSE | |
t@@ -0,0 +1,258 @@ | |
+The Plan 9 software is provided under the terms of the | |
+Lucent Public License, Version 1.02, reproduced below, | |
+with the following exceptions: | |
+ | |
+1. No right is granted to create derivative works of or | |
+ to redistribute (other than with the Plan 9 Operating System) | |
+ the screen imprinter fonts identified in subdirectory | |
+ /lib/font/bit/lucida and printer fonts (Lucida Sans Unicode, Lucida | |
+ Sans Italic, Lucida Sans Demibold, Lucida Typewriter, Lucida Sans | |
+ Typewriter83), identified in subdirectory /sys/lib/postscript/font. | |
+ These directories contain material copyrights by B&H Inc. and Y&Y Inc. | |
+ | |
+2. The printer fonts identified in subdirectory /sys/lib/ghostscript/font | |
+ are subject to the GNU GPL, reproduced in the file /LICENSE.gpl. | |
+ | |
+3. The ghostscript program in the subdirectory /sys/src/cmd/gs is | |
+ covered by the Aladdin Free Public License, reproduced in the file | |
+ /LICENSE.afpl. | |
+ | |
+=================================================================== | |
+ | |
+Lucent Public License Version 1.02 | |
+ | |
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC | |
+LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE | |
+PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. | |
+ | |
+1. DEFINITIONS | |
+ | |
+"Contribution" means: | |
+ | |
+ a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original | |
+ Program, and | |
+ b. in the case of each Contributor, | |
+ | |
+ i. changes to the Program, and | |
+ ii. additions to the Program; | |
+ | |
+ where such changes and/or additions to the Program were added to the | |
+ Program by such Contributor itself or anyone acting on such | |
+ Contributor's behalf, and the Contributor explicitly consents, in | |
+ accordance with Section 3C, to characterization of the changes and/or | |
+ additions as Contributions. | |
+ | |
+"Contributor" means LUCENT and any other entity that has Contributed a | |
+Contribution to the Program. | |
+ | |
+"Distributor" means a Recipient that distributes the Program, | |
+modifications to the Program, or any part thereof. | |
+ | |
+"Licensed Patents" mean patent claims licensable by a Contributor | |
+which are necessarily infringed by the use or sale of its Contribution | |
+alone or when combined with the Program. | |
+ | |
+"Original Program" means the original version of the software | |
+accompanying this Agreement as released by LUCENT, including source | |
+code, object code and documentation, if any. | |
+ | |
+"Program" means the Original Program and Contributions or any part | |
+thereof | |
+ | |
+"Recipient" means anyone who receives the Program under this | |
+Agreement, including all Contributors. | |
+ | |
+2. GRANT OF RIGHTS | |
+ | |
+ a. Subject to the terms of this Agreement, each Contributor hereby | |
+ grants Recipient a non-exclusive, worldwide, royalty-free copyright | |
+ license to reproduce, prepare derivative works of, publicly display, | |
+ publicly perform, distribute and sublicense the Contribution of such | |
+ Contributor, if any, and such derivative works, in source code and | |
+ object code form. | |
+ | |
+ b. Subject to the terms of this Agreement, each Contributor hereby | |
+ grants Recipient a non-exclusive, worldwide, royalty-free patent | |
+ license under Licensed Patents to make, use, sell, offer to sell, | |
+ import and otherwise transfer the Contribution of such Contributor, if | |
+ any, in source code and object code form. The patent license granted | |
+ by a Contributor shall also apply to the combination of the | |
+ Contribution of that Contributor and the Program if, at the time the | |
+ Contribution is added by the Contributor, such addition of the | |
+ Contribution causes such combination to be covered by the Licensed | |
+ Patents. The patent license granted by a Contributor shall not apply | |
+ to (i) any other combinations which include the Contribution, nor to | |
+ (ii) Contributions of other Contributors. No hardware per se is | |
+ licensed hereunder. | |
+ | |
+ c. Recipient understands that although each Contributor grants the | |
+ licenses to its Contributions set forth herein, no assurances are | |
+ provided by any Contributor that the Program does not infringe the | |
+ patent or other intellectual property rights of any other entity. Each | |
+ Contributor disclaims any liability to Recipient for claims brought by | |
+ any other entity based on infringement of intellectual property rights | |
+ or otherwise. As a condition to exercising the rights and licenses | |
+ granted hereunder, each Recipient hereby assumes sole responsibility | |
+ to secure any other intellectual property rights needed, if any. For | |
+ example, if a third party patent license is required to allow | |
+ Recipient to distribute the Program, it is Recipient's responsibility | |
+ to acquire that license before distributing the Program. | |
+ | |
+ d. Each Contributor represents that to its knowledge it has sufficient | |
+ copyright rights in its Contribution, if any, to grant the copyright | |
+ license set forth in this Agreement. | |
+ | |
+3. REQUIREMENTS | |
+ | |
+A. Distributor may choose to distribute the Program in any form under | |
+this Agreement or under its own license agreement, provided that: | |
+ | |
+ a. it complies with the terms and conditions of this Agreement; | |
+ | |
+ b. if the Program is distributed in source code or other tangible | |
+ form, a copy of this Agreement or Distributor's own license agreement | |
+ is included with each copy of the Program; and | |
+ | |
+ c. if distributed under Distributor's own license agreement, such | |
+ license agreement: | |
+ | |
+ i. effectively disclaims on behalf of all Contributors all warranties | |
+ and conditions, express and implied, including warranties or | |
+ conditions of title and non-infringement, and implied warranties or | |
+ conditions of merchantability and fitness for a particular purpose; | |
+ ii. effectively excludes on behalf of all Contributors all liability | |
+ for damages, including direct, indirect, special, incidental and | |
+ consequential damages, such as lost profits; and | |
+ iii. states that any provisions which differ from this Agreement are | |
+ offered by that Contributor alone and not by any other party. | |
+ | |
+B. Each Distributor must include the following in a conspicuous | |
+ location in the Program: | |
+ | |
+ Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights | |
+ Reserved. | |
+ | |
+C. In addition, each Contributor must identify itself as the | |
+originator of its Contribution in a manner that reasonably allows | |
+subsequent Recipients to identify the originator of the Contribution. | |
+Also, each Contributor must agree that the additions and/or changes | |
+are intended to be a Contribution. Once a Contribution is contributed, | |
+it may not thereafter be revoked. | |
+ | |
+4. COMMERCIAL DISTRIBUTION | |
+ | |
+Commercial distributors of software may accept certain | |
+responsibilities with respect to end users, business partners and the | |
+like. While this license is intended to facilitate the commercial use | |
+of the Program, the Distributor who includes the Program in a | |
+commercial product offering should do so in a manner which does not | |
+create potential liability for Contributors. Therefore, if a | |
+Distributor includes the Program in a commercial product offering, | |
+such Distributor ("Commercial Distributor") hereby agrees to defend | |
+and indemnify every Contributor ("Indemnified Contributor") against | |
+any losses, damages and costs (collectively"Losses") arising from | |
+claims, lawsuits and other legal actions brought by a third party | |
+against the Indemnified Contributor to the extent caused by the acts | |
+or omissions of such Commercial Distributor in connection with its | |
+distribution of the Program in a commercial product offering. The | |
+obligations in this section do not apply to any claims or Losses | |
+relating to any actual or alleged intellectual property infringement. | |
+In order to qualify, an Indemnified Contributor must: a) promptly | |
+notify the Commercial Distributor in writing of such claim, and b) | |
+allow the Commercial Distributor to control, and cooperate with the | |
+Commercial Distributor in, the defense and any related settlement | |
+negotiations. The Indemnified Contributor may participate in any such | |
+claim at its own expense. | |
+ | |
+For example, a Distributor might include the Program in a commercial | |
+product offering, Product X. That Distributor is then a Commercial | |
+Distributor. If that Commercial Distributor then makes performance | |
+claims, or offers warranties related to Product X, those performance | |
+claims and warranties are such Commercial Distributor's responsibility | |
+alone. Under this section, the Commercial Distributor would have to | |
+defend claims against the Contributors related to those performance | |
+claims and warranties, and if a court requires any Contributor to pay | |
+any damages as a result, the Commercial Distributor must pay those | |
+damages. | |
+ | |
+5. NO WARRANTY | |
+ | |
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS | |
+PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
+KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY | |
+WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY | |
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely | |
+responsible for determining the appropriateness of using and | |
+distributing the Program and assumes all risks associated with its | |
+exercise of rights under this Agreement, including but not limited to | |
+the risks and costs of program errors, compliance with applicable | |
+laws, damage to or loss of data, programs or equipment, and | |
+unavailability or interruption of operations. | |
+ | |
+6. DISCLAIMER OF LIABILITY | |
+ | |
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR | |
+ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, | |
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING | |
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF | |
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR | |
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED | |
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
+ | |
+7. EXPORT CONTROL | |
+ | |
+Recipient agrees that Recipient alone is responsible for compliance | |
+with the United States export administration regulations (and the | |
+export control laws and regulation of any other countries). | |
+ | |
+8. GENERAL | |
+ | |
+If any provision of this Agreement is invalid or unenforceable under | |
+applicable law, it shall not affect the validity or enforceability of | |
+the remainder of the terms of this Agreement, and without further | |
+action by the parties hereto, such provision shall be reformed to the | |
+minimum extent necessary to make such provision valid and enforceable. | |
+ | |
+If Recipient institutes patent litigation against a Contributor with | |
+respect to a patent applicable to software (including a cross-claim or | |
+counterclaim in a lawsuit), then any patent licenses granted by that | |
+Contributor to such Recipient under this Agreement shall terminate as | |
+of the date such litigation is filed. In addition, if Recipient | |
+institutes patent litigation against any entity (including a | |
+cross-claim or counterclaim in a lawsuit) alleging that the Program | |
+itself (excluding combinations of the Program with other software or | |
+hardware) infringes such Recipient's patent(s), then such Recipient's | |
+rights granted under Section 2(b) shall terminate as of the date such | |
+litigation is filed. | |
+ | |
+All Recipient's rights under this Agreement shall terminate if it | |
+fails to comply with any of the material terms or conditions of this | |
+Agreement and does not cure such failure in a reasonable period of | |
+time after becoming aware of such noncompliance. If all Recipient's | |
+rights under this Agreement terminate, Recipient agrees to cease use | |
+and distribution of the Program as soon as reasonably practicable. | |
+However, Recipient's obligations under this Agreement and any licenses | |
+granted by Recipient relating to the Program shall continue and | |
+survive. | |
+ | |
+LUCENT may publish new versions (including revisions) of this | |
+Agreement from time to time. Each new version of the Agreement will be | |
+given a distinguishing version number. The Program (including | |
+Contributions) may always be distributed subject to the version of the | |
+Agreement under which it was received. In addition, after a new | |
+version of the Agreement is published, Contributor may elect to | |
+distribute the Program (including its Contributions) under the new | |
+version. No one other than LUCENT has the right to modify this | |
+Agreement. Except as expressly stated in Sections 2(a) and 2(b) above, | |
+Recipient receives no rights or licenses to the intellectual property | |
+of any Contributor under this Agreement, whether expressly, by | |
+implication, estoppel or otherwise. All rights in the Program not | |
+expressly granted under this Agreement are reserved. | |
+ | |
+This Agreement is governed by the laws of the State of New York and | |
+the intellectual property laws of the United States of America. No | |
+party to this Agreement will bring a legal action under this Agreement | |
+more than one year after the cause of action arose. Each party waives | |
+its rights to a jury trial in any resulting litigation. | |
+ | |
diff --git a/src/libthread/Make.Darwin-PowerMacintosh b/src/libthread/Make.Darw… | |
t@@ -0,0 +1,6 @@ | |
+CC=gcc | |
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -… | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O | |
diff --git a/src/libthread/Make.FreeBSD-386 b/src/libthread/Make.FreeBSD-386 | |
t@@ -0,0 +1,7 @@ | |
+CC=gcc | |
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -… | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME) | |
+NAN=nan64.$O | |
diff --git a/src/libthread/Make.HP-UX-9000 b/src/libthread/Make.HP-UX-9000 | |
t@@ -0,0 +1,6 @@ | |
+CC=cc | |
+CFLAGS=-O -c -Ae -I. | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O | |
diff --git a/src/libthread/Make.Linux-386 b/src/libthread/Make.Linux-386 | |
t@@ -0,0 +1,7 @@ | |
+CC=gcc | |
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME) | |
+NAN=nan64.$O | |
diff --git a/src/libthread/Make.NetBSD-386 b/src/libthread/Make.NetBSD-386 | |
t@@ -0,0 +1,7 @@ | |
+CC=gcc | |
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -… | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME) | |
+NAN=nan64.$O | |
diff --git a/src/libthread/Make.OSF1-alpha b/src/libthread/Make.OSF1-alpha | |
t@@ -0,0 +1,6 @@ | |
+CC=cc | |
+CFLAGS+=-g -c -I. | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O | |
diff --git a/src/libthread/Make.SunOS-sun4u b/src/libthread/Make.SunOS-sun4u | |
t@@ -0,0 +1,2 @@ | |
+include Make.SunOS-sun4u-$(CC) | |
+NAN=nan64.$O | |
diff --git a/src/libthread/Make.SunOS-sun4u-cc b/src/libthread/Make.SunOS-sun4u… | |
t@@ -0,0 +1,6 @@ | |
+CC=cc | |
+CFLAGS+=-g -c -I. -O | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O | |
diff --git a/src/libthread/Make.SunOS-sun4u-gcc b/src/libthread/Make.SunOS-sun4… | |
t@@ -0,0 +1,6 @@ | |
+CC=gcc | |
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c | |
+O=o | |
+AR=ar | |
+ARFLAGS=rvc | |
+NAN=nan64.$O | |
diff --git a/src/libthread/Makefile b/src/libthread/Makefile | |
t@@ -0,0 +1,125 @@ | |
+ | |
+# this works in gnu make | |
+SYSNAME:=${shell uname} | |
+OBJTYPE:=${shell uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g'} | |
+ | |
+# this works in bsd make | |
+SYSNAME!=uname | |
+OBJTYPE!=uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g' | |
+ | |
+# the gnu rules will mess up bsd but not vice versa, | |
+# hence the gnu rules come first. | |
+ | |
+include Make.$(SYSNAME)-$(OBJTYPE) | |
+ | |
+PREFIX=/usr/local | |
+ | |
+NUKEFILES= | |
+ | |
+TGZFILES= | |
+ | |
+LIB=libthread.a | |
+VERSION=2.0 | |
+PORTPLACE=devel/libthread | |
+NAME=libthread | |
+ | |
+OFILES=\ | |
+ $(OBJTYPE).$O\ | |
+ asm-$(SYSNAME)-$(OBJTYPE).$O\ | |
+ channel.$O\ | |
+ chanprint.$O\ | |
+ create.$O\ | |
+ debug.$O\ | |
+ exec-unix.$O\ | |
+ exit.$O\ | |
+ getpid.$O\ | |
+ id.$O\ | |
+ iocall.$O\ | |
+ ioclose.$O\ | |
+ ioopen.$O\ | |
+ ioproc.$O\ | |
+ ioread.$O\ | |
+ ioreadn.$O\ | |
+ iowrite.$O\ | |
+ kill.$O\ | |
+ lib.$O\ | |
+ main.$O\ | |
+ memset.$O\ | |
+ memsetd.$O\ | |
+ note.$O\ | |
+ proctab.$O\ | |
+ ref.$O\ | |
+ rendez.$O\ | |
+ sched.$O\ | |
+ | |
+HFILES=\ | |
+ thread.h\ | |
+ label.h\ | |
+ threadimpl.h\ | |
+ | |
+all: $(LIB) | |
+ | |
+install: $(LIB) | |
+ test -d $(PREFIX)/man/man3 || mkdir $(PREFIX)/man/man3 | |
+ install -m 0644 thread.3 $(PREFIX)/man/man3/thread.3 | |
+ install -m 0644 ioproc.3 $(PREFIX)/man/man3/ioproc.3 | |
+ install -m 0644 thread.h $(PREFIX)/include/thread.h | |
+ install -m 0644 $(LIB) $(PREFIX)/lib/$(LIB) | |
+ | |
+tprimes: $(LIB) tprimes.$O | |
+ $(CC) -o tprimes tprimes.$O $(LIB) -L$(PREFIX)/lib -l9 -lfmt -lutf | |
+ | |
+texec: $(LIB) texec.$O | |
+ $(CC) -o texec texec.$O $(LIB) -L$(PREFIX)/lib -l9 -lfmt -lutf | |
+ | |
+$(LIB): $(OFILES) | |
+ $(AR) $(ARFLAGS) $(LIB) $(OFILES) | |
+ | |
+NUKEFILES+=$(LIB) | |
+.c.$O: | |
+ $(CC) $(CFLAGS) -I/usr/X11R6/include -I../sam -I$(PREFIX)/include $*.c | |
+ | |
+%.$O: %.c | |
+ $(CC) $(CFLAGS) -I/usr/X11R6/include -I../sam -I$(PREFIX)/include $*.c | |
+ | |
+ | |
+$(OFILES): $(HFILES) | |
+ | |
+tgz: | |
+ rm -rf $(NAME)-$(VERSION) | |
+ mkdir $(NAME)-$(VERSION) | |
+ cp Makefile Make.* README LICENSE NOTICE *.[ch137] rpm.spec bundle.por… | |
+ tar cf - $(NAME)-$(VERSION) | gzip >$(NAME)-$(VERSION).tgz | |
+ rm -rf $(NAME)-$(VERSION) | |
+ | |
+clean: | |
+ rm -f $(OFILES) $(LIB) | |
+ | |
+nuke: | |
+ rm -f $(OFILES) *.tgz *.rpm $(NUKEFILES) | |
+ | |
+rpm: | |
+ make tgz | |
+ cp $(NAME)-$(VERSION).tgz /usr/src/RPM/SOURCES | |
+ rpm -ba rpm.spec | |
+ cp /usr/src/RPM/SRPMS/$(NAME)-$(VERSION)-1.src.rpm . | |
+ cp /usr/src/RPM/RPMS/i586/$(NAME)-$(VERSION)-1.i586.rpm . | |
+ scp *.rpm [email protected]:public_html/software | |
+ | |
+PORTDIR=/usr/ports/$(PORTPLACE) | |
+ | |
+ports: | |
+ make tgz | |
+ rm -rf $(PORTDIR) | |
+ mkdir $(PORTDIR) | |
+ cp $(NAME)-$(VERSION).tgz /usr/ports/distfiles | |
+ cat bundle.ports | (cd $(PORTDIR) && awk '$$1=="---" && $$3=="---" { o… | |
+ (cd $(PORTDIR); make makesum) | |
+ (cd $(PORTDIR); make) | |
+ (cd $(PORTDIR); /usr/local/bin/portlint) | |
+ rm -rf $(PORTDIR)/work | |
+ shar `find $(PORTDIR)` > ports.shar | |
+ (cd $(PORTDIR); tar cf - *) | gzip >$(NAME)-$(VERSION)-ports.tgz | |
+ scp *.tgz [email protected]:public_html/software | |
+ | |
+.phony: all clean nuke install tgz rpm ports | |
diff --git a/src/libthread/Makefile.MID b/src/libthread/Makefile.MID | |
t@@ -0,0 +1,54 @@ | |
+LIB=libthread.a | |
+VERSION=2.0 | |
+PORTPLACE=devel/libthread | |
+NAME=libthread | |
+ | |
+OFILES=\ | |
+ $(OBJTYPE).$O\ | |
+ asm-$(SYSNAME)-$(OBJTYPE).$O\ | |
+ channel.$O\ | |
+ chanprint.$O\ | |
+ create.$O\ | |
+ debug.$O\ | |
+ exec-unix.$O\ | |
+ exit.$O\ | |
+ getpid.$O\ | |
+ id.$O\ | |
+ iocall.$O\ | |
+ ioclose.$O\ | |
+ ioopen.$O\ | |
+ ioproc.$O\ | |
+ ioread.$O\ | |
+ ioreadn.$O\ | |
+ iowrite.$O\ | |
+ kill.$O\ | |
+ lib.$O\ | |
+ main.$O\ | |
+ memset.$O\ | |
+ memsetd.$O\ | |
+ note.$O\ | |
+ proctab.$O\ | |
+ ref.$O\ | |
+ rendez.$O\ | |
+ sched.$O\ | |
+ | |
+HFILES=\ | |
+ thread.h\ | |
+ label.h\ | |
+ threadimpl.h\ | |
+ | |
+all: $(LIB) | |
+ | |
+install: $(LIB) | |
+ test -d $(PREFIX)/man/man3 || mkdir $(PREFIX)/man/man3 | |
+ install -m 0644 thread.3 $(PREFIX)/man/man3/thread.3 | |
+ install -m 0644 ioproc.3 $(PREFIX)/man/man3/ioproc.3 | |
+ install -m 0644 thread.h $(PREFIX)/include/thread.h | |
+ install -m 0644 $(LIB) $(PREFIX)/lib/$(LIB) | |
+ | |
+tprimes: $(LIB) tprimes.$O | |
+ $(CC) -o tprimes tprimes.$O $(LIB) -L$(PREFIX)/lib -l9 -lfmt -lutf | |
+ | |
+texec: $(LIB) texec.$O | |
+ $(CC) -o texec texec.$O $(LIB) -L$(PREFIX)/lib -l9 -lfmt -lutf | |
+ | |
diff --git a/src/libthread/NOTICE b/src/libthread/NOTICE | |
t@@ -0,0 +1,19 @@ | |
+/* | |
+ * The authors of this software are Russ Cox, Sape Mullender, and Rob Pike. | |
+ * Copyright (c) 2003 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 … | |
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY | |
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. | |
+*/ | |
+ | |
+This is a Unix port of the Plan 9 thread library. | |
+ | |
+Please send comments about the packaging | |
+to Russ Cox <[email protected]>. | |
+ | |
diff --git a/src/libthread/README b/src/libthread/README | |
t@@ -0,0 +1,19 @@ | |
+/* | |
+ * The authors of this software are Russ Cox, Sape Mullender, and Rob Pike. | |
+ * Copyright (c) 2003 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 … | |
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY | |
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. | |
+*/ | |
+ | |
+This is a Unix port of the Plan 9 thread library. | |
+ | |
+Please send comments about the packaging | |
+to Russ Cox <[email protected]>. | |
+ | |
diff --git a/src/libthread/asm-FreeBSD-386.s b/src/libthread/asm-FreeBSD-386.s | |
t@@ -0,0 +1,49 @@ | |
+.globl _setlabel | |
+.type _setlabel,@function | |
+ | |
+_setlabel: | |
+ movl 4(%esp), %eax | |
+ movl 0(%esp), %edx | |
+ movl %edx, 0(%eax) | |
+ movl %ebx, 4(%eax) | |
+ movl %esp, 8(%eax) | |
+ movl %ebp, 12(%eax) | |
+ movl %esi, 16(%eax) | |
+ movl %edi, 20(%eax) | |
+ xorl %eax, %eax | |
+ ret | |
+ | |
+.globl _gotolabel | |
+.type _gotolabel,@function | |
+ | |
+_gotolabel: | |
+ movl 4(%esp), %edx | |
+ movl 0(%edx), %ecx | |
+ movl 4(%edx), %ebx | |
+ movl 8(%edx), %esp | |
+ movl 12(%edx), %ebp | |
+ movl 16(%edx), %esi | |
+ movl 20(%edx), %edi | |
+ xorl %eax, %eax | |
+ incl %eax | |
+ movl %ecx, 0(%esp) | |
+ ret | |
+ | |
+ | |
+.globl _xinc | |
+_xinc: | |
+ movl 4(%esp), %eax | |
+ lock incl 0(%eax) | |
+ ret | |
+ | |
+.globl _xdec | |
+_xdec: | |
+ movl 4(%esp), %eax | |
+ lock decl 0(%eax) | |
+ jz iszero | |
+ movl %eax, 1 | |
+ ret | |
+iszero: | |
+ movl %eax, 0 | |
+ ret | |
+ | |
diff --git a/src/libthread/asm-Linux-386.s b/src/libthread/asm-Linux-386.s | |
t@@ -0,0 +1 @@ | |
+.include "asm-FreeBSD-386.s" | |
diff --git a/src/libthread/bundle.ports b/src/libthread/bundle.ports | |
t@@ -0,0 +1,42 @@ | |
+--- Makefile --- | |
+# New ports collection makefile for: libthread | |
+# Date Created: 11 Feb 2003 | |
+# Whom: rsc | |
+# | |
+ | |
+PORTNAME= libthread | |
+PORTVERSION= 1.0 | |
+CATEGORIES= devel | |
+MASTER_SITES= http://pdos.lcs.mit.edu/~rsc/software/ | |
+DISTNAME= libthread | |
+EXTRACT_SUFX= .tgz | |
+ | |
+MAINTAINER= [email protected] | |
+ | |
+MAN3= print.3 fmtinstall.3 | |
+MLINKS= XXX | |
+USE_REINPLACE= XXX (wkj says yes) | |
+ | |
+.include <bsd.port.pre.mk> | |
+ | |
+post-patch: | |
+ ${REINPLACE_CMD} -e 's,@@LOCAL@@,${PREFIX},g' ${WRKSRC}/Makefile | |
+ | |
+.include <bsd.port.post.mk> | |
+ | |
+--- pkg-comment --- | |
+Plan 9 thread library | |
+--- pkg-descr --- | |
+Libthread is a port of Plan 9's thread library. | |
+ | |
+WWW: http://pdos.lcs.mit.edu/~rsc/software/ | |
+WWW: http://plan9.bell-labs.com/magic/man2html/2/thread | |
+ | |
+Russ Cox | |
[email protected] | |
+--- pkg-plist --- | |
+lib/libthread.a | |
+include/thread.h | |
+--- /dev/null --- | |
+This is just a way to make sure blank lines don't | |
+creep into pkg-plist. | |
diff --git a/src/libthread/channel.c b/src/libthread/channel.c | |
t@@ -0,0 +1,485 @@ | |
+#include "threadimpl.h" | |
+ | |
+static Lock chanlock; /* central channel access lock */ | |
+ | |
+static void enqueue(Alt*, Channel**); | |
+static void dequeue(Alt*); | |
+static int altexec(Alt*, int); | |
+ | |
+int _threadhighnentry; | |
+int _threadnalt; | |
+ | |
+static int | |
+canexec(Alt *a) | |
+{ | |
+ int i, otherop; | |
+ Channel *c; | |
+ | |
+ c = a->c; | |
+ /* are there senders or receivers blocked? */ | |
+ otherop = (CHANSND+CHANRCV) - a->op; | |
+ for(i=0; i<c->nentry; i++) | |
+ if(c->qentry[i] && c->qentry[i]->op==otherop && *c->qentry[i]-… | |
+ _threaddebug(DBGCHAN, "can rendez alt %p chan %p", a, … | |
+ return 1; | |
+ } | |
+ | |
+ /* is there room in the channel? */ | |
+ if((a->op==CHANSND && c->n < c->s) | |
+ || (a->op==CHANRCV && c->n > 0)){ | |
+ _threaddebug(DBGCHAN, "can buffer alt %p chan %p", a, c); | |
+ return 1; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void | |
+_chanfree(Channel *c) | |
+{ | |
+ int i, inuse; | |
+ | |
+ inuse = 0; | |
+ for(i = 0; i < c->nentry; i++) | |
+ if(c->qentry[i]) | |
+ inuse = 1; | |
+ if(inuse) | |
+ c->freed = 1; | |
+ else{ | |
+ if(c->qentry) | |
+ free(c->qentry); | |
+ free(c); | |
+ } | |
+} | |
+ | |
+void | |
+chanfree(Channel *c) | |
+{ | |
+ lock(&chanlock); | |
+ _chanfree(c); | |
+ unlock(&chanlock); | |
+} | |
+ | |
+int | |
+chaninit(Channel *c, int elemsize, int elemcnt) | |
+{ | |
+ if(elemcnt < 0 || elemsize <= 0 || c == nil) | |
+ return -1; | |
+ c->f = 0; | |
+ c->n = 0; | |
+ c->freed = 0; | |
+ c->e = elemsize; | |
+ c->s = elemcnt; | |
+ _threaddebug(DBGCHAN, "chaninit %p", c); | |
+ return 1; | |
+} | |
+ | |
+Channel* | |
+chancreate(int elemsize, int elemcnt) | |
+{ | |
+ Channel *c; | |
+ | |
+ if(elemcnt < 0 || elemsize <= 0) | |
+ return nil; | |
+ c = _threadmalloc(sizeof(Channel)+elemsize*elemcnt, 1); | |
+ c->e = elemsize; | |
+ c->s = elemcnt; | |
+ _threaddebug(DBGCHAN, "chancreate %p", c); | |
+ return c; | |
+} | |
+ | |
+int | |
+alt(Alt *alts) | |
+{ | |
+ Alt *a, *xa; | |
+ Channel *volatile c; | |
+ int n, s; | |
+ ulong r; | |
+ Thread *t; | |
+ | |
+ /* | |
+ * The point of going splhi here is that note handlers | |
+ * might reasonably want to use channel operations, | |
+ * but that will hang if the note comes while we hold the | |
+ * chanlock. Instead, we delay the note until we've dropped | |
+ * the lock. | |
+ */ | |
+ t = _threadgetproc()->thread; | |
+ if(t->moribund || _threadexitsallstatus) | |
+ yield(); /* won't return */ | |
+ s = _procsplhi(); | |
+ lock(&chanlock); | |
+ t->alt = alts; | |
+ t->chan = Chanalt; | |
+ | |
+ /* test whether any channels can proceed */ | |
+ n = 0; | |
+ a = nil; | |
+ | |
+ for(xa=alts; xa->op!=CHANEND && xa->op!=CHANNOBLK; xa++){ | |
+ xa->entryno = -1; | |
+ if(xa->op == CHANNOP) | |
+ continue; | |
+ | |
+ c = xa->c; | |
+ if(c==nil){ | |
+ unlock(&chanlock); | |
+ _procsplx(s); | |
+ t->chan = Channone; | |
+ return -1; | |
+ } | |
+ if(canexec(xa)) | |
+ if(nrand(++n) == 0) | |
+ a = xa; | |
+ } | |
+ | |
+ if(a==nil){ | |
+ /* nothing can proceed */ | |
+ if(xa->op == CHANNOBLK){ | |
+ unlock(&chanlock); | |
+ _procsplx(s); | |
+ t->chan = Channone; | |
+_threadnalt++; | |
+ return xa - alts; | |
+ } | |
+ | |
+ /* enqueue on all channels. */ | |
+ c = nil; | |
+ for(xa=alts; xa->op!=CHANEND; xa++){ | |
+ if(xa->op==CHANNOP) | |
+ continue; | |
+ enqueue(xa, (Channel**)&c); | |
+ } | |
+ | |
+ /* | |
+ * wait for successful rendezvous. | |
+ * we can't just give up if the rendezvous | |
+ * is interrupted -- someone else might come | |
+ * along and try to rendezvous with us, so | |
+ * we need to be here. | |
+ */ | |
+ Again: | |
+ unlock(&chanlock); | |
+ _procsplx(s); | |
+ r = _threadrendezvous((ulong)&c, 0); | |
+ s = _procsplhi(); | |
+ lock(&chanlock); | |
+ | |
+ if(r==~0){ /* interrupted */ | |
+ if(c!=nil) /* someone will meet us; go … | |
+ goto Again; | |
+ c = (Channel*)~0; /* so no one tries to meet us… | |
+ } | |
+ | |
+ /* dequeue from channels, find selected one */ | |
+ a = nil; | |
+ for(xa=alts; xa->op!=CHANEND; xa++){ | |
+ if(xa->op==CHANNOP) | |
+ continue; | |
+ if(xa->c == c) | |
+ a = xa; | |
+ dequeue(xa); | |
+ } | |
+ unlock(&chanlock); | |
+ _procsplx(s); | |
+ if(a == nil){ /* we were interrupted */ | |
+ assert(c==(Channel*)~0); | |
+ return -1; | |
+ } | |
+ }else{ | |
+ altexec(a, s); /* unlocks chanlock, does splx */ | |
+ } | |
+ _sched(); | |
+ t->chan = Channone; | |
+_threadnalt++; | |
+ return a - alts; | |
+} | |
+ | |
+static int | |
+runop(int op, Channel *c, void *v, int nb) | |
+{ | |
+ int r; | |
+ Alt a[2]; | |
+ | |
+ /* | |
+ * we could do this without calling alt, | |
+ * but the only reason would be performance, | |
+ * and i'm not convinced it matters. | |
+ */ | |
+ a[0].op = op; | |
+ a[0].c = c; | |
+ a[0].v = v; | |
+ a[1].op = CHANEND; | |
+ if(nb) | |
+ a[1].op = CHANNOBLK; | |
+ switch(r=alt(a)){ | |
+ case -1: /* interrupted */ | |
+ return -1; | |
+ case 1: /* nonblocking, didn't accomplish anything */ | |
+ assert(nb); | |
+ return 0; | |
+ case 0: | |
+ return 1; | |
+ default: | |
+ fprint(2, "ERROR: channel alt returned %d\n", r); | |
+ abort(); | |
+ return -1; | |
+ } | |
+} | |
+ | |
+int | |
+recv(Channel *c, void *v) | |
+{ | |
+ return runop(CHANRCV, c, v, 0); | |
+} | |
+ | |
+int | |
+nbrecv(Channel *c, void *v) | |
+{ | |
+ return runop(CHANRCV, c, v, 1); | |
+} | |
+ | |
+int | |
+send(Channel *c, void *v) | |
+{ | |
+ return runop(CHANSND, c, v, 0); | |
+} | |
+ | |
+int | |
+nbsend(Channel *c, void *v) | |
+{ | |
+ return runop(CHANSND, c, v, 1); | |
+} | |
+ | |
+static void | |
+channelsize(Channel *c, int sz) | |
+{ | |
+ if(c->e != sz){ | |
+ fprint(2, "expected channel with elements of size %d, got size… | |
+ sz, c->e); | |
+ abort(); | |
+ } | |
+} | |
+ | |
+int | |
+sendul(Channel *c, ulong v) | |
+{ | |
+ channelsize(c, sizeof(ulong)); | |
+ return send(c, &v); | |
+} | |
+ | |
+ulong | |
+recvul(Channel *c) | |
+{ | |
+ ulong v; | |
+ | |
+ channelsize(c, sizeof(ulong)); | |
+ if(recv(c, &v) < 0) | |
+ return ~0; | |
+ return v; | |
+} | |
+ | |
+int | |
+sendp(Channel *c, void *v) | |
+{ | |
+ channelsize(c, sizeof(void*)); | |
+ return send(c, &v); | |
+} | |
+ | |
+void* | |
+recvp(Channel *c) | |
+{ | |
+ void *v; | |
+ | |
+ channelsize(c, sizeof(void*)); | |
+ if(recv(c, &v) < 0) | |
+ return nil; | |
+ return v; | |
+} | |
+ | |
+int | |
+nbsendul(Channel *c, ulong v) | |
+{ | |
+ channelsize(c, sizeof(ulong)); | |
+ return nbsend(c, &v); | |
+} | |
+ | |
+ulong | |
+nbrecvul(Channel *c) | |
+{ | |
+ ulong v; | |
+ | |
+ channelsize(c, sizeof(ulong)); | |
+ if(nbrecv(c, &v) == 0) | |
+ return 0; | |
+ return v; | |
+} | |
+ | |
+int | |
+nbsendp(Channel *c, void *v) | |
+{ | |
+ channelsize(c, sizeof(void*)); | |
+ return nbsend(c, &v); | |
+} | |
+ | |
+void* | |
+nbrecvp(Channel *c) | |
+{ | |
+ void *v; | |
+ | |
+ channelsize(c, sizeof(void*)); | |
+ if(nbrecv(c, &v) == 0) | |
+ return nil; | |
+ return v; | |
+} | |
+ | |
+static int | |
+emptyentry(Channel *c) | |
+{ | |
+ int i, extra; | |
+ | |
+ assert((c->nentry==0 && c->qentry==nil) || (c->nentry && c->qentry)); | |
+ | |
+ for(i=0; i<c->nentry; i++) | |
+ if(c->qentry[i]==nil) | |
+ return i; | |
+ | |
+ extra = 16; | |
+ c->nentry += extra; | |
+if(c->nentry > _threadhighnentry) _threadhighnentry = c->nentry; | |
+ c->qentry = realloc((void*)c->qentry, c->nentry*sizeof(c->qentry[0])); | |
+ if(c->qentry == nil) | |
+ sysfatal("realloc channel entries: %r"); | |
+ _threadmemset(&c->qentry[i], 0, extra*sizeof(c->qentry[0])); | |
+ return i; | |
+} | |
+ | |
+static void | |
+enqueue(Alt *a, Channel **c) | |
+{ | |
+ int i; | |
+ | |
+ _threaddebug(DBGCHAN, "Queuing alt %p on channel %p", a, a->c); | |
+ a->tag = c; | |
+ i = emptyentry(a->c); | |
+ a->c->qentry[i] = a; | |
+} | |
+ | |
+static void | |
+dequeue(Alt *a) | |
+{ | |
+ int i; | |
+ Channel *c; | |
+ | |
+ c = a->c; | |
+ for(i=0; i<c->nentry; i++) | |
+ if(c->qentry[i]==a){ | |
+ _threaddebug(DBGCHAN, "Dequeuing alt %p from channel %… | |
+ c->qentry[i] = nil; | |
+ if(c->freed) | |
+ _chanfree(c); | |
+ return; | |
+ } | |
+} | |
+ | |
+static void* | |
+altexecbuffered(Alt *a, int willreplace) | |
+{ | |
+ uchar *v; | |
+ Channel *c; | |
+ | |
+ c = a->c; | |
+ /* use buffered channel queue */ | |
+ if(a->op==CHANRCV && c->n > 0){ | |
+ _threaddebug(DBGCHAN, "buffer recv alt %p chan %p", a, c); | |
+ v = c->v + c->e*(c->f%c->s); | |
+ if(!willreplace) | |
+ c->n--; | |
+ c->f++; | |
+ return v; | |
+ } | |
+ if(a->op==CHANSND && c->n < c->s){ | |
+ _threaddebug(DBGCHAN, "buffer send alt %p chan %p", a, c); | |
+ v = c->v + c->e*((c->f+c->n)%c->s); | |
+ if(!willreplace) | |
+ c->n++; | |
+ return v; | |
+ } | |
+ abort(); | |
+ return nil; | |
+} | |
+ | |
+static void | |
+altcopy(void *dst, void *src, int sz) | |
+{ | |
+ if(dst){ | |
+ if(src) | |
+ memmove(dst, src, sz); | |
+ else | |
+ _threadmemset(dst, 0, sz); | |
+ } | |
+} | |
+ | |
+static int | |
+altexec(Alt *a, int spl) | |
+{ | |
+ volatile Alt *b; | |
+ int i, n, otherop; | |
+ Channel *c; | |
+ void *me, *waiter, *buf; | |
+ | |
+ c = a->c; | |
+ | |
+ /* rendezvous with others */ | |
+ otherop = (CHANSND+CHANRCV) - a->op; | |
+ n = 0; | |
+ b = nil; | |
+ me = a->v; | |
+ for(i=0; i<c->nentry; i++) | |
+ if(c->qentry[i] && c->qentry[i]->op==otherop && *c->qentry[i]-… | |
+ if(nrand(++n) == 0) | |
+ b = c->qentry[i]; | |
+ if(b != nil){ | |
+ _threaddebug(DBGCHAN, "rendez %s alt %p chan %p alt %p", a->op… | |
+ waiter = b->v; | |
+ if(c->s && c->n){ | |
+ /* | |
+ * if buffer is full and there are waiters | |
+ * and we're meeting a waiter, | |
+ * we must be receiving. | |
+ * | |
+ * we use the value in the channel buffer, | |
+ * copy the waiter's value into the channel buffer | |
+ * on behalf of the waiter, and then wake the waiter. | |
+ */ | |
+ if(a->op!=CHANRCV) | |
+ abort(); | |
+ buf = altexecbuffered(a, 1); | |
+ altcopy(me, buf, c->e); | |
+ altcopy(buf, waiter, c->e); | |
+ }else{ | |
+ if(a->op==CHANRCV) | |
+ altcopy(me, waiter, c->e); | |
+ else | |
+ altcopy(waiter, me, c->e); | |
+ } | |
+ *b->tag = c; /* commits us to rendezvous */ | |
+ _threaddebug(DBGCHAN, "unlocking the chanlock"); | |
+ unlock(&chanlock); | |
+ _procsplx(spl); | |
+ _threaddebug(DBGCHAN, "chanlock is %lud", *(ulong*)&chanlock); | |
+ while(_threadrendezvous((ulong)b->tag, 0) == ~0) | |
+ ; | |
+ return 1; | |
+ } | |
+ | |
+ buf = altexecbuffered(a, 0); | |
+ if(a->op==CHANRCV) | |
+ altcopy(me, buf, c->e); | |
+ else | |
+ altcopy(buf, me, c->e); | |
+ | |
+ unlock(&chanlock); | |
+ _procsplx(spl); | |
+ return 1; | |
+} | |
diff --git a/src/libthread/chanprint.c b/src/libthread/chanprint.c | |
t@@ -0,0 +1,18 @@ | |
+#include "threadimpl.h" | |
+ | |
+int | |
+chanprint(Channel *c, char *fmt, ...) | |
+{ | |
+ va_list arg; | |
+ char *p; | |
+ int n; | |
+ | |
+ va_start(arg, fmt); | |
+ p = vsmprint(fmt, arg); | |
+ va_end(arg); | |
+ if(p == nil) | |
+ sysfatal("vsmprint failed: %r"); | |
+ n = sendp(c, p); | |
+ yield(); /* let recipient handle message immediately */ | |
+ return n; | |
+} | |
diff --git a/src/libthread/create.c b/src/libthread/create.c | |
t@@ -0,0 +1,182 @@ | |
+#include "threadimpl.h" | |
+ | |
+#define free | |
+Pqueue _threadpq; | |
+ | |
+static int nextID(void); | |
+ | |
+/* | |
+ * Create and initialize a new Thread structure attached to a given proc. | |
+ */ | |
+ | |
+typedef struct Stack Stack; | |
+struct Stack { | |
+ ulong magic; | |
+ Thread *thr; | |
+ Stack *next; | |
+ uchar buf[STKSIZE-12]; | |
+}; | |
+ | |
+static Stack *stkfree; | |
+static Lock stklock; | |
+ | |
+void | |
+_stackfree(void *v) | |
+{ | |
+ Stack *s; | |
+ | |
+ s = v; | |
+ lock(&stklock); | |
+ s->thr = nil; | |
+ s->magic = 0; | |
+ s->next = stkfree; | |
+ stkfree = s; | |
+ unlock(&stklock); | |
+} | |
+ | |
+static Stack* | |
+stackalloc(void) | |
+{ | |
+ char *buf; | |
+ Stack *s; | |
+ int i; | |
+ | |
+ lock(&stklock); | |
+ while(stkfree == nil){ | |
+ unlock(&stklock); | |
+ assert(STKSIZE == sizeof(Stack)); | |
+ buf = malloc(STKSIZE+128*STKSIZE); | |
+ s = (Stack*)(((ulong)buf+STKSIZE)&~(STKSIZE-1)); | |
+ for(i=0; i<128; i++) | |
+ _stackfree(&s[i]); | |
+ lock(&stklock); | |
+ } | |
+ s = stkfree; | |
+ stkfree = stkfree->next; | |
+ unlock(&stklock); | |
+ s->magic = STKMAGIC; | |
+ return s; | |
+} | |
+ | |
+static int | |
+newthread(Proc *p, void (*f)(void *arg), void *arg, uint stacksize, char *name… | |
+{ | |
+ int id; | |
+ Thread *t; | |
+ Stack *s; | |
+ | |
+ if(stacksize < 32) | |
+ sysfatal("bad stacksize %d", stacksize); | |
+ t = _threadmalloc(sizeof(Thread), 1); | |
+ s = stackalloc(); | |
+ s->thr = t; | |
+ t->stk = (char*)s; | |
+ t->stksize = STKSIZE; | |
+ _threaddebugmemset(s->buf, 0xFE, sizeof s->buf); | |
+ _threadinitstack(t, f, arg); | |
+ t->proc = p; | |
+ t->grp = grp; | |
+ if(name) | |
+ t->cmdname = strdup(name); | |
+ t->id = nextID(); | |
+ id = t->id; | |
+ t->next = (Thread*)~0; | |
+ _threaddebug(DBGSCHED, "create thread %d.%d name %s", p->pid, t->id, n… | |
+ lock(&p->lock); | |
+ p->nthreads++; | |
+ if(p->threads.head == nil) | |
+ p->threads.head = t; | |
+ else{ | |
+ t->prevt = p->threads.tail; | |
+ t->prevt->nextt = t; | |
+ } | |
+ p->threads.tail = t; | |
+ t->state = Ready; | |
+ _threadready(t); | |
+ unlock(&p->lock); | |
+ return id; | |
+} | |
+ | |
+static int | |
+nextID(void) | |
+{ | |
+ static Lock l; | |
+ static int id; | |
+ int i; | |
+ | |
+ lock(&l); | |
+ i = ++id; | |
+ unlock(&l); | |
+ return i; | |
+} | |
+ | |
+int | |
+procrfork(void (*f)(void *), void *arg, uint stacksize, int rforkflag) | |
+{ | |
+ Proc *p; | |
+ int id; | |
+ | |
+ p = _threadgetproc(); | |
+ assert(p->newproc == nil); | |
+ p->newproc = _newproc(f, arg, stacksize, nil, p->thread->grp, rforkfla… | |
+ id = p->newproc->threads.head->id; | |
+ _sched(); | |
+ return id; | |
+} | |
+ | |
+int | |
+proccreate(void (*f)(void*), void *arg, uint stacksize) | |
+{ | |
+ return procrfork(f, arg, stacksize, 0); | |
+} | |
+ | |
+void | |
+_freeproc(Proc *p) | |
+{ | |
+ Thread *t, *nextt; | |
+ | |
+ for(t = p->threads.head; t; t = nextt){ | |
+ if(t->cmdname) | |
+ free(t->cmdname); | |
+ assert(t->stk != nil); | |
+ _stackfree((Stack*)t->stk); | |
+ nextt = t->nextt; | |
+ free(t); | |
+ } | |
+ free(p); | |
+} | |
+ | |
+/* | |
+ * Create a new thread and schedule it to run. | |
+ * The thread grp is inherited from the currently running thread. | |
+ */ | |
+int | |
+threadcreate(void (*f)(void *arg), void *arg, uint stacksize) | |
+{ | |
+ return newthread(_threadgetproc(), f, arg, stacksize, nil, threadgetgr… | |
+} | |
+ | |
+/* | |
+ * Create and initialize a new Proc structure with a single Thread | |
+ * running inside it. Add the Proc to the global process list. | |
+ */ | |
+Proc* | |
+_newproc(void (*f)(void *arg), void *arg, uint stacksize, char *name, int grp,… | |
+{ | |
+ Proc *p; | |
+ | |
+ p = _threadmalloc(sizeof *p, 1); | |
+ p->pid = -1; | |
+ p->rforkflag = rforkflag; | |
+ newthread(p, f, arg, stacksize, name, grp); | |
+ | |
+ lock(&_threadpq.lock); | |
+ if(_threadpq.head == nil) | |
+ _threadpq.head = p; | |
+ else | |
+ *_threadpq.tail = p; | |
+ _threadpq.tail = &p->next; | |
+ unlock(&_threadpq.lock); | |
+ return p; | |
+} | |
+ | |
diff --git a/src/libthread/debug.c b/src/libthread/debug.c | |
t@@ -0,0 +1,48 @@ | |
+#include "threadimpl.h" | |
+ | |
+int _threaddebuglevel; | |
+ | |
+void | |
+__threaddebug(ulong flag, char *fmt, ...) | |
+{ | |
+ char buf[128]; | |
+ va_list arg; | |
+ Fmt f; | |
+ Proc *p; | |
+ | |
+ if((_threaddebuglevel&flag) == 0) | |
+ return; | |
+ | |
+ fmtfdinit(&f, 2, buf, sizeof buf); | |
+ | |
+ p = _threadgetproc(); | |
+ if(p==nil) | |
+ fmtprint(&f, "noproc "); | |
+ else if(p->thread) | |
+ fmtprint(&f, "%d.%d ", p->pid, p->thread->id); | |
+ else | |
+ fmtprint(&f, "%d._ ", p->pid); | |
+ | |
+ va_start(arg, fmt); | |
+ fmtvprint(&f, fmt, arg); | |
+ va_end(arg); | |
+ fmtprint(&f, "\n"); | |
+ fmtfdflush(&f); | |
+} | |
+ | |
+void | |
+_threadassert(char *s) | |
+{ | |
+ char buf[256]; | |
+ int n; | |
+ Proc *p; | |
+ | |
+ p = _threadgetproc(); | |
+ if(p && p->thread) | |
+ n = sprint(buf, "%d.%d ", p->pid, p->thread->id); | |
+ else | |
+ n = 0; | |
+ snprint(buf+n, sizeof(buf)-n, "%s: assertion failed\n", s); | |
+ write(2, buf, strlen(buf)); | |
+ abort(); | |
+} | |
diff --git a/src/libthread/exec-unix.c b/src/libthread/exec-unix.c | |
t@@ -0,0 +1,124 @@ | |
+#include <fcntl.h> | |
+#include <unistd.h> | |
+#include "threadimpl.h" | |
+ | |
+void | |
+procexec(Channel *pidc, char *prog, char *args[]) | |
+{ | |
+ int n; | |
+ Proc *p; | |
+ Thread *t; | |
+ | |
+ _threaddebug(DBGEXEC, "procexec %s", prog); | |
+ /* must be only thread in proc */ | |
+ p = _threadgetproc(); | |
+ t = p->thread; | |
+ if(p->threads.head != t || p->threads.head->nextt != nil){ | |
+ werrstr("not only thread in proc"); | |
+ Bad: | |
+ if(pidc) | |
+ sendul(pidc, ~0); | |
+ return; | |
+ } | |
+ | |
+ /* | |
+ * We want procexec to behave like exec; if exec succeeds, | |
+ * never return, and if it fails, return with errstr set. | |
+ * Unfortunately, the exec happens in another proc since | |
+ * we have to wait for the exec'ed process to finish. | |
+ * To provide the semantics, we open a pipe with the | |
+ * write end close-on-exec and hand it to the proc that | |
+ * is doing the exec. If the exec succeeds, the pipe will | |
+ * close so that our read below fails. If the exec fails, | |
+ * then the proc doing the exec sends the errstr down the | |
+ * pipe to us. | |
+ */ | |
+ if(pipe(p->exec.fd) < 0) | |
+ goto Bad; | |
+ if(fcntl(p->exec.fd[1], F_SETFD, 1) < 0) | |
+ goto Bad; | |
+ | |
+ /* exec in parallel via the scheduler */ | |
+ assert(p->needexec==0); | |
+ p->exec.prog = prog; | |
+ p->exec.args = args; | |
+ p->needexec = 1; | |
+ _sched(); | |
+ | |
+ close(p->exec.fd[1]); | |
+ if((n = read(p->exec.fd[0], p->exitstr, ERRMAX-1)) > 0){ /* exe… | |
+ p->exitstr[n] = '\0'; | |
+ errstr(p->exitstr, ERRMAX); | |
+ close(p->exec.fd[0]); | |
+ goto Bad; | |
+ } | |
+ close(p->exec.fd[0]); | |
+ | |
+ if(pidc) | |
+ sendul(pidc, t->ret); | |
+ | |
+ /* wait for exec'ed program, then exit */ | |
+ _schedexecwait(); | |
+} | |
+ | |
+void | |
+procexecl(Channel *pidc, char *f, ...) | |
+{ | |
+ procexec(pidc, f, &f+1); | |
+} | |
+ | |
+void | |
+_schedexecwait(void) | |
+{ | |
+ int pid; | |
+ Channel *c; | |
+ Proc *p; | |
+ Thread *t; | |
+ Waitmsg *w; | |
+ | |
+ p = _threadgetproc(); | |
+ t = p->thread; | |
+ pid = t->ret; | |
+ _threaddebug(DBGEXEC, "_schedexecwait %d", t->ret); | |
+ | |
+ for(;;){ | |
+ w = wait(); | |
+ if(w == nil) | |
+ break; | |
+ if(w->pid == pid) | |
+ break; | |
+ free(w); | |
+ } | |
+ if(w != nil){ | |
+ if((c = _threadwaitchan) != nil) | |
+ sendp(c, w); | |
+ else | |
+ free(w); | |
+ } | |
+ threadexits("procexec"); | |
+} | |
+ | |
+static void | |
+efork(void *ve) | |
+{ | |
+ char buf[ERRMAX]; | |
+ Execargs *e; | |
+ | |
+ e = ve; | |
+ _threaddebug(DBGEXEC, "_schedexec %s", e->prog); | |
+ close(e->fd[0]); | |
+ execv(e->prog, e->args); | |
+ _threaddebug(DBGEXEC, "_schedexec failed: %r"); | |
+ rerrstr(buf, sizeof buf); | |
+ if(buf[0]=='\0') | |
+ strcpy(buf, "exec failed"); | |
+ write(e->fd[1], buf, strlen(buf)); | |
+ close(e->fd[1]); | |
+ _exits(buf); | |
+} | |
+ | |
+int | |
+_schedexec(Execargs *e) | |
+{ | |
+ return ffork(RFFDG|RFPROC|RFMEM, efork, e); | |
+} | |
diff --git a/src/libthread/exec.c b/src/libthread/exec.c | |
t@@ -0,0 +1,77 @@ | |
+#include "threadimpl.h" | |
+ | |
+#define PIPEMNT "/mnt/temp" | |
+ | |
+void | |
+procexec(Channel *pidc, char *prog, char *args[]) | |
+{ | |
+ int n; | |
+ Proc *p; | |
+ Thread *t; | |
+ | |
+ _threaddebug(DBGEXEC, "procexec %s", prog); | |
+ /* must be only thread in proc */ | |
+ p = _threadgetproc(); | |
+ t = p->thread; | |
+ if(p->threads.head != t || p->threads.head->nextt != nil){ | |
+ werrstr("not only thread in proc"); | |
+ Bad: | |
+ if(pidc) | |
+ sendul(pidc, ~0); | |
+ return; | |
+ } | |
+ | |
+ /* | |
+ * We want procexec to behave like exec; if exec succeeds, | |
+ * never return, and if it fails, return with errstr set. | |
+ * Unfortunately, the exec happens in another proc since | |
+ * we have to wait for the exec'ed process to finish. | |
+ * To provide the semantics, we open a pipe with the | |
+ * write end close-on-exec and hand it to the proc that | |
+ * is doing the exec. If the exec succeeds, the pipe will | |
+ * close so that our read below fails. If the exec fails, | |
+ * then the proc doing the exec sends the errstr down the | |
+ * pipe to us. | |
+ */ | |
+ if(bind("#|", PIPEMNT, MREPL) < 0) | |
+ goto Bad; | |
+ if((p->exec.fd[0] = open(PIPEMNT "/data", OREAD)) < 0){ | |
+ unmount(nil, PIPEMNT); | |
+ goto Bad; | |
+ } | |
+ if((p->exec.fd[1] = open(PIPEMNT "/data1", OWRITE|OCEXEC)) < 0){ | |
+ close(p->exec.fd[0]); | |
+ unmount(nil, PIPEMNT); | |
+ goto Bad; | |
+ } | |
+ unmount(nil, PIPEMNT); | |
+ | |
+ /* exec in parallel via the scheduler */ | |
+ assert(p->needexec==0); | |
+ p->exec.prog = prog; | |
+ p->exec.args = args; | |
+ p->needexec = 1; | |
+ _sched(); | |
+ | |
+ close(p->exec.fd[1]); | |
+ if((n = read(p->exec.fd[0], p->exitstr, ERRMAX-1)) > 0){ /* exe… | |
+ p->exitstr[n] = '\0'; | |
+ errstr(p->exitstr, ERRMAX); | |
+ close(p->exec.fd[0]); | |
+ goto Bad; | |
+ } | |
+ close(p->exec.fd[0]); | |
+ | |
+ if(pidc) | |
+ sendul(pidc, t->ret); | |
+ | |
+ /* wait for exec'ed program, then exit */ | |
+ _schedexecwait(); | |
+} | |
+ | |
+void | |
+procexecl(Channel *pidc, char *f, ...) | |
+{ | |
+ procexec(pidc, f, &f+1); | |
+} | |
+ | |
diff --git a/src/libthread/exit.c b/src/libthread/exit.c | |
t@@ -0,0 +1,63 @@ | |
+#include "threadimpl.h" | |
+#include <signal.h> | |
+ | |
+char *_threadexitsallstatus; | |
+Channel *_threadwaitchan; | |
+ | |
+void | |
+threadexits(char *exitstr) | |
+{ | |
+ Proc *p; | |
+ Thread *t; | |
+ | |
+ p = _threadgetproc(); | |
+ t = p->thread; | |
+ t->moribund = 1; | |
+ if(exitstr==nil) | |
+ exitstr=""; | |
+ utfecpy(p->exitstr, p->exitstr+ERRMAX, exitstr); | |
+ _sched(); | |
+} | |
+ | |
+void | |
+threadexitsall(char *exitstr) | |
+{ | |
+ Proc *p; | |
+ int *pid; | |
+ int i, npid, mypid; | |
+ | |
+ if(exitstr == nil) | |
+ exitstr = ""; | |
+ _threadexitsallstatus = exitstr; | |
+ _threaddebug(DBGSCHED, "_threadexitsallstatus set to %p", _threadexits… | |
+ mypid = _threadgetpid(); | |
+ | |
+ /* | |
+ * signal others. | |
+ * copying all the pids first avoids other threads | |
+ * teardown procedures getting in the way. | |
+ */ | |
+ lock(&_threadpq.lock); | |
+ npid = 0; | |
+ for(p=_threadpq.head; p; p=p->next) | |
+ npid++; | |
+ pid = _threadmalloc(npid*sizeof(pid[0]), 0); | |
+ npid = 0; | |
+ for(p = _threadpq.head; p; p=p->next) | |
+ pid[npid++] = p->pid; | |
+ unlock(&_threadpq.lock); | |
+ for(i=0; i<npid; i++) | |
+ if(pid[i] != mypid) | |
+ kill(pid[i], SIGTERM); | |
+ | |
+ /* leave */ | |
+ exit(0); | |
+} | |
+ | |
+Channel* | |
+threadwaitchan(void) | |
+{ | |
+ if(_threadwaitchan==nil) | |
+ _threadwaitchan = chancreate(sizeof(Waitmsg*), 16); | |
+ return _threadwaitchan; | |
+} | |
diff --git a/src/libthread/getpid.c b/src/libthread/getpid.c | |
t@@ -0,0 +1,8 @@ | |
+#include "threadimpl.h" | |
+#include <unistd.h> | |
+ | |
+int | |
+_threadgetpid(void) | |
+{ | |
+ return getpid(); | |
+} | |
diff --git a/src/libthread/id.c b/src/libthread/id.c | |
t@@ -0,0 +1,135 @@ | |
+#include "threadimpl.h" | |
+ | |
+int | |
+threadid(void) | |
+{ | |
+ return _threadgetproc()->thread->id; | |
+} | |
+ | |
+int | |
+threadpid(int id) | |
+{ | |
+ int pid; | |
+ Proc *p; | |
+ Thread *t; | |
+ | |
+ if (id < 0) | |
+ return -1; | |
+ if (id == 0) | |
+ return _threadgetproc()->pid; | |
+ lock(&_threadpq.lock); | |
+ for (p = _threadpq.head; p->next; p = p->next){ | |
+ lock(&p->lock); | |
+ for (t = p->threads.head; t; t = t->nextt) | |
+ if (t->id == id){ | |
+ pid = p->pid; | |
+ unlock(&p->lock); | |
+ unlock(&_threadpq.lock); | |
+ return pid; | |
+ } | |
+ unlock(&p->lock); | |
+ } | |
+ unlock(&_threadpq.lock); | |
+ return -1; | |
+} | |
+ | |
+int | |
+threadsetgrp(int ng) | |
+{ | |
+ int og; | |
+ Thread *t; | |
+ | |
+ t = _threadgetproc()->thread; | |
+ og = t->grp; | |
+ t->grp = ng; | |
+ return og; | |
+} | |
+ | |
+int | |
+threadgetgrp(void) | |
+{ | |
+ return _threadgetproc()->thread->grp; | |
+} | |
+ | |
+void | |
+threadsetname(char *name) | |
+{ | |
+/* | |
+ int fd, n; | |
+ char buf[128], *s; | |
+*/ | |
+ Proc *p; | |
+ Thread *t; | |
+ | |
+ p = _threadgetproc(); | |
+ t = p->thread; | |
+ if (t->cmdname) | |
+ free(t->cmdname); | |
+ t->cmdname = strdup(name); | |
+/* Plan 9 only | |
+ if(p->nthreads == 1){ | |
+ snprint(buf, sizeof buf, "#p/%d/args", getpid()); | |
+ if((fd = open(buf, OWRITE)) >= 0){ | |
+ snprint(buf, sizeof buf, "%s [%s]", argv0, name); | |
+ n = strlen(buf)+1; | |
+ s = strchr(buf, ' '); | |
+ if(s) | |
+ *s = '\0'; | |
+ write(fd, buf, n); | |
+ close(fd); | |
+ } | |
+ } | |
+*/ | |
+} | |
+ | |
+char* | |
+threadgetname(void) | |
+{ | |
+ return _threadgetproc()->thread->cmdname; | |
+} | |
+ | |
+void** | |
+threaddata(void) | |
+{ | |
+ return &_threadgetproc()->thread->udata[0]; | |
+} | |
+ | |
+void** | |
+procdata(void) | |
+{ | |
+ return &_threadgetproc()->udata; | |
+} | |
+ | |
+static Lock privlock; | |
+static int privmask = 1; | |
+ | |
+int | |
+tprivalloc(void) | |
+{ | |
+ int i; | |
+ | |
+ lock(&privlock); | |
+ for(i=0; i<NPRIV; i++) | |
+ if(!(privmask&(1<<i))){ | |
+ privmask |= 1<<i; | |
+ unlock(&privlock); | |
+ return i; | |
+ } | |
+ unlock(&privlock); | |
+ return -1; | |
+} | |
+ | |
+void | |
+tprivfree(int i) | |
+{ | |
+ if(i < 0 || i >= NPRIV) | |
+ abort(); | |
+ lock(&privlock); | |
+ privmask &= ~(1<<i); | |
+} | |
+ | |
+void** | |
+tprivaddr(int i) | |
+{ | |
+ return &_threadgetproc()->thread->udata[i]; | |
+} | |
diff --git a/src/libthread/iocall.c b/src/libthread/iocall.c | |
t@@ -0,0 +1,49 @@ | |
+#include "threadimpl.h" | |
+ | |
+long | |
+iocall(Ioproc *io, long (*op)(va_list*), ...) | |
+{ | |
+ int ret, inted; | |
+ Ioproc *msg; | |
+ | |
+ if(send(io->c, &io) == -1){ | |
+ werrstr("interrupted"); | |
+ return -1; | |
+ } | |
+ assert(!io->inuse); | |
+ io->inuse = 1; | |
+ io->op = op; | |
+ va_start(io->arg, op); | |
+ msg = io; | |
+ inted = 0; | |
+ while(send(io->creply, &msg) == -1){ | |
+ msg = nil; | |
+ inted = 1; | |
+ } | |
+ if(inted){ | |
+ werrstr("interrupted"); | |
+ return -1; | |
+ } | |
+ | |
+ /* | |
+ * If we get interrupted, we have stick around so that | |
+ * the IO proc has someone to talk to. Send it an interrupt | |
+ * and try again. | |
+ */ | |
+ inted = 0; | |
+ while(recv(io->creply, nil) == -1){ | |
+ inted = 1; | |
+ iointerrupt(io); | |
+ } | |
+ USED(inted); | |
+ va_end(io->arg); | |
+ ret = io->ret; | |
+ if(ret < 0) | |
+ errstr(io->err, sizeof io->err); | |
+ io->inuse = 0; | |
+ | |
+ /* release resources */ | |
+ while(send(io->creply, &io) == -1) | |
+ ; | |
+ return ret; | |
+} | |
diff --git a/src/libthread/ioclose.c b/src/libthread/ioclose.c | |
t@@ -0,0 +1,16 @@ | |
+#include "threadimpl.h" | |
+ | |
+static long | |
+_ioclose(va_list *arg) | |
+{ | |
+ int fd; | |
+ | |
+ fd = va_arg(*arg, int); | |
+ return close(fd); | |
+} | |
+ | |
+int | |
+ioclose(Ioproc *io, int fd) | |
+{ | |
+ return iocall(io, _ioclose, fd); | |
+} | |
diff --git a/src/libthread/iodial.c b/src/libthread/iodial.c | |
t@@ -0,0 +1,21 @@ | |
+#include "threadimpl.h" | |
+ | |
+static long | |
+_iodial(va_list *arg) | |
+{ | |
+ char *addr, *local, *dir; | |
+ int *cdfp; | |
+ | |
+ addr = va_arg(*arg, char*); | |
+ local = va_arg(*arg, char*); | |
+ dir = va_arg(*arg, char*); | |
+ cdfp = va_arg(*arg, int*); | |
+ | |
+ return dial(addr, local, dir, cdfp); | |
+} | |
+ | |
+int | |
+iodial(Ioproc *io, char *addr, char *local, char *dir, int *cdfp) | |
+{ | |
+ return iocall(io, _iodial, addr, local, dir, cdfp); | |
+} | |
diff --git a/src/libthread/ioopen.c b/src/libthread/ioopen.c | |
t@@ -0,0 +1,20 @@ | |
+#include <unistd.h> | |
+#include <fcntl.h> | |
+#include "threadimpl.h" | |
+ | |
+static long | |
+_ioopen(va_list *arg) | |
+{ | |
+ char *path; | |
+ int mode; | |
+ | |
+ path = va_arg(*arg, char*); | |
+ mode = va_arg(*arg, int); | |
+ return open(path, mode); | |
+} | |
+ | |
+int | |
+ioopen(Ioproc *io, char *path, int mode) | |
+{ | |
+ return iocall(io, _ioopen, path, mode); | |
+} | |
diff --git a/src/libthread/ioproc.3 b/src/libthread/ioproc.3 | |
t@@ -0,0 +1,179 @@ | |
+.TH IOPROC 2 | |
+.SH NAME | |
+closeioproc, | |
+iocall, | |
+ioclose, | |
+iointerrupt, | |
+iodial, | |
+ioopen, | |
+ioproc, | |
+ioread, | |
+ioreadn, | |
+iowrite \- slave I/O processes for threaded programs | |
+.SH SYNOPSIS | |
+.PP | |
+.de XX | |
+.ift .sp 0.5 | |
+.ifn .sp | |
+.. | |
+.EX | |
+.ta \w'Ioproc* 'u | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <thread.h> | |
+.sp | |
+typedef struct Ioproc Ioproc; | |
+.sp | |
+Ioproc* ioproc(void); | |
+.XX | |
+int ioopen(Ioproc *io, char *file, int omode); | |
+int ioclose(Ioproc *io, int fd); | |
+long ioread(Ioproc *io, int fd, void *a, long n); | |
+long ioreadn(Ioproc *io, int fd, void *a, long n); | |
+long iowrite(Ioproc *io, int fd, void *a, long n); | |
+int iodial(Ioproc *io, char *addr, char *local, char *dir, char *cdfp); | |
+.XX | |
+void iointerrupt(Ioproc *io); | |
+void closeioproc(Ioproc *io); | |
+.XX | |
+long iocall(Ioproc *io, long (*op)(va_list *arg), ...); | |
+.EE | |
+.SH DESCRIPTION | |
+.PP | |
+These routines provide access to I/O in slave procs. | |
+Since the I/O itself is done in a slave proc, other threads | |
+in the calling proc can run while the calling thread | |
+waits for the I/O to complete. | |
+.PP | |
+.I Ioproc | |
+forks a new slave proc and returns a pointer to the | |
+.B Ioproc | |
+associated with it. | |
+.I Ioproc | |
+uses | |
+.I mallocz | |
+and | |
+.IR proccreate ; | |
+if either fails, it calls | |
+.I sysfatal | |
+rather than return an error. | |
+.PP | |
+.IR Ioopen , | |
+.IR ioclose , | |
+.IR ioread , | |
+.IR ioreadn , | |
+.IR iowrite , | |
+and | |
+.IR iodial | |
+are execute the | |
+similarly named library or system calls | |
+(see | |
+.IR open (2), | |
+.IR read (2), | |
+and | |
+.IR dial (2)) | |
+in the slave process associated with | |
+.IR io . | |
+It is an error to execute more than one call | |
+at a time in an I/O proc. | |
+.PP | |
+.I Iointerrupt | |
+interrupts the call currently executing in the I/O proc. | |
+If no call is executing, | |
+.IR iointerrupt | |
+is a no-op. | |
+.PP | |
+.I Closeioproc | |
+terminates the I/O proc and frees the associated | |
+.B Ioproc . | |
+.PP | |
+.I Iocall | |
+is a primitive that may be used to implement | |
+more slave I/O routines. | |
+.I Iocall | |
+arranges for | |
+.I op | |
+to be called in | |
+.IR io 's | |
+proc, with | |
+.I arg | |
+set to the variable parameter list, | |
+returning the value that | |
+.I op | |
+returns. | |
+.SH EXAMPLE | |
+Relay messages between two file descriptors, | |
+counting the total number of bytes seen: | |
+.IP | |
+.EX | |
+.ta +\w'xxxx'u +\w'xxxx'u +\w'xxxx'u | |
+int tot; | |
+ | |
+void | |
+relaythread(void *v) | |
+{ | |
+ int *fd, n; | |
+ char buf[1024]; | |
+ Ioproc *io; | |
+ | |
+ fd = v; | |
+ io = ioproc(); | |
+ while((n = ioread(io, fd[0], buf, sizeof buf)) > 0){ | |
+ if(iowrite(io, fd[1], buf, n) != n) | |
+ sysfatal("iowrite: %r"); | |
+ tot += n; | |
+ } | |
+ closeioproc(io); | |
+} | |
+ | |
+void | |
+relay(int fd0, int fd1) | |
+{ | |
+ int fd[4]; | |
+ | |
+ fd[0] = fd[3] = fd0; | |
+ fd[1] = fd[2] = fd1; | |
+ threadcreate(relaythread, fd, 8192); | |
+ threadcreate(relaythread, fd+2, 8192); | |
+} | |
+.EE | |
+.LP | |
+If the two | |
+.I relaythread | |
+instances were running in different procs, the | |
+common access to | |
+.I tot | |
+would be unsafe. | |
+.EE | |
+.PP | |
+Implement | |
+.IR ioread : | |
+.IP | |
+.EX | |
+static long | |
+_ioread(va_list *arg) | |
+{ | |
+ int fd; | |
+ void *a; | |
+ long n; | |
+ | |
+ fd = va_arg(*arg, int); | |
+ a = va_arg(*arg, void*); | |
+ n = va_arg(*arg, long); | |
+ return read(fd, a, n); | |
+} | |
+ | |
+long | |
+ioread(Ioproc *io, int fd, void *a, long n) | |
+{ | |
+ return iocall(io, _ioread, fd, a, n); | |
+} | |
+.EE | |
+.SH SOURCE | |
+.B /sys/src/libthread/io*.c | |
+.SH SEE ALSO | |
+.IR dial (2), | |
+.IR open (2), | |
+.IR read (2), | |
+.IR thread (2) | |
+ | |
diff --git a/src/libthread/ioproc.c b/src/libthread/ioproc.c | |
t@@ -0,0 +1,74 @@ | |
+#include "threadimpl.h" | |
+ | |
+enum | |
+{ | |
+ STACK = 8192, | |
+}; | |
+ | |
+void | |
+iointerrupt(Ioproc *io) | |
+{ | |
+ if(!io->inuse) | |
+ return; | |
+ threadint(io->tid); | |
+} | |
+ | |
+static void | |
+xioproc(void *a) | |
+{ | |
+ Ioproc *io, *x; | |
+ io = a; | |
+ /* | |
+ * first recvp acquires the ioproc. | |
+ * second tells us that the data is ready. | |
+ */ | |
+ for(;;){ | |
+ while(recv(io->c, &x) == -1) | |
+ ; | |
+ if(x == 0) /* our cue to leave */ | |
+ break; | |
+ assert(x == io); | |
+ | |
+ /* caller is now committed -- even if interrupted he'll return… | |
+ while(recv(io->creply, &x) == -1) | |
+ ; | |
+ if(x == 0) /* caller backed out */ | |
+ continue; | |
+ assert(x == io); | |
+ | |
+ io->ret = io->op(&io->arg); | |
+ if(io->ret < 0) | |
+ rerrstr(io->err, sizeof io->err); | |
+ while(send(io->creply, &io) == -1) | |
+ ; | |
+ while(recv(io->creply, &x) == -1) | |
+ ; | |
+ } | |
+} | |
+ | |
+Ioproc* | |
+ioproc(void) | |
+{ | |
+ Ioproc *io; | |
+ | |
+ io = mallocz(sizeof(*io), 1); | |
+ if(io == nil) | |
+ sysfatal("ioproc malloc: %r"); | |
+ io->c = chancreate(sizeof(void*), 0); | |
+ io->creply = chancreate(sizeof(void*), 0); | |
+ io->tid = proccreate(xioproc, io, STACK); | |
+ return io; | |
+} | |
+ | |
+void | |
+closeioproc(Ioproc *io) | |
+{ | |
+ if(io == nil) | |
+ return; | |
+ iointerrupt(io); | |
+ while(send(io->c, 0) == -1) | |
+ ; | |
+ chanfree(io->c); | |
+ chanfree(io->creply); | |
+ free(io); | |
+} | |
diff --git a/src/libthread/ioread.c b/src/libthread/ioread.c | |
t@@ -0,0 +1,20 @@ | |
+#include "threadimpl.h" | |
+ | |
+static long | |
+_ioread(va_list *arg) | |
+{ | |
+ int fd; | |
+ void *a; | |
+ long n; | |
+ | |
+ fd = va_arg(*arg, int); | |
+ a = va_arg(*arg, void*); | |
+ n = va_arg(*arg, long); | |
+ return read(fd, a, n); | |
+} | |
+ | |
+long | |
+ioread(Ioproc *io, int fd, void *a, long n) | |
+{ | |
+ return iocall(io, _ioread, fd, a, n); | |
+} | |
diff --git a/src/libthread/ioreadn.c b/src/libthread/ioreadn.c | |
t@@ -0,0 +1,21 @@ | |
+#include "threadimpl.h" | |
+ | |
+static long | |
+_ioreadn(va_list *arg) | |
+{ | |
+ int fd; | |
+ void *a; | |
+ long n; | |
+ | |
+ fd = va_arg(*arg, int); | |
+ a = va_arg(*arg, void*); | |
+ n = va_arg(*arg, long); | |
+ n = readn(fd, a, n); | |
+ return n; | |
+} | |
+ | |
+long | |
+ioreadn(Ioproc *io, int fd, void *a, long n) | |
+{ | |
+ return iocall(io, _ioreadn, fd, a, n); | |
+} | |
diff --git a/src/libthread/iosleep.c b/src/libthread/iosleep.c | |
t@@ -0,0 +1,16 @@ | |
+#include "threadimpl.h" | |
+ | |
+static long | |
+_iosleep(va_list *arg) | |
+{ | |
+ long n; | |
+ | |
+ n = va_arg(*arg, long); | |
+ return sleep(n); | |
+} | |
+ | |
+int | |
+iosleep(Ioproc *io, long n) | |
+{ | |
+ return iocall(io, _iosleep, n); | |
+} | |
diff --git a/src/libthread/iowrite.c b/src/libthread/iowrite.c | |
t@@ -0,0 +1,21 @@ | |
+#include "threadimpl.h" | |
+ | |
+static long | |
+_iowrite(va_list *arg) | |
+{ | |
+ int fd; | |
+ void *a; | |
+ long n; | |
+ | |
+ fd = va_arg(*arg, int); | |
+ a = va_arg(*arg, void*); | |
+ n = va_arg(*arg, long); | |
+ n = write(fd, a, n); | |
+ return n; | |
+} | |
+ | |
+long | |
+iowrite(Ioproc *io, int fd, void *a, long n) | |
+{ | |
+ return iocall(io, _iowrite, fd, a, n); | |
+} | |
diff --git a/src/libthread/kill.c b/src/libthread/kill.c | |
t@@ -0,0 +1,89 @@ | |
+#include "threadimpl.h" | |
+#include <signal.h> | |
+ | |
+static void tinterrupt(Proc*, Thread*); | |
+ | |
+static void | |
+threadxxxgrp(int grp, int dokill) | |
+{ | |
+ Proc *p; | |
+ Thread *t; | |
+ | |
+ lock(&_threadpq.lock); | |
+ for(p=_threadpq.head; p; p=p->next){ | |
+ lock(&p->lock); | |
+ for(t=p->threads.head; t; t=t->nextt) | |
+ if(t->grp == grp){ | |
+ if(dokill) | |
+ t->moribund = 1; | |
+ tinterrupt(p, t); | |
+ } | |
+ unlock(&p->lock); | |
+ } | |
+ unlock(&_threadpq.lock); | |
+ _threadbreakrendez(); | |
+} | |
+ | |
+static void | |
+threadxxx(int id, int dokill) | |
+{ | |
+ Proc *p; | |
+ Thread *t; | |
+ | |
+ lock(&_threadpq.lock); | |
+ for(p=_threadpq.head; p; p=p->next){ | |
+ lock(&p->lock); | |
+ for(t=p->threads.head; t; t=t->nextt) | |
+ if(t->id == id){ | |
+ if(dokill) | |
+ t->moribund = 1; | |
+ tinterrupt(p, t); | |
+ unlock(&p->lock); | |
+ unlock(&_threadpq.lock); | |
+ _threadbreakrendez(); | |
+ return; | |
+ } | |
+ unlock(&p->lock); | |
+ } | |
+ unlock(&_threadpq.lock); | |
+ _threaddebug(DBGNOTE, "Can't find thread to kill"); | |
+ return; | |
+} | |
+ | |
+void | |
+threadkillgrp(int grp) | |
+{ | |
+ threadxxxgrp(grp, 1); | |
+} | |
+ | |
+void | |
+threadkill(int id) | |
+{ | |
+ threadxxx(id, 1); | |
+} | |
+ | |
+void | |
+threadintgrp(int grp) | |
+{ | |
+ threadxxxgrp(grp, 0); | |
+} | |
+ | |
+void | |
+threadint(int id) | |
+{ | |
+ threadxxx(id, 0); | |
+} | |
+ | |
+static void | |
+tinterrupt(Proc *p, Thread *t) | |
+{ | |
+ switch(t->state){ | |
+ case Running: | |
+ kill(p->pid, SIGINT); | |
+ // postnote(PNPROC, p->pid, "threadint"); | |
+ break; | |
+ case Rendezvous: | |
+ _threadflagrendez(t); | |
+ break; | |
+ } | |
+} | |
diff --git a/src/libthread/label.h b/src/libthread/label.h | |
t@@ -0,0 +1,24 @@ | |
+/* | |
+ * setjmp and longjmp, but our own because some (stupid) c libraries | |
+ * assume longjmp is only used to move up the stack, and error out | |
+ * if you do otherwise. | |
+ */ | |
+ | |
+typedef struct Label Label; | |
+#define LABELDPC 0 | |
+ | |
+#if defined (__i386__) && (defined(__FreeBSD__) || defined(__linux__)) | |
+struct Label | |
+{ | |
+ ulong pc; | |
+ ulong bx; | |
+ ulong sp; | |
+ ulong bp; | |
+ ulong si; | |
+ ulong di; | |
+}; | |
+#else | |
+#error "Unknown or unsupported architecture" | |
+#endif | |
+ | |
+ | |
diff --git a/src/libthread/lib.c b/src/libthread/lib.c | |
t@@ -0,0 +1,35 @@ | |
+#include "threadimpl.h" | |
+ | |
+static long totalmalloc; | |
+ | |
+void* | |
+_threadmalloc(long size, int z) | |
+{ | |
+ void *m; | |
+ | |
+ m = malloc(size); | |
+ if (m == nil) | |
+ sysfatal("Malloc of size %ld failed: %r\n", size); | |
+ setmalloctag(m, getcallerpc(&size)); | |
+ totalmalloc += size; | |
+ if (size > 1000000) { | |
+ fprint(2, "Malloc of size %ld, total %ld\n", size, totalmalloc… | |
+ abort(); | |
+ } | |
+ if (z) | |
+ _threadmemset(m, 0, size); | |
+ return m; | |
+} | |
+ | |
+void | |
+_threadsysfatal(char *fmt, va_list arg) | |
+{ | |
+ char buf[1024]; /* size doesn't matter; we're about to exit */ | |
+ | |
+ vseprint(buf, buf+sizeof(buf), fmt, arg); | |
+ if(argv0) | |
+ fprint(2, "%s: %s\n", argv0, buf); | |
+ else | |
+ fprint(2, "%s\n", buf); | |
+ threadexitsall(buf); | |
+} | |
diff --git a/src/libthread/main.c b/src/libthread/main.c | |
t@@ -0,0 +1,124 @@ | |
+#include "threadimpl.h" | |
+#include <signal.h> | |
+ | |
+typedef struct Mainarg Mainarg; | |
+struct Mainarg | |
+{ | |
+ int argc; | |
+ char **argv; | |
+}; | |
+ | |
+int mainstacksize; | |
+int _threadnotefd; | |
+int _threadpasserpid; | |
+static void mainlauncher(void*); | |
+extern void (*_sysfatal)(char*, va_list); | |
+ | |
+void | |
+_threaddie(int x) | |
+{ | |
+ extern char *_threadexitsallstatus; | |
+ USED(x); | |
+ | |
+ if(_threadexitsallstatus) | |
+ exit(_threadexitsallstatus[0] ? 1 : 0); | |
+} | |
+ | |
+int | |
+main(int argc, char **argv) | |
+{ | |
+ Mainarg *a; | |
+ Proc *p; | |
+ | |
+ signal(SIGTERM, _threaddie); | |
+// rfork(RFREND); | |
+ | |
+//_threaddebuglevel = (DBGSCHED|DBGCHAN|DBGREND)^~0; | |
+ _systhreadinit(); | |
+ _qlockinit(_threadrendezvous); | |
+ _sysfatal = _threadsysfatal; | |
+// notify(_threadnote); | |
+ if(mainstacksize == 0) | |
+ mainstacksize = 32*1024; | |
+ | |
+ a = _threadmalloc(sizeof *a, 1); | |
+ a->argc = argc; | |
+ a->argv = argv; | |
+ | |
+ p = _newproc(mainlauncher, a, mainstacksize, "threadmain", 0, 0); | |
+ _schedinit(p); | |
+ abort(); /* not reached */ | |
+ return 0; | |
+} | |
+ | |
+static void | |
+mainlauncher(void *arg) | |
+{ | |
+ Mainarg *a; | |
+ | |
+ a = arg; | |
+ threadmain(a->argc, a->argv); | |
+ threadexits("threadmain"); | |
+} | |
+ | |
+void | |
+_threadsignal(void) | |
+{ | |
+} | |
+ | |
+void | |
+_threadsignalpasser(void) | |
+{ | |
+} | |
+ | |
+int | |
+_schedfork(Proc *p) | |
+{ | |
+ return ffork(RFMEM|RFNOWAIT, _schedinit, p); | |
+} | |
+ | |
+void | |
+_schedexit(Proc *p) | |
+{ | |
+ char ex[ERRMAX]; | |
+ Proc **l; | |
+ | |
+ lock(&_threadpq.lock); | |
+ for(l=&_threadpq.head; *l; l=&(*l)->next){ | |
+ if(*l == p){ | |
+ *l = p->next; | |
+ if(*l == nil) | |
+ _threadpq.tail = l; | |
+ break; | |
+ } | |
+ } | |
+ unlock(&_threadpq.lock); | |
+ | |
+ strncpy(ex, p->exitstr, sizeof ex); | |
+ ex[sizeof ex-1] = '\0'; | |
+ free(p); | |
+ _exit(ex[0]); | |
+} | |
+ | |
+int | |
+nrand(int n) | |
+{ | |
+ return random()%n; | |
+} | |
+ | |
+void | |
+_systhreadinit(void) | |
+{ | |
+} | |
+ | |
+void | |
+threadstats(void) | |
+{ | |
+ extern int _threadnrendez, _threadhighnrendez, | |
+ _threadnalt, _threadhighnentry; | |
+ fprint(2, "*** THREAD LIBRARY STATS ***\n"); | |
+ fprint(2, "nrendez %d high simultaneous %d\n", | |
+ _threadnrendez, _threadhighnrendez); | |
+ fprint(2, "nalt %d high simultaneous entry %d\n", | |
+ _threadnalt, _threadhighnentry); | |
+} | |
diff --git a/src/libthread/memset.c b/src/libthread/memset.c | |
t@@ -0,0 +1,8 @@ | |
+#include "threadimpl.h" | |
+#include <string.h> | |
+ | |
+void | |
+_threadmemset(void *v, int c, int n) | |
+{ | |
+ memset(v, c, n); | |
+} | |
diff --git a/src/libthread/memsetd.c b/src/libthread/memsetd.c | |
t@@ -0,0 +1,8 @@ | |
+#include "threadimpl.h" | |
+#include <string.h> | |
+ | |
+void | |
+_threaddebugmemset(void *v, int c, int n) | |
+{ | |
+ memset(v, c, n); | |
+} | |
diff --git a/src/libthread/mkfile b/src/libthread/mkfile | |
t@@ -0,0 +1,2 @@ | |
+<../libutf/mkfile | |
+ | |
diff --git a/src/libthread/note.c b/src/libthread/note.c | |
t@@ -0,0 +1,143 @@ | |
+#include "threadimpl.h" | |
+ | |
+int _threadnopasser; | |
+ | |
+#ifdef NOTDEF | |
+#define NFN 33 | |
+#define ERRLEN 48 | |
+typedef struct Note Note; | |
+struct Note | |
+{ | |
+ Lock inuse; | |
+ Proc *proc; /* recipient */ | |
+ char s[ERRMAX]; /* arg2 */ | |
+}; | |
+ | |
+static Note notes[128]; | |
+static Note *enotes = notes+nelem(notes); | |
+static int (*onnote[NFN])(void*, char*); | |
+static int onnotepid[NFN]; | |
+static Lock onnotelock; | |
+ | |
+int | |
+threadnotify(int (*f)(void*, char*), int in) | |
+{ | |
+ int i, topid; | |
+ int (*from)(void*, char*), (*to)(void*, char*); | |
+ | |
+ if(in){ | |
+ from = nil; | |
+ to = f; | |
+ topid = _threadgetproc()->pid; | |
+ }else{ | |
+ from = f; | |
+ to = nil; | |
+ topid = 0; | |
+ } | |
+ lock(&onnotelock); | |
+ for(i=0; i<NFN; i++) | |
+ if(onnote[i]==from){ | |
+ onnote[i] = to; | |
+ onnotepid[i] = topid; | |
+ break; | |
+ } | |
+ unlock(&onnotelock); | |
+ return i<NFN; | |
+} | |
+ | |
+static void | |
+delayednotes(Proc *p, void *v) | |
+{ | |
+ int i; | |
+ Note *n; | |
+ int (*fn)(void*, char*); | |
+ | |
+ if(!p->pending) | |
+ return; | |
+ | |
+ p->pending = 0; | |
+ for(n=notes; n<enotes; n++){ | |
+ if(n->proc == p){ | |
+ for(i=0; i<NFN; i++){ | |
+ if(onnotepid[i]!=p->pid || (fn = onnote[i])==n… | |
+ continue; | |
+ if((*fn)(v, n->s)) | |
+ break; | |
+ } | |
+ if(i==NFN){ | |
+ _threaddebug(DBGNOTE, "Unhandled note %s, proc… | |
+ if(v != nil) | |
+ noted(NDFLT); | |
+ else if(strncmp(n->s, "sys:", 4)==0) | |
+ abort(); | |
+ threadexitsall(n->s); | |
+ } | |
+ n->proc = nil; | |
+ unlock(&n->inuse); | |
+ } | |
+ } | |
+} | |
+ | |
+void | |
+_threadnote(void *v, char *s) | |
+{ | |
+ Proc *p; | |
+ Note *n; | |
+ | |
+ _threaddebug(DBGNOTE, "Got note %s", s); | |
+ if(strncmp(s, "sys:", 4) == 0) | |
+ noted(NDFLT); | |
+ | |
+// if(_threadexitsallstatus){ | |
+// _threaddebug(DBGNOTE, "Threadexitsallstatus = '%s'\n", _thre… | |
+// _exits(_threadexitsallstatus); | |
+// } | |
+ | |
+ if(strcmp(s, "threadint")==0) | |
+ noted(NCONT); | |
+ | |
+ p = _threadgetproc(); | |
+ if(p == nil) | |
+ noted(NDFLT); | |
+ | |
+ for(n=notes; n<enotes; n++) | |
+ if(canlock(&n->inuse)) | |
+ break; | |
+ if(n==enotes) | |
+ sysfatal("libthread: too many delayed notes"); | |
+ utfecpy(n->s, n->s+ERRMAX, s); | |
+ n->proc = p; | |
+ p->pending = 1; | |
+ if(!p->splhi) | |
+ delayednotes(p, v); | |
+ noted(NCONT); | |
+} | |
+#endif | |
+ | |
+int | |
+_procsplhi(void) | |
+{ | |
+ int s; | |
+ Proc *p; | |
+ | |
+ p = _threadgetproc(); | |
+ s = p->splhi; | |
+ p->splhi = 1; | |
+ return s; | |
+} | |
+ | |
+void | |
+_procsplx(int s) | |
+{ | |
+ Proc *p; | |
+ | |
+ p = _threadgetproc(); | |
+ p->splhi = s; | |
+ if(s) | |
+ return; | |
+/* | |
+ if(p->pending) | |
+ delayednotes(p, nil); | |
+*/ | |
+} | |
+ | |
diff --git a/src/libthread/proctab.c b/src/libthread/proctab.c | |
t@@ -0,0 +1,64 @@ | |
+#include "threadimpl.h" | |
+ | |
+/* this will need work */ | |
+enum | |
+{ | |
+ PTABHASH = 257, | |
+}; | |
+ | |
+static Lock ptablock; | |
+Proc *ptab[PTABHASH]; | |
+ | |
+void | |
+_threadsetproc(Proc *p) | |
+{ | |
+ int h; | |
+ | |
+ lock(&ptablock); | |
+ h = ((unsigned)p->pid)%PTABHASH; | |
+ p->link = ptab[h]; | |
+ unlock(&ptablock); | |
+ ptab[h] = p; | |
+} | |
+ | |
+static Proc* | |
+__threadgetproc(int rm) | |
+{ | |
+ Proc **l, *p; | |
+ int h, pid; | |
+ Thread *t; | |
+ ulong *s; | |
+ | |
+ s = (ulong*)((ulong)&pid & ~(STKSIZE-1)); | |
+ if(s[0] == STKMAGIC){ | |
+ t = (Thread*)s[1]; | |
+ return t->proc; | |
+ } | |
+ | |
+ pid = _threadgetpid(); | |
+ | |
+ lock(&ptablock); | |
+ h = ((unsigned)pid)%PTABHASH; | |
+ for(l=&ptab[h]; p=*l; l=&p->link){ | |
+ if(p->pid == pid){ | |
+ if(rm) | |
+ *l = p->link; | |
+ unlock(&ptablock); | |
+ return p; | |
+ } | |
+ } | |
+ unlock(&ptablock); | |
+ return nil; | |
+} | |
+ | |
+Proc* | |
+_threadgetproc(void) | |
+{ | |
+ return __threadgetproc(0); | |
+} | |
+ | |
+Proc* | |
+_threaddelproc(void) | |
+{ | |
+ return __threadgetproc(1); | |
+} | |
diff --git a/src/libthread/ref.c b/src/libthread/ref.c | |
t@@ -0,0 +1,13 @@ | |
+#include "threadimpl.h" | |
+ | |
+void | |
+incref(Ref *r) | |
+{ | |
+ _xinc(&r->ref); | |
+} | |
+ | |
+long | |
+decref(Ref *r) | |
+{ | |
+ return _xdec(&r->ref); | |
+} | |
diff --git a/src/libthread/rendez.c b/src/libthread/rendez.c | |
t@@ -0,0 +1,104 @@ | |
+#include "threadimpl.h" | |
+ | |
+Rgrp _threadrgrp; | |
+static int isdirty; | |
+int _threadhighnrendez; | |
+int _threadnrendez; | |
+static int nrendez; | |
+ | |
+static ulong | |
+finish(Thread *t, ulong val) | |
+{ | |
+ ulong ret; | |
+ | |
+ ret = t->rendval; | |
+ t->rendval = val; | |
+ while(t->state == Running) | |
+ sleep(0); | |
+ lock(&t->proc->lock); | |
+ if(t->state == Rendezvous){ /* not always true: might be Dead */ | |
+ t->state = Ready; | |
+ _threadready(t); | |
+ } | |
+ unlock(&t->proc->lock); | |
+ return ret; | |
+} | |
+ | |
+ulong | |
+_threadrendezvous(ulong tag, ulong val) | |
+{ | |
+ ulong ret; | |
+ Thread *t, **l; | |
+ | |
+ lock(&_threadrgrp.lock); | |
+_threadnrendez++; | |
+ l = &_threadrgrp.hash[tag%nelem(_threadrgrp.hash)]; | |
+ for(t=*l; t; l=&t->rendhash, t=*l){ | |
+ if(t->rendtag==tag){ | |
+ _threaddebug(DBGREND, "Rendezvous with thread %d.%d", … | |
+ *l = t->rendhash; | |
+ ret = finish(t, val); | |
+ --nrendez; | |
+ unlock(&_threadrgrp.lock); | |
+ return ret; | |
+ } | |
+ } | |
+ | |
+ /* Going to sleep here. */ | |
+ t = _threadgetproc()->thread; | |
+ t->rendbreak = 0; | |
+ t->inrendez = 1; | |
+ t->rendtag = tag; | |
+ t->rendval = val; | |
+ t->rendhash = *l; | |
+ *l = t; | |
+ t->nextstate = Rendezvous; | |
+ ++nrendez; | |
+ if(nrendez > _threadhighnrendez) | |
+ _threadhighnrendez = nrendez; | |
+ _threaddebug(DBGREND, "Rendezvous for tag %lud", t->rendtag); | |
+ unlock(&_threadrgrp.lock); | |
+ _sched(); | |
+ t->inrendez = 0; | |
+ _threaddebug(DBGREND, "Woke after rendezvous; val is %lud", t->rendval… | |
+ return t->rendval; | |
+} | |
+ | |
+/* | |
+ * This is called while holding _threadpq.lock and p->lock, | |
+ * so we can't lock _threadrgrp.lock. Instead our caller has | |
+ * to call _threadbreakrendez after dropping those locks. | |
+ */ | |
+void | |
+_threadflagrendez(Thread *t) | |
+{ | |
+ t->rendbreak = 1; | |
+ isdirty = 1; | |
+} | |
+ | |
+void | |
+_threadbreakrendez(void) | |
+{ | |
+ int i; | |
+ Thread *t, **l; | |
+ | |
+ if(isdirty == 0) | |
+ return; | |
+ lock(&_threadrgrp.lock); | |
+ if(isdirty == 0){ | |
+ unlock(&_threadrgrp.lock); | |
+ return; | |
+ } | |
+ isdirty = 0; | |
+ for(i=0; i<nelem(_threadrgrp.hash); i++){ | |
+ l = &_threadrgrp.hash[i]; | |
+ for(t=*l; t; t=*l){ | |
+ if(t->rendbreak){ | |
+ *l = t->rendhash; | |
+ finish(t, ~0); | |
+ }else | |
+ l=&t->rendhash; | |
+ } | |
+ } | |
+ unlock(&_threadrgrp.lock); | |
+} | |
diff --git a/src/libthread/rpm.spec b/src/libthread/rpm.spec | |
t@@ -0,0 +1,26 @@ | |
+Summary: Port of Plan 9's thread library | |
+Name: libthread | |
+Version: 2.0 | |
+Release: 1 | |
+Group: Development/C | |
+Copyright: BSD-like | |
+Packager: Russ Cox <[email protected]> | |
+Source: http://pdos.lcs.mit.edu/~rsc/software/libthread-2.0.tgz | |
+URL: http://pdos.lcs.mit.edu/~rsc/software/#libthread | |
+ | |
+%description | |
+Libthread is a port of Plan 9's thread library | |
+%prep | |
+%setup | |
+ | |
+%build | |
+make | |
+ | |
+%install | |
+make install | |
+ | |
+%files | |
+/usr/local/include/thread.h | |
+/usr/local/lib/libthread.a | |
+/usr/local/man/man3/thread.3 | |
+/usr/local/man/man3/ioproc.3 | |
diff --git a/src/libthread/sched.c b/src/libthread/sched.c | |
t@@ -0,0 +1,192 @@ | |
+#include "threadimpl.h" | |
+#include <signal.h> | |
+ | |
+//static Thread *runthread(Proc*); | |
+ | |
+static char *_psstate[] = { | |
+ "Dead", | |
+ "Running", | |
+ "Ready", | |
+ "Rendezvous", | |
+}; | |
+ | |
+static char* | |
+psstate(int s) | |
+{ | |
+ if(s < 0 || s >= nelem(_psstate)) | |
+ return "unknown"; | |
+ return _psstate[s]; | |
+} | |
+ | |
+void | |
+_schedinit(void *arg) | |
+{ | |
+ Proc *p; | |
+ Thread *t; | |
+ extern void ignusr1(void), _threaddie(int); | |
+ ignusr1(); | |
+ signal(SIGTERM, _threaddie); | |
+ | |
+ | |
+ | |
+ p = arg; | |
+ p->pid = _threadgetpid(); | |
+ _threadsetproc(p); | |
+ while(_setlabel(&p->sched)) | |
+ ; | |
+ _threaddebug(DBGSCHED, "top of schedinit, _threadexitsallstatus=%p", _… | |
+ if(_threadexitsallstatus) | |
+ exits(_threadexitsallstatus); | |
+ lock(&p->lock); | |
+ if((t=p->thread) != nil){ | |
+ p->thread = nil; | |
+ if(t->moribund){ | |
+ assert(t->moribund == 1); | |
+ t->state = Dead; | |
+ if(t->prevt) | |
+ t->prevt->nextt = t->nextt; | |
+ else | |
+ p->threads.head = t->nextt; | |
+ if(t->nextt) | |
+ t->nextt->prevt = t->prevt; | |
+ else | |
+ p->threads.tail = t->prevt; | |
+ unlock(&p->lock); | |
+ if(t->inrendez){ | |
+ _threadflagrendez(t); | |
+ _threadbreakrendez(); | |
+ } | |
+ _stackfree(t->stk); | |
+ free(t->cmdname); | |
+ free(t); /* XXX how do we know there are no ref… | |
+ t = nil; | |
+ _sched(); | |
+ } | |
+ if(p->needexec){ | |
+ t->ret = _schedexec(&p->exec); | |
+ p->needexec = 0; | |
+ } | |
+ if(p->newproc){ | |
+ t->ret = _schedfork(p->newproc); | |
+ if(t->ret < 0){ | |
+//fprint(2, "_schedfork: %r\n"); | |
+ abort(); | |
+} | |
+ p->newproc = nil; | |
+ } | |
+ t->state = t->nextstate; | |
+ if(t->state == Ready) | |
+ _threadready(t); | |
+ } | |
+ unlock(&p->lock); | |
+ _sched(); | |
+} | |
+ | |
+static inline Thread* | |
+runthread(Proc *p) | |
+{ | |
+ Thread *t; | |
+ Tqueue *q; | |
+ | |
+ if(p->nthreads==0) | |
+ return nil; | |
+ q = &p->ready; | |
+ lock(&p->readylock); | |
+ if(q->head == nil){ | |
+ q->asleep = 1; | |
+ _threaddebug(DBGSCHED, "sleeping for more work"); | |
+ unlock(&p->readylock); | |
+ while(rendezvous((ulong)q, 0) == ~0){ | |
+ if(_threadexitsallstatus) | |
+ exits(_threadexitsallstatus); | |
+ } | |
+ /* lock picked up from _threadready */ | |
+ } | |
+ t = q->head; | |
+ q->head = t->next; | |
+ unlock(&p->readylock); | |
+ return t; | |
+} | |
+ | |
+void | |
+_sched(void) | |
+{ | |
+ Proc *p; | |
+ Thread *t; | |
+ | |
+Resched: | |
+ p = _threadgetproc(); | |
+//fprint(2, "p %p\n", p); | |
+ if((t = p->thread) != nil){ | |
+ if((ulong)&p < (ulong)t->stk){ /* stack overflow */ | |
+ fprint(2, "stack overflow %lux %lux\n", (ulong)&p, (ul… | |
+ abort(); | |
+ } | |
+ // _threaddebug(DBGSCHED, "pausing, state=%s set %p goto %p", | |
+ // psstate(t->state), &t->sched, &p->sched); | |
+ if(_setlabel(&t->sched)==0) | |
+ _gotolabel(&p->sched); | |
+ return; | |
+ }else{ | |
+ t = runthread(p); | |
+ if(t == nil){ | |
+ _threaddebug(DBGSCHED, "all threads gone; exiting"); | |
+ _threaddelproc(); | |
+ _schedexit(p); | |
+ } | |
+ // _threaddebug(DBGSCHED, "running %d.%d", t->proc->pid, t->id); | |
+ p->thread = t; | |
+ if(t->moribund){ | |
+ _threaddebug(DBGSCHED, "%d.%d marked to die"); | |
+ goto Resched; | |
+ } | |
+ t->state = Running; | |
+ t->nextstate = Ready; | |
+ _gotolabel(&t->sched); | |
+ } | |
+} | |
+ | |
+long | |
+threadstack(void) | |
+{ | |
+ Proc *p; | |
+ Thread *t; | |
+ | |
+ p = _threadgetproc(); | |
+ t = p->thread; | |
+ return (ulong)&p - (ulong)t->stk; | |
+} | |
+ | |
+void | |
+_threadready(Thread *t) | |
+{ | |
+ Tqueue *q; | |
+ | |
+ assert(t->state == Ready); | |
+ _threaddebug(DBGSCHED, "readying %d.%d", t->proc->pid, t->id); | |
+ q = &t->proc->ready; | |
+ lock(&t->proc->readylock); | |
+ t->next = nil; | |
+ if(q->head==nil) | |
+ q->head = t; | |
+ else | |
+ q->tail->next = t; | |
+ q->tail = t; | |
+ if(q->asleep){ | |
+ q->asleep = 0; | |
+ /* lock passes to runthread */ | |
+ _threaddebug(DBGSCHED, "waking process %d", t->proc->pid); | |
+ while(rendezvous((ulong)q, 0) == ~0){ | |
+ if(_threadexitsallstatus) | |
+ exits(_threadexitsallstatus); | |
+ } | |
+ }else | |
+ unlock(&t->proc->readylock); | |
+} | |
+ | |
+void | |
+yield(void) | |
+{ | |
+ _sched(); | |
+} | |
+ | |
diff --git a/src/libthread/texec.c b/src/libthread/texec.c | |
t@@ -0,0 +1,34 @@ | |
+#include <lib9.h> | |
+#include <thread.h> | |
+extern int _threaddebuglevel; | |
+ | |
+void | |
+doexec(void *v) | |
+{ | |
+ char **argv = v; | |
+ | |
+ procexec(nil, argv[0], argv); | |
+ sendp(threadwaitchan(), nil); | |
+} | |
+ | |
+void | |
+threadmain(int argc, char **argv) | |
+{ | |
+ Channel *c; | |
+ Waitmsg *w; | |
+ | |
+ ARGBEGIN{ | |
+ case 'D': | |
+ _threaddebuglevel = ~0; | |
+ break; | |
+ }ARGEND | |
+ | |
+ c = threadwaitchan(); | |
+ proccreate(doexec, argv, 8192); | |
+ w = recvp(c); | |
+ if(w == nil) | |
+ print("exec failed\n"); | |
+ else | |
+ print("%d %lu %lu %lu %s\n", w->pid, w->time[0], w->time[1], w… | |
+ threadexits(nil); | |
+} | |
diff --git a/src/libthread/thread.3 b/src/libthread/thread.3 | |
t@@ -0,0 +1,576 @@ | |
+.TH THREAD 2 | |
+.SH NAME | |
+alt, | |
+chancreate, | |
+chanfree, | |
+chaninit, | |
+chanprint, | |
+mainstacksize, | |
+proccreate, | |
+procdata, | |
+procexec, | |
+procexecl, | |
+procrfork, | |
+recv, | |
+recvp, | |
+recvul, | |
+send, | |
+sendp, | |
+sendul, | |
+nbrecv, | |
+nbrecvp, | |
+nbrecvul, | |
+nbsend, | |
+nbsendp, | |
+nbsendul, | |
+threadcreate, | |
+threaddata, | |
+threadexits, | |
+threadexitsall, | |
+threadgetgrp, | |
+threadgetname, | |
+threadint, | |
+threadintgrp, | |
+threadkill, | |
+threadkillgrp, | |
+threadmain, | |
+threadnotify, | |
+threadid, | |
+threadpid, | |
+threadsetgrp, | |
+threadsetname, | |
+threadwaitchan, | |
+yield \- thread and proc management | |
+.SH SYNOPSIS | |
+.PP | |
+.EX | |
+.ta 4n +4n +4n +4n +4n +4n +4n | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <thread.h> | |
+.sp | |
+#define CHANEND 0 | |
+#define CHANSND 1 | |
+#define CHANRCV 2 | |
+#define CHANNOP 3 | |
+#define CHANNOBLK 4 | |
+.sp | |
+.ta \w' 'u +\w'Channel 'u | |
+typedef struct Alt Alt; | |
+struct Alt { | |
+ Channel *c; | |
+ void *v; | |
+ int op; | |
+ Channel **tag; | |
+ int entryno; | |
+}; | |
+.fi | |
+.de XX | |
+.ift .sp 0.5 | |
+.ifn .sp | |
+.. | |
+.PP | |
+.nf | |
+.ft L | |
+.ta \w'\fLChannel* 'u +4n +4n +4n +4n | |
+void threadmain(int argc, char *argv[]) | |
+int mainstacksize | |
+int proccreate(void (*fn)(void*), void *arg, uint stacksize) | |
+int procrfork(void (*fn)(void*), void *arg, uint stacksize, | |
+ int rforkflag) | |
+int threadcreate(void (*fn)(void*), void *arg, uint stacksize) | |
+void threadexits(char *status) | |
+void threadexitsall(char *status) | |
+void yield(void) | |
+.XX | |
+int threadid(void) | |
+int threadgrp(void) | |
+int threadsetgrp(int group) | |
+int threadpid(int id) | |
+.XX | |
+int threadint(int id) | |
+int threadintgrp(int group) | |
+int threadkill(int id) | |
+int threadkillgrp(int group) | |
+.XX | |
+void threadsetname(char *name) | |
+char* threadgetname(void) | |
+.XX | |
+void** threaddata(void) | |
+void** procdata(void) | |
+.XX | |
+int chaninit(Channel *c, int elsize, int nel) | |
+Channel* chancreate(int elsize, int nel) | |
+void chanfree(Channel *c) | |
+.XX | |
+int alt(Alt *alts) | |
+int recv(Channel *c, void *v) | |
+void* recvp(Channel *c) | |
+ulong recvul(Channel *c) | |
+int nbrecv(Channel *c, void *v) | |
+void* nbrecvp(Channel *c) | |
+ulong nbrecvul(Channel *c) | |
+int send(Channel *c, void *v) | |
+int sendp(Channel *c, void *v) | |
+int sendul(Channel *c, ulong v) | |
+int nbsend(Channel *c, void *v) | |
+int nbsendp(Channel *c, void *v) | |
+int nbsendul(Channel *c, ulong v) | |
+int chanprint(Channel *c, char *fmt, ...) | |
+.XX | |
+int procexecl(Channel *cpid, char *file, ...) | |
+int procexec(Channel *cpid, char *file, char *args[]) | |
+Channel* threadwaitchan(void) | |
+.XX | |
+int threadnotify(int (*f)(void*, char*), int in) | |
+.EE | |
+.SH DESCRIPTION | |
+.PP | |
+The thread library provides parallel programming support similar to that | |
+of the languages | |
+Alef and Newsqueak. | |
+Threads | |
+and | |
+procs | |
+occupy a shared address space, | |
+communicating and synchronizing through | |
+.I channels | |
+and shared variables. | |
+.PP | |
+A | |
+.I proc | |
+is a Plan 9 process that contains one or more cooperatively scheduled | |
+.IR threads . | |
+Programs using threads must replace | |
+.I main | |
+by | |
+.IR threadmain . | |
+The thread library provides a | |
+.I main | |
+function that sets up a proc with a single thread executing | |
+.I threadmain | |
+on a stack of size | |
+.I mainstacksize | |
+(default eight kilobytes). | |
+To set | |
+.IR mainstacksize , | |
+declare a global variable | |
+initialized to the desired value | |
+.RI ( e.g. , | |
+.B int | |
+.B mainstacksize | |
+.B = | |
+.BR 1024 ). | |
+.PP | |
+.I Threadcreate | |
+creates a new thread in the calling proc, returning a unique integer | |
+identifying the thread; the thread | |
+executes | |
+.I fn(arg) | |
+on a stack of size | |
+.IR stacksize . | |
+Thread stacks are allocated in shared memory, making it valid to pass | |
+pointers to stack variables between threads and procs. | |
+.I Procrfork | |
+creates a new proc, and inside that proc creates | |
+a single thread as | |
+.I threadcreate | |
+would, | |
+returning the id of the created thread. | |
+.I Procrfork | |
+creates the new proc by calling | |
+.B rfork | |
+(see | |
+.IR fork (2)) | |
+with flags | |
+.BR RFPROC|RFMEM|RFNOWAIT| \fIrforkflag\fR. | |
+(The thread library depends on all its procs | |
+running in the same rendezvous group. | |
+Do not include | |
+.B RFREND | |
+in | |
+.IR rforkflag .) | |
+.I Proccreate | |
+is identical to | |
+.I procrfork | |
+with | |
+.I rforkflag | |
+set to zero. | |
+Be aware that the calling thread may continue | |
+execution before | |
+the newly created proc and thread | |
+are scheduled. | |
+Because of this, | |
+.I arg | |
+should not point to data on the stack of a function that could | |
+return before the new process is scheduled. | |
+.PP | |
+.I Threadexits | |
+terminates the calling thread. | |
+If the thread is the last in its proc, | |
+.I threadexits | |
+also terminates the proc, using | |
+.I status | |
+as the exit status. | |
+.I Threadexitsall | |
+terminates all procs in the program, | |
+using | |
+.I status | |
+as the exit status. | |
+.PP | |
+The threads in a proc are coroutines, scheduled nonpreemptively | |
+in a round-robin fashion. | |
+A thread must explicitly relinquish control of the processor | |
+before another thread in the same proc is run. | |
+Calls that do this are | |
+.IR yield , | |
+.IR proccreate , | |
+.IR procexec , | |
+.IR procexecl , | |
+.IR threadexits , | |
+.IR alt , | |
+.IR send , | |
+and | |
+.I recv | |
+(and the calls related to | |
+.I send | |
+and | |
+.IR recv \(emsee | |
+their descriptions further on). | |
+Procs are scheduled by the operating system. | |
+Therefore, threads in different procs can preempt one another | |
+in arbitrary ways and should synchronize their | |
+actions using | |
+.B qlocks | |
+(see | |
+.IR lock (2)) | |
+or channel communication. | |
+System calls such as | |
+.IR read (2) | |
+block the entire proc; | |
+all threads in a proc block until the system call finishes. | |
+.PP | |
+As mentioned above, each thread has a unique integer thread id. | |
+Thread ids are not reused; they are unique across the life of the program. | |
+.I Threadid | |
+returns the id for the current thread. | |
+Each thread also has a thread group id. | |
+The initial thread has a group id of zero. | |
+Each new thread inherits the group id of | |
+the thread that created it. | |
+.I Threadgrp | |
+returns the group id for the current thread; | |
+.I threadsetgrp | |
+sets it. | |
+.I Threadpid | |
+returns the pid of the Plan 9 process containing | |
+the thread identified by | |
+.IR id , | |
+or \-1 | |
+if no such thread is found. | |
+.PP | |
+.I Threadint | |
+interrupts a thread that is blocked in a channel operation | |
+or system call. | |
+.I Threadintgrp | |
+interrupts all threads with the given group id. | |
+.I Threadkill | |
+marks a thread to die when it next relinquishes the processor | |
+(via one of the calls listed above). | |
+If the thread is blocked in a channel operation or system call, | |
+it is also interrupted. | |
+.I Threadkillgrp | |
+kills all threads with the given group id. | |
+Note that | |
+.I threadkill | |
+and | |
+.I threadkillgrp | |
+will not terminate a thread that never relinquishes | |
+the processor. | |
+.PP | |
+Primarily for debugging, | |
+threads can have string names associated with them. | |
+.I Threadgetname | |
+returns the current thread's name; | |
+.I threadsetname | |
+sets it. | |
+The pointer returned by | |
+.I threadgetname | |
+is only valid until the next call to | |
+.IR threadsetname . | |
+.PP | |
+.I Threaddata | |
+returns a pointer to a per-thread pointer | |
+that may be modified by threaded programs for | |
+per-thread storage. | |
+Similarly, | |
+.I procdata | |
+returns a pointer to a per-proc pointer. | |
+.PP | |
+.I Procexecl | |
+and | |
+.I procexec | |
+are threaded analogues of | |
+.I exec | |
+and | |
+.I execl | |
+(see | |
+.IR exec (2)); | |
+on success, | |
+they replace the calling thread (which must be the only thread in its proc) | |
+and invoke the external program, never returning. | |
+On error, they return \-1. | |
+If | |
+.I cpid | |
+is not null, the pid of the invoked program | |
+will be sent along | |
+.I cpid | |
+once the program has been started, or \-1 will be sent if an | |
+error occurs. | |
+.I Procexec | |
+and | |
+.I procexecl | |
+will not access their arguments after sending a result | |
+along | |
+.IR cpid . | |
+Thus, programs that malloc the | |
+.I argv | |
+passed to | |
+.I procexec | |
+can safely free it once they have | |
+received the | |
+.I cpid | |
+response. | |
+.I Threadwaitchan | |
+returns a channel of pointers to | |
+.B Waitmsg | |
+structures (see | |
+.IR wait (2)). | |
+When an exec'ed process exits, a pointer to a | |
+.B Waitmsg | |
+is sent to this channel. | |
+These | |
+.B Waitmsg | |
+structures have been allocated with | |
+.IR malloc (2) | |
+and should be freed after use. | |
+.PP | |
+A | |
+.B Channel | |
+is a buffered or unbuffered queue for fixed-size messages. | |
+Procs and threads | |
+.I send | |
+messages into the channel and | |
+.I recv | |
+messages from the channel. If the channel is unbuffered, a | |
+.I send | |
+operation blocks until the corresponding | |
+.I recv | |
+operation occurs and | |
+.IR "vice versa" . | |
+.I Chaninit | |
+initializes a | |
+.B Channel | |
+for messages of size | |
+.I elsize | |
+and with a buffer holding | |
+.I nel | |
+messages. | |
+If | |
+.I nel | |
+is zero, the channel is unbuffered. | |
+.IR Chancreate | |
+allocates a new channel and initializes it. | |
+.I Chanfree | |
+frees a channel that is no longer used. | |
+.I Chanfree | |
+can be called by either sender or receiver after the last item has been | |
+sent or received. Freeing the channel will be delayed if there is a thread | |
+blocked on it until that thread unblocks (but | |
+.I chanfree | |
+returns immediately). | |
+.PP | |
+.I Send | |
+sends the element pointed at by | |
+.I v | |
+to the channel | |
+.IR c . | |
+If | |
+.I v | |
+is null, zeros are sent. | |
+.I Recv | |
+receives an element from | |
+.I c | |
+and stores it in | |
+.IR v . | |
+If | |
+.I v | |
+is null, | |
+the received value is discarded. | |
+.I Send | |
+and | |
+.I recv | |
+return 1 on success, \-1 if interrupted. | |
+.I Nbsend | |
+and | |
+.I nbrecv | |
+behave similarly, but return 0 rather than blocking. | |
+.PP | |
+.IR Sendp , | |
+.IR nbsendp , | |
+.IR sendul , | |
+and | |
+.I nbsendul | |
+send a pointer or an unsigned long; the channel must | |
+have been initialized with the appropriate | |
+.IR elsize . | |
+.IR Recvp , | |
+.IR nbrecvp , | |
+.IR recvul , | |
+and | |
+.I nbrecvul | |
+receive a pointer or an unsigned long; | |
+they return zero when a zero is received, | |
+when interrupted, or | |
+(for | |
+.I nbrecvp | |
+and | |
+.IR nbrecvul ) | |
+when the operation would have blocked. | |
+To distinguish between these three cases, | |
+use | |
+.I recv | |
+or | |
+.IR nbrecv . | |
+.PP | |
+.I Alt | |
+can be used to recv from or send to one of a number of channels, | |
+as directed by an array of | |
+.B Alt | |
+structures, | |
+each of which describes a potential send or receive operation. | |
+In an | |
+.B Alt | |
+structure, | |
+.B c | |
+is the channel; | |
+.B v | |
+the value pointer (which may be null); and | |
+.B op | |
+the operation: | |
+.B CHANSND | |
+for a send operation, | |
+.B CHANRECV | |
+for a recv operation; | |
+.B CHANNOP | |
+for no operation | |
+(useful | |
+when | |
+.I alt | |
+is called with a varying set of operations). | |
+The array of | |
+.B Alt | |
+structures is terminated by an entry with | |
+.I op | |
+.B CHANEND | |
+or | |
+.BR CHANNOBLK . | |
+If at least one | |
+.B Alt | |
+structure can proceed, one of them is | |
+chosen at random to be executed. | |
+.I Alt | |
+returns the index of the chosen structure. | |
+If no operations can proceed and the list is terminated with | |
+.BR CHANNOBLK , | |
+.I alt | |
+returns the index of the terminating | |
+.B CHANNOBLK | |
+structure. | |
+Otherwise, | |
+.I alt | |
+blocks until one of the operations can proceed, | |
+eventually returning the index of the structure executes. | |
+.I Alt | |
+returns \-1 when interrupted. | |
+The | |
+.B tag | |
+and | |
+.B entryno | |
+fields in the | |
+.B Alt | |
+structure are used internally by | |
+.I alt | |
+and need not be initialized. | |
+They are not used between | |
+.I alt | |
+calls. | |
+.PP | |
+.I Chanprint | |
+formats its arguments in the manner of | |
+.IR print (2) | |
+and sends the result to the channel | |
+.IR c. | |
+The string delivered by | |
+.I chanprint | |
+is allocated with | |
+.IR malloc (2) | |
+and should be freed upon receipt. | |
+.PP | |
+Thread library functions do not return on failure; | |
+if errors occur, the entire program is aborted. | |
+.PP | |
+Threaded programs should use | |
+.I threadnotify | |
+in place of | |
+.I atnotify | |
+(see | |
+.IR notify (2)). | |
+.PP | |
+It is safe to use | |
+.B sysfatal | |
+(see | |
+.IR perror (2)) | |
+in threaded programs. | |
+.I Sysfatal | |
+will print the error string and call | |
+.IR threadexitsall . | |
+.PP | |
+It is safe to use | |
+.IR rfork | |
+(see | |
+.IR fork (2)) | |
+to manage the namespace, file descriptors, note group, and environment of a | |
+single process. | |
+That is, it is safe to call | |
+.I rfork | |
+with the flags | |
+.BR RFNAMEG , | |
+.BR RFFDG , | |
+.BR RFCFDG , | |
+.BR RFNOTEG , | |
+.BR RFENVG , | |
+and | |
+.BR RFCENVG. | |
+(To create new processes, use | |
+.I proccreate | |
+and | |
+.IR procrfork .) | |
+As mentioned above, | |
+the thread library depends on all procs being in the | |
+same rendezvous group; do not change the rendezvous | |
+group with | |
+.IR rfork . | |
+.SH FILES | |
+.B /sys/lib/acid/thread | |
+contains useful | |
+.IR acid (1) | |
+functions for debugging threaded programs. | |
+.PP | |
+.B /sys/src/libthread/example.c | |
+contains a full example program. | |
+.SH SOURCE | |
+.B /sys/src/libthread | |
+.SH SEE ALSO | |
+.IR intro (2), | |
+.IR ioproc (2) | |
diff --git a/src/libthread/thread.h b/src/libthread/thread.h | |
t@@ -0,0 +1,132 @@ | |
+#ifndef _THREADH_ | |
+#define _THREADH_ 1 | |
+ | |
+/* avoid conflicts with socket library */ | |
+#undef send | |
+#define send _threadsend | |
+#undef recv | |
+#define recv _threadrecv | |
+ | |
+typedef struct Alt Alt; | |
+typedef struct Channel Channel; | |
+typedef struct Ref Ref; | |
+ | |
+/* Channel structure. S is the size of the buffer. For unbuffered channels | |
+ * s is zero. v is an array of s values. If s is zero, v is unused. | |
+ * f and n represent the state of the queue pointed to by v. | |
+ */ | |
+ | |
+enum { | |
+ Nqwds = 2, | |
+ Nqshift = 5, // 2log #of bits in long | |
+ Nqmask = - 1, | |
+ Nqbits = (1 << Nqshift) * 2, | |
+}; | |
+ | |
+struct Channel { | |
+ int s; // Size of the channel (m… | |
+ unsigned int f; // Extraction point (insertion p… | |
+ unsigned int n; // Number of values in the chann… | |
+ int e; // Element size | |
+ int freed; // Set when channel is being … | |
+ volatile Alt **qentry; // Receivers/senders waiting (mal… | |
+ volatile int nentry; // # of entries malloc-ed | |
+ unsigned char v[1]; // Array of s values… | |
+}; | |
+ | |
+ | |
+/* Channel operations for alt: */ | |
+typedef enum { | |
+ CHANEND, | |
+ CHANSND, | |
+ CHANRCV, | |
+ CHANNOP, | |
+ CHANNOBLK, | |
+} ChanOp; | |
+ | |
+struct Alt { | |
+ Channel *c; /* channel */ | |
+ void *v; /* pointer to value */ | |
+ ChanOp op; /* operation */ | |
+ | |
+ /* the next variables are used internally to alt | |
+ * they need not be initialized | |
+ */ | |
+ Channel **tag; /* pointer to rendez-vous tag */ | |
+ int entryno; /* entry number */ | |
+}; | |
+ | |
+struct Ref { | |
+ long ref; | |
+}; | |
+ | |
+int alt(Alt alts[]); | |
+Channel* chancreate(int elemsize, int bufsize); | |
+int chaninit(Channel *c, int elemsize, int elemcnt); | |
+void chanfree(Channel *c); | |
+int chanprint(Channel *, char *, ...); | |
+long decref(Ref *r); /* returns 0 iff value is n… | |
+void incref(Ref *r); | |
+int nbrecv(Channel *c, void *v); | |
+void* nbrecvp(Channel *c); | |
+unsigned long nbrecvul(Channel *c); | |
+int nbsend(Channel *c, void *v); | |
+int nbsendp(Channel *c, void *v); | |
+int nbsendul(Channel *c, unsigned long v); | |
+int proccreate(void (*f)(void *arg), void *arg, unsigned int st… | |
+int procrfork(void (*f)(void *arg), void *arg, unsigned int sta… | |
+void** procdata(void); | |
+void procexec(Channel *, char *, char *[]); | |
+void procexecl(Channel *, char *, ...); | |
+int recv(Channel *c, void *v); | |
+void* recvp(Channel *c); | |
+unsigned long recvul(Channel *c); | |
+int send(Channel *c, void *v); | |
+int sendp(Channel *c, void *v); | |
+int sendul(Channel *c, unsigned long v); | |
+int threadcreate(void (*f)(void *arg), void *arg, unsigned int … | |
+void** threaddata(void); | |
+void threadexits(char *); | |
+void threadexitsall(char *); | |
+int threadgetgrp(void); /* return thread group of curren… | |
+char* threadgetname(void); | |
+void threadint(int); /* interrupt thread */ | |
+void threadintgrp(int); /* interrupt threads in grp */ | |
+void threadkill(int); /* kill thread */ | |
+void threadkillgrp(int); /* kill threads in group */ | |
+void threadmain(int argc, char *argv[]); | |
+void threadnonotes(void); | |
+int threadnotify(int (*f)(void*, char*), int in); | |
+int threadid(void); | |
+int threadpid(int); | |
+int threadsetgrp(int); /* set thread group, return old */ | |
+void threadsetname(char *name); | |
+Channel* threadwaitchan(void); | |
+int tprivalloc(void); | |
+void tprivfree(int); | |
+void **tprivaddr(int); | |
+void yield(void); | |
+ | |
+long threadstack(void); | |
+ | |
+extern int mainstacksize; | |
+ | |
+/* slave I/O processes */ | |
+typedef struct Ioproc Ioproc; | |
+ | |
+Ioproc* ioproc(void); | |
+void closeioproc(Ioproc*); | |
+void iointerrupt(Ioproc*); | |
+ | |
+int ioclose(Ioproc*, int); | |
+int iodial(Ioproc*, char*, char*, char*, int*); | |
+int ioopen(Ioproc*, char*, int); | |
+long ioread(Ioproc*, int, void*, long); | |
+long ioreadn(Ioproc*, int, void*, long); | |
+long iowrite(Ioproc*, int, void*, long); | |
+int iosleep(Ioproc*, long); | |
+ | |
+long iocall(Ioproc*, long (*)(va_list*), ...); | |
+void ioret(Ioproc*, int); | |
+ | |
+#endif /* _THREADH_ */ | |
diff --git a/src/libthread/threadimpl.h b/src/libthread/threadimpl.h | |
t@@ -0,0 +1,219 @@ | |
+/* | |
+ * Some notes on locking: | |
+ * | |
+ * All the locking woes come from implementing | |
+ * threadinterrupt (and threadkill). | |
+ * | |
+ * _threadgetproc()->thread is always a live pointer. | |
+ * p->threads, p->ready, and _threadrgrp also contain | |
+ * live thread pointers. These may only be consulted | |
+ * while holding p->lock or _threadrgrp.lock; in procs | |
+ * other than p, the pointers are only guaranteed to be live | |
+ * while the lock is still being held. | |
+ * | |
+ * Thread structures can only be freed by the proc | |
+ * they belong to. Threads marked with t->inrendez | |
+ * need to be extracted from the _threadrgrp before | |
+ * being freed. | |
+ * | |
+ * _threadrgrp.lock cannot be acquired while holding p->lock. | |
+ */ | |
+ | |
+#include <assert.h> | |
+#include <lib9.h> | |
+#include <thread.h> | |
+#include "label.h" | |
+ | |
+enum{ | |
+STKSIZE = 16384, | |
+STKMAGIC = 0xCAFEBEEF | |
+}; | |
+ | |
+typedef struct Thread Thread; | |
+typedef struct Proc Proc; | |
+typedef struct Tqueue Tqueue; | |
+typedef struct Pqueue Pqueue; | |
+typedef struct Rgrp Rgrp; | |
+typedef struct Execargs Execargs; | |
+ | |
+/* must match list in sched.c */ | |
+typedef enum | |
+{ | |
+ Dead, | |
+ Running, | |
+ Ready, | |
+ Rendezvous, | |
+} State; | |
+ | |
+typedef enum | |
+{ | |
+ Channone, | |
+ Chanalt, | |
+ Chansend, | |
+ Chanrecv, | |
+} Chanstate; | |
+ | |
+enum | |
+{ | |
+ RENDHASH = 10009, | |
+ Printsize = 2048, | |
+ NPRIV = 8, | |
+}; | |
+ | |
+struct Rgrp | |
+{ | |
+ Lock lock; | |
+ Thread *hash[RENDHASH]; | |
+}; | |
+ | |
+struct Tqueue /* Thread queue */ | |
+{ | |
+ int asleep; | |
+ Thread *head; | |
+ Thread *tail; | |
+}; | |
+ | |
+struct Thread | |
+{ | |
+ Lock lock; /* protects thread da… | |
+ Label sched; /* for context switches */ | |
+ int id; /* thread id */ | |
+ int grp; /* thread group */ | |
+ int moribund; /* thread needs to die */ | |
+ State state; /* run state */ | |
+ State nextstate; /* next run state */ | |
+ uchar *stk; /* top of stack (lowest addr… | |
+ uint stksize; /* stack size */ | |
+ Thread *next; /* next on ready queue */ | |
+ | |
+ Proc *proc; /* proc of this thread */ | |
+ Thread *nextt; /* next on list of threads in thi… | |
+ Thread *prevt; /* prev on list of threads in thi… | |
+ int ret; /* return value for Exe… | |
+ | |
+ char *cmdname; /* ptr to name of thread */ | |
+ | |
+ int inrendez; | |
+ Thread *rendhash; /* Trgrp linked list */ | |
+ ulong rendtag; /* rendezvous tag */ | |
+ ulong rendval; /* rendezvous value */ | |
+ int rendbreak; /* rendezvous has been taken */ | |
+ | |
+ Chanstate chan; /* which channel operation is cu… | |
+ Alt *alt; /* pointer to current … | |
+ | |
+ void* udata[NPRIV]; /* User per-thread data pointer */ | |
+}; | |
+ | |
+struct Execargs | |
+{ | |
+ char *prog; | |
+ char **args; | |
+ int fd[2]; | |
+}; | |
+ | |
+struct Proc | |
+{ | |
+ Lock lock; | |
+ Label sched; /* for context switches */ | |
+ Proc *link; /* in proctab */ | |
+ int pid; /* process id */ | |
+ int splhi; /* delay notes */ | |
+ Thread *thread; /* running thread */ | |
+ | |
+ int needexec; | |
+ Execargs exec; /* exec argument */ | |
+ Proc *newproc; /* fork argument */ | |
+ char exitstr[ERRMAX]; /* exit status */ | |
+ | |
+ int rforkflag; | |
+ int nthreads; | |
+ Tqueue threads; /* All threads of this proc */ | |
+ Tqueue ready; /* Runnable threads */ | |
+ Lock readylock; | |
+ | |
+ char printbuf[Printsize]; | |
+ int blocked; /* In a rendezvous */ | |
+ int pending; /* delayed note pending */ | |
+ int nonotes; /* delay notes */ | |
+ uint nextID; /* ID of most recently crea… | |
+ Proc *next; /* linked list of Procs */ | |
+ | |
+ void *arg; /* passed between sha… | |
+ char str[ERRMAX]; /* used by threadexits to avoi… | |
+ char errbuf[ERRMAX]; /* errstr */ | |
+ | |
+ void* udata; /* User per-proc data pointer */ | |
+}; | |
+ | |
+struct Pqueue { /* Proc queue */ | |
+ Lock lock; | |
+ Proc *head; | |
+ Proc **tail; | |
+}; | |
+ | |
+struct Ioproc | |
+{ | |
+ int tid; | |
+ Channel *c, *creply; | |
+ int inuse; | |
+ long (*op)(va_list*); | |
+ va_list arg; | |
+ long ret; | |
+ char err[ERRMAX]; | |
+ Ioproc *next; | |
+}; | |
+ | |
+void _gotolabel(Label*); | |
+int _setlabel(Label*); | |
+void _freeproc(Proc*); | |
+Proc* _newproc(void(*)(void*), void*, uint, char*, int, int); | |
+int _procsplhi(void); | |
+void _procsplx(int); | |
+void _sched(void); | |
+int _schedexec(Execargs*); | |
+void _schedexecwait(void); | |
+void _schedexit(Proc*); | |
+int _schedfork(Proc*); | |
+void _schedinit(void*); | |
+void _systhreadinit(void); | |
+void _threadassert(char*); | |
+void _threadbreakrendez(void); | |
+void __threaddebug(ulong, char*, ...); | |
+#define _threaddebug if(!_threaddebuglevel){}else __threaddebug | |
+void _threadexitsall(char*); | |
+void _threadflagrendez(Thread*); | |
+Proc* _threadgetproc(void); | |
+Proc* _threaddelproc(void); | |
+void _threadsetproc(Proc*); | |
+void _threadinitstack(Thread*, void(*)(void*), void*); | |
+void* _threadmalloc(long, int); | |
+void _threadnote(void*, char*); | |
+void _threadready(Thread*); | |
+ulong _threadrendezvous(ulong, ulong); | |
+void _threadsignal(void); | |
+void _threadsysfatal(char*, va_list); | |
+long _xdec(long*); | |
+void _xinc(long*); | |
+void _threadremove(Proc*, Thread*); | |
+ | |
+extern int _threaddebuglevel; | |
+extern char* _threadexitsallstatus; | |
+extern Pqueue _threadpq; | |
+extern Channel* _threadwaitchan; | |
+extern Rgrp _threadrgrp; | |
+extern void _stackfree(void*); | |
+ | |
+#define DBGAPPL (1 << 0) | |
+#define DBGSCHED (1 << 16) | |
+#define DBGCHAN (1 << 17) | |
+#define DBGREND (1 << 18) | |
+/* #define DBGKILL (1 << 19) */ | |
+#define DBGNOTE (1 << 20) | |
+#define DBGEXEC (1 << 21) | |
+ | |
+#define ioproc_arg(io, type) (va_arg((io)->arg, type)) | |
+extern int _threadgetpid(void); | |
+extern void _threadmemset(void*, int, int); | |
+extern void _threaddebugmemset(void*, int, int); | |
+ | |
diff --git a/src/libthread/tprimes b/src/libthread/tprimes | |
Binary files differ. | |
diff --git a/src/libthread/tprimes.c b/src/libthread/tprimes.c | |
t@@ -0,0 +1,62 @@ | |
+#include <lib9.h> | |
+#include <thread.h> | |
+ | |
+int quiet; | |
+int goal; | |
+int buffer; | |
+int (*fn)(void(*)(void*), void*, uint) = threadcreate; | |
+ | |
+void | |
+primethread(void *arg) | |
+{ | |
+ Channel *c, *nc; | |
+ int p, i; | |
+ | |
+ c = arg; | |
+ p = recvul(c); | |
+ if(p > goal) | |
+ threadexitsall(nil); | |
+ if(!quiet) | |
+ print("%d\n", p); | |
+ nc = chancreate(sizeof(ulong), buffer); | |
+ (*fn)(primethread, nc, 8192); | |
+ for(;;){ | |
+ i = recvul(c); | |
+ if(i%p) | |
+ sendul(nc, i); | |
+ } | |
+} | |
+ | |
+extern int _threaddebuglevel; | |
+ | |
+void | |
+threadmain(int argc, char **argv) | |
+{ | |
+ int i; | |
+ Channel *c; | |
+ | |
+ ARGBEGIN{ | |
+ case 'D': | |
+ _threaddebuglevel = atoi(ARGF()); | |
+ break; | |
+ case 'q': | |
+ quiet = 1; | |
+ break; | |
+ case 'b': | |
+ buffer = atoi(ARGF()); | |
+ break; | |
+ case 'p': | |
+ fn=proccreate; | |
+ break; | |
+ }ARGEND | |
+ | |
+ if(argc>0) | |
+ goal = atoi(argv[0]); | |
+ else | |
+ goal = 100; | |
+ | |
+ c = chancreate(sizeof(ulong), buffer); | |
+ (*fn)(primethread, c, 8192); | |
+ for(i=2;; i++) | |
+ sendul(c, i); | |
+} |