commit 76193d7cb0457807b2f0b95f909ab5de19480cd7 from: rsc date: Tue Sep 30 17:47:42 2003 UTC Initial revision commit - ed7c8e8d02c02bdbff1e88a6d8d1419f39af48ad commit + 76193d7cb0457807b2f0b95f909ab5de19480cd7 blob - /dev/null blob + 14a90d0f9360c11a6227473cc89264c5d96451d7 (mode 644) --- /dev/null +++ man/man7/regexp9.7 @@ -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) blob - /dev/null blob + 97b7b1e7147a5350d7d5e70db522d849595324f7 (mode 644) --- /dev/null +++ man/man7/utf.7 @@ -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" . blob - /dev/null blob + a5d7d87d79f0af66f7a5f54320edabb411b0e803 (mode 644) --- /dev/null +++ src/cmd/mk/LICENSE @@ -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. + blob - /dev/null blob + 087ed3abab5ea1bb549989f2c6abbae4ce024922 (mode 644) --- /dev/null +++ src/cmd/mk/Make.FreeBSD-386 @@ -0,0 +1,7 @@ +CC=gcc +CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME) +NAN=nan64.$O blob - /dev/null blob + edbdc111a2a0c008975e297c7ca60e46199397a9 (mode 644) --- /dev/null +++ src/cmd/mk/Make.HP-UX-9000 @@ -0,0 +1,6 @@ +CC=cc +CFLAGS=-O -c -Ae -I. +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O blob - /dev/null blob + 74b0252c6eae6331b48ede7fcfa1ed760609d1a3 (mode 644) --- /dev/null +++ src/cmd/mk/Make.Linux-386 @@ -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 blob - /dev/null blob + 3d45279bba28dbe69c00f89568c444ab83a4ac72 (mode 644) --- /dev/null +++ src/cmd/mk/Make.OSF1-alpha @@ -0,0 +1,6 @@ +CC=cc +CFLAGS+=-g -c -I. +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O blob - /dev/null blob + c5fe67b82d86c731a56c101e522e804390e5bd5a (mode 644) --- /dev/null +++ src/cmd/mk/Make.SunOS-sun4u @@ -0,0 +1,2 @@ +include Make.SunOS-sun4u-$(CC) +NAN=nan64.$O blob - /dev/null blob + 829301de8649d40632eb809ae512598af11d48c5 (mode 644) --- /dev/null +++ src/cmd/mk/Make.SunOS-sun4u-cc @@ -0,0 +1,6 @@ +CC=cc +CFLAGS+=-g -c -I. -O +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O blob - /dev/null blob + 5c41594812a4a27eb848d384cbc356d206c0a44b (mode 644) --- /dev/null +++ src/cmd/mk/Make.SunOS-sun4u-gcc @@ -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 blob - /dev/null blob + df21b60dc87bfff61a07ab417951318ecba9c229 (mode 644) --- /dev/null +++ src/cmd/mk/Makefile @@ -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.ports $(TGZFILES) $(NAME)-$(VERSION) + 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 rsc@amsterdam.lcs.mit.edu: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=="---" { ofile=$$2; next} {if(ofile) print >ofile}') + (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 rsc@amsterdam.lcs.mit.edu:public_html/software + +.phony: all clean nuke install tgz rpm ports blob - /dev/null blob + f840b18e9054c99f6b883851233628879c9f29b2 (mode 644) --- /dev/null +++ src/cmd/mk/Makefile.MID @@ -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 + blob - /dev/null blob + 521ef7a7d90564d89bb4e2741c02727fcc18d3a5 (mode 644) --- /dev/null +++ src/cmd/mk/arc.c @@ -0,0 +1,52 @@ +#include "mk.h" + +Arc * +newarc(Node *n, Rule *r, char *stem, Resub *match) +{ + Arc *a; + + a = (Arc *)Malloc(sizeof(Arc)); + a->n = n; + a->r = r; + a->stem = strdup(stem); + rcopy(a->match, match, NREGEXP); + a->next = 0; + a->flag = 0; + a->prog = r->prog; + return(a); +} + +void +dumpa(char *s, Arc *a) +{ + char buf[1024]; + + Bprint(&bout, "%sArc@%p: n=%p r=%p flag=0x%x stem='%s'", + s, a, a->n, a->r, a->flag, a->stem); + if(a->prog) + Bprint(&bout, " prog='%s'", a->prog); + Bprint(&bout, "\n"); + + if(a->n){ + snprint(buf, sizeof(buf), "%s ", (*s == ' ')? s:""); + dumpn(buf, a->n); + } +} + +void +nrep(void) +{ + Symtab *sym; + Word *w; + + sym = symlook("NREP", S_VAR, 0); + if(sym){ + w = (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); +} blob - /dev/null blob + 9ba7a12d584af73d22a43d1c75fb695447cdab93 (mode 644) --- /dev/null +++ src/cmd/mk/archive.c @@ -0,0 +1,180 @@ +#include "mk.h" +#define ARMAG "!\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 archive header */ + if(dolong) + snprint(buf, sizeof(buf), "%s(%s)", archive, member); + else + snprint(buf, sizeof(buf), "%s(%.*s)", archive, SARNAME, member); + sym = symlook(buf, S_TIME, 0); + if (sym) + return (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 an archive\n", file); + symlook(file, S_BITCH, (void *)file); + } + return 1; + } + if(read(fd, buf, SARMAG) != SARMAG){ + close(fd); + return 0; + } + close(fd); + return !strncmp(ARMAG, buf, SARMAG); +} + +static char* +split(char *name, char **member) +{ + char *p, *q; + + p = strdup(name); + q = utfrune(p, '('); + if(q){ + *q++ = 0; + if(member) + *member = q; + q = utfrune(q, ')'); + if (q) + *q = 0; + if(type(p)) + return p; + free(p); + fprint(2, "mk: '%s' is not an archive\n", name); + } + return 0; +} blob - /dev/null blob + 46498310499b357cd492afde652e911e3fe6a8eb (mode 644) --- /dev/null +++ src/cmd/mk/bundle.ports @@ -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= rsc@post.harvard.edu + +DEPENDS= ${PORTSDIR}/devel/libutf \ + ${PORTSDIR}/devel/libfmt \ + ${PORTSDIR}/devel/libbio \ + ${PORTSDIR}/devel/libregexp9 + +MAN1= mk.1 +USE_REINPLACE= yes + +.include + +post-patch: + ${REINPLACE_CMD} -e 's,$$(PREFIX),${PREFIX},g' ${WRKSRC}/Makefile + +.include +--- 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 +rsc@post.harvard.edu +--- 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. blob - /dev/null blob + c040db58f103a8b60246d1086c2c98dbeed5a17a (mode 644) --- /dev/null +++ src/cmd/mk/env.c @@ -0,0 +1,149 @@ +#include "mk.h" + +enum { + ENVQUANTA=10 +}; + +Envy *envy; +static int nextv; + +static char *myenv[] = +{ + "target", + "stem", + "prereq", + "pid", + "nproc", + "newprereq", + "alltarget", + "newmember", + "stem0", /* must be in order from here */ + "stem1", + "stem2", + "stem3", + "stem4", + "stem5", + "stem6", + "stem7", + "stem8", + "stem9", + 0, +}; + +void +initenv(void) +{ + char **p; + + for(p = myenv; *p; p++) + symlook(*p, S_INTERNAL, (void *)""); + readenv(); /* o.s. dependent */ +} + +static void +envinsert(char *name, Word *value) +{ + static int envsize; + + if (nextv >= envsize) { + envsize += ENVQUANTA; + envy = (Envy *) Realloc((char *) envy, envsize*sizeof(Envy)); + } + envy[nextv].name = name; + envy[nextv++].values = value; +} + +static void +envupd(char *name, Word *value) +{ + Envy *e; + + for(e = envy; e->name; e++) + if(strcmp(name, e->name) == 0){ + delword(e->values); + e->values = value; + return; + } + e->name = name; + e->values = value; + envinsert(0,0); +} + +static void +ecopy(Symtab *s) +{ + char **p; + + if(symlook(s->name, S_NOEXPORT, 0)) + return; + for(p = myenv; *p; p++) + if(strcmp(*p, s->name) == 0) + return; + envinsert(s->name, (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; +} blob - /dev/null blob + 2533343fd8ddabab29e513918c7f0e53bb0c3c35 (mode 644) --- /dev/null +++ src/cmd/mk/file.c @@ -0,0 +1,90 @@ +#include "mk.h" + +/* table-driven version in bootes dump of 12/31/96 */ + +long +mtime(char *name) +{ + return mkmtime(name); +} + +long +timeof(char *name, int force) +{ + Symtab *sym; + long t; + + if(utfrune(name, '(')) + return atimeof(force, name); /* archive */ + + if(force) + return mtime(name); + + + sym = symlook(name, S_TIME, 0); + if (sym) + return (long) sym->value; /* uggh */ + + t = mtime(name); + if(t == 0) + return 0; + + symlook(name, S_TIME, (void*)t); /* install time in cache */ + return t; +} + +void +touch(char *name) +{ + Bprint(&bout, "touch(%s)\n", name); + if(nflag) + return; + + if(utfrune(name, '(')) + atouch(name); /* archive */ + else if(chgtime(name) < 0) { + fprint(2, "%s: %r\n", name); + Exit(); + } +} + +void +delete(char *name) +{ + if(utfrune(name, '(') == 0) { /* file */ + if(remove(name) < 0) + fprint(2, "remove %s: %r\n", name); + } else + fprint(2, "hoon off; mk can'tdelete archive members\n"); +} + +void +timeinit(char *s) +{ + long t; + char *cp; + Rune r; + int c, n; + + t = time(0); + while (*s) { + cp = s; + do{ + n = chartorune(&r, s); + if (r == ' ' || r == ',' || r == '\n') + break; + s += n; + } while(*s); + c = *s; + *s = 0; + symlook(strdup(cp), S_TIME, (void *)t)->value = (void *)t; + if (c) + *s++ = c; + while(*s){ + n = chartorune(&r, s); + if(r != ' ' && r != ',' && r != '\n') + break; + s += n; + } + } +} blob - /dev/null blob + 2c9f46a38c7a6e89b415eb853a6f6b9802c9bc57 (mode 644) --- /dev/null +++ src/cmd/mk/fns.h @@ -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); blob - /dev/null blob + 58529259930ab2e864e95e503d75cae7986adc83 (mode 644) --- /dev/null +++ src/cmd/mk/graph.c @@ -0,0 +1,279 @@ +#include "mk.h" + +static Node *applyrules(char *, char *); +static void togo(Node *); +static int vacuous(Node *); +static Node *newnode(char *); +static void trace(char *, Arc *); +static void cyclechk(Node *); +static void ambiguous(Node *); +static void attribute(Node *); + +Node * +graph(char *target) +{ + Node *node; + char *cnt; + + cnt = rulecnt(); + node = applyrules(target, cnt); + free(cnt); + cyclechk(node); + node->flags |= PROBABLE; /* make sure it doesn't get deleted */ + vacuous(node); + ambiguous(node); + attribute(node); + return(node); +} + +static Node * +applyrules(char *target, char *cnt) +{ + Symtab *sym; + Node *node; + Rule *r; + Arc head, *a = &head; + Word *w; + char stem[NAMEBLOCK], buf[NAMEBLOCK]; + Resub rmatch[NREGEXP]; + +/* print("applyrules(%lux='%s')\n", target, target);*//**/ + sym = symlook(target, S_NODE, 0); + if(sym) + return (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 || !*r->tail->s)) continue; /* no effect; ignore */ + if(cnt[r->rule] >= nreps) continue; + cnt[r->rule]++; + node->flags |= PROBABLE; + +/* if(r->attr&VIR) + * node->flags |= VIRTUAL; + * if(r->attr&NOREC) + * node->flags |= NORECIPE; + * if(r->attr&DEL) + * node->flags |= DELETE; + */ + if(!r->tail || !r->tail->s || !*r->tail->s) { + a->next = newarc((Node *)0, r, "", rmatch); + a = a->next; + } else + for(w = r->tail; w; w = w->next){ + a->next = newarc(applyrules(w->s, cnt), r, "", rmatch); + a = a->next; + } + cnt[r->rule]--; + head.n = node; + } + for(r = metarules; r; r = r->next){ + if((!r->recipe || !*r->recipe) && (!r->tail || !r->tail->s || !*r->tail->s)) continue; /* no effect; ignore */ + if ((r->attr&NOVIRT) && a != &head && (a->r->attr&VIR)) + continue; + if(r->attr®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, NREGEXP); + else + subst(stem, w->s, buf); + a->next = newarc(applyrules(buf, cnt), r, stem, rmatch); + a = a->next; + } + cnt[r->rule]--; + } + a->next = node->prereqs; + node->prereqs = head.next; + return(node); +} + +static void +togo(Node *node) +{ + Arc *la, *a; + + /* delete them now */ + la = 0; + for(a = node->prereqs; a; la = a, a = a->next) + if(a->flag&TOGO){ + if(a == node->prereqs) + node->prereqs = a->next; + else + la->next = a->next, a = la; + } +} + +static int +vacuous(Node *node) +{ + Arc *la, *a; + int vac = !(node->flags&PROBABLE); + + if(node->flags&READY) + return(node->flags&VACUOUS); + node->flags |= READY; + for(a = node->prereqs; a; a = a->next) + if(a->n && vacuous(a->n) && (a->r->attr&META)) + a->flag |= TOGO; + else + vac = 0; + /* if a rule generated arcs that DON'T go; no others from that rule go */ + for(a = node->prereqs; a; a = a->next) + if((a->flag&TOGO) == 0) + for(la = node->prereqs; la; la = la->next) + if((la->flag&TOGO) && (la->r == a->r)){ + la->flag &= ~TOGO; + } + togo(node); + if(vac) + node->flags |= VACUOUS; + return(vac); +} + +static Node * +newnode(char *name) +{ + register Node *node; + + node = (Node *)Malloc(sizeof(Node)); + symlook(name, S_NODE, (void *)node); + node->name = name; + node->time = timeof(name, 0); + node->prereqs = 0; + node->flags = node->time? PROBABLE : 0; + node->next = 0; + return(node); +} + +void +dumpn(char *s, Node *n) +{ + char buf[1024]; + Arc *a; + + 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->name); + Exit(); + } + n->flags |= CYCLE; + for(a = n->prereqs; a; a = a->next) + if(a->n) + cyclechk(a->n); + n->flags &= ~CYCLE; +} + +static void +ambiguous(Node *n) +{ + Arc *a; + Rule *r = 0; + Arc *la; + int bad = 0; + + la = 0; + for(a = n->prereqs; a; a = a->next){ + if(a->n) + ambiguous(a->n); + if(*a->r->recipe == 0) continue; + if(r == 0) + r = a->r, la = a; + else{ + if(r->recipe != a->r->recipe){ + if((r->attr&META) && !(a->r->attr&META)){ + la->flag |= TOGO; + r = a->r, la = a; + } else if(!(r->attr&META) && (a->r->attr&META)){ + a->flag |= TOGO; + continue; + } + } + if(r->recipe != a->r->recipe){ + if(bad == 0){ + fprint(2, "mk: ambiguous recipes for %s:\n", n->name); + bad = 1; + trace(n->name, la); + } + trace(n->name, a); + } + } + } + if(bad) + Exit(); + togo(n); +} + +static void +attribute(Node *n) +{ + register Arc *a; + + for(a = n->prereqs; a; a = a->next){ + if(a->r->attr&VIR) + n->flags |= VIRTUAL; + if(a->r->attr&NOREC) + n->flags |= NORECIPE; + if(a->r->attr&DEL) + n->flags |= DELETE; + if(a->n) + attribute(a->n); + } + if(n->flags&VIRTUAL) + n->time = 0; +} blob - /dev/null blob + 3ee244f184e6af975e48fc973bcee89e0b62c051 (mode 644) --- /dev/null +++ src/cmd/mk/lex.c @@ -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 newlines??*/ + if (buf->current != buf->start) { + insert(buf, 0); + return 1; + } + break; + default: + rinsert(buf, c); + break; + } + } +eof: + insert(buf, 0); + return *buf->start != 0; +} + +/* + * assemble a back-quoted shell command into a buffer + */ +static int +bquote(Biobuf *bp, Bufblock *buf) +{ + int c, line, term; + int start; + + line = mkinline; + while((c = Bgetrune(bp)) == ' ' || c == '\t') + ; + if(c == '{'){ + term = '}'; /* rc style */ + while((c = Bgetrune(bp)) == ' ' || c == '\t') + ; + } else + term = '`'; /* sh style */ + + start = buf->current-buf->start; + for(;c > 0; c = nextrune(bp, 0)){ + if(c == term){ + insert(buf, '\n'); + insert(buf,0); + buf->current = buf->start+start; + execinit(); + execsh(0, buf->current, buf, envy); + 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; +} blob - /dev/null blob + 267ed73fba323edbebfc7e5d20581f253c84baac (mode 644) --- /dev/null +++ src/cmd/mk/main.c @@ -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, 0); + 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]] [targets ...]\n"); + Exit(); +} + +void * +Malloc(int n) +{ + register void *s; + + s = malloc(n); + if(!s) { + fprint(2, "mk: cannot alloc %d bytes\n", n); + Exit(); + } + return(s); +} + +void * +Realloc(void *s, int n) +{ + if(s) + s = realloc(s, n); + else + s = malloc(n); + if(!s) { + fprint(2, "mk: cannot alloc %d bytes\n", n); + Exit(); + } + return(s); +} + +void +assert(char *s, int n) +{ + if(!n){ + fprint(2, "mk: Assertion ``%s'' failed.\n", s); + Exit(); + } +} + +void +regerror(char *s) +{ + if(patrule) + fprint(2, "mk: %s:%d: regular expression error; %s\n", + patrule->file, patrule->line, s); + else + fprint(2, "mk: %s:%d: regular expression error; %s\n", + infile, mkinline, s); + Exit(); +} blob - /dev/null blob + 2a96394aa8c64f846ae09db549e0b3c44839228f (mode 644) --- /dev/null +++ src/cmd/mk/match.c @@ -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; +} blob - /dev/null blob + c58a6dfe631034a2765097482935231c45dd6228 (mode 644) --- /dev/null +++ src/cmd/mk/mk.1 @@ -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 +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, node->time);*//**/ + if(node->flags&BEINGMADE) + return(did); + if((node->flags&MADE) && (node->flags&PRETENDING) && p && outofdate(p, parc, 0)){ + if(explain) + fprint(1, "unpretending %s(%ld) because %s is out of date(%ld)\n", + node->name, node->time, p->name, p->time); + unpretend(node); + } + /* + have a look if we are pretending in case + someone has been unpretended out from underneath us + */ + if(node->flags&MADE){ + if(node->flags&PRETENDING){ + node->time = 0; + }else + return(did); + } + /* consider no prerequsite case */ + if(node->prereqs == 0){ + if(node->time == 0){ + fprint(2, "mk: don't know how to make '%s'\n", node->name); + if(kflag){ + node->flags |= BEINGMADE; + runerrs++; + } else + Exit(); + } else + MADESET(node, MADE); + return(did); + } + /* + now see if we are out of date or what + */ + ready = 1; + weoutofdate = aflag; + ra = 0; + for(a = node->prereqs; a; a = a->next) + if(a->n){ + did = work(a->n, node, a) || did; + if(a->n->flags&(NOTMADE|BEINGMADE)) + ready = 0; + if(outofdate(node, a, 0)){ + weoutofdate = 1; + if((ra == 0) || (ra->n == 0) + || (ra->n->time < a->n->time)) + ra = a; + } + } else { + if(node->time == 0){ + if(ra == 0) + ra = a; + weoutofdate = 1; + } + } + if(ready == 0) /* can't do anything now */ + return(did); + if(weoutofdate == 0){ + MADESET(node, MADE); + return(did); + } + /* + can we pretend to be made? + */ + if((iflag == 0) && (node->time == 0) && (node->flags&(PRETENDING|CANPRETEND)) + && p && ra->n && !outofdate(p, ra, 0)){ + node->flags &= ~CANPRETEND; + MADESET(node, MADE); + if(explain && ((node->flags&PRETENDING) == 0)) + fprint(1, "pretending %s has time %ld\n", node->name, node->time); + node->flags |= PRETENDING; + return(did); + } + /* + node is out of date and we REALLY do have to do something. + quickly rescan for pretenders + */ + for(a = node->prereqs; a; a = a->next) + if(a->n && (a->n->flags&PRETENDING)){ + if(explain) + Bprint(&bout, "unpretending %s because of %s because of %s\n", + a->n->name, node->name, ra->n? ra->n->name : "rule with no prerequisites"); + + unpretend(a->n); + did = work(a->n, node, a) || did; + ready = 0; + } + if(ready == 0) /* try later unless nothing has happened for -k's sake */ + return(did || work(node, p, parc)); + did = dorecipe(node) || did; + return(did); +} + +void +update(int fake, Node *node) +{ + Arc *a; + + MADESET(node, fake? BEINGMADE : MADE); + if(((node->flags&VIRTUAL) == 0) && (access(node->name, 0) == 0)){ + node->time = timeof(node->name, 1); + node->flags &= ~(CANPRETEND|PRETENDING); + for(a = node->prereqs; a; a = a->next) + if(a->prog) + outofdate(node, a, 1); + } else { + node->time = 1; + for(a = node->prereqs; a; a = a->next) + if(a->n && outofdate(node, a, 1)) + node->time = a->n->time; + } +/* print("----node %s time=%ld flags=0x%x\n", node->name, node->time, node->flags);*//**/ +} + +static int +pcmp(char *prog, char *p, char *q) +{ + 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 archive member */ + return 1; + else + return node->time < arc->n->time; +} blob - /dev/null blob + 9b3f627d80ad310963b1d42d006bebabd3dccd45 (mode 644) --- /dev/null +++ src/cmd/mk/mk.h @@ -0,0 +1,203 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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|BEINGMADE|MADE))|(m) +#define PROBABLE 0x0100 +#define VACUOUS 0x0200 +#define NORECIPE 0x0400 +#define DELETE 0x0800 +#define NOMINUSE 0x1000 + +typedef struct Job +{ + Rule *r; /* master rule for job */ + Node *n; /* list of node targets */ + char *stem; + char **match; + Word *p; /* prerequistes */ + Word *np; /* new prerequistes */ + Word *t; /* targets */ + Word *at; /* all targets */ + int nproc; /* slot number */ + struct Job *next; +} Job; +extern Job *jobs; + +typedef struct Symtab +{ + short space; + char *name; + 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, ((l)>=0)?(l):mkinline)) +#define RERR(r) (fprint(2, "mk: %s:%d: rule error; ", (r)->file, (r)->line)) +#define NAMEBLOCK 1000 +#define BIGBLOCK 20000 + +#define SEP(c) (((c)==' ')||((c)=='\t')||((c)=='\n')) +#define WORDCHR(r) ((r) > ' ' && !utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", (r))) + +#define DEBUG(x) (debug&(x)) +#define D_PARSE 0x01 +#define D_GRAPH 0x02 +#define D_EXEC 0x04 + +#define LSEEK(f,o,p) seek(f,o,p) + +#define PERCENT(ch) (((ch) == '%') || ((ch) == '&')) + +#include "fns.h" blob - /dev/null blob + 101513566065d97abe1f03cab2e5e6b08c4a4cc1 (mode 644) --- /dev/null +++ src/cmd/mk/mkfile @@ -0,0 +1,9 @@ +all:V: Makefile Make.FreeBSD-386 Make.Linux-386 Make.HP-UX-9000 Make.OSF1-alpha \ + 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 ../libutf/Makefile.BOT + cat $prereq >$target + +Make.%: ../libutf/Make.% + cp $prereq $target blob - /dev/null blob + 5c1c0edaffa699225488fdf5a9e8b04a1951addc (mode 644) --- /dev/null +++ src/cmd/mk/mkfile.test @@ -0,0 +1,5 @@ +a: b + cp b a + +c:V: + echo hello world blob - /dev/null blob + b7737ef9bbe9db1e75937ddcb1e1b70e96c7a54f (mode 644) --- /dev/null +++ src/cmd/mk/parse.c @@ -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 file %s: %r\n", p); + } else + parse(p, newfd, 0); + break; + case '|': + p = wtos(tail, ' '); + if(*p == 0){ + SYNERR(-1); + fprint(2, "missing include program name\n"); + Exit(); + } + execinit(); + pid=pipecmd(p, envy, &newfd); + if(newfd < 0){ + fprint(2, "warning: skipping missing program file %s: %r\n", p); + } else + parse(p, newfd, 0); + while(waitup(-3, &pid) >= 0) + ; + if(pid != 0){ + fprint(2, "bad include program status\n"); + Exit(); + } + break; + case ':': + body = rbody(&in); + addrules(head, tail, body, attr, hline, prog); + break; + case '=': + if(head->next){ + SYNERR(-1); + fprint(2, "multiple vars on left side of assignment\n"); + Exit(); + } + if(symlook(head->s, S_OVERRIDE, 0)){ + set = varoverride; + } else { + set = 1; + if(varoverride) + symlook(head->s, S_OVERRIDE, (void *)""); + } + if(set){ +/* +char *cp; +dumpw("tail", tail); +cp = wtos(tail, ' '); print("assign %s to %s\n", head->s, cp); free(cp); +*/ + setvar(head->s, (void *) tail); + symlook(head->s, S_WESET, (void *)""); + } + if(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-dependent */ + if (pp && *pp == '=') { + while (p != pp) { + n = chartorune(&r, p); + switch(r) + { + default: + SYNERR(-1); + fprint(2, "unknown attribute '%c'\n",*p); + Exit(); + case 'U': + *attr = 1; + break; + } + p += n; + } + p++; /* skip trailing '=' */ + } + } + if((sep == ':') && *p && (*p != ' ') && (*p != '\t')){ + while (*p) { + n = chartorune(&r, p); + if (r == ':') + break; + p += n; + switch(r) + { + default: + SYNERR(-1); + fprint(2, "unknown attribute '%c'\n", p[-1]); + Exit(); + case 'D': + *attr |= DEL; + break; + case 'E': + *attr |= NOMINUSE; + break; + case 'n': + *attr |= NOVIRT; + break; + case 'N': + *attr |= NOREC; + break; + case 'P': + pp = utfrune(p, ':'); + if (pp == 0 || *pp == 0) + goto eos; + *pp = 0; + *prog = strdup(p); + *pp = ':'; + p = pp; + break; + case 'Q': + *attr |= QUIET; + break; + case 'R': + *attr |= REGEXP; + break; + case 'U': + *attr |= UPD; + break; + case 'V': + *attr |= VIR; + break; + } + } + if (*p++ != ':') { + eos: + SYNERR(-1); + fprint(2, "missing trailing :\n"); + Exit(); + } + } + *h = w = stow(line); + if(*w->s == 0 && sep != '<' && sep != '|') { + 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); +} blob - /dev/null blob + 657ddf2783019a0b34309bf0a10162f301e18b81 (mode 644) --- /dev/null +++ src/cmd/mk/rc.c @@ -0,0 +1,175 @@ +#include "mk.h" + +char *termchars = "'= \t"; /*used in parse.c to isolate assignment attribute*/ +char *shflags = "-I"; /* rc flag to force non-interactive mode */ +int IWS = '\1'; /* inter-word separator in env - not used in plan 9 */ + +/* + * 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 quotes above. + * 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; +} blob - /dev/null blob + 144a49041313501e1fc442dcb8ae62f07795322c (mode 644) --- /dev/null +++ src/cmd/mk/recipe.c @@ -0,0 +1,117 @@ +#include "mk.h" + +int +dorecipe(Node *node) +{ + char buf[BIGBLOCK]; + register Node *n; + Rule *r = 0; + Arc *a, *aa; + Word head, ahead, lp, ln, *w, *ww, *aw; + Symtab *s; + int did = 0; + + aa = 0; + /* + pick up the rule + */ + for(a = node->prereqs; a; a = a->next) + if(*a->r->recipe) + r = (aa = a)->r; + /* + no recipe? go to buggery! + */ + if(r == 0){ + if(!(node->flags&VIRTUAL) && !(node->flags&NORECIPE)){ + fprint(2, "mk: no recipe to make '%s'\n", node->name); + Exit(); + } + if(strchr(node->name, '(') && node->time == 0) + MADESET(node, MADE); + else + update(0, node); + if(tflag){ + if(!(node->flags&VIRTUAL)) + touch(node->name); + else if(explain) + Bprint(&bout, "no touch of virtual '%s'\n", node->name); + } + return(did); + } + /* + build the node list + */ + node->next = 0; + head.next = 0; + ww = &head; + ahead.next = 0; + aw = &ahead; + if(r->attr®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 interested in */ + 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->name, a->n->time); + } + } else { + if(explain) + fprint(1, "%s has no prerequisites\n", + n->name); + } + } + MADESET(n, BEINGMADE); + } + /*print("lt=%s ln=%s lp=%s\n",wtos(head.next, ' '),wtos(ln.next, ' '),wtos(lp.next, ' '));*//**/ + run(newjob(r, node, aa->stem, aa->match, lp.next, ln.next, head.next, ahead.next)); + return(1); +} + +void +addw(Word *w, char *s) +{ + Word *lw; + + for(lw = w; w = w->next; lw = w){ + if(strcmp(s, w->s) == 0) + return; + } + lw->next = newword(s); +} blob - /dev/null blob + be75e6b8677c4785735416a778e4e3700548a563 (mode 644) --- /dev/null +++ src/cmd/mk/rpm.spec @@ -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 +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 blob - /dev/null blob + 662f067f1a242a13fe08ab311b9ec91ee5e8e572 (mode 644) --- /dev/null +++ src/cmd/mk/rule.c @@ -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, char *prog) +{ + 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 alltarget='%s'", + r, r->file, r->line, r->attr, r->next, r->chain, wtos(r->alltargets, ' ')); + if(r->prog) + Bprint(&bout, " prog='%s'", r->prog); + Bprint(&bout, "\n\ttarget=%s: %s\n", r->target, wtos(r->tail, ' ')); + Bprint(&bout, "\trecipe@%ld='%s'\n", r->recipe, r->recipe); + } +} + +static int +rcmp(Rule *r, char *target, Word *tail) +{ + Word *w; + + if(strcmp(r->target, target)) + return 1; + for(w = r->tail; w && tail; w = w->next, tail = tail->next) + if(strcmp(w->s, tail->s)) + return 1; + return(w || tail); +} + +char * +rulecnt(void) +{ + char *s; + + s = Malloc(nrules); + memset(s, 0, nrules); + return(s); +} blob - /dev/null blob + 3c0c7f3e213307b3d69c3b574f8230995568ee8d (mode 644) --- /dev/null +++ src/cmd/mk/run.c @@ -0,0 +1,296 @@ +#include "mk.h" + +typedef struct Event +{ + int pid; + Job *job; +} Event; +static Event *events; +static int nevents, nrunning, nproclimit; + +typedef struct Process +{ + int pid; + int status; + struct Process *b, *f; +} Process; +static Process *phead, *pfree; +static void sched(void); +static void pnew(int, int), pdelete(Process *); + +int pidslot(int); + +void +run(Job *j) +{ + Job *jj; + + if(jobs){ + for(jj = jobs; jj->next; jj = jj->next) + ; + jj->next = j; + } else + jobs = j; + j->next = 0; + /* this code also in waitup after parse redirect */ + if(nrunning < nproclimit) + sched(); +} + +static void +sched(void) +{ + char *flags; + Job *j; + Bufblock *buf; + int slot; + Node *n; + Envy *e; + + if(jobs == 0){ + usage(); + return; + } + j = jobs; + jobs = j->next; + if(DEBUG(D_EXEC)) + fprint(1, "firing up job for target %s\n", wtos(j->t, ' ')); + slot = nextslot(); + events[slot].job = j; + buf = newbuf(); + e = buildenv(j, slot); + shprint(j->r->recipe, e, buf); + if(!tflag && (nflag || !(j->r->attr&QUIET))) + Bwrite(&bout, buf->start, (long)strlen(buf->start)); + freebuf(buf); + if(nflag||tflag){ + for(n = j->n; n; n = n->next){ + if(tflag){ + if(!(n->flags&VIRTUAL)) + touch(n->name); + else if(explain) + Bprint(&bout, "no touch of virtual '%s'\n", n->name); + } + n->time = time((long *)0); + MADESET(n, MADE); + } + } else { + if(DEBUG(D_EXEC)) + fprint(1, "recipe='%s'", j->r->recipe);/**/ + Bflush(&bout); + if(j->r->attr&NOMINUSE) + flags = 0; + else + flags = "-e"; + events[slot].pid = execsh(flags, j->r->recipe, 0, e); + usage(); + nrunning++; + if(DEBUG(D_EXEC)) + fprint(1, "pid for target %s = %d\n", wtos(j->t, ' '), events[slot].pid); + } +} + +int +waitup(int echildok, int *retstatus) +{ + Envy *e; + int pid; + int slot; + Symtab *s; + Word *w; + Job *j; + char buf[ERRMAX]; + Bufblock *bp; + int uarg = 0; + int done; + Node *n; + Process *p; + extern int runerrs; + + /* first check against the proces slist */ + if(retstatus) + for(p = phead; p; p = p->f) + if(p->pid == *retstatus){ + *retstatus = p->status; + pdelete(p); + return(-1); + } +again: /* rogue processes */ + pid = waitfor(buf); + if(pid == -1){ + if(echildok > 0) + return(1); + else { + fprint(2, "mk: (waitup %d): %r\n", echildok); + Exit(); + } + } + if(DEBUG(D_EXEC)) + fprint(1, "waitup got pid=%d, status='%s'\n", pid, buf); + if(retstatus && pid == *retstatus){ + *retstatus = buf[0]? 1:0; + return(-1); + } + slot = pidslot(pid); + if(slot < 0){ + if(DEBUG(D_EXEC)) + fprint(2, "mk: wait returned unexpected process %d\n", pid); + pnew(pid, buf[0]? 1:0); + goto again; + } + j = events[slot].job; + usage(); + nrunning--; + events[slot].pid = -1; + if(buf[0]){ + e = buildenv(j, slot); + bp = newbuf(); + shprint(j->r->recipe, e, bp); + 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*sizeof(Event)); + else + events = (Event *)Malloc(nproclimit*sizeof(Event)); + while(nevents < nproclimit) + events[nevents++].pid = 0; + } +} + +int +nextslot(void) +{ + int i; + + for(i = 0; i < nproclimit; i++) + if(events[i].pid <= 0) return i; + assert("out of slots!!", 0); + return 0; /* cyntax */ +} + +int +pidslot(int pid) +{ + int i; + + for(i = 0; i < nevents; i++) + if(events[i].pid == pid) return(i); + if(DEBUG(D_EXEC)) + fprint(2, "mk: wait returned unexpected process %d\n", pid); + return(-1); +} + + +static void +pnew(int pid, int status) +{ + Process *p; + + if(pfree){ + p = pfree; + pfree = p->f; + } else + p = (Process *)Malloc(sizeof(Process)); + p->pid = pid; + p->status = status; + p->f = phead; + phead = p; + if(p->f) + p->f->b = p; + p->b = 0; +} + +static void +pdelete(Process *p) +{ + if(p->f) + p->f->b = p->b; + if(p->b) + p->b->f = p->f; + else + phead = p->f; + p->f = pfree; + pfree = p; +} + +void +killchildren(char *msg) +{ + Process *p; + + kflag = 1; /* to make sure waitup doesn't exit */ + jobs = 0; /* make sure no more get scheduled */ + for(p = phead; p; p = p->f) + expunge(p->pid, msg); + while(waitup(1, (int *)0) == 0) + ; + Bprint(&bout, "mk: %s\n", msg); + Exit(); +} + +static long tslot[1000]; +static long tick; + +void +usage(void) +{ + long t; + + time(&t); + if(tick) + tslot[nrunning] += (t-tick); + tick = t; +} + +void +prusage(void) +{ + int i; + + usage(); + for(i = 0; i <= nevents; i++) + fprint(1, "%d: %ld\n", i, tslot[i]); +} blob - /dev/null blob + 524167a52bc1b3c948134574351c015646433a37 (mode 644) --- /dev/null +++ src/cmd/mk/sh.c @@ -0,0 +1,189 @@ +#include "mk.h" + +char *termchars = "\"'= \t"; /*used in parse.c to isolate assignment attribute*/ +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 conventions. + */ + + +/* + * skip a token in quotes. + */ +static char * +squote(char *cp, int c) +{ + Rune r; + int n; + + while(*cp){ + n = chartorune(&r, cp); + if(r == c) + return cp; + if(r == '\\') + n += chartorune(&r, cp+n); + cp += n; + } + SYNERR(-1); /* should never occur */ + fprint(2, "missing closing '\n"); + return 0; +} +/* + * search a string for unescaped characters in a pattern set + */ +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 quotes above. + * 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 string */ + } + return s; +} blob - /dev/null blob + 9e15bcefb493b7d258493011878064b66200515e (mode 644) --- /dev/null +++ src/cmd/mk/shprint.c @@ -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) == 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); +} blob - /dev/null blob + 6f7b8886bf76a61e29f30257692cfd7490ba974f (mode 644) --- /dev/null +++ src/cmd/mk/symtab.c @@ -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); +} blob - /dev/null blob + 602227881a8993068fc2028e21ad29bdc29eed17 (mode 644) --- /dev/null +++ src/cmd/mk/unix.c @@ -0,0 +1,306 @@ +#include "mk.h" +#include +#include +#include +#include + +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", status&0x7f); + else + snprint(msg, ERRMAX, "signal %d", status&0x7f); + } else if(status&0xff00) + snprint(msg, ERRMAX, "exit(%d)", (status>>8)&0xff); + } + return pid; +} + +void +expunge(int pid, char *msg) +{ + if(strcmp(msg, "interrupt")) + kill(pid, SIGINT); + else + kill(pid, SIGHUP); +} + +int +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; +} blob - /dev/null blob + 8429918d57093e0a2d27cf1262f086df04588923 (mode 644) --- /dev/null +++ src/cmd/mk/var.c @@ -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; +} blob - /dev/null blob + 2a9ad9878af452db998f1e01afb4a541f0226b58 (mode 644) --- /dev/null +++ src/cmd/mk/varsub.c @@ -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 whitespace */ + ; + *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 of B + */ + if(c){ + h = w = wdup(c); + while(w->next) + w = w->next; + } + if(PERCENT(*cp) && nmid > 0){ + if(w){ + bufcpy(buf, w->s, strlen(w->s)); + bufcpy(buf, enda, nmid); + insert(buf, 0); + free(w->s); + w->s = strdup(buf->start); + } else { + bufcpy(buf, enda, nmid); + insert(buf, 0); + h = w = newword(buf->start); + } + buf->current = buf->start; + } + if(d && *d->s){ + if(w){ + + bufcpy(buf, w->s, strlen(w->s)); + bufcpy(buf, d->s, strlen(d->s)); + insert(buf, 0); + free(w->s); + w->s = strdup(buf->start); + w->next = wdup(d->next); + while(w->next) + w = w->next; + buf->current = buf->start; + } else + h = w = wdup(d); + } + } + if(w == 0) + h = w = newword(v->s); + + if(head == 0) + head = h; + else + tail->next = h; + tail = w; + } + freebuf(buf); + delword(a); + delword(b); + delword(c); + delword(d); + return head; +} + +static int +submatch(char *s, Word *a, Word *b, int *nmid, char **enda) +{ + Word *w; + int n; + char *end; + + n = 0; + for(w = a; w; w = w->next){ + n = strlen(w->s); + if(strncmp(s, w->s, n) == 0) + break; + } + if(a && w == 0) /* a == NULL matches everything*/ + return 0; + + *enda = s+n; /* pointer to end a A part match */ + *nmid = strlen(s)-n; /* size of remainder of source */ + end = *enda+*nmid; + for(w = b; w; w = w->next){ + n = strlen(w->s); + if(strcmp(w->s, end-n) == 0){ + *nmid -= n; + break; + } + } + if(b && w == 0) /* b == NULL matches everything */ + return 0; + return 1; +} blob - /dev/null blob + ac34c47be4aa35a5dc3f85131448439b52a341e3 (mode 644) --- /dev/null +++ src/cmd/mk/word.c @@ -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'); +} blob - /dev/null blob + a5d7d87d79f0af66f7a5f54320edabb411b0e803 (mode 644) --- /dev/null +++ src/cmd/sam/LICENSE @@ -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. + blob - /dev/null blob + f5e91463c1a74991067ae7f679fb7461cdd03a06 (mode 644) --- /dev/null +++ src/cmd/sam/Makefile @@ -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 blob - /dev/null blob + 85cca1703393d891243f688229fffe6d56117c4d (mode 644) --- /dev/null +++ src/cmd/sam/address.c @@ -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, sign); + 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->type=='-') + 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; iname); + 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 0) + p--; + } + while(p > 0 && filereadc(f, p-1)!='\n') /* lines start after a newline */ + p--; + a.r.p1 = p; + } + return a; +} blob - /dev/null blob + 30493c3efb5cc5cdf3b4470eecec9f3b3579ce37 (mode 644) --- /dev/null +++ src/cmd/sam/buff.c @@ -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 && q0cq+b->cnc)) + return; + /* + * if q0 is at end of file and end of cache, continue to grow this block + */ + 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!=0"); + 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!=0"); + 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; +} blob - /dev/null blob + 8c152f94eb726aef7b778e1cfdb0141a75beb217 (mode 644) --- /dev/null +++ src/cmd/sam/cmd.c @@ -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 defaddr count token fn */ + '\n', 0, 0, 0, 0, aDot, 0, 0, nl_cmd, + 'a', 1, 0, 0, 0, aDot, 0, 0, a_cmd, + 'b', 0, 0, 0, 0, aNo, 0, linex, b_cmd, + 'B', 0, 0, 0, 0, aNo, 0, linex, b_cmd, + 'c', 1, 0, 0, 0, aDot, 0, 0, c_cmd, + 'd', 0, 0, 0, 0, aDot, 0, 0, d_cmd, + 'D', 0, 0, 0, 0, aNo, 0, linex, D_cmd, + 'e', 0, 0, 0, 0, aNo, 0, wordx, e_cmd, + 'f', 0, 0, 0, 0, aNo, 0, wordx, f_cmd, + 'g', 0, 1, 0, 'p', aDot, 0, 0, g_cmd, + 'i', 1, 0, 0, 0, aDot, 0, 0, i_cmd, + 'k', 0, 0, 0, 0, aDot, 0, 0, k_cmd, + 'm', 0, 0, 1, 0, aDot, 0, 0, m_cmd, + 'n', 0, 0, 0, 0, aNo, 0, 0, n_cmd, + 'p', 0, 0, 0, 0, aDot, 0, 0, p_cmd, + 'q', 0, 0, 0, 0, aNo, 0, 0, q_cmd, + 'r', 0, 0, 0, 0, aDot, 0, wordx, e_cmd, + 's', 0, 1, 0, 0, aDot, 1, 0, s_cmd, + 't', 0, 0, 1, 0, aDot, 0, 0, m_cmd, + 'u', 0, 0, 0, 0, aNo, 2, 0, u_cmd, + 'v', 0, 1, 0, 'p', aDot, 0, 0, g_cmd, + 'w', 0, 0, 0, 0, aAll, 0, wordx, w_cmd, + 'x', 0, 1, 0, 'p', aDot, 0, 0, x_cmd, + 'y', 0, 1, 0, 'p', aDot, 0, 0, x_cmd, + 'X', 0, 1, 0, 'f', aNo, 0, 0, X_cmd, + 'Y', 0, 1, 0, 'f', aNo, 0, 0, X_cmd, + '!', 0, 0, 0, 0, aNo, 0, linex, plan9_cmd, + '>', 0, 0, 0, 0, aDot, 0, linex, plan9_cmd, + '<', 0, 0, 0, 0, aDot, 0, linex, plan9_cmd, + '|', 0, 0, 0, 0, aDot, 0, linex, plan9_cmd, + '=', 0, 0, 0, 0, aDot, 0, linex, eq_cmd, + 'c'|0x100,0, 0, 0, 0, aNo, 0, wordx, cd_cmd, + 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'= 0) + ungetch(); + return c; +} + +void +termcommand(void) +{ + Posn p; + + for(p=cmdpt; p_.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 does its own */ + Straddc(s, '\\'); + } + Straddc(s, c); + } + ungetch(); /* let client read whether delimeter, '\n' or whatever */ +} + +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; +} blob - /dev/null blob + 83b2553db6705261ae1bb1b63f24f05c2b996429 (mode 644) --- /dev/null +++ src/cmd/sam/disk.c @@ -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 Maxblock */ + if(pread(d->fd, r, n*sizeof(Rune), b->addr) != n*sizeof(Rune)) + panic("read error from temp file"); +} blob - /dev/null blob + d19b9621b55166ba080d3080ab7a4ee1bd945fae (mode 644) --- /dev/null +++ src/cmd/sam/error.c @@ -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)); +} blob - /dev/null blob + 7bf46ea11902502d110eb78aa5bb25fb5aeefd41 (mode 644) --- /dev/null +++ src/cmd/sam/errors.h @@ -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; blob - /dev/null blob + 0428379dae478364f4b5c9524360739818675558 (mode 644) --- /dev/null +++ src/cmd/sam/file.c @@ -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, Mark */ + 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.buf, merge.nbuf); + 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 long */ + 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; intext; 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 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 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; +} blob - /dev/null blob + 236090a00b7d79400e9002f63ae9eb09ee1241f7 (mode 644) --- /dev/null +++ src/cmd/sam/io.c @@ -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; idev==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->mtimedev = 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 && length>0) + 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-genbuf)){ + 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); +} blob - /dev/null blob + a8105425f3839fe92704509367172dffe5fc51ea (mode 644) --- /dev/null +++ src/cmd/sam/list.c @@ -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); +} blob - /dev/null blob + 189c11acd10eb0f291c71fe53292e35f5ed6376b (mode 644) --- /dev/null +++ src/cmd/sam/mesg.c @@ -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; itag==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 pointers */ + 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 already */ + 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 already */ + break; + + case Tpaste: + f = whichfile(inshort()); + p0 = inlong(); + journaln(0, p0); + for(l=0; lBLOCKSIZE) + 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; ldot.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; is, 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' && i!='\n') + p0--; + while(p1_.nc && (i=filereadc(f, p1))!=' ' && i!='\t' && i!='\n') + 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, f->_.nc); /*ZZZ should never happen, can remove */ + p2 = f->_.nc; + } + for(l=p1; lBLOCKSIZE? 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; +} blob - /dev/null blob + b88bf1487e7e9e5ce50bcacb24bc88868b910ee6 (mode 644) --- /dev/null +++ src/cmd/sam/mesg.h @@ -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 to terminal */ +#define DATASIZE (UTFmax*TBLOCKSIZE+30) /* ... including protocol header stuff */ +#define SNARFSIZE 32000 /* maximum length of exchanged snarf buffer, must fit in 15 bits */ +/* + * 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 knows */ + Tcut, + Tpaste, + Tsnarf, + Tstartnewfile, /* terminal just opened a new frame */ + Twrite, /* write file */ + Tclose, /* terminal requests file close; check mod. status */ + 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 data */ + 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: gerard@research.bell-labs.com + * Date: Tue Jul 17 13:47:23 EDT 2001 + * To: rob@research.bell-labs.com + * + * 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") + * + */ blob - /dev/null blob + cb6049765f0413d25866bd330d86d8ec570f5379 (mode 644) --- /dev/null +++ src/cmd/sam/mkfile @@ -0,0 +1,40 @@ +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=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_.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--; +} + blob - /dev/null blob + 3df0dbee32afee95c5ffa2e3f2024f213631f973 (mode 644) --- /dev/null +++ src/cmd/sam/multi.c @@ -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; itag); + 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; iname, &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; iname, s) == 0) + return file.filepptr[i]; + return 0; +} blob - /dev/null blob + 12f293f29ffaf5bfdfd98755309bae5b4723cc3a (mode 644) --- /dev/null +++ src/cmd/sam/parse.h @@ -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.g. 'x' */ + union{ + Cmd *cmd; /* target of x, g, {, etc. */ + String *text; /* text of a, c, i; rhs of s */ + Addr *addr; /* address for m, t */ + } g; + Cmd *next; /* pointer to next element in {} */ + short num; + ushort flag; /* whatever */ + ushort cmdc; /* command character; 'x' etc. */ +}; + +#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 these */ + int (*fn)(File*, Cmd*); /* function to call with parse tree */ +}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*); blob - /dev/null blob + 6c3a60e9b9bec6f9949a1af5e4304cfefcd4761b (mode 644) --- /dev/null +++ src/cmd/sam/plan9.c @@ -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 *appendonly) +{ + 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 *appendonly) +{ + 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, getuser(), i++); + 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; +} blob - /dev/null blob + d8db33d4cea8f8f505919c0ed3b4c9b648347906 (mode 644) --- /dev/null +++ src/cmd/sam/plumb.c @@ -0,0 +1,9 @@ +#include +#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; } + blob - /dev/null blob + 45ac820679f5d88cdac6482647c98c857dab0b33 (mode 644) --- /dev/null +++ src/cmd/sam/rasp.c @@ -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 with grow */ + +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 && p1tag, 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 && p1tag, 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; inused && 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(inused && 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 && inused && 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; inused && 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 life */ + 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; inused && 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; inused && 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)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; +} blob - /dev/null blob + dee4377d031ff6f0663658d29f868cef2dcddbf3 (mode 644) --- /dev/null +++ src/cmd/sam/regexp.c @@ -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[0] */ +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 on stack */ +#define RBRA 0x10001 /* Right bracket, ) */ +#define LBRA 0x10002 /* Left bracket, ( */ +#define OR 0x10003 /* Alternation, | */ +#define CAT 0x10004 /* Concatentation, implicit operator */ +#define STAR 0x10005 /* Closure, * */ +#define PLUS 0x10006 /* a+ == aa* */ +#define QUEST 0x10007 /* a? == a|nothing, i.e. 0 or 1 a's */ +#define ANY 0x20000 /* Any character but newline, . */ +#define NOP 0x20001 /* No operation, internal use only */ +#define BOL 0x20002 /* Beginning of line, ^ */ +#define EOL 0x20003 /* End of line, $ */ +#define CCLASS 0x20004 /* Character class, [] */ +#define NCCLASS 0x20005 /* Negated character class, [^] */ +#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 expression */ +#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; is); + 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; ipfirst->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 case */ + 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->typetype : 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= 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].p1p[0].p1==sel.p[0].p1 && sp->p[0].p2>sel.p[0].p2)) + for(i = 0; ip[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->typetype : 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 sign */ + 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].p2 && sp->p[0].p2p[i].p2; + sel.p[i].p2 = sp->p[i].p1; + } +} blob - /dev/null blob + 733b555ce476ecaf832cd263bbb4076463a5a186 (mode 755) Binary files /dev/null and src/cmd/sam/sam differ blob - /dev/null blob + 81ccaf7816e9e5343bf212d1778d89189d5a7e6a (mode 644) --- /dev/null +++ src/cmd/sam/sam.c @@ -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_.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; iseq==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; cname); + 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; ideleted) { + 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_.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; iname.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 file */ + 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 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.p2-1)=='\n') + --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.nused); + tempfile.nalloc = file.nused; + } + tempfile.nused = file.nused; + memmove(&tempfile.filepptr[0], &file.filepptr[0], file.nused*sizeof(File*)); +} blob - /dev/null blob + 6a2708c18db63b7145aa96df95e45f201a3551d2 (mode 644) --- /dev/null +++ src/cmd/sam/sam.h @@ -0,0 +1,407 @@ +#include +#include +#include +#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*(transcript+buf) */ +#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 number */ + +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 from disk */ + + long seq; /* if seq==0, File acts like Buffer */ + long cleanseq; /* f->seq at last read/write of file */ + int mod; /* file appears modified in menu */ + char rescuing; /* sam exiting; this file unusable */ + +// Text *curtext; /* most recently used associated text */ +// Text **text; /* list of associated texts */ +// int ntext; +// int dumpid; /* used in dumping zeroxed windows */ + + Posn hiposn; /* highest address touched this Mod */ + Address dot; /* current position */ + Address ndot; /* new current position after update */ + Range tdot; /* what terminal thinks is current range */ + Range mark; /* tagged spot in text (don't confuse with Mark) */ + List *rasp; /* map of what terminal's got */ + short tag; /* for communicating with terminal */ + 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); blob - /dev/null blob + 2cac31bcc1e5059cdb67256a039e0b39458e6963 (mode 644) --- /dev/null +++ src/cmd/sam/shell.c @@ -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)){ /* assignment = */ + char *c; + for(l = 0; lBLOCKSIZE-1) + m = BLOCKSIZE-1; + bufread(&plan9buf, l, genbuf, m); + 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); +} blob - /dev/null blob + cc15db44a0ef9e48e8780f6b873e2be6aedaaa5a (mode 644) --- /dev/null +++ src/cmd/sam/unix.c @@ -0,0 +1,272 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 *appendonly) +{ + 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 *appendonly) +{ + 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; +} blob - /dev/null blob + 42acab0e1a8eff36b542a61b1de4b7dd21771214 (mode 644) --- /dev/null +++ src/cmd/sam/xec.c @@ -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; iname); + 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; ictext->n; i++) + if((c = cp->ctext->s[i])=='\\' && ictext->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[j].p2-sel.p[j].p1); + Strinsert(&genstr, tmprstr(genbuf, (sel.p[j].p2-sel.p[j].p1)), genstr.n); + }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-sel.p[0].p1); + Strinsert(&genstr, + tmprstr(genbuf, (int)(sel.p[0].p2-sel.p[0].p1)), + 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, p2, f->_.nc); /*ZZZ should never happen, can remove */ + 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) + 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; ire==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; +} blob - /dev/null blob + e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 (mode 644) blob - /dev/null blob + 14b8d4e7925867693d110c320d9c5a67dcd16758 (mode 644) --- /dev/null +++ src/libdraw/Make.Darwin-PowerMacintosh @@ -0,0 +1,6 @@ +CC=gcc +CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I${PREFIX}/include +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O blob - /dev/null blob + 087ed3abab5ea1bb549989f2c6abbae4ce024922 (mode 644) --- /dev/null +++ src/libdraw/Make.FreeBSD-386 @@ -0,0 +1,7 @@ +CC=gcc +CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME) +NAN=nan64.$O blob - /dev/null blob + edbdc111a2a0c008975e297c7ca60e46199397a9 (mode 644) --- /dev/null +++ src/libdraw/Make.HP-UX-9000 @@ -0,0 +1,6 @@ +CC=cc +CFLAGS=-O -c -Ae -I. +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O blob - /dev/null blob + 74b0252c6eae6331b48ede7fcfa1ed760609d1a3 (mode 644) --- /dev/null +++ src/libdraw/Make.Linux-386 @@ -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 blob - /dev/null blob + 087ed3abab5ea1bb549989f2c6abbae4ce024922 (mode 644) --- /dev/null +++ src/libdraw/Make.NetBSD-386 @@ -0,0 +1,7 @@ +CC=gcc +CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME) +NAN=nan64.$O blob - /dev/null blob + 3d45279bba28dbe69c00f89568c444ab83a4ac72 (mode 644) --- /dev/null +++ src/libdraw/Make.OSF1-alpha @@ -0,0 +1,6 @@ +CC=cc +CFLAGS+=-g -c -I. +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O blob - /dev/null blob + c5fe67b82d86c731a56c101e522e804390e5bd5a (mode 644) --- /dev/null +++ src/libdraw/Make.SunOS-sun4u @@ -0,0 +1,2 @@ +include Make.SunOS-sun4u-$(CC) +NAN=nan64.$O blob - /dev/null blob + 829301de8649d40632eb809ae512598af11d48c5 (mode 644) --- /dev/null +++ src/libdraw/Make.SunOS-sun4u-cc @@ -0,0 +1,6 @@ +CC=cc +CFLAGS+=-g -c -I. -O +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O blob - /dev/null blob + 5c41594812a4a27eb848d384cbc356d206c0a44b (mode 644) --- /dev/null +++ src/libdraw/Make.SunOS-sun4u-gcc @@ -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 blob - /dev/null blob + 0aa2cd2dd6379be55e50c439b185e306e5925a78 (mode 644) --- /dev/null +++ src/libdraw/Makefile @@ -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 -lX11 -lm + +$(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.ports $(TGZFILES) $(NAME)-$(VERSION) + 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 rsc@amsterdam.lcs.mit.edu: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=="---" { ofile=$$2; next} {if(ofile) print >ofile}') + (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 rsc@amsterdam.lcs.mit.edu:public_html/software + +.phony: all clean nuke install tgz rpm ports blob - /dev/null blob + 55cf7d4abfbdf9d55701c49d0eaa413cc622eba6 (mode 644) --- /dev/null +++ src/libdraw/Makefile.MID @@ -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/lib -lX11 -lm + blob - /dev/null blob + 50b96fd885f4f60e9fdb570a1572444bd7458b96 (mode 644) --- /dev/null +++ src/libdraw/alloc.c @@ -0,0 +1,237 @@ +#include +#include +#include + +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 val, int screenid, int refresh) +{ + uchar *a; + char *err; + Image *i; + Rectangle clipr; + int id; + int depth; + + err = 0; + i = 0; + + if(chan == 0){ + werrstr("bad channel descriptor"); + return nil; + } + + depth = chantodepth(chan); + if(depth == 0){ + err = "bad channel descriptor"; + Error: + if(err) + werrstr("allocimage: %s", err); + else + werrstr("allocimage: %r"); + free(i); + return 0; + } + + /* flush pending data so we don't get error allocating the image */ + flushimage(d, 0); + a = bufimage(d, 1+4+4+1+4+1+4*4+4*4+4); + if(a == 0) + goto Error; + d->imageid++; + id = d->imageid; + a[0] = 'b'; + BPLONG(a+1, id); + BPLONG(a+5, screenid); + a[9] = refresh; + BPLONG(a+10, chan); + a[14] = repl; + BPLONG(a+15, r.min.x); + BPLONG(a+19, r.min.y); + BPLONG(a+23, r.max.x); + BPLONG(a+27, r.max.y); + if(repl) + /* huge but not infinite, so various offsets will leave it huge, not overflow */ + clipr = Rect(-0x3FFFFFFF, -0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF); + else + clipr = r; + BPLONG(a+31, clipr.min.x); + BPLONG(a+35, clipr.min.y); + BPLONG(a+39, clipr.max.x); + BPLONG(a+43, clipr.max.y); + BPLONG(a+47, val); + if(flushimage(d, 0) < 0) + goto Error; + + if(ai) + i = ai; + else{ + i = malloc(sizeof(Image)); + if(i == nil){ + a = bufimage(d, 1+4); + if(a){ + a[0] = 'f'; + BPLONG(a+1, id); + flushimage(d, 0); + } + goto Error; + } + } + i->display = d; + i->id = id; + i->depth = depth; + i->chan = chan; + i->r = r; + i->clipr = clipr; + i->repl = repl; + i->screen = 0; + i->next = 0; + return i; +} + +Image* +namedimage(Display *d, char *name) +{ + uchar *a; + char *err, buf[12*12+1]; + Image *i; + int id, n; + 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; +} blob - /dev/null blob + 41b30620f22e982acc7c164d41bb2a747056d663 (mode 644) --- /dev/null +++ src/libdraw/arith.c @@ -0,0 +1,206 @@ +#include +#include +#include + +Point +Pt(int x, int y) +{ + Point p; + + p.x = x; + p.y = y; + return p; +} + +Rectangle +Rect(int x, int y, int bx, int by) +{ + Rectangle r; + + r.min.x = x; + r.min.y = y; + r.max.x = bx; + r.max.y = by; + return r; +} + +Rectangle +Rpt(Point min, Point max) +{ + Rectangle r; + + r.min = min; + r.max = max; + return r; +} + +Point +addpt(Point a, Point b) +{ + a.x += b.x; + a.y += b.y; + return a; +} + +Point +subpt(Point a, Point b) +{ + a.x -= b.x; + a.y -= b.y; + return a; +} + +Rectangle +insetrect(Rectangle r, int n) +{ + r.min.x += n; + r.min.y += n; + r.max.x -= n; + r.max.y -= n; + return r; +} + +Point +divpt(Point a, int b) +{ + a.x /= b; + a.y /= b; + return a; +} + +Point +mulpt(Point a, int b) +{ + a.x *= b; + a.y *= b; + return a; +} + +Rectangle +rectsubpt(Rectangle r, Point p) +{ + r.min.x -= p.x; + r.min.y -= p.y; + r.max.x -= p.x; + r.max.y -= p.y; + return r; +} + +Rectangle +rectaddpt(Rectangle r, Point p) +{ + r.min.x += p.x; + r.min.y += p.y; + r.max.x += p.x; + r.max.y += p.y; + return r; +} + +int +eqpt(Point p, Point q) +{ + return p.x==q.x && p.y==q.y; +} + +int +eqrect(Rectangle r, Rectangle s) +{ + return r.min.x==s.min.x && r.max.x==s.max.x && + r.min.y==s.min.y && r.max.y==s.max.y; +} + +int +rectXrect(Rectangle r, Rectangle s) +{ + return r.min.x=r.min.x && p.x=r.min.y && p.ymin.x > r2.min.x) + r1->min.x = r2.min.x; + if(r1->min.y > r2.min.y) + r1->min.y = r2.min.y; + if(r1->max.x < r2.max.x) + r1->max.x = r2.max.x; + if(r1->max.y < r2.max.y) + r1->max.y = r2.max.y; +} + +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, -1, -1, -1, -1, -1, -1, -1, 4 /* BUG */, -1, -1, -1, -1, -1, -1, -1, 5 }; + +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); +} + blob - /dev/null blob + ba32e775b6eeefaa2c458a59f07379b26e2467ce (mode 644) --- /dev/null +++ src/libdraw/buildfont.c @@ -0,0 +1,141 @@ +#include +#include +#include + +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 destroyed */ + 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; insub; i++){ + c = f->sub[i]; + free(c->subfontname); + free(c->name); + free(c); + } + for(i=0; insubf; 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); +} blob - /dev/null blob + 08ff7d7fe4ee8a3391b3ad3ff65e044843c06bee (mode 644) --- /dev/null +++ src/libdraw/bytesperline.c @@ -0,0 +1,34 @@ +#include +#include +#include + +static +int +unitsperline(Rectangle r, int d, int bitsperunit) +{ + ulong l, t; + + if(d <= 0 || d > 32) /* being called wrong. d is image depth. */ + abort(); + + if(r.min.x >= 0){ + l = (r.max.x*d+bitsperunit-1)/bitsperunit; + l -= (r.min.x*d)/bitsperunit; + }else{ /* make positive before divide */ + t = (-r.min.x*d+bitsperunit-1)/bitsperunit; + l = t+(r.max.x*d+bitsperunit-1)/bitsperunit; + } + return l; +} + +int +wordsperline(Rectangle r, int d) +{ + return unitsperline(r, d, 8*sizeof(ulong)); +} + +int +bytesperline(Rectangle r, int d) +{ + return unitsperline(r, d, 8); +} blob - /dev/null blob + 3b76a32ae4f27db0361185c64a259899eca988a1 (mode 644) --- /dev/null +++ src/libdraw/chan.c @@ -0,0 +1,77 @@ +#include +#include +#include + +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 in the right order */ + rc = 0; + for(c=cc; c; c>>=8){ + rc <<= 8; + rc |= c&0xFF; + } + + p = buf; + for(c=rc; c; c>>=8) { + *p++ = channames[TYPE(c)]; + *p++ = '0'+NBITS(c); + } + *p = 0; + + return buf; +} + +/* avoid pulling in ctype when using with drawterm etc. */ +static int +isspace(char c) +{ + return c==' ' || c== '\t' || c=='\r' || c=='\n'; +} + +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; +} blob - /dev/null blob + 99c42752095fc8bc7380e1cb3c65d31e9e19f5b5 (mode 644) --- /dev/null +++ src/libdraw/creadimage.c @@ -0,0 +1,113 @@ +#include +#include +#include + +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.ydisplay, 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; +} blob - /dev/null blob + 105cd0eff35c958b0902dcb709204b40d9e85bc8 (mode 644) --- /dev/null +++ src/libdraw/cursor.h @@ -0,0 +1,7 @@ +typedef struct Cursor Cursor; +struct Cursor +{ + Point offset; + uchar clr[2*16]; + uchar set[2*16]; +}; blob - /dev/null blob + b0036385dbcfa82285fbf70d45dddebb0d6cb011 (mode 644) --- /dev/null +++ src/libdraw/devdraw.c @@ -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 simple. + */ + +#include +#include +#include +#include +#include + +#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 name */ + DImage* next; +}; + +struct CScreen +{ + DScreen* dscreen; + CScreen* next; +}; + +struct DScreen +{ + int id; + int public; + int ref; + DImage *dimage; + DImage *dfill; + Memscreen* screen; + Client* owner; + DScreen* next; +}; + +static Draw sdraw; +static 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*2layer; + if(l == nil) + return; + do{ + if(l->screen->image->data != screenimage->data) + return; + r = rectaddpt(r, l->delta); + l = l->screen->image->layer; + }while(l); + addflush(r); +} + +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(; namename, 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 *dfill, int public) +{ + 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; ifromname){ /* acquired by name; owned by someone else*/ + drawfreedimage(dimage->fromname); + goto Return; + } + //if(dimage->image == screenimage) /* don't free the display */ + // goto Return; + ds = dimage->dscreen; + if(ds){ + l = dimage->image; + if(l->data == screenimage->data) + addflush(l->layer->screenr); + if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */ + free(l->layer->refreshptr); + l->layer->refreshptr = nil; + if(drawgoodname(dimage)) + memldelete(l); + else + memlfree(l); + drawfreedscreen(ds); + }else + freememimage(dimage->image); + Return: + free(dimage->fchar); + free(dimage); +} + +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(; namename, 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 index, int op) +{ + FChar *fc; + Rectangle r; + Point sp1; + + fc = &font->fchar[index]; + r.min.x = p.x+fc->left; + r.min.y = p.y-(font->ascent-fc->miny); + r.max.x = r.min.x+(fc->maxx-fc->minx); + r.max.y = r.min.y+(fc->maxy-fc->miny); + sp1.x = sp->x+fc->left; + sp1.y = sp->y+fc->miny; + memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op); + p.x += fc->width; + sp->x += fc->width; + return p; +} + +static +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 with screen"; + 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(client, dstid) < 0) + goto Enodrawimage; + goto Edrawmem; + } + refx->client = client; + refx->dimage = drawlookup(client, dstid, 1); + } + 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[13]) == 0) + 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[4] sp[2*4] alpha[4] phi[4]*/ + 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+1, p.y+e1+j+1)); + 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-zero? */ + 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 %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->r.max.y, + 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*4] left[1] width[1] */ + 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] radius[4] srcid[4] sp[2*4] */ + 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 maximal containing rect*/ + r = memlinebbox(p, q, e0, e1, j); + dstflush(dstid, dst, insetrect(r, -(1+1+j))); + } + continue; + + /* create image mask: 'm' newid[4] id[4] */ +/* + * + case 'm': + 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) == 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) < 0) + 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[4] sp[2*4] p0[2*4] dp[2*2*n] */ + /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ + case 'p': + case 'P': + 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->image->data == screenimage->data)) + doflush = 1; /* simplify test in loop */ + ox = oy = 0; + esize = 0; + u = a+m; + for(y=0; y esize) + esize = c; + } + if(y == ni-1){ + c = memlineendsize(e1); + if(c > esize) + esize = c; + } + } + if(*a=='P' && e0!=1 && e0 !=~0) + r = dst->clipr; + else if(y > 0){ + r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1); + combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + } + if(rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */ + dstflush(dstid, dst, r); + } + pp[y] = p; + } + if(y == 1) + dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + op = drawclientop(client); + if(*a == 'p') + mempoly(dst, pp, ni, e0, e1, j, src, sp, op); + else + memfillpoly(dst, pp, ni, e0, src, sp, op); + free(pp); + m = u-a; + continue; + + /* read: 'r' id[4] R[4*4] */ + case 'r': + 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, c); + 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] sp[2*4] ni[2] ni*(index[2]) */ + /* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */ + case 's': + case 'x': + 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->image->r))); + 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!=client)) + 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; jlayer == 0){ + err = "images are not windows"; + free(lp); + goto error; + } + for(j=1; jlayer->screen != lp[0]->layer->screen){ + 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->data) + for(j=0; jlayer->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; +} + + blob - /dev/null blob + 0f9ba63aeb6e1f2ad3539c8e3e304583266af299 (mode 644) --- /dev/null +++ src/libdraw/draw.h @@ -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 allocimage, memfillcolor */ + DBlack = 0x000000FF, + DWhite = 0xFFFFFFFF, + DRed = 0xFF0000FF, + DGreen = 0x00FF00FF, + DBlue = 0x0000FFFF, + DCyan = 0x00FFFFFF, + DMagenta = 0xFF00FFFF, + DYellow = 0xFFFF00FF, + DPaleyellow = 0xFFFFAAFF, + DDarkyellow = 0xEEEE9EFF, + DDarkgreen = 0x448844FF, + DPalegreen = 0xAAFFAAFF, + DMedgreen = 0x88CC88FF, + DDarkblue = 0x000055FF, + DPalebluegreen= 0xAAFFFFFF, + DPaleblue = 0x0000BBFF, + DBluegreen = 0x008888FF, + DGreygreen = 0x55AAAAFF, + DPalegreygreen = 0x9EEEEEFF, + DYellowgreen = 0x99994CFF, + DMedblue = 0x000099FF, + DGreyblue = 0x005DBBFF, + DPalegreyblue = 0x4993DDFF, + DPurpleblue = 0x8888CCFF, + + DNotacolor = 0xFFFFFF00, + DNofill = DNotacolor, + +}; + +enum +{ + Displaybufsize = 8000, + ICOSSCALE = 1024, + Borderwidth = 4, +}; + +enum +{ + /* refresh methods */ + Refbackup = 0, + Refnone = 1, + Refmesg = 2 +}; +#define NOREFRESH ((void*)-1) + +enum +{ + /* line ends */ + Endsquare = 0, + Enddisc = 1, + Endarrow = 2, + Endmask = 0x1F +}; + +#define ARROW(a, b, c) (Endarrow|((a)<<5)|((b)<<14)|((c)<<23)) + +typedef enum +{ + /* Porter-Duff compositing operators */ + Clear = 0, + + SinD = 8, + DinS = 4, + SoutD = 2, + DoutS = 1, + + S = SinD|SoutD, + SoverD = SinD|SoutD|DoutS, + SatopD = SinD|DoutS, + SxorD = SoutD|DoutS, + + D = DinS|DoutS, + DoverS = DinS|DoutS|SoutD, + DatopS = DinS|SoutD, + DxorS = DoutS|SoutD, /* == SxorD */ + + Ncomp = 12, +} Drawop; + +/* + * image channel descriptors + */ +enum { + CRed = 0, + CGreen, + CBlue, + CGrey, + CAlpha, + CMap, + CIgnore, + NChan, +}; + +#define __DC(type, nbits) ((((type)&15)<<4)|((nbits)&15)) +#define CHAN1(a,b) __DC(a,b) +#define CHAN2(a,b,c,d) (CHAN1((a),(b))<<8|__DC((c),(d))) +#define CHAN3(a,b,c,d,e,f) (CHAN2((a),(b),(c),(d))<<8|__DC((e),(f))) +#define CHAN4(a,b,c,d,e,f,g,h) (CHAN3((a),(b),(c),(d),(e),(f))<<8|__DC((g),(h))) + +#define NBITS(c) ((c)&15) +#define TYPE(c) (((c)>>4)&15) + +enum { + GREY1 = CHAN1(CGrey, 1), + GREY2 = CHAN1(CGrey, 2), + GREY4 = CHAN1(CGrey, 4), + GREY8 = CHAN1(CGrey, 8), + CMAP8 = CHAN1(CMap, 8), + RGB15 = CHAN4(CIgnore, 1, CRed, 5, CGreen, 5, CBlue, 5), + RGB16 = CHAN3(CRed, 5, CGreen, 6, CBlue, 5), + RGB24 = CHAN3(CRed, 8, CGreen, 8, CBlue, 8), + BGR24 = CHAN3(CBlue, 8, CGreen, 8, CRed, 8), + RGBA32 = CHAN4(CRed, 8, CGreen, 8, CBlue, 8, CAlpha, 8), + ARGB32 = CHAN4(CAlpha, 8, CRed, 8, CGreen, 8, CBlue, 8), /* stupid VGAs */ + XRGB32 = CHAN4(CIgnore, 8, CRed, 8, CGreen, 8, CBlue, 8), + XBGR32 = CHAN4(CIgnore, 8, CBlue, 8, CGreen, 8, CRed, 8), +}; + +extern char* chantostr(char*, 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 coords */ + Rectangle clipr; /* clipping region */ + int depth; /* number of bits per pixel */ + u32int chan; + int repl; /* flag: data replicates to tile clipr */ + Screen *screen; /* 0 if not a window */ + Image *next; /* next in list of windows */ +}; + +struct RGB +{ + 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 + 1 */ + char left; /* offset of baseline */ + uchar width; /* width of baseline */ +}; + +struct Subfont +{ + char *name; + short n; /* number of chars in font */ + uchar height; /* height of image */ + char ascent; /* top of image to baseline */ + Fontchar *info; /* n+1 character descriptors */ + Image *bits; /* of font */ + int ref; +}; + +enum +{ + /* starting values */ + LOG2NFCACHE = 6, + NFCACHE = (1<>8)) +#define BPLONG(p, v) (BPSHORT(p, (v)), BPSHORT(p+2, (v)>>16)) + +/* + * Compressed image file parameters and helper routines + */ +#define NMATCH 3 /* shortest match possible */ +#define NRUN (NMATCH+31) /* longest match possible */ +#define NMEM 1024 /* window size */ +#define NDUMP 128 /* maximum length of dump */ +#define NCBLOCK 6000 /* size of compressed blocks */ +extern void _twiddlecompressed(uchar*, int); +extern int _compblocksize(Rectangle, int); + +/* XXX backwards helps; should go */ +extern int log2[]; +extern u32int drawld2chan[]; +extern void drawsetdebug(int); + +/* + * Port magic. + */ +int _drawmsgread(Display*, void*, int); +int _drawmsgwrite(Display*, void*, int); blob - /dev/null blob + 7a063f11558290979e3feb9b74acad1b0f5e9906 (mode 644) --- /dev/null +++ src/libdraw/ellipse.c @@ -0,0 +1,82 @@ +#include +#include +#include + +static +void +doellipse(int cmd, Image *dst, Point *c, int xr, int yr, int thick, Image *src, Point *sp, int alpha, int phi, Drawop op) +{ + 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, Drawop op) +{ + 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 op) +{ + 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 alpha, int phi) +{ + 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, int phi, Drawop op) +{ + 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, int phi) +{ + 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, int phi, Drawop op) +{ + alpha |= 1<<31; + doellipse('E', dst, &c, a, b, 0, src, &sp, alpha, phi, op); +} blob - /dev/null blob + f596d7a2e2836130ce59592b55c98b5f6c94bcc2 (mode 644) --- /dev/null +++ src/libdraw/emenuhit.c @@ -0,0 +1,271 @@ +#include +#include +#include +#include + +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); /* dark green */ + bord = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedgreen); /* not as dark green */ + 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 *save, Image *restore) +{ + 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, Image *save) +{ + 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 set */ + *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; iclipr; + 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)), -Margin); + r = rectsubpt(r, Pt(wid/2, lasti*(font->height+Vspacing)+font->height/2)); + 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.xr.min.x) + pt.x = screen->r.min.x-r.min.x; + if(r.min.yr.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(scrollr); + 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, nitemdrawn); + } + } + flushimage(display, 1); /* in case display->locking is set */ + *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; +} blob - /dev/null blob + 2f67cde88a92b539fdbf7c79bb578ead82af8653 (mode 644) --- /dev/null +++ src/libdraw/event.c @@ -0,0 +1,486 @@ +#include +#include +#include +#include +#include + +typedef struct Slave Slave; +typedef struct Ebuf Ebuf; + +struct Slave +{ + int pid; + Ebuf *head; /* ueue of messages for this descriptor */ + 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; imouse = 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->buf, eb->n); + else + memmove(e->data, eb->buf, eb->n); + 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; ilength; + 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<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<>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<locking){ + /* if locking is being done by program, this means it can't depend on automatic flush in emouse() etc. */ + 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)==0){ + 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; ibuf+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; +} blob - /dev/null blob + e74183d4efedcfd33146569a3ff2c28bf5523e66 (mode 644) --- /dev/null +++ src/libdraw/event.h @@ -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-handling function */ + uchar data[EMAXMSG]; /* message from an arbitrary file descriptor */ +}; + +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*, int)); +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); blob - /dev/null blob + 6b14d3fc338b5f9b67a0705318c814cddf9824d7 (mode 644) --- /dev/null +++ src/libdraw/font.c @@ -0,0 +1,401 @@ +#include +#include +#include + +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 **subfontname) +{ + 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) && incache-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; resize */ + nc = 2*(f->ncache-NFLOOK) + NFLOOK; + if(nc <= MAXFCACHE){ + if(i == 0) + fontresize(f, f->width, nc, f->maxdepth); + /* 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->agecf->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; insub; 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; insubf; 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 instead */ + 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), f->display->black, nil, b->r.min); + for(i=0; if->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->depth){ + /* + * 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, depth), 0, 0); + 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; +} blob - /dev/null blob + b7a8e44a63457ac9426e37e022831683a1b5d76a (mode 644) --- /dev/null +++ src/libdraw/getsubfont.c @@ -0,0 +1,36 @@ +#include +#include +#include + +/* + * 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 things. + * _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; +} blob - /dev/null blob + 0cf590d8cdb9622f0ca0dcd5b2785846c232a98c (mode 644) --- /dev/null +++ src/libdraw/init.c @@ -0,0 +1,203 @@ +#include +#include +#include + +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->ascent, + 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", fontname); + 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", getpid()); + 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 continuing */ + return -1; + } + d->bufp = d->buf; + return 1; +} + +int +flushimage(Display *d, int visible) +{ + if(visible){ + *d->bufp++ = 'v'; /* five bytes always reserved for this */ + 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; +} + blob - /dev/null blob + 5ab911ab8c71ee8efc3c48681d53fc474f04cd3d (mode 644) --- /dev/null +++ src/libdraw/keyboard.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include + + +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 exit here */ + 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", t); + 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)); +} blob - /dev/null blob + a6d99bf6ae3e6493068236a86f34507074fbd9ea (mode 644) --- /dev/null +++ src/libdraw/keyboard.h @@ -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, +}; blob - /dev/null blob + 8b277f0dd5dcdcb9c4b6c0b7a32153664f111266 (mode 644) --- /dev/null +++ src/libdraw/libdraw.x @@ -0,0 +1 @@ +! blob - /dev/null blob + 801c39309b0c8436f1ca8d9605e0f525a07eb1c3 (mode 644) --- /dev/null +++ src/libdraw/md-alloc.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include + +#define poolalloc(a, b) malloc(b) +#define poolfree(a, b) free(b) + +void +memimagemove(void *from, void *to) +{ + Memdata *md; + + md = *(Memdata**)to; + if(md->base != from){ + print("compacted data not right: #%p\n", md->base); + abort(); + } + md->base = to; + + /* if allocmemimage changes this must change too */ + md->bdata = (uchar*)&md->base[2]; +} + +Memimage* +allocmemimaged(Rectangle r, 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[t] = NBITS(cc); + if(NBITS(cc) != 8) + bytes = 0; + } + i->nchan = k; + if(bytes) + i->flags |= Fbytes; + return 0; +} blob - /dev/null blob + df836a00e324c99a4c8f7dee7db0a0af37e81123 (mode 644) --- /dev/null +++ src/libdraw/md-arc.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include + +/* + * elarc(dst,c,a,b,t,src,sp,alpha,phi) + * draws the part of an ellipse between rays at angles alpha and alpha+phi + * measured counterclockwise from the positive x axis. other + * arguments are as for ellipse(dst,c,a,b,t,src,sp) + */ + +enum +{ + R, T, L, B /* right, top, left, bottom */ +}; + +static +Point corners[] = { + {1,1}, + {-1,1}, + {-1,-1}, + {1,-1} +}; + +static +Point p00; + +/* + * make a "wedge" mask covering the desired angle and contained in + * a surrounding square; draw a full ellipse; intersect that with the + * wedge to make a mask through which to copy src to dst. + */ +void +memarc(Memimage *dst, Point c, int a, int b, int t, Memimage *src, Point sp, int alpha, int phi, int op) +{ + int i, w, beta, tmp, c1, c2, m, m1; + Rectangle rect; + Point p, bnd[8]; + Memimage *wedge, *figure, *mask; + + if(a < 0) + a = -a; + if(b < 0) + b = -b; + w = t; + if(w < 0) + w = 0; + alpha = -alpha; /* compensate for upside-down coords */ + phi = -phi; + beta = alpha + phi; + if(phi < 0){ + tmp = alpha; + alpha = beta; + beta = tmp; + phi = -phi; + } + if(phi >= 360){ + memellipse(dst, c, a, b, t, src, sp, op); + return; + } + while(alpha < 0) + alpha += 360; + while(beta < 0) + beta += 360; + c1 = alpha/90 & 3; /* number of nearest corner */ + c2 = beta/90 & 3; + /* + * icossin returns point at radius ICOSSCALE. + * multiplying by m1 moves it outside the ellipse + */ + rect = Rect(-a-w, -b-w, a+w+1, b+w+1); + m = rect.max.x; /* inradius of bounding square */ + if(m < rect.max.y) + m = rect.max.y; + m1 = (m+ICOSSCALE-1) >> 10; + m = m1 << 10; /* assure m1*cossin is inside */ + i = 0; + bnd[i++] = Pt(0,0); + icossin(alpha, &p.x, &p.y); + bnd[i++] = mulpt(p, m1); + for(;;) { + bnd[i++] = mulpt(corners[c1], m); + if(c1==c2 && phi<180) + break; + c1 = (c1+1) & 3; + phi -= 90; + } + icossin(beta, &p.x, &p.y); + bnd[i++] = mulpt(p, m1); + + figure = nil; + mask = nil; + wedge = allocmemimage(rect, GREY1); + if(wedge == nil) + goto Return; + memfillcolor(wedge, DTransparent); + memfillpoly(wedge, bnd, i, ~0, memopaque, p00, S); + figure = allocmemimage(rect, GREY1); + if(figure == nil) + goto Return; + memfillcolor(figure, DTransparent); + memellipse(figure, p00, a, b, t, memopaque, p00, S); + mask = allocmemimage(rect, GREY1); + if(mask == nil) + goto Return; + memfillcolor(mask, DTransparent); + memimagedraw(mask, rect, figure, rect.min, wedge, rect.min, S); + c = subpt(c, dst->r.min); + memdraw(dst, dst->r, src, subpt(sp, c), mask, subpt(p00, c), op); + + Return: + freememimage(wedge); + freememimage(figure); + freememimage(mask); +} blob - /dev/null blob + b3a8c21119b67ad4a227fbe2caa4e497b1ed1350 (mode 644) --- /dev/null +++ src/libdraw/md-arctest.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include + +extern int drawdebug; +void +main(int argc, char **argv) +{ + char cc; + Memimage *x; + Point c = {208,871}; + int a = 441; + int b = 441; + int thick = 0; + Point sp = {0,0}; + int alpha = 51; + int phi = 3; + vlong t0, t1; + int i, n; + vlong del; + + memimageinit(); + + x = allocmemimage(Rect(0,0,1000,1000), CMAP8); + n = atoi(argv[1]); + + t0 = nsec(); + t0 = nsec(); + t0 = nsec(); + t1 = nsec(); + del = t1-t0; + t0 = nsec(); + for(i=0; i +#include +#include +#include + +int +_cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) +{ + int y, bpl, c, cnt, offs; + uchar mem[NMEM], *memp, *omemp, *emem, *linep, *elinep, *u, *eu; + + if(!rectinrect(r, i->r)) + return -1; + bpl = bytesperline(r, i->depth); + u = data; + eu = data+ndata; + memp = mem; + emem = mem+NMEM; + y = r.min.y; + linep = byteaddr(i, Pt(r.min.x, y)); + elinep = linep+bpl; + for(;;){ + if(linep == elinep){ + if(++y == r.max.y) + break; + linep = byteaddr(i, Pt(r.min.x, y)); + elinep = linep+bpl; + } + if(u == eu){ /* buffer too small */ + return -1; + } + c = *u++; + if(c >= 128){ + for(cnt=c-128+1; cnt!=0 ;--cnt){ + if(u == eu){ /* buffer too small */ + return -1; + } + if(linep == elinep){ /* phase error */ + return -1; + } + *linep++ = *u; + *memp++ = *u++; + if(memp == emem) + memp = mem; + } + } + else{ + if(u == eu) /* short buffer */ + return -1; + offs = *u++ + ((c&3)<<8)+1; + if(memp-mem < offs) + omemp = memp+(NMEM-offs); + else + omemp = memp-offs; + for(cnt=(c>>2)+NMATCH; cnt!=0; --cnt){ + if(linep == elinep) /* phase error */ + return -1; + *linep++ = *omemp; + *memp++ = *omemp++; + if(omemp == emem) + omemp = mem; + if(memp == emem) + memp = mem; + } + } + } + return u-data; +} blob - /dev/null blob + 2561c3f1cd2e9309849728ab0f16e3c0fc00c504 (mode 644) --- /dev/null +++ src/libdraw/md-cmap.c @@ -0,0 +1,320 @@ +/* + * generated by mkcmap.c + */ +#include +#include +#include +#include + +static Memcmap def = { +/* cmap2rgb */ { + 0x00,0x00,0x00,0x00,0x00,0x44,0x00,0x00,0x88,0x00,0x00,0xcc,0x00,0x44,0x00,0x00, + 0x44,0x44,0x00,0x44,0x88,0x00,0x44,0xcc,0x00,0x88,0x00,0x00,0x88,0x44,0x00,0x88, + 0x88,0x00,0x88,0xcc,0x00,0xcc,0x00,0x00,0xcc,0x44,0x00,0xcc,0x88,0x00,0xcc,0xcc, + 0x00,0xdd,0xdd,0x11,0x11,0x11,0x00,0x00,0x55,0x00,0x00,0x99,0x00,0x00,0xdd,0x00, + 0x55,0x00,0x00,0x55,0x55,0x00,0x4c,0x99,0x00,0x49,0xdd,0x00,0x99,0x00,0x00,0x99, + 0x4c,0x00,0x99,0x99,0x00,0x93,0xdd,0x00,0xdd,0x00,0x00,0xdd,0x49,0x00,0xdd,0x93, + 0x00,0xee,0x9e,0x00,0xee,0xee,0x22,0x22,0x22,0x00,0x00,0x66,0x00,0x00,0xaa,0x00, + 0x00,0xee,0x00,0x66,0x00,0x00,0x66,0x66,0x00,0x55,0xaa,0x00,0x4f,0xee,0x00,0xaa, + 0x00,0x00,0xaa,0x55,0x00,0xaa,0xaa,0x00,0x9e,0xee,0x00,0xee,0x00,0x00,0xee,0x4f, + 0x00,0xff,0x55,0x00,0xff,0xaa,0x00,0xff,0xff,0x33,0x33,0x33,0x00,0x00,0x77,0x00, + 0x00,0xbb,0x00,0x00,0xff,0x00,0x77,0x00,0x00,0x77,0x77,0x00,0x5d,0xbb,0x00,0x55, + 0xff,0x00,0xbb,0x00,0x00,0xbb,0x5d,0x00,0xbb,0xbb,0x00,0xaa,0xff,0x00,0xff,0x00, + 0x44,0x00,0x44,0x44,0x00,0x88,0x44,0x00,0xcc,0x44,0x44,0x00,0x44,0x44,0x44,0x44, + 0x44,0x88,0x44,0x44,0xcc,0x44,0x88,0x00,0x44,0x88,0x44,0x44,0x88,0x88,0x44,0x88, + 0xcc,0x44,0xcc,0x00,0x44,0xcc,0x44,0x44,0xcc,0x88,0x44,0xcc,0xcc,0x44,0x00,0x00, + 0x55,0x00,0x00,0x55,0x00,0x55,0x4c,0x00,0x99,0x49,0x00,0xdd,0x55,0x55,0x00,0x55, + 0x55,0x55,0x4c,0x4c,0x99,0x49,0x49,0xdd,0x4c,0x99,0x00,0x4c,0x99,0x4c,0x4c,0x99, + 0x99,0x49,0x93,0xdd,0x49,0xdd,0x00,0x49,0xdd,0x49,0x49,0xdd,0x93,0x49,0xdd,0xdd, + 0x4f,0xee,0xee,0x66,0x00,0x00,0x66,0x00,0x66,0x55,0x00,0xaa,0x4f,0x00,0xee,0x66, + 0x66,0x00,0x66,0x66,0x66,0x55,0x55,0xaa,0x4f,0x4f,0xee,0x55,0xaa,0x00,0x55,0xaa, + 0x55,0x55,0xaa,0xaa,0x4f,0x9e,0xee,0x4f,0xee,0x00,0x4f,0xee,0x4f,0x4f,0xee,0x9e, + 0x55,0xff,0xaa,0x55,0xff,0xff,0x77,0x00,0x00,0x77,0x00,0x77,0x5d,0x00,0xbb,0x55, + 0x00,0xff,0x77,0x77,0x00,0x77,0x77,0x77,0x5d,0x5d,0xbb,0x55,0x55,0xff,0x5d,0xbb, + 0x00,0x5d,0xbb,0x5d,0x5d,0xbb,0xbb,0x55,0xaa,0xff,0x55,0xff,0x00,0x55,0xff,0x55, + 0x88,0x00,0x88,0x88,0x00,0xcc,0x88,0x44,0x00,0x88,0x44,0x44,0x88,0x44,0x88,0x88, + 0x44,0xcc,0x88,0x88,0x00,0x88,0x88,0x44,0x88,0x88,0x88,0x88,0x88,0xcc,0x88,0xcc, + 0x00,0x88,0xcc,0x44,0x88,0xcc,0x88,0x88,0xcc,0xcc,0x88,0x00,0x00,0x88,0x00,0x44, + 0x99,0x00,0x4c,0x99,0x00,0x99,0x93,0x00,0xdd,0x99,0x4c,0x00,0x99,0x4c,0x4c,0x99, + 0x4c,0x99,0x93,0x49,0xdd,0x99,0x99,0x00,0x99,0x99,0x4c,0x99,0x99,0x99,0x93,0x93, + 0xdd,0x93,0xdd,0x00,0x93,0xdd,0x49,0x93,0xdd,0x93,0x93,0xdd,0xdd,0x99,0x00,0x00, + 0xaa,0x00,0x00,0xaa,0x00,0x55,0xaa,0x00,0xaa,0x9e,0x00,0xee,0xaa,0x55,0x00,0xaa, + 0x55,0x55,0xaa,0x55,0xaa,0x9e,0x4f,0xee,0xaa,0xaa,0x00,0xaa,0xaa,0x55,0xaa,0xaa, + 0xaa,0x9e,0x9e,0xee,0x9e,0xee,0x00,0x9e,0xee,0x4f,0x9e,0xee,0x9e,0x9e,0xee,0xee, + 0xaa,0xff,0xff,0xbb,0x00,0x00,0xbb,0x00,0x5d,0xbb,0x00,0xbb,0xaa,0x00,0xff,0xbb, + 0x5d,0x00,0xbb,0x5d,0x5d,0xbb,0x5d,0xbb,0xaa,0x55,0xff,0xbb,0xbb,0x00,0xbb,0xbb, + 0x5d,0xbb,0xbb,0xbb,0xaa,0xaa,0xff,0xaa,0xff,0x00,0xaa,0xff,0x55,0xaa,0xff,0xaa, + 0xcc,0x00,0xcc,0xcc,0x44,0x00,0xcc,0x44,0x44,0xcc,0x44,0x88,0xcc,0x44,0xcc,0xcc, + 0x88,0x00,0xcc,0x88,0x44,0xcc,0x88,0x88,0xcc,0x88,0xcc,0xcc,0xcc,0x00,0xcc,0xcc, + 0x44,0xcc,0xcc,0x88,0xcc,0xcc,0xcc,0xcc,0x00,0x00,0xcc,0x00,0x44,0xcc,0x00,0x88, + 0xdd,0x00,0x93,0xdd,0x00,0xdd,0xdd,0x49,0x00,0xdd,0x49,0x49,0xdd,0x49,0x93,0xdd, + 0x49,0xdd,0xdd,0x93,0x00,0xdd,0x93,0x49,0xdd,0x93,0x93,0xdd,0x93,0xdd,0xdd,0xdd, + 0x00,0xdd,0xdd,0x49,0xdd,0xdd,0x93,0xdd,0xdd,0xdd,0xdd,0x00,0x00,0xdd,0x00,0x49, + 0xee,0x00,0x4f,0xee,0x00,0x9e,0xee,0x00,0xee,0xee,0x4f,0x00,0xee,0x4f,0x4f,0xee, + 0x4f,0x9e,0xee,0x4f,0xee,0xee,0x9e,0x00,0xee,0x9e,0x4f,0xee,0x9e,0x9e,0xee,0x9e, + 0xee,0xee,0xee,0x00,0xee,0xee,0x4f,0xee,0xee,0x9e,0xee,0xee,0xee,0xee,0x00,0x00, + 0xff,0x00,0x00,0xff,0x00,0x55,0xff,0x00,0xaa,0xff,0x00,0xff,0xff,0x55,0x00,0xff, + 0x55,0x55,0xff,0x55,0xaa,0xff,0x55,0xff,0xff,0xaa,0x00,0xff,0xaa,0x55,0xff,0xaa, + 0xaa,0xff,0xaa,0xff,0xff,0xff,0x00,0xff,0xff,0x55,0xff,0xff,0xaa,0xff,0xff,0xff, +}, +/* rgb2cmap */ { + 0x00,0x00,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x00,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x11,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x04,0x04,0x04,0x05,0x05,0x05,0x05,0x06,0x06,0x06,0x17,0x07,0x07,0x18,0x18,0x29, + 0x04,0x04,0x04,0x05,0x05,0x05,0x16,0x06,0x06,0x17,0x28,0x07,0x07,0x18,0x29,0x3a, + 0x15,0x15,0x15,0x05,0x05,0x16,0x16,0x06,0x06,0x17,0x28,0x39,0x07,0x18,0x29,0x3a, + 0x26,0x26,0x26,0x05,0x16,0x16,0x27,0x27,0x38,0x28,0x28,0x39,0x39,0x29,0x29,0x3a, + 0x37,0x37,0x37,0x09,0x09,0x09,0x27,0x38,0x0a,0x0a,0x39,0x0b,0x0b,0x0b,0x1c,0x3a, + 0x08,0x08,0x08,0x09,0x09,0x09,0x38,0x0a,0x0a,0x0a,0x1b,0x0b,0x0b,0x1c,0x1c,0x2d, + 0x19,0x19,0x19,0x09,0x1a,0x1a,0x2b,0x0a,0x0a,0x1b,0x1b,0x0b,0x0b,0x1c,0x2d,0x3e, + 0x2a,0x2a,0x2a,0x1a,0x2b,0x2b,0x2b,0x3c,0x1b,0x1b,0x2c,0x2c,0x3d,0x2d,0x2d,0x3e, + 0x3b,0x3b,0x3b,0x0d,0x0d,0x3c,0x3c,0x0e,0x0e,0x0e,0x2c,0x3d,0x0f,0x0f,0x3e,0x3e, + 0x0c,0x0c,0x0c,0x0d,0x0d,0x0d,0x3c,0x0e,0x0e,0x0e,0x3d,0x0f,0x0f,0x0f,0x10,0x3e, + 0x1d,0x1d,0x1d,0x1e,0x1e,0x1e,0x2f,0x0e,0x1f,0x1f,0x20,0x0f,0x0f,0x10,0x10,0x21, + 0x2e,0x2e,0x2e,0x1e,0x2f,0x2f,0x2f,0x1f,0x1f,0x20,0x20,0x31,0x10,0x10,0x21,0x21, + 0x3f,0x3f,0x3f,0x2f,0x30,0x30,0x30,0x30,0x20,0x31,0x31,0x31,0x31,0x21,0x21,0x32, + 0x00,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x11,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x11,0x11,0x22,0x22,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x04,0x04,0x22,0x05,0x05,0x05,0x05,0x06,0x06,0x06,0x17,0x07,0x07,0x18,0x18,0x29, + 0x04,0x04,0x04,0x05,0x05,0x05,0x16,0x06,0x06,0x17,0x28,0x07,0x07,0x18,0x29,0x3a, + 0x15,0x15,0x15,0x05,0x05,0x16,0x16,0x06,0x06,0x17,0x28,0x39,0x07,0x18,0x29,0x3a, + 0x26,0x26,0x26,0x05,0x16,0x16,0x27,0x27,0x38,0x28,0x28,0x39,0x39,0x29,0x29,0x3a, + 0x37,0x37,0x37,0x09,0x09,0x09,0x27,0x38,0x0a,0x0a,0x39,0x0b,0x0b,0x0b,0x1c,0x3a, + 0x08,0x08,0x08,0x09,0x09,0x09,0x38,0x0a,0x0a,0x0a,0x1b,0x0b,0x0b,0x1c,0x1c,0x2d, + 0x19,0x19,0x19,0x09,0x1a,0x1a,0x2b,0x0a,0x0a,0x1b,0x1b,0x0b,0x0b,0x1c,0x2d,0x3e, + 0x2a,0x2a,0x2a,0x1a,0x2b,0x2b,0x2b,0x3c,0x1b,0x1b,0x2c,0x2c,0x3d,0x2d,0x2d,0x3e, + 0x3b,0x3b,0x3b,0x0d,0x0d,0x3c,0x3c,0x0e,0x0e,0x0e,0x2c,0x3d,0x0f,0x0f,0x3e,0x3e, + 0x0c,0x0c,0x0c,0x0d,0x0d,0x0d,0x3c,0x0e,0x0e,0x0e,0x3d,0x0f,0x0f,0x0f,0x10,0x3e, + 0x1d,0x1d,0x1d,0x1e,0x1e,0x1e,0x2f,0x0e,0x1f,0x1f,0x20,0x0f,0x0f,0x10,0x10,0x21, + 0x2e,0x2e,0x2e,0x1e,0x2f,0x2f,0x2f,0x1f,0x1f,0x20,0x20,0x31,0x10,0x10,0x21,0x21, + 0x3f,0x3f,0x3f,0x2f,0x30,0x30,0x30,0x30,0x20,0x31,0x31,0x31,0x31,0x21,0x21,0x32, + 0x11,0x11,0x11,0x01,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x11,0x11,0x22,0x22,0x01,0x12,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x11,0x22,0x22,0x22,0x33,0x33,0x23,0x34,0x02,0x13,0x24,0x35,0x03,0x14,0x25,0x36, + 0x04,0x22,0x22,0x33,0x33,0x33,0x05,0x06,0x06,0x06,0x17,0x07,0x07,0x18,0x18,0x29, + 0x04,0x04,0x33,0x33,0x33,0x05,0x16,0x06,0x06,0x17,0x28,0x07,0x07,0x18,0x29,0x3a, + 0x15,0x15,0x33,0x33,0x05,0x16,0x16,0x06,0x06,0x17,0x28,0x39,0x07,0x18,0x29,0x3a, + 0x26,0x26,0x26,0x05,0x16,0x16,0x27,0x27,0x38,0x28,0x28,0x39,0x39,0x29,0x29,0x3a, + 0x37,0x37,0x37,0x09,0x09,0x09,0x27,0x38,0x0a,0x0a,0x39,0x0b,0x0b,0x0b,0x1c,0x3a, + 0x08,0x08,0x08,0x09,0x09,0x09,0x38,0x0a,0x0a,0x0a,0x1b,0x0b,0x0b,0x1c,0x1c,0x2d, + 0x19,0x19,0x19,0x09,0x1a,0x1a,0x2b,0x0a,0x0a,0x1b,0x1b,0x0b,0x0b,0x1c,0x2d,0x3e, + 0x2a,0x2a,0x2a,0x1a,0x2b,0x2b,0x2b,0x3c,0x1b,0x1b,0x2c,0x2c,0x3d,0x2d,0x2d,0x3e, + 0x3b,0x3b,0x3b,0x0d,0x0d,0x3c,0x3c,0x0e,0x0e,0x0e,0x2c,0x3d,0x0f,0x0f,0x3e,0x3e, + 0x0c,0x0c,0x0c,0x0d,0x0d,0x0d,0x3c,0x0e,0x0e,0x0e,0x3d,0x0f,0x0f,0x0f,0x10,0x3e, + 0x1d,0x1d,0x1d,0x1e,0x1e,0x1e,0x2f,0x0e,0x1f,0x1f,0x20,0x0f,0x0f,0x10,0x10,0x21, + 0x2e,0x2e,0x2e,0x1e,0x2f,0x2f,0x2f,0x1f,0x1f,0x20,0x20,0x31,0x10,0x10,0x21,0x21, + 0x3f,0x3f,0x3f,0x2f,0x30,0x30,0x30,0x30,0x20,0x31,0x31,0x31,0x31,0x21,0x21,0x32, + 0x4f,0x4f,0x22,0x40,0x40,0x40,0x40,0x41,0x41,0x41,0x52,0x42,0x42,0x53,0x53,0x64, + 0x4f,0x22,0x22,0x22,0x40,0x40,0x40,0x41,0x41,0x41,0x52,0x42,0x42,0x53,0x53,0x64, + 0x22,0x22,0x22,0x33,0x33,0x33,0x40,0x41,0x41,0x41,0x52,0x42,0x42,0x53,0x53,0x64, + 0x43,0x22,0x33,0x33,0x33,0x44,0x44,0x45,0x45,0x45,0x56,0x46,0x46,0x46,0x57,0x68, + 0x43,0x43,0x33,0x33,0x44,0x44,0x44,0x45,0x45,0x45,0x56,0x46,0x46,0x57,0x57,0x68, + 0x43,0x43,0x33,0x44,0x44,0x44,0x55,0x45,0x45,0x56,0x56,0x46,0x46,0x57,0x68,0x68, + 0x43,0x43,0x43,0x44,0x44,0x55,0x55,0x45,0x45,0x56,0x67,0x46,0x46,0x57,0x68,0x79, + 0x47,0x47,0x47,0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x49,0x4a,0x4a,0x4a,0x5b,0x79, + 0x47,0x47,0x47,0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x5a,0x4a,0x4a,0x4a,0x5b,0x6c, + 0x47,0x47,0x47,0x48,0x48,0x59,0x59,0x49,0x49,0x5a,0x5a,0x4a,0x4a,0x5b,0x5b,0x6c, + 0x58,0x58,0x58,0x59,0x59,0x59,0x6a,0x49,0x5a,0x5a,0x6b,0x6b,0x5b,0x5b,0x6c,0x7d, + 0x4b,0x4b,0x4b,0x4c,0x4c,0x4c,0x4c,0x4d,0x4d,0x4d,0x6b,0x4e,0x4e,0x4e,0x6c,0x7d, + 0x4b,0x4b,0x4b,0x4c,0x4c,0x4c,0x4c,0x4d,0x4d,0x4d,0x5e,0x4e,0x4e,0x4e,0x5f,0x5f, + 0x5c,0x5c,0x5c,0x4c,0x5d,0x5d,0x5d,0x4d,0x4d,0x5e,0x5e,0x4e,0x4e,0x5f,0x5f,0x60, + 0x5c,0x5c,0x5c,0x5d,0x5d,0x6e,0x6e,0x5e,0x5e,0x5e,0x6f,0x6f,0x5f,0x5f,0x60,0x60, + 0x6d,0x6d,0x6d,0x6e,0x6e,0x6e,0x7f,0x7f,0x6f,0x6f,0x70,0x70,0x5f,0x60,0x60,0x71, + 0x4f,0x4f,0x40,0x40,0x40,0x40,0x51,0x41,0x41,0x52,0x63,0x42,0x42,0x53,0x64,0x75, + 0x4f,0x4f,0x22,0x40,0x40,0x40,0x51,0x41,0x41,0x52,0x63,0x42,0x42,0x53,0x64,0x75, + 0x43,0x22,0x33,0x33,0x33,0x40,0x51,0x41,0x41,0x52,0x63,0x42,0x42,0x53,0x64,0x75, + 0x43,0x43,0x33,0x33,0x44,0x44,0x44,0x45,0x45,0x45,0x56,0x46,0x46,0x57,0x57,0x68, + 0x43,0x43,0x33,0x44,0x44,0x44,0x55,0x45,0x45,0x56,0x56,0x46,0x46,0x57,0x68,0x68, + 0x43,0x43,0x43,0x44,0x44,0x55,0x55,0x45,0x45,0x56,0x67,0x46,0x46,0x57,0x68,0x79, + 0x54,0x54,0x54,0x44,0x55,0x55,0x55,0x45,0x56,0x56,0x67,0x78,0x78,0x57,0x68,0x79, + 0x47,0x47,0x47,0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x49,0x4a,0x4a,0x4a,0x5b,0x79, + 0x47,0x47,0x47,0x48,0x48,0x48,0x59,0x49,0x49,0x49,0x5a,0x4a,0x4a,0x5b,0x5b,0x6c, + 0x58,0x58,0x58,0x48,0x59,0x59,0x59,0x49,0x49,0x5a,0x5a,0x4a,0x4a,0x5b,0x6c,0x6c, + 0x69,0x69,0x69,0x59,0x59,0x6a,0x6a,0x49,0x5a,0x5a,0x6b,0x6b,0x5b,0x5b,0x6c,0x7d, + 0x4b,0x4b,0x4b,0x4c,0x4c,0x4c,0x7b,0x4d,0x4d,0x4d,0x6b,0x4e,0x4e,0x4e,0x7d,0x7d, + 0x4b,0x4b,0x4b,0x4c,0x4c,0x4c,0x7b,0x4d,0x4d,0x4d,0x5e,0x4e,0x4e,0x4e,0x5f,0x7d, + 0x5c,0x5c,0x5c,0x5d,0x5d,0x5d,0x5d,0x4d,0x5e,0x5e,0x5e,0x4e,0x4e,0x5f,0x5f,0x60, + 0x6d,0x6d,0x6d,0x5d,0x6e,0x6e,0x6e,0x5e,0x5e,0x6f,0x6f,0x70,0x5f,0x5f,0x60,0x60, + 0x7e,0x7e,0x7e,0x6e,0x6e,0x7f,0x7f,0x7f,0x6f,0x6f,0x70,0x70,0x70,0x60,0x60,0x71, + 0x50,0x50,0x50,0x40,0x40,0x51,0x51,0x41,0x41,0x52,0x63,0x74,0x42,0x53,0x64,0x75, + 0x50,0x50,0x50,0x40,0x40,0x51,0x51,0x41,0x41,0x52,0x63,0x74,0x42,0x53,0x64,0x75, + 0x50,0x50,0x33,0x33,0x40,0x51,0x51,0x41,0x41,0x52,0x63,0x74,0x42,0x53,0x64,0x75, + 0x43,0x43,0x33,0x44,0x44,0x44,0x55,0x45,0x45,0x56,0x56,0x46,0x46,0x57,0x68,0x68, + 0x43,0x43,0x43,0x44,0x44,0x55,0x55,0x45,0x45,0x56,0x67,0x46,0x46,0x57,0x68,0x79, + 0x54,0x54,0x54,0x44,0x55,0x55,0x55,0x45,0x56,0x56,0x67,0x78,0x78,0x57,0x68,0x79, + 0x54,0x54,0x54,0x55,0x55,0x55,0x66,0x66,0x56,0x67,0x67,0x78,0x78,0x68,0x68,0x79, + 0x47,0x47,0x47,0x48,0x48,0x48,0x66,0x49,0x49,0x49,0x78,0x78,0x4a,0x4a,0x5b,0x79, + 0x47,0x47,0x47,0x48,0x48,0x59,0x59,0x49,0x49,0x5a,0x5a,0x4a,0x4a,0x5b,0x6c,0x6c, + 0x58,0x58,0x58,0x59,0x59,0x59,0x6a,0x49,0x5a,0x5a,0x6b,0x6b,0x5b,0x5b,0x6c,0x7d, + 0x69,0x69,0x69,0x59,0x6a,0x6a,0x6a,0x7b,0x5a,0x6b,0x6b,0x6b,0x7c,0x6c,0x6c,0x7d, + 0x7a,0x7a,0x7a,0x4c,0x4c,0x7b,0x7b,0x7b,0x4d,0x6b,0x6b,0x7c,0x7c,0x4e,0x7d,0x7d, + 0x4b,0x4b,0x4b,0x4c,0x4c,0x7b,0x7b,0x4d,0x4d,0x5e,0x7c,0x7c,0x4e,0x5f,0x5f,0x7d, + 0x5c,0x5c,0x5c,0x5d,0x5d,0x5d,0x6e,0x4d,0x5e,0x5e,0x6f,0x4e,0x5f,0x5f,0x60,0x60, + 0x6d,0x6d,0x6d,0x6e,0x6e,0x6e,0x6e,0x5e,0x6f,0x6f,0x6f,0x70,0x5f,0x60,0x60,0x71, + 0x7e,0x7e,0x7e,0x6e,0x7f,0x7f,0x7f,0x7f,0x6f,0x70,0x70,0x70,0x70,0x60,0x71,0x71, + 0x61,0x61,0x61,0x40,0x51,0x51,0x62,0x62,0x73,0x63,0x63,0x74,0x74,0x64,0x64,0x75, + 0x61,0x61,0x61,0x40,0x51,0x51,0x62,0x62,0x73,0x63,0x63,0x74,0x74,0x64,0x64,0x75, + 0x61,0x61,0x61,0x40,0x51,0x51,0x62,0x62,0x73,0x63,0x63,0x74,0x74,0x64,0x64,0x75, + 0x43,0x43,0x43,0x44,0x44,0x55,0x55,0x45,0x45,0x56,0x67,0x46,0x46,0x57,0x68,0x79, + 0x54,0x54,0x54,0x44,0x55,0x55,0x55,0x45,0x56,0x56,0x67,0x78,0x78,0x57,0x68,0x79, + 0x54,0x54,0x54,0x55,0x55,0x55,0x66,0x66,0x56,0x67,0x67,0x78,0x78,0x68,0x68,0x79, + 0x65,0x65,0x65,0x55,0x55,0x66,0x66,0x66,0x77,0x67,0x78,0x78,0x78,0x78,0x79,0x79, + 0x65,0x65,0x65,0x48,0x48,0x66,0x66,0x77,0x77,0x77,0x78,0x78,0x78,0x5b,0x79,0x79, + 0x76,0x76,0x76,0x48,0x59,0x59,0x77,0x77,0x77,0x5a,0x5a,0x4a,0x4a,0x5b,0x6c,0x6c, + 0x69,0x69,0x69,0x59,0x59,0x6a,0x6a,0x77,0x5a,0x5a,0x6b,0x6b,0x5b,0x6c,0x6c,0x7d, + 0x69,0x69,0x69,0x6a,0x6a,0x6a,0x7b,0x7b,0x5a,0x6b,0x6b,0x7c,0x7c,0x6c,0x7d,0x7d, + 0x7a,0x7a,0x7a,0x4c,0x7b,0x7b,0x7b,0x7b,0x4d,0x6b,0x7c,0x7c,0x7c,0x7c,0x7d,0x7d, + 0x7a,0x7a,0x7a,0x4c,0x7b,0x7b,0x7b,0x7b,0x4d,0x5e,0x7c,0x7c,0x7c,0x5f,0x5f,0x7d, + 0x6d,0x6d,0x6d,0x5d,0x5d,0x6e,0x7b,0x5e,0x5e,0x6f,0x6f,0x7c,0x5f,0x5f,0x60,0x60, + 0x6d,0x6d,0x6d,0x6e,0x6e,0x6e,0x7f,0x7f,0x6f,0x6f,0x70,0x70,0x5f,0x60,0x60,0x71, + 0x7e,0x7e,0x7e,0x7f,0x7f,0x7f,0x7f,0x7f,0x6f,0x70,0x70,0x70,0x70,0x60,0x71,0x71, + 0x72,0x72,0x72,0x8f,0x8f,0x62,0x62,0x73,0x73,0x80,0x74,0x81,0x81,0x81,0x92,0x75, + 0x72,0x72,0x72,0x8f,0x8f,0x62,0x62,0x73,0x73,0x80,0x74,0x81,0x81,0x81,0x92,0x75, + 0x72,0x72,0x72,0x83,0x83,0x62,0x62,0x73,0x73,0x80,0x74,0x81,0x81,0x81,0x92,0x75, + 0x82,0x82,0x82,0x83,0x83,0x83,0x83,0x84,0x84,0x84,0x84,0x85,0x85,0x85,0x96,0x79, + 0x82,0x82,0x82,0x83,0x83,0x83,0x66,0x84,0x84,0x84,0x67,0x85,0x85,0x85,0x96,0x79, + 0x65,0x65,0x65,0x83,0x83,0x66,0x66,0x66,0x84,0x84,0x78,0x78,0x85,0x85,0x96,0x79, + 0x65,0x65,0x65,0x83,0x66,0x66,0x66,0x77,0x77,0x77,0x78,0x78,0x78,0x96,0x79,0x79, + 0x76,0x76,0x76,0x87,0x87,0x66,0x77,0x77,0x77,0x88,0x78,0x89,0x89,0x89,0x89,0x79, + 0x76,0x76,0x76,0x87,0x87,0x87,0x77,0x77,0x88,0x88,0x88,0x89,0x89,0x89,0x9a,0x9a, + 0x86,0x86,0x86,0x87,0x87,0x87,0x77,0x88,0x88,0x88,0x6b,0x89,0x89,0x9a,0x9a,0x7d, + 0x7a,0x7a,0x7a,0x87,0x6a,0x7b,0x7b,0x7b,0x88,0x6b,0x6b,0x7c,0x7c,0x9a,0x7d,0x7d, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x7b,0x7b,0x8c,0x8c,0x8c,0x7c,0x7c,0x8d,0x8d,0x7d,0x7d, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x8b,0x7b,0x8c,0x8c,0x8c,0x7c,0x8d,0x8d,0x8d,0x9e,0x9e, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x8b,0x9c,0x8c,0x8c,0x9d,0x9d,0x8d,0x8d,0x9e,0x9e,0x9e, + 0x9b,0x9b,0x9b,0x9c,0x9c,0x9c,0x7f,0x8c,0x9d,0x9d,0x70,0x70,0x9e,0x9e,0x9e,0x71, + 0x7e,0x7e,0x7e,0x7f,0x7f,0x7f,0x7f,0x7f,0x9d,0x70,0x70,0x70,0x9e,0x9e,0x71,0x71, + 0x8e,0x8e,0x8e,0x8f,0x8f,0x8f,0x73,0x73,0x80,0x80,0x91,0x81,0x81,0x92,0x92,0xa3, + 0x8e,0x8e,0x8e,0x8f,0x8f,0x8f,0x73,0x73,0x80,0x80,0x91,0x81,0x81,0x92,0x92,0xa3, + 0x82,0x82,0x82,0x83,0x83,0x83,0x73,0x73,0x80,0x80,0x91,0x81,0x81,0x92,0x92,0xa3, + 0x82,0x82,0x82,0x83,0x83,0x83,0x83,0x84,0x84,0x84,0x95,0x85,0x85,0x85,0x96,0xa7, + 0x82,0x82,0x82,0x83,0x83,0x83,0x94,0x84,0x84,0x84,0x95,0x85,0x85,0x96,0x96,0xa7, + 0x82,0x82,0x82,0x83,0x83,0x94,0x94,0x84,0x84,0x95,0x95,0x85,0x85,0x96,0xa7,0xa7, + 0x76,0x76,0x76,0x83,0x94,0x94,0x77,0x77,0x77,0x95,0x95,0x85,0x85,0x96,0xa7,0xa7, + 0x76,0x76,0x76,0x87,0x87,0x87,0x77,0x77,0x88,0x88,0x88,0x89,0x89,0x89,0x9a,0x9a, + 0x86,0x86,0x86,0x87,0x87,0x87,0x77,0x88,0x88,0x88,0x99,0x89,0x89,0x9a,0x9a,0xab, + 0x86,0x86,0x86,0x87,0x87,0x98,0x98,0x88,0x88,0x99,0x99,0x89,0x89,0x9a,0x9a,0xab, + 0x97,0x97,0x97,0x98,0x98,0x98,0x98,0x88,0x99,0x99,0x99,0x89,0x9a,0x9a,0xab,0xab, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x8b,0x8b,0x8c,0x8c,0x8c,0x8c,0x8d,0x8d,0x8d,0xab,0xbc, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x8b,0x8b,0x8c,0x8c,0x8c,0x9d,0x8d,0x8d,0x8d,0x9e,0x9e, + 0x9b,0x9b,0x9b,0x8b,0x9c,0x9c,0x9c,0x8c,0x9d,0x9d,0x9d,0x8d,0x8d,0x9e,0x9e,0xaf, + 0x9b,0x9b,0x9b,0x9c,0x9c,0xad,0xad,0x9d,0x9d,0x9d,0xae,0xae,0x9e,0x9e,0xaf,0xaf, + 0xac,0xac,0xac,0xad,0xad,0xad,0xad,0x9d,0xae,0xae,0xae,0xbf,0x9e,0xaf,0xaf,0xaf, + 0x9f,0x9f,0x9f,0x8f,0x90,0x90,0xa1,0x80,0x80,0x91,0x91,0x81,0x81,0x92,0xa3,0xb4, + 0x9f,0x9f,0x9f,0x8f,0x90,0x90,0xa1,0x80,0x80,0x91,0x91,0x81,0x81,0x92,0xa3,0xb4, + 0x9f,0x9f,0x9f,0x83,0x90,0x90,0xa1,0x80,0x80,0x91,0x91,0x81,0x81,0x92,0xa3,0xb4, + 0x82,0x82,0x82,0x83,0x83,0x94,0x94,0x84,0x84,0x95,0x95,0x85,0x85,0x96,0x96,0xa7, + 0x93,0x93,0x93,0x83,0x94,0x94,0x94,0x84,0x84,0x95,0x95,0x85,0x85,0x96,0xa7,0xa7, + 0x93,0x93,0x93,0x94,0x94,0x94,0xa5,0x84,0x95,0x95,0xa6,0xa6,0x96,0x96,0xa7,0xb8, + 0xa4,0xa4,0xa4,0x94,0x94,0xa5,0xa5,0x77,0x95,0x95,0xa6,0xa6,0x96,0xa7,0xa7,0xb8, + 0x86,0x86,0x86,0x87,0x87,0x87,0x77,0x88,0x88,0x88,0x99,0x89,0x89,0x9a,0x9a,0xb8, + 0x86,0x86,0x86,0x87,0x87,0x98,0x98,0x88,0x88,0x99,0x99,0x89,0x89,0x9a,0x9a,0xab, + 0x97,0x97,0x97,0x98,0x98,0x98,0x98,0x88,0x99,0x99,0x99,0x89,0x9a,0x9a,0xab,0xab, + 0x97,0x97,0x97,0x98,0x98,0xa9,0xa9,0x99,0x99,0x99,0xaa,0xaa,0x9a,0xab,0xab,0xbc, + 0x8a,0x8a,0x8a,0x8b,0x8b,0xa9,0xa9,0x8c,0x8c,0x8c,0xaa,0x8d,0x8d,0x8d,0xab,0xbc, + 0x8a,0x8a,0x8a,0x8b,0x8b,0x9c,0x9c,0x8c,0x8c,0x9d,0x9d,0x8d,0x8d,0x9e,0x9e,0xbc, + 0x9b,0x9b,0x9b,0x9c,0x9c,0x9c,0xad,0x9d,0x9d,0x9d,0xae,0x8d,0x9e,0x9e,0xaf,0xaf, + 0xac,0xac,0xac,0x9c,0xad,0xad,0xad,0x9d,0x9d,0xae,0xae,0xae,0x9e,0xaf,0xaf,0xaf, + 0xbd,0xbd,0xbd,0xad,0xad,0xbe,0xbe,0xbe,0xae,0xae,0xbf,0xbf,0xbf,0xaf,0xaf,0xb0, + 0xa0,0xa0,0xa0,0x90,0xa1,0xa1,0xa1,0xb2,0x91,0x91,0xa2,0xa2,0xb3,0xa3,0xa3,0xb4, + 0xa0,0xa0,0xa0,0x90,0xa1,0xa1,0xa1,0xb2,0x91,0x91,0xa2,0xa2,0xb3,0xa3,0xa3,0xb4, + 0xa0,0xa0,0xa0,0x90,0xa1,0xa1,0xa1,0xb2,0x91,0x91,0xa2,0xa2,0xb3,0xa3,0xa3,0xb4, + 0x93,0x93,0x93,0x94,0x94,0x94,0xa5,0x84,0x95,0x95,0xa6,0xa6,0x96,0x96,0xa7,0xb8, + 0xa4,0xa4,0xa4,0x94,0x94,0xa5,0xa5,0x84,0x95,0x95,0xa6,0xa6,0x96,0x96,0xa7,0xb8, + 0xa4,0xa4,0xa4,0x94,0xa5,0xa5,0xa5,0xb6,0x95,0xa6,0xa6,0xa6,0xb7,0xa7,0xa7,0xb8, + 0xa4,0xa4,0xa4,0xa5,0xa5,0xa5,0xb6,0xb6,0x95,0xa6,0xa6,0xb7,0xb7,0xa7,0xb8,0xb8, + 0xb5,0xb5,0xb5,0x87,0x87,0xb6,0xb6,0xb6,0x88,0x99,0xa6,0xb7,0xb7,0x9a,0xb8,0xb8, + 0x97,0x97,0x97,0x98,0x98,0x98,0x98,0x88,0x99,0x99,0x99,0x89,0x9a,0x9a,0xab,0xab, + 0x97,0x97,0x97,0x98,0x98,0xa9,0xa9,0x99,0x99,0x99,0xaa,0xaa,0x9a,0xab,0xab,0xbc, + 0xa8,0xa8,0xa8,0xa9,0xa9,0xa9,0xa9,0xa9,0x99,0xaa,0xaa,0xaa,0xbb,0xab,0xab,0xbc, + 0xa8,0xa8,0xa8,0xa9,0xa9,0xa9,0xba,0xba,0x8c,0xaa,0xaa,0xbb,0xbb,0xab,0xbc,0xbc, + 0xb9,0xb9,0xb9,0x9c,0x9c,0xba,0xba,0xba,0x9d,0x9d,0xbb,0xbb,0xbb,0x9e,0x9e,0xbc, + 0xac,0xac,0xac,0x9c,0x9c,0xad,0xad,0x9d,0x9d,0xae,0xae,0xae,0x9e,0x9e,0xaf,0xaf, + 0xac,0xac,0xac,0xad,0xad,0xad,0xbe,0xbe,0xae,0xae,0xae,0xbf,0x9e,0xaf,0xaf,0xb0, + 0xbd,0xbd,0xbd,0xbe,0xbe,0xbe,0xbe,0xbe,0xae,0xbf,0xbf,0xbf,0xbf,0xaf,0xb0,0xb0, + 0xb1,0xb1,0xb1,0xce,0xce,0xb2,0xb2,0xcf,0xcf,0xa2,0xa2,0xb3,0xb3,0xc0,0xb4,0xb4, + 0xb1,0xb1,0xb1,0xce,0xce,0xb2,0xb2,0xcf,0xcf,0xa2,0xa2,0xb3,0xb3,0xc0,0xb4,0xb4, + 0xb1,0xb1,0xb1,0xc2,0xc2,0xb2,0xb2,0xc3,0xc3,0xa2,0xa2,0xb3,0xb3,0xc0,0xb4,0xb4, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xc2,0xa5,0xc3,0xc3,0xc3,0xa6,0xc4,0xc4,0xc4,0xa7,0xb8, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xa5,0xb6,0xc3,0xc3,0xc3,0xa6,0xc4,0xc4,0xc4,0xb8,0xb8, + 0xb5,0xb5,0xb5,0xc2,0xa5,0xb6,0xb6,0xb6,0xc3,0xa6,0xa6,0xb7,0xb7,0xc4,0xb8,0xb8, + 0xb5,0xb5,0xb5,0xa5,0xb6,0xb6,0xb6,0xb6,0xc3,0xa6,0xb7,0xb7,0xb7,0xb7,0xb8,0xb8, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xb6,0xb6,0xc7,0xc7,0xc7,0xb7,0xb7,0xc8,0xc8,0xb8,0xb8, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xc6,0xc7,0xc7,0xc7,0xaa,0xc8,0xc8,0xc8,0xab,0xbc, + 0xa8,0xa8,0xa8,0xc6,0xc6,0xa9,0xa9,0xc7,0xc7,0xaa,0xaa,0xaa,0xc8,0xc8,0xab,0xbc, + 0xa8,0xa8,0xa8,0xa9,0xa9,0xa9,0xba,0xba,0xaa,0xaa,0xaa,0xbb,0xbb,0xab,0xbc,0xbc, + 0xb9,0xb9,0xb9,0xca,0xca,0xba,0xba,0xba,0xcb,0xaa,0xbb,0xbb,0xbb,0xcc,0xbc,0xbc, + 0xb9,0xb9,0xb9,0xca,0xca,0xba,0xba,0xcb,0xcb,0xcb,0xbb,0xbb,0xcc,0xcc,0xcc,0xbc, + 0xc9,0xc9,0xc9,0xca,0xca,0xca,0xba,0xcb,0xcb,0xcb,0xae,0xcc,0xcc,0xcc,0xaf,0xaf, + 0xbd,0xbd,0xbd,0xad,0xbe,0xbe,0xbe,0xbe,0xae,0xae,0xbf,0xbf,0xcc,0xaf,0xaf,0xb0, + 0xbd,0xbd,0xbd,0xbe,0xbe,0xbe,0xbe,0xbe,0xbf,0xbf,0xbf,0xbf,0xbf,0xaf,0xb0,0xb0, + 0xcd,0xcd,0xcd,0xce,0xce,0xce,0xb2,0xcf,0xcf,0xcf,0xb3,0xb3,0xc0,0xc0,0xd1,0xb4, + 0xcd,0xcd,0xcd,0xce,0xce,0xce,0xb2,0xcf,0xcf,0xcf,0xb3,0xb3,0xc0,0xc0,0xd1,0xb4, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xc2,0xb2,0xc3,0xc3,0xc3,0xb3,0xb3,0xc0,0xc0,0xd1,0xb4, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xc2,0xc2,0xc3,0xc3,0xc3,0xd4,0xc4,0xc4,0xc4,0xd5,0xd5, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xc2,0xb6,0xc3,0xc3,0xc3,0xd4,0xc4,0xc4,0xc4,0xd5,0xb8, + 0xc1,0xc1,0xc1,0xc2,0xc2,0xb6,0xb6,0xc3,0xc3,0xd4,0xb7,0xb7,0xc4,0xd5,0xd5,0xb8, + 0xb5,0xb5,0xb5,0xc2,0xb6,0xb6,0xb6,0xb6,0xc3,0xd4,0xb7,0xb7,0xb7,0xd5,0xd5,0xb8, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xb6,0xc7,0xc7,0xc7,0xb7,0xc8,0xc8,0xc8,0xd9,0xd9, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xc6,0xc7,0xc7,0xc7,0xd8,0xc8,0xc8,0xc8,0xd9,0xd9, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xd7,0xd7,0xc7,0xc7,0xd8,0xd8,0xc8,0xc8,0xd9,0xd9,0xbc, + 0xb9,0xb9,0xb9,0xd7,0xd7,0xba,0xba,0xba,0xd8,0xd8,0xbb,0xbb,0xbb,0xd9,0xd9,0xbc, + 0xb9,0xb9,0xb9,0xca,0xca,0xba,0xba,0xcb,0xcb,0xcb,0xbb,0xbb,0xcc,0xcc,0xcc,0xbc, + 0xc9,0xc9,0xc9,0xca,0xca,0xca,0xba,0xcb,0xcb,0xcb,0xbb,0xcc,0xcc,0xcc,0xdd,0xdd, + 0xc9,0xc9,0xc9,0xca,0xca,0xdb,0xdb,0xcb,0xcb,0xdc,0xdc,0xcc,0xcc,0xdd,0xdd,0xdd, + 0xda,0xda,0xda,0xdb,0xdb,0xdb,0xdb,0xdc,0xdc,0xdc,0xdc,0xcc,0xdd,0xdd,0xdd,0xb0, + 0xbd,0xbd,0xbd,0xdb,0xbe,0xbe,0xbe,0xdc,0xdc,0xbf,0xbf,0xbf,0xdd,0xdd,0xb0,0xb0, + 0xde,0xde,0xde,0xdf,0xdf,0xdf,0xe0,0xcf,0xd0,0xd0,0xe1,0xc0,0xc0,0xd1,0xd1,0xe2, + 0xde,0xde,0xde,0xdf,0xdf,0xdf,0xe0,0xcf,0xd0,0xd0,0xe1,0xc0,0xc0,0xd1,0xd1,0xe2, + 0xde,0xde,0xde,0xdf,0xdf,0xdf,0xe0,0xc3,0xd0,0xd0,0xe1,0xc0,0xc0,0xd1,0xd1,0xe2, + 0xd2,0xd2,0xd2,0xc2,0xd3,0xd3,0xd3,0xc3,0xc3,0xd4,0xd4,0xc4,0xc4,0xd5,0xd5,0xe6, + 0xd2,0xd2,0xd2,0xd3,0xd3,0xd3,0xd3,0xc3,0xd4,0xd4,0xd4,0xc4,0xc4,0xd5,0xd5,0xe6, + 0xd2,0xd2,0xd2,0xd3,0xd3,0xd3,0xe4,0xc3,0xd4,0xd4,0xe5,0xc4,0xd5,0xd5,0xe6,0xe6, + 0xe3,0xe3,0xe3,0xd3,0xd3,0xe4,0xb6,0xd4,0xd4,0xe5,0xe5,0xb7,0xd5,0xd5,0xe6,0xe6, + 0xc5,0xc5,0xc5,0xc6,0xc6,0xc6,0xd7,0xc7,0xc7,0xd8,0xd8,0xc8,0xc8,0xd9,0xd9,0xd9, + 0xd6,0xd6,0xd6,0xc6,0xd7,0xd7,0xd7,0xc7,0xd8,0xd8,0xd8,0xc8,0xc8,0xd9,0xd9,0xea, + 0xd6,0xd6,0xd6,0xd7,0xd7,0xd7,0xe8,0xd8,0xd8,0xd8,0xe9,0xc8,0xd9,0xd9,0xea,0xea, + 0xe7,0xe7,0xe7,0xd7,0xd7,0xe8,0xe8,0xd8,0xd8,0xe9,0xe9,0xe9,0xd9,0xd9,0xea,0xea, + 0xc9,0xc9,0xc9,0xca,0xca,0xca,0xba,0xcb,0xcb,0xcb,0xe9,0xcc,0xcc,0xcc,0xea,0xea, + 0xc9,0xc9,0xc9,0xca,0xca,0xdb,0xdb,0xcb,0xcb,0xdc,0xdc,0xcc,0xcc,0xdd,0xdd,0xdd, + 0xda,0xda,0xda,0xdb,0xdb,0xdb,0xdb,0xdc,0xdc,0xdc,0xdc,0xcc,0xdd,0xdd,0xdd,0xee, + 0xda,0xda,0xda,0xdb,0xdb,0xec,0xec,0xdc,0xdc,0xed,0xed,0xed,0xdd,0xdd,0xee,0xee, + 0xeb,0xeb,0xeb,0xec,0xec,0xec,0xec,0xdc,0xed,0xed,0xed,0xed,0xdd,0xee,0xee,0xee, + 0xef,0xef,0xef,0xdf,0xe0,0xe0,0xe0,0xd0,0xd0,0xe1,0xe1,0xf2,0xd1,0xd1,0xe2,0xe2, + 0xef,0xef,0xef,0xdf,0xe0,0xe0,0xe0,0xd0,0xd0,0xe1,0xe1,0xf2,0xd1,0xd1,0xe2,0xe2, + 0xef,0xef,0xef,0xdf,0xe0,0xe0,0xe0,0xd0,0xd0,0xe1,0xe1,0xf2,0xd1,0xd1,0xe2,0xe2, + 0xd2,0xd2,0xd2,0xd3,0xd3,0xe4,0xe4,0xd4,0xd4,0xd4,0xe5,0xe5,0xd5,0xd5,0xe6,0xe6, + 0xe3,0xe3,0xe3,0xd3,0xe4,0xe4,0xe4,0xd4,0xd4,0xe5,0xe5,0xf6,0xd5,0xd5,0xe6,0xe6, + 0xe3,0xe3,0xe3,0xe4,0xe4,0xe4,0xe4,0xd4,0xe5,0xe5,0xe5,0xf6,0xd5,0xe6,0xe6,0xf7, + 0xe3,0xe3,0xe3,0xe4,0xe4,0xe4,0xf5,0xf5,0xe5,0xe5,0xf6,0xf6,0xd5,0xe6,0xe6,0xf7, + 0xd6,0xd6,0xd6,0xd7,0xd7,0xd7,0xf5,0xc7,0xd8,0xd8,0xf6,0xc8,0xd9,0xd9,0xd9,0xf7, + 0xd6,0xd6,0xd6,0xd7,0xd7,0xe8,0xe8,0xd8,0xd8,0xd8,0xe9,0xe9,0xd9,0xd9,0xea,0xea, + 0xe7,0xe7,0xe7,0xd7,0xe8,0xe8,0xe8,0xd8,0xd8,0xe9,0xe9,0xe9,0xd9,0xea,0xea,0xea, + 0xe7,0xe7,0xe7,0xe8,0xe8,0xe8,0xf9,0xf9,0xe9,0xe9,0xe9,0xfa,0xd9,0xea,0xea,0xfb, + 0xf8,0xf8,0xf8,0xe8,0xf9,0xf9,0xf9,0xcb,0xe9,0xe9,0xfa,0xfa,0xcc,0xea,0xea,0xfb, + 0xda,0xda,0xda,0xdb,0xdb,0xdb,0xdb,0xdc,0xdc,0xdc,0xdc,0xcc,0xdd,0xdd,0xdd,0xee, + 0xda,0xda,0xda,0xdb,0xdb,0xec,0xec,0xdc,0xdc,0xed,0xed,0xed,0xdd,0xdd,0xee,0xee, + 0xeb,0xeb,0xeb,0xec,0xec,0xec,0xec,0xdc,0xed,0xed,0xed,0xed,0xdd,0xee,0xee,0xee, + 0xeb,0xeb,0xeb,0xec,0xec,0xfd,0xfd,0xfd,0xed,0xed,0xfe,0xfe,0xee,0xee,0xee,0xff, + 0xf0,0xf0,0xf0,0xe0,0xf1,0xf1,0xf1,0xf1,0xe1,0xf2,0xf2,0xf2,0xf2,0xe2,0xe2,0xf3, + 0xf0,0xf0,0xf0,0xe0,0xf1,0xf1,0xf1,0xf1,0xe1,0xf2,0xf2,0xf2,0xf2,0xe2,0xe2,0xf3, + 0xf0,0xf0,0xf0,0xe0,0xf1,0xf1,0xf1,0xf1,0xe1,0xf2,0xf2,0xf2,0xf2,0xe2,0xe2,0xf3, + 0xe3,0xe3,0xe3,0xe4,0xe4,0xe4,0xf5,0xf5,0xe5,0xe5,0xf6,0xf6,0xd5,0xe6,0xe6,0xf7, + 0xf4,0xf4,0xf4,0xe4,0xe4,0xf5,0xf5,0xf5,0xe5,0xe5,0xf6,0xf6,0xf6,0xe6,0xe6,0xf7, + 0xf4,0xf4,0xf4,0xe4,0xf5,0xf5,0xf5,0xf5,0xe5,0xf6,0xf6,0xf6,0xf6,0xe6,0xf7,0xf7, + 0xf4,0xf4,0xf4,0xf5,0xf5,0xf5,0xf5,0xf5,0xe5,0xf6,0xf6,0xf6,0xf6,0xe6,0xf7,0xf7, + 0xf4,0xf4,0xf4,0xf5,0xf5,0xf5,0xf5,0xf5,0xd8,0xf6,0xf6,0xf6,0xd9,0xd9,0xf7,0xf7, + 0xe7,0xe7,0xe7,0xe8,0xe8,0xe8,0xe8,0xd8,0xe9,0xe9,0xe9,0xfa,0xd9,0xea,0xea,0xea, + 0xf8,0xf8,0xf8,0xe8,0xe8,0xf9,0xf9,0xf9,0xe9,0xe9,0xfa,0xfa,0xfa,0xea,0xea,0xfb, + 0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xf9,0xf9,0xe9,0xfa,0xfa,0xfa,0xfa,0xea,0xfb,0xfb, + 0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xf9,0xf9,0xfa,0xfa,0xfa,0xfa,0xfa,0xea,0xfb,0xfb, + 0xf8,0xf8,0xf8,0xdb,0xf9,0xf9,0xf9,0xdc,0xdc,0xfa,0xfa,0xfa,0xdd,0xdd,0xee,0xfb, + 0xeb,0xeb,0xeb,0xec,0xec,0xec,0xec,0xdc,0xed,0xed,0xed,0xed,0xdd,0xee,0xee,0xee, + 0xeb,0xeb,0xeb,0xec,0xec,0xfd,0xfd,0xfd,0xed,0xed,0xfe,0xfe,0xee,0xee,0xee,0xff, + 0xfc,0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfd,0xed,0xfe,0xfe,0xfe,0xfe,0xee,0xff,0xff, +} +}; +Memcmap *memdefcmap = &def; +void _memmkcmap(void){} blob - /dev/null blob + 4fa5942a4bb9b578fd213cc7debafbc2230ba35b (mode 644) --- /dev/null +++ src/libdraw/md-cread.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include + +Memimage* +creadmemimage(int fd) +{ + char hdr[5*12+1]; + Rectangle r; + int m, nb, miny, maxy, new, ldepth, ncblock; + uchar *buf; + Memimage *i; + 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 +#include +#include +#include + +Memsubfont* +getmemdefont(void) +{ + char *hdr, *p; + int n; + Fontchar *fc; + Memsubfont *f; + int ld; + Rectangle r; + Memdata *md; + Memimage *i; + + /* + * make sure data is word-aligned. this is true with Plan 9 compilers + * but not in general. the byte order is right because the data is + * declared as char*, not 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; +} blob - /dev/null blob + d0f2d4fa1e884d0b85335a704f972fbce2d191b5 (mode 644) --- /dev/null +++ src/libdraw/md-draw.c @@ -0,0 +1,2487 @@ +#include +#include +#include +#include + +int drawdebug; +static int tablesbuilt; + +/* perfect approximation to NTSC = .299r+.587g+.114b when 0 ≤ r,g,b < 256 */ +#define RGB2K(r,g,b) ((156763*(r)+307758*(g)+59769*(b))>>19) + +/* + * for 0 ≤ x ≤ 255*255, (x*0x0101+0x100)>>16 is a perfect approximation. + * for 0 ≤ x < (1<<16), x/255 = ((x+1)*0x0101)>>16 is a perfect approximation. + * the last one is perfect for all up to 1<<16, avoids a multiply, but requires a rathole. + */ +/* #define DIV255(x) (((x)*257+256)>>16) */ +#define DIV255(x) ((((x)+1)*257)>>16) +/* #define DIV255(x) (tmp=(x)+1, (tmp+(tmp>>8))>>8) */ + +#define MUL(x, y, t) (t = (x)*(y)+128, (t+(t>>8))>>8) +#define MASK13 0xFF00FF00 +#define MASK02 0x00FF00FF +#define MUL13(a, x, t) (t = (a)*(((x)&MASK13)>>8)+128, ((t+((t>>8)&MASK02))>>8)&MASK02) +#define MUL02(a, x, t) (t = (a)*(((x)&MASK02)>>0)+128, ((t+((t>>8)&MASK02))>>8)&MASK02) +#define MUL0123(a, x, s, t) ((MUL13(a, x, s)<<8)|MUL02(a, x, t)) + +#define MUL2(u, v, x, y) (t = (u)*(v)+(x)*(y)+256, (t+(t>>8))>>8) + +static void mktables(void); +typedef int Subdraw(Memdrawparam*); +static Subdraw chardraw, alphadraw, memoptdraw; + +static Memimage* memones; +static Memimage* memzeros; +Memimage *memwhite; +Memimage *memblack; +Memimage *memtransparent; +Memimage *memopaque; + +int __ifmt(Fmt*); + +void +memimageinit(void) +{ + static int didinit = 0; + + if(didinit) + return; + + didinit = 1; + + mktables(); + _memmkcmap(); + + fmtinstall('R', Rfmt); + fmtinstall('P', Pfmt); + fmtinstall('b', __ifmt); + + memones = allocmemimage(Rect(0,0,1,1), GREY1); + memones->flags |= Frepl; + memones->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); + *byteaddr(memones, ZP) = ~0; + + memzeros = allocmemimage(Rect(0,0,1,1), GREY1); + memzeros->flags |= Frepl; + memzeros->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); + *byteaddr(memzeros, ZP) = 0; + + if(memones == nil || memzeros == nil) + assert(0 /*cannot initialize memimage library */); /* RSC BUG */ + + memwhite = memones; + memblack = memzeros; + memopaque = memones; + memtransparent = memzeros; +} + +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, Memimage *mask, Point p1, int op) +{ + if(mask == nil) + mask = memopaque; + +DBG print("memimagedraw %p/%luX %R @ %p %p/%luX %P %p/%luX %P... ", dst, dst->chan, r, dst->data->bdata, src, src->chan, p0, mask, mask->chan, p1); + + if(drawclip(dst, &r, src, &p0, mask, &p1, &par.sr, &par.mr) == 0){ +// if(drawdebug) +// iprint("empty clipped rectangle\n"); + return nil; + } + + if(op < Clear || op > SoverD){ +// if(drawdebug) +// iprint("op out of range: %d\n", op); + return nil; + } + + par.op = op; + par.dst = dst; + par.r = r; + par.src = src; + /* par.sr set by drawclip */ + par.mask = mask; + /* par.mr set by drawclip */ + + par.state = 0; + if(src->flags&Frepl){ + par.state |= Replsrc; + if(Dx(src->r)==1 && Dy(src->r)==1){ + par.sval = pixelbits(src, src->r.min); + par.state |= Simplesrc; + par.srgba = _imgtorgba(src, par.sval); + par.sdval = _rgbatoimg(dst, par.srgba); + if((par.srgba&0xFF) == 0 && (op&DoutS)){ +// if (drawdebug) iprint("fill with transparent source\n"); + return nil; /* no-op successfully handled */ + } + } + } + + if(mask->flags & Frepl){ + par.state |= Replmask; + if(Dx(mask->r)==1 && Dy(mask->r)==1){ + par.mval = pixelbits(mask, mask->r.min); + if(par.mval == 0 && (op&DoutS)){ +// if(drawdebug) iprint("fill with zero mask\n"); + return nil; /* no-op successfully handled */ + } + par.state |= Simplemask; + if(par.mval == ~0) + par.state |= Fullmask; + par.mrgba = _imgtorgba(mask, par.mval); + } + } + +// if(drawdebug) +// iprint("dr %R sr %R mr %R...", r, par.sr, par.mr); +DBG print("draw dr %R sr %R mr %R %lux\n", r, par.sr, par.mr, par.state); + + return ∥ +} + +void +_memimagedraw(Memdrawparam *par) +{ + /* + * Now that we've clipped the parameters down to be consistent, we + * simply try sub-drawing routines in order until we find one that was able + * to handle us. If the sub-drawing routine returns zero, it means it was + * unable to satisfy the request, so we do not return. + */ + + /* + * Hardware support. Each video driver provides this function, + * which checks to see if there is anything it can help with. + * There could be an if around this checking to see if dst is in video memory. + */ +DBG print("test hwdraw\n"); + if(hwdraw(par)){ +//if(drawdebug) iprint("hw handled\n"); +DBG print("hwdraw handled\n"); + return; + } + /* + * Optimizations using memmove and memset. + */ +DBG print("test memoptdraw\n"); + if(memoptdraw(par)){ +//if(drawdebug) iprint("memopt handled\n"); +DBG print("memopt handled\n"); + return; + } + + /* + * Character drawing. + * Solid source color being painted through a boolean mask onto a high res image. + */ +DBG print("test chardraw\n"); + if(chardraw(par)){ +//if(drawdebug) iprint("chardraw handled\n"); +DBG print("chardraw handled\n"); + return; + } + + /* + * General calculation-laden case that does alpha for each pixel. + */ +DBG print("do alphadraw\n"); + alphadraw(par); +//if(drawdebug) iprint("alphadraw handled\n"); +DBG print("alphadraw handled\n"); +} +#undef DBG + +/* + * Clip the destination rectangle further based on the properties of the + * source and mask rectangles. Once the destination rectangle is properly + * clipped, adjust the source and mask rectangles to be the same size. + * Then if source or mask is replicated, move its clipped rectangle + * so that its minimum point falls within the repl rectangle. + * + * Return zero if the final rectangle is null. + */ +int +drawclip(Memimage *dst, Rectangle *r, Memimage *src, Point *p0, Memimage *mask, Point *p1, Rectangle *sr, Rectangle *mr) +{ + Point rmin, delta; + int splitcoords; + Rectangle omr; + + if(r->min.x>=r->max.x || r->min.y>=r->max.y) + return 0; + splitcoords = (p0->x!=p1->x) || (p0->y!=p1->y); + /* clip to destination */ + rmin = r->min; + if(!rectclip(r, dst->r) || !rectclip(r, dst->clipr)) + return 0; + /* move mask point */ + p1->x += r->min.x-rmin.x; + p1->y += r->min.y-rmin.y; + /* move source point */ + p0->x += r->min.x-rmin.x; + p0->y += r->min.y-rmin.y; + /* map destination rectangle into source */ + sr->min = *p0; + sr->max.x = p0->x+Dx(*r); + sr->max.y = p0->y+Dy(*r); + /* sr is r in source coordinates; clip to source */ + if(!(src->flags&Frepl) && !rectclip(sr, src->r)) + return 0; + if(!rectclip(sr, src->clipr)) + return 0; + /* compute and clip rectangle in mask */ + if(splitcoords){ + /* move mask point with source */ + p1->x += sr->min.x-p0->x; + p1->y += sr->min.y-p0->y; + mr->min = *p1; + mr->max.x = p1->x+Dx(*sr); + mr->max.y = p1->y+Dy(*sr); + omr = *mr; + /* mr is now rectangle in mask; clip it */ + if(!(mask->flags&Frepl) && !rectclip(mr, mask->r)) + return 0; + if(!rectclip(mr, mask->clipr)) + return 0; + /* reflect any clips back to source */ + sr->min.x += mr->min.x-omr.min.x; + sr->min.y += mr->min.y-omr.min.y; + sr->max.x += mr->max.x-omr.max.x; + sr->max.y += mr->max.y-omr.max.y; + *p1 = mr->min; + }else{ + if(!(mask->flags&Frepl) && !rectclip(sr, mask->r)) + return 0; + if(!rectclip(sr, mask->clipr)) + return 0; + *p1 = sr->min; + } + + /* move source clipping back to destination */ + delta.x = r->min.x - p0->x; + delta.y = r->min.y - p0->y; + r->min.x = sr->min.x + delta.x; + r->min.y = sr->min.y + delta.y; + r->max.x = sr->max.x + delta.x; + r->max.y = sr->max.y + delta.y; + + /* move source rectangle so sr->min is in src->r */ + if(src->flags&Frepl) { + delta.x = drawreplxy(src->r.min.x, src->r.max.x, sr->min.x) - sr->min.x; + delta.y = drawreplxy(src->r.min.y, src->r.max.y, sr->min.y) - sr->min.y; + sr->min.x += delta.x; + sr->min.y += delta.y; + sr->max.x += delta.x; + sr->max.y += delta.y; + } + *p0 = sr->min; + + /* move mask point so it is in mask->r */ + *p1 = drawrepl(mask->r, *p1); + mr->min = *p1; + mr->max.x = p1->x+Dx(*sr); + mr->max.y = p1->y+Dy(*sr); + + assert(Dx(*sr) == Dx(*mr) && Dx(*mr) == Dx(*r)); + assert(Dy(*sr) == Dy(*mr) && Dy(*mr) == Dy(*r)); + assert(ptinrect(*p0, src->r)); + assert(ptinrect(*p1, mask->r)); + assert(ptinrect(r->min, dst->r)); + + return 1; +} + +/* + * Conversion tables. + */ +static uchar replbit[1+8][256]; /* replbit[x][y] is the replication of the x-bit quantity y to 8-bit depth */ +static uchar conv18[256][8]; /* conv18[x][y] is the yth pixel in the depth-1 pixel x */ +static uchar conv28[256][4]; /* ... */ +static uchar conv48[256][2]; + +/* + * bitmap of how to replicate n bits to fill 8, for 1 ≤ n ≤ 8. + * the X's are where to put the bottom (ones) bit of the n-bit pattern. + * only the top 8 bits of the result are actually used. + * (the lower 8 bits are needed to get bits in the right place + * when n is not a divisor of 8.) + * + * Should check to see if its easier to just refer to replmul than + * use the precomputed values in replbit. On PCs it may well + * be; on machines with slow multiply instructions it probably isn't. + */ +#define a ((((((((((((((((0 +#define X *2+1) +#define _ *2) +static int replmul[1+8] = { + 0, + a X X X X X X X X X X X X X X X X, + a _ X _ X _ X _ X _ X _ X _ X _ X, + a _ _ X _ _ X _ _ X _ _ X _ _ X _, + a _ _ _ X _ _ _ X _ _ _ X _ _ _ X, + a _ _ _ _ X _ _ _ _ X _ _ _ _ X _, + a _ _ _ _ _ X _ _ _ _ _ X _ _ _ _, + a _ _ _ _ _ _ X _ _ _ _ _ _ X _ _, + a _ _ _ _ _ _ _ X _ _ _ _ _ _ _ X, +}; +#undef a +#undef X +#undef _ + +static void +mktables(void) +{ + int i, j, mask, sh, small; + + if(tablesbuilt) + return; + + fmtinstall('R', Rfmt); + fmtinstall('P', Pfmt); + tablesbuilt = 1; + + /* bit replication up to 8 bits */ + for(i=0; i<256; i++){ + for(j=0; j<=8; j++){ /* j <= 8 [sic] */ + small = i & ((1<>8; + } + } + + /* bit unpacking up to 8 bits, only powers of 2 */ + for(i=0; i<256; i++){ + for(j=0, sh=7, mask=1; j<8; j++, sh--) + conv18[i][j] = replbit[1][(i>>sh)&mask]; + + for(j=0, sh=6, mask=3; j<4; j++, sh-=2) + conv28[i][j] = replbit[2][(i>>sh)&mask]; + + for(j=0, sh=4, mask=15; j<2; j++, sh-=4) + conv48[i][j] = replbit[4][(i>>sh)&mask]; + } +} + +static uchar ones = 0xff; + +/* + * General alpha drawing case. Can handle anything. + */ +typedef struct Buffer Buffer; +struct Buffer { + /* used by most routines */ + uchar *red; + uchar *grn; + uchar *blu; + uchar *alpha; + uchar *grey; + u32int *rgba; + int delta; /* number of bytes to add to pointer to get next pixel to the right */ + + /* used by boolcalc* for mask data */ + uchar *m; /* ptr to mask data r.min byte; like p->bytermin */ + int mskip; /* no. of left bits to skip in *m */ + uchar *bm; /* ptr to mask data img->r.min byte; like p->bytey0s */ + int bmskip; /* no. of left bits to skip in *bm */ + uchar *em; /* ptr to mask data img->r.max.x byte; like p->bytey0e */ + int emskip; /* no. of right bits to skip in *em */ +}; + +typedef struct Param Param; +typedef Buffer Readfn(Param*, uchar*, int); +typedef void Writefn(Param*, uchar*, Buffer); +typedef Buffer Calcfn(Buffer, Buffer, Buffer, int, int, int); + +enum { + MAXBCACHE = 16 +}; + +/* giant rathole to customize functions with */ +struct Param { + Readfn *replcall; + Readfn *greymaskcall; + Readfn *convreadcall; + Writefn *convwritecall; + + Memimage *img; + Rectangle r; + int dx; /* of r */ + int needbuf; + int convgrey; + int alphaonly; + + uchar *bytey0s; /* byteaddr(Pt(img->r.min.x, img->r.min.y)) */ + uchar *bytermin; /* byteaddr(Pt(r.min.x, img->r.min.y)) */ + uchar *bytey0e; /* byteaddr(Pt(img->r.max.x, img->r.min.y)) */ + int bwidth; + + int replcache; /* if set, cache buffers */ + Buffer bcache[MAXBCACHE]; + 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, alphacalc5, alphacalc11, alphacalcS; +static Calcfn boolcalc14, boolcalc236789, boolcalc1011; + +static Readfn* readfn(Memimage*); +static Readfn* readalphafn(Memimage*); +static Writefn* writefn(Memimage*); + +static Calcfn* boolcopyfn(Memimage*, Memimage*); +static Readfn* convfn(Memimage*, Param*, Memimage*, Param*); + +static 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; ir; + dx = Dx(r); + dy = Dy(r); + + ndrawbuf = 0; + + src = par->src; + mask = par->mask; + dst = par->dst; + sr = par->sr; + mr = par->mr; + op = par->op; + + isgrey = dst->flags&Fgrey; + + /* + * Buffering when src and dst are the same bitmap is sufficient but not + * necessary. There are stronger conditions we could use. We could + * check to see if the rectangles intersect, and if simply moving in the + * correct y direction can avoid the need to buffer. + */ + needbuf = (src->data == dst->data); + + spar = getparam(src, sr, isgrey, needbuf); + dpar = getparam(dst, r, isgrey, needbuf); + mpar = getparam(mask, mr, 0, needbuf); + + dir = (needbuf && byteaddr(dst, r.min) > byteaddr(src, sr.min)) ? -1 : 1; + spar.dir = mpar.dir = dpar.dir = dir; + + /* + * If the mask is purely boolean, we can convert from src to dst format + * when we read src, and then just copy it to dst where the mask tells us to. + * This requires a boolean (1-bit grey) mask and lack of a source alpha channel. + * + * The computation is accomplished by assigning the function pointers as follows: + * rdsrc - read and convert source into dst format in a buffer + * rdmask - convert mask to bytes, set pointer to it + * rddst - fill with pointer to real dst data, but do no reads + * calc - copy src onto dst when mask says to. + * wrdst - do nothing + * This is slightly sleazy, since things aren't doing exactly what their names say, + * but it avoids a fair amount of code duplication to make this a case here + * rather than have a separate booldraw. + */ +//if(drawdebug) iprint("flag %lud mchan %lux=?%x dd %d\n", src->flags&Falpha, mask->chan, GREY1, dst->depth); + if(!(src->flags&Falpha) && mask->chan == GREY1 && dst->depth >= 8 && op == SoverD){ +//if(drawdebug) iprint("boolcopy..."); + rdsrc = convfn(dst, &dpar, src, &spar); + rddst = readptr; + rdmask = readfn(mask); + calc = boolcopyfn(dst, mask); + wrdst = nullwrite; + }else{ + /* usual alphadraw parameter fetching */ + rdsrc = readfn(src); + rddst = readfn(dst); + wrdst = writefn(dst); + calc = alphacalc[op]; + + /* + * If there is no alpha channel, we'll ask for a grey channel + * and pretend it is the alpha. + */ + if(mask->flags&Falpha){ + rdmask = readalphafn(mask); + mpar.alphaonly = 1; + }else{ + mpar.greymaskcall = readfn(mask); + mpar.convgrey = 1; + rdmask = greymaskread; + + /* + * Should really be above, but then boolcopyfns would have + * to deal with bit alignment, and I haven't written that. + * + * This is a common case for things like ellipse drawing. + * When there's no alpha involved and the mask is boolean, + * we can avoid all the division and multiplication. + */ + if(mask->chan == GREY1 && !(src->flags&Falpha)) + calc = boolcalc[op]; + else if(op == SoverD && !(src->flags&Falpha)) + calc = alphacalcS; + } + } + + /* + * If the image has a small enough repl rectangle, + * we can just read each line once and cache them. + */ + if(spar.replcache){ + spar.replcall = rdsrc; + rdsrc = replread; + } + if(mpar.replcache){ + mpar.replcall = rdmask; + rdmask = replread; + } + + if(allocdrawbuf() < 0) + return 0; + + /* + * Before we were saving only offsets from drawbuf in the parameter + * structures; now that drawbuf has been grown to accomodate us, + * we can fill in the pointers. + */ + spar.bufbase = drawbuf+spar.bufoff; + mpar.bufbase = drawbuf+mpar.bufoff; + dpar.bufbase = drawbuf+dpar.bufoff; + spar.convbuf = drawbuf+spar.convbufoff; + + if(dir == 1){ + starty = 0; + endy = dy; + }else{ + starty = dy-1; + endy = -1; + } + + /* + * srcy, masky, and dsty are offsets from the top of their + * respective Rectangles. they need to be contained within + * the rectangles, so clipy can keep them there without division. + */ + srcy = (starty + sr.min.y - src->r.min.y)%Dy(src->r); + masky = (starty + mr.min.y - mask->r.min.y)%Dy(mask->r); + dsty = starty + r.min.y - dst->r.min.y; + + assert(0 <= srcy && srcy < Dy(src->r)); + assert(0 <= masky && masky < Dy(mask->r)); + assert(0 <= dsty && dsty < Dy(dst->r)); + + for(y=starty; y!=endy; y+=dir, srcy+=dir, masky+=dir, dsty+=dir){ + clipy(src, &srcy); + clipy(dst, &dsty); + clipy(mask, &masky); + + bsrc = rdsrc(&spar, spar.bufbase, srcy); +DBG print("["); + bmask = rdmask(&mpar, mpar.bufbase, masky); +DBG print("]\n"); + bdst = rddst(&dpar, dpar.bufbase, dsty); +DBG dumpbuf("src", bsrc, dx); +DBG dumpbuf("mask", bmask, dx); +DBG dumpbuf("dst", bdst, dx); + bdst = calc(bdst, bsrc, bmask, dx, isgrey, op); + wrdst(&dpar, dpar.bytermin+dsty*dpar.bwidth, bdst); + } + + return 1; +} +#undef DBG + +static Buffer +alphacalc0(Buffer bdst, Buffer b1, Buffer b2, int dx, int grey, int op) +{ + USED(grey); + USED(op); + memset(bdst.rgba, 0, dx*bdst.delta); + return bdst; +} + +static Buffer +alphacalc14(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op) +{ + Buffer obdst; + int fd, sadelta; + int i, sa, ma, q; + u32int s, t; + + obdst = bdst; + sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta; + q = bsrc.delta == 4 && bdst.delta == 4; + + for(i=0; ibcache[y]; + if((p->bfilled & (1<bfilled |= 1<replcall(p, p->bufbase+y*p->bufdelta, y); + } + return *b; +} + +/* + * Alpha reading function that simply relabels the grey pointer. + */ +static Buffer +greymaskread(Param *p, uchar *buf, int y) +{ + Buffer b; + + b = p->greymaskcall(p, buf, y); + b.alpha = b.grey; + return b; +} + +#define DBG if(0) +static Buffer +readnbit(Param *p, uchar *buf, int y) +{ + Buffer b; + Memimage *img; + uchar *repl, *r, *w, *ow, bits; + int i, n, sh, depth, x, dx, npack, nbits; + + b.rgba = (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, p->bwidth, *r, n); + bits = *r++; + nbits = 8; + if(i=x&(npack-1)){ +DBG print("throwaway %d...", i); + bits <<= depth*i; + nbits -= depth*i; + } + for(i=0; i>sh]; +DBG print("bit %x...", repl[bits>>sh]); + bits <<= depth; + nbits -= depth; + } + dx -= n; + if(dx == 0) + return b; + + assert(x+i == p->img->r.max.x); + + /* copy from beginning of repl rectangle until where we were before. */ + x = p->img->r.min.x; + n = dx; + if(n > p->r.min.x - x) + n = p->r.min.x - x; + + r = p->bytey0s + y*p->bwidth; +DBG print("x=%d r=%p...", x, r); + bits = *r++; + nbits = 8; + if(i=x&(npack-1)){ + bits <<= depth*i; + nbits -= depth*i; + } +DBG print("nbits=%d...", nbits); + for(i=0; i>sh]; +DBG print("bit %x...", repl[bits>>sh]); + bits <<= depth; + nbits -= depth; +DBG print("bits %x nbits %d...", bits, nbits); + } + dx -= n; + if(dx == 0) + return b; + + assert(dx > 0); + /* now we have exactly one full scan line: just replicate the buffer itself until we are done */ + ow = buf; + while(dx--) + *w++ = *ow++; + + return b; +} +#undef DBG + +#define DBG if(0) +static void +writenbit(Param *p, uchar *w, Buffer src) +{ + uchar *r; + 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> sh); + nbits += depth; + if(nbits == 8){ + *w++ = bits; + nbits = 0; + } + } + + if(nbits){ + sh = 8-nbits; + bits <<= sh; + bits |= *w & ((1<bytey0s + y*p->bwidth; + r = p->bytermin + y*p->bwidth; + end = p->bytey0e + y*p->bwidth; + cmap = p->img->cmap->cmap2rgb; + convgrey = p->convgrey; + copyalpha = (p->img->flags&Falpha) ? 1 : 0; + + w = buf; + dx = p->dx; + if(copyalpha){ + b.alpha = buf++; + a = p->img->shift[CAlpha]/8; + m = p->img->shift[CMap]/8; + for(i=0; iimg->cmap->rgb2cmap; + + delta = src.delta; + red= src.red; + grn = src.grn; + blu = src.blu; + + dx = p->dx; + for(i=0; i>4)*256+(*grn>>4)*16+(*blu>>4)]; +} + +#define DBG if(0) +static Buffer +readbyte(Param *p, uchar *buf, int y) +{ + Buffer b; + Memimage *img; + int dx, isgrey, convgrey, alphaonly, copyalpha, i, nb; + uchar *begin, *end, *r, *w, *rrepl, *grepl, *brepl, *arepl, *krepl; + uchar ured, ugrn, ublu; + 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, alphaonly, convgrey, isgrey); + /* if we can, avoid processing everything */ + if(!(img->flags&Frepl) && !convgrey && (img->flags&Fbytes)){ + memset(&b, 0, sizeof b); + if(p->needbuf){ + memmove(buf, r, dx*nb); + r = buf; + } + b.rgba = (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>img->shift[CAlpha]) & img->mask[CAlpha]]; +DBG print("a %x\n", w[-1]); + } + + if(isgrey) + *w++ = krepl[(u >> img->shift[CGrey]) & img->mask[CGrey]]; + else if(!alphaonly){ + ured = rrepl[(u >> img->shift[CRed]) & img->mask[CRed]]; + ugrn = grepl[(u >> img->shift[CGreen]) & img->mask[CGreen]]; + ublu = brepl[(u >> img->shift[CBlue]) & img->mask[CBlue]]; + if(convgrey){ +DBG print("g %x %x %x\n", ured, ugrn, ublu); + *w++ = RGB2K(ured, ugrn, ublu); +DBG print("%x\n", w[-1]); + }else{ + *w++ = brepl[(u >> img->shift[CBlue]) & img->mask[CBlue]]; + *w++ = grepl[(u >> img->shift[CGreen]) & img->mask[CGreen]]; + *w++ = rrepl[(u >> img->shift[CRed]) & img->mask[CRed]]; + } + } + r += nb; + if(r == end) + r = begin; + } + + b.alpha = copyalpha ? buf : &ones; + b.rgba = (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<depth)-1); + + isalpha = img->flags&Falpha; + isgrey = img->flags&Fgrey; + adelta = src.delta; + + if(isalpha && (alpha == nil || alpha == &ones)){ + ff = 0xFF; + alpha = &ff; + adelta = 0; + } + + for(i=0; i> (8-img->nbits[CGrey])) & img->mask[CGrey]) << img->shift[CGrey]; +DBG print("|grey %.8lux...", u); + grey += delta; + }else{ + u |= ((*red >> (8-img->nbits[CRed])) & img->mask[CRed]) << img->shift[CRed]; + u |= ((*grn >> (8-img->nbits[CGreen])) & img->mask[CGreen]) << img->shift[CGreen]; + u |= ((*blu >> (8-img->nbits[CBlue])) & img->mask[CBlue]) << img->shift[CBlue]; + red += delta; + grn += delta; + blu += delta; +DBG print("|rgb %.8lux...", u); + } + + if(isalpha){ + u |= ((*alpha >> (8-img->nbits[CAlpha])) & img->mask[CAlpha]) << img->shift[CAlpha]; + alpha += adelta; +DBG print("|alpha %.8lux...", u); + } + + w[0] = u; + w[1] = u>>8; + w[2] = u>>16; + w[3] = u>>24; + w += nb; + } +} +#undef DBG + +static Readfn* +readfn(Memimage *img) +{ + if(img->depth < 8) + return readnbit; + if(img->nbits[CMap] == 8) + return readcmap; + return readbyte; +} + +static Readfn* +readalphafn(Memimage *m) +{ + USED(m); + return readbyte; +} + +static Writefn* +writefn(Memimage *img) +{ + if(img->depth < 8) + return writenbit; + if(img->chan == CMAP8) + return writecmap; + return writebyte; +} + +static void +nullwrite(Param *p, uchar *s, Buffer b) +{ + USED(p); + USED(s); +} + +static Buffer +readptr(Param *p, uchar *s, int y) +{ + Buffer b; + uchar *q; + + USED(s); + q = p->bytermin + y*p->bwidth; + b.red = q; /* ptr to data */ + b.grn = b.blu = b.grey = b.alpha = nil; + b.rgba = (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(wchan == src->chan && !(src->flags&Frepl)){ +//if(drawdebug) iprint("readptr..."); + return readptr; + } + + if(dst->chan==CMAP8 && (src->chan==GREY1||src->chan==GREY2||src->chan==GREY4)){ + /* cheat because we know the replicated value is exactly the color map entry. */ +//if(drawdebug) iprint("Readnbit..."); + return readnbit; + } + + spar->convreadcall = readfn(src); + spar->convwritecall = writefn(dst); + spar->convdpar = dpar; + + /* allocate a conversion buffer */ + spar->convbufoff = ndrawbuf; + ndrawbuf += spar->dx*4; + + if(spar->dx > Dx(spar->img->r)){ + spar->convdx = spar->dx; + spar->dx = Dx(spar->img->r); + } + +//if(drawdebug) iprint("genconv..."); + return genconv; +} + +/* + * 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<flags&Frepl && Dx(mask->r)==1 && Dy(mask->r)==1 && pixelbits(mask, mask->r.min)==~0) + return boolmemmove; + + switch(img->depth){ + case 8: + return boolcopy8; + case 16: + return boolcopy16; + case 24: + return boolcopy24; + case 32: + return boolcopy32; + default: + assert(0 /* boolcopyfn */); + } + return nil; +} + +/* + * Optimized draw for filling and scrolling; uses memset and memmove. + */ +static void +memsets(void *vp, ushort val, int n) +{ + ushort *p, *ep; + + p = vp; + ep = p+n; + while(p>8; + c = val>>16; + while(pchan; chan; chan>>=8){ + nb = NBITS(chan); + ov = v = val&((1<>= nb; + + while(nb < 8){ + v |= v<>= (nb-8); + + switch(TYPE(chan)){ + case CRed: + r = v; + break; + case CGreen: + g = v; + break; + case CBlue: + b = v; + break; + case CAlpha: + a = v; + break; + case CGrey: + r = g = b = v; + break; + case CMap: + p = img->cmap->cmap2rgb+3*ov; + r = *p++; + g = *p++; + b = *p; + break; + } + } + return (r<<24)|(g<<16)|(b<<8)|a; +} + +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))<>(8-nb))<>(8-nb))<>(8-nb))<cmap->rgb2cmap; + m = p[(r>>4)*256+(g>>4)*16+(b>>4)]; + v |= (m>>(8-nb))<>(8-nb))<r); + dy = Dy(par->r); + src = par->src; + dst = par->dst; + op = par->op; + +DBG print("state %lux mval %lux dd %d\n", par->state, par->mval, dst->depth); + /* + * If we have an opaque mask and source is one opaque pixel we can convert to the + * destination format and just replicate with memset. + */ + m = Simplesrc|Simplemask|Fullmask; + if((par->state&m)==m && (par->srgba&0xFF) == 0xFF && (op ==S || op == SoverD)){ + uchar *dp, p[4]; + int d, dwid, ppb, np, nb; + uchar lm, rm; + +DBG print("memopt, dst %p, dst->data->bdata %p\n", dst, dst->data->bdata); + dwid = dst->width*sizeof(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<depth; /* pixels per byte */ + m = ppb-1; + /* left edge */ + np = par->r.min.x&m; /* no. pixels unused on left side of word */ + dx -= (ppb-np); + nb = 8 - np * dst->depth; /* no. bits used on right side of word */ + lm = (1<r.min.x, nb, lm, ppb, m); + + /* right edge */ + np = par->r.max.x&m; /* no. pixels used on left side of word */ + dx -= np; + nb = 8 - np * dst->depth; /* no. bits unused on right side of word */ + rm = ~((1<r.max.x, nb, rm, ppb, m); + +DBG print("dx %d Dx %d\n", dx, Dx(par->r)); + /* lm, rm are masks that are 1 where we should touch the bits */ + if(dx < 0){ /* just one byte */ + lm &= rm; + for(y=0; y>8; + v = *(ushort*)p; +DBG print("dp=%p; dx=%d; for(y=0; y<%d; y++, dp+=%d)\nmemsets(dp, v, dx);\n", + dp, dx, dy, dwid); + for(y=0; y>8; + p[2] = v>>16; + p[3] = v>>24; + v = *(u32int*)p; + for(y=0; ystate&(m|Replsrc))==m && src->depth >= 8 + && src->chan == dst->chan && !(src->flags&Falpha) && (op == S || op == SoverD)){ + uchar *sp, *dp; + long swid, dwid, nb; + int dir; + + if(src->data == dst->data && byteaddr(dst, par->r.min) > byteaddr(src, par->sr.min)) + dir = -1; + else + dir = 1; + + swid = src->width*sizeof(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; ystate&(Simplemask|Simplesrc|Replmask|Replsrc))==0 + && dst->chan==GREY1 && src->chan==GREY1 && par->mask->chan==GREY1 + && (par->r.min.x&7)==(par->sr.min.x&7) && (par->r.min.x&7)==(par->mr.min.x&7)){ + uchar *sp, *dp, *mp; + uchar lm, rm; + long swid, dwid, mwid; + int i, x, dir; + + sp = byteaddr(src, par->sr.min); + dp = byteaddr(dst, par->r.min); + mp = byteaddr(par->mask, par->mr.min); + swid = src->width*sizeof(u32int); + dwid = dst->width*sizeof(u32int); + mwid = par->mask->width*sizeof(u32int); + + if(src->data == dst->data && byteaddr(dst, par->r.min) > byteaddr(src, par->sr.min)){ + dir = -1; + }else + dir = 1; + + lm = 0xFF>>(par->r.min.x&7); + rm = 0xFF<<(8-(par->r.max.x&7)); + dx -= (8-(par->r.min.x&7)) + (par->r.max.x&7); + + if(dx < 0){ /* one byte wide */ + lm &= rm; + if(dir == -1){ + dp += dwid*(dy-1); + sp += swid*(dy-1); + mp += mwid*(dy-1); + dwid = -dwid; + swid = -swid; + mwid = -mwid; + } + for(y=0; ymask->flags, par->mask->depth, par->src->flags, + Dx(par->src->r), Dy(par->src->r), par->dst->depth, par->dst->data, par->src->data); + + mask = par->mask; + src = par->src; + dst = par->dst; + r = par->r; + mr = par->mr; + op = par->op; + + if((par->state&(Replsrc|Simplesrc|Replmask)) != (Replsrc|Simplesrc) + || mask->depth != 1 || src->flags&Falpha || dst->depth<8 || dst->data==src->data + || op != SoverD) + return 0; + +//if(drawdebug) iprint("chardraw..."); + + depth = mask->depth; + maskwid = mask->width*sizeof(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; yex; x--, wc++){ + i = x&7; + if(i == 8-1) + bits = *q++; +DBG print("bits %lux sh %d...", bits, i); + if((bits>>i)&1) + *wc = v; + } + break; + case 16: + ws = (ushort*)wp; + v = *(ushort*)sp; + for(x=bx; x>ex; x--, ws++){ + i = x&7; + if(i == 8-1) + bits = *q++; +DBG print("bits %lux sh %d...", bits, i); + if((bits>>i)&1) + *ws = v; + } + break; + case 24: + wc = wp; + for(x=bx; x>ex; x--, wc+=3){ + i = x&7; + if(i == 8-1) + bits = *q++; +DBG print("bits %lux sh %d...", bits, i); + if((bits>>i)&1){ + wc[0] = sp[0]; + wc[1] = sp[1]; + wc[2] = sp[2]; + } + } + break; + case 32: + wl = (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; yr.max.y; y++) + memset24(byteaddr(i, Pt(i->r.min.x, y)), bits, Dx(i->r)); + break; + default: /* 1, 2, 4, 8, 16, 32 */ + for(d=i->depth; d<32; d*=2) + bits = (bits << d) | bits; + p[0] = bits; /* make little endian */ + p[1] = bits>>8; + p[2] = bits>>16; + p[3] = bits>>24; + bits = *(u32int*)p; + memsetl(wordaddr(i, i->r.min), bits, i->width*Dy(i->r)); + break; + } +} + blob - /dev/null blob + 26eb54de3af2b75f33c6845d1dad1e18a02be8d2 (mode 644) --- /dev/null +++ src/libdraw/md-drawtest.c @@ -0,0 +1,1004 @@ +#include +#include +#include +#include +#include + +#define DBG if(0) +#define RGB2K(r,g,b) ((299*((u32int)(r))+587*((u32int)(g))+114*((u32int)(b)))/1000) + +/* + * This program tests the 'memimagedraw' primitive stochastically. + * It tests the combination aspects of it thoroughly, but since the + * three images it uses are disjoint, it makes no check of the + * correct behavior when images overlap. That is, however, much + * easier to get right and to test. + */ + +void drawonepixel(Memimage*, Point, Memimage*, Point, Memimage*, Point); +void verifyone(void); +void verifyline(void); +void verifyrect(void); +void verifyrectrepl(int, int); +void putpixel(Memimage *img, Point pt, 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, seed, dchan, schan, mchan); + srand(seed); + + dst = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(dchan)); + src = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(schan)); + mask = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan)); + stmp = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(schan)); + mtmp = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan)); + ones = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan)); +// print("chan %lux %lux %lux %lux %lux %lux\n", dst->chan, src->chan, mask->chan, stmp->chan, mtmp->chan, ones->chan); + if(dst==0 || src==0 || mask==0 || mtmp==0 || ones==0) { + Alloc: + fprint(2, "dtest: allocation failed: %r\n"); + exits("alloc"); + } + nbytes = (4*Xrange+4)*Yrange; + srcbits = malloc(nbytes); + dstbits = malloc(nbytes); + maskbits = malloc(nbytes); + savedstbits = malloc(nbytes); + if(dstbits==0 || srcbits==0 || maskbits==0 || savedstbits==0) + goto Alloc; + dbpp = dst->depth; + sbpp = src->depth; + mbpp = mask->depth; + dpm = 0xFF ^ (0xFF>>dbpp); + memset(ones->data->bdata, 0xFF, ones->width*sizeof(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.x, r, img->clipr, (img->flags&Frepl) ? 1 : 0, vdata, labelpt); + mask = (1ULL<data->bdata); + Bprint(&b, "%-4d\t", y); +// for(x=r.min.x; x>nb)&mask); + } + Bprint(&b, "\n"); + } + Bterm(&b); +} + +/* + * Verify that the destination pixel has the specified value. + * The value is in the high bits of v, suitably masked, but must + * be extracted from the destination Memimage. + */ +void +checkone(Point p, Point sp, Point mp) +{ + int delta; + uchar *dp, *sdp; + + delta = (uchar*)byteaddr(dst, p)-(uchar*)dst->data->bdata; + dp = (uchar*)dst->data->bdata+delta; + sdp = (uchar*)savedstbits+delta; + + if(memcmp(dp, sdp, (dst->depth+7)/8) != 0) { + fprint(2, "dtest: one bad pixel drawing at dst %P from source %P mask %P\n", p, sp, mp); + fprint(2, " %.2ux %.2ux %.2ux %.2ux should be %.2ux %.2ux %.2ux %.2ux\n", + dp[0], dp[1], dp[2], dp[3], sdp[0], sdp[1], sdp[2], sdp[3]); + fprint(2, "addresses dst %p src %p mask %p\n", dp, byteaddr(src, sp), byteaddr(mask, mp)); + dumpimage("src", src, src->data->bdata, sp); + dumpimage("mask", mask, mask->data->bdata, mp); + dumpimage("origdst", dst, dstbits, p); + dumpimage("dst", dst, dst->data->bdata, p); + dumpimage("gooddst", dst, savedstbits, p); + abort(); + } +} + +/* + * Verify that the destination line has the same value as the saved line. + */ +#define RECTPTS(r) (r).min.x, (r).min.y, (r).max.x, (r).max.y +void +checkline(Rectangle r, Point sp, Point mp, int y, Memimage *stmp, Memimage *mtmp) +{ + 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, saved, dp); + fprint(2, "draw dst %R src %P mask %P\n", r, sp, mp); + dumpimage("src", src, src->data->bdata, sp); + if(stmp) dumpimage("stmp", stmp, stmp->data->bdata, sp); + dumpimage("mask", mask, mask->data->bdata, mp); + if(mtmp) dumpimage("mtmp", mtmp, mtmp->data->bdata, mp); + dumpimage("origdst", dst, dstbits, r.min); + dumpimage("dst", dst, dst->data->bdata, r.min); + dumpimage("gooddst", dst, savedstbits, r.min); + abort(); + } +} + +/* + * Fill the bits of an image with random data. + * The Memimage parameter is used only to make sure + * the data is well formatted: only ucbits is written. + */ +void +fill(Memimage *img, uchar *ucbits) +{ + int i, x, y; + ushort *up; + uchar alpha, r, g, b; + void *data; + + if((img->flags&Falpha) == 0){ + up = (ushort*)ucbits; + for(i=0; i> 7; + if(i+i != nbytes) + *(uchar*)up = lrand() >> 7; + }else{ + data = img->data->bdata; + img->data->bdata = ucbits; + + for(x=img->r.min.x; xr.max.x; x++) + for(y=img->r.min.y; yr.max.y; y++){ + alpha = rand() >> 4; + r = rand()%(alpha+1); + g = rand()%(alpha+1); + b = rand()%(alpha+1); + putpixel(img, Pt(x,y), rgbatopix(r,g,b,alpha)); + } + img->data->bdata = data; + } + +} + +/* + * Mask is preset; do the rest + */ +void +verifyonemask(void) +{ + Point dp, sp, mp; + + fill(dst, dstbits); + fill(src, srcbits); + memmove(dst->data->bdata, dstbits, dst->width*sizeof(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)*Yrange); + + 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, SoverD); + 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; idata->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; xdata->bdata, dst->width*sizeof(u32int)*Yrange); + + 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, nil, nil); +} + +void +verifyline(void) +{ + int i; + + /* mask all ones */ + memset(maskbits, 0xFF, nbytes); + for(i=0; idata->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; ydata->bdata, dst->width*sizeof(u32int)*Yrange); + + memmove(dst->data->bdata, dstbits, dst->width*sizeof(u32int)*Yrange); + + memimagedraw(dst, dr, src, sp, mask, mp, SoverD); + for(y=0; yr, sp), drawrepl(mask->r, mp), y, nil, nil); +} + +void +verifyrect(void) +{ + int i; + + /* mask all zeros */ + memset(maskbits, 0, nbytes); + for(i=0; ir) */ + r.min.x = nrand(Xrange-1); + r.min.y = nrand(Yrange-1); + /* make it trivial more often than pure chance allows */ + switch(lrand()&0){ + case 1: + r.max.x = r.min.x + 2; + r.max.y = r.min.y + 2; + if(r.max.x < Xrange && r.max.y < Yrange) + break; + /* fall through */ + case 0: + r.max.x = r.min.x + 1; + r.max.y = r.min.y + 1; + break; + default: + if(r.min.x+3 >= Xrange) + r.max.x = Xrange; + else + r.max.x = r.min.x+3 + nrand(Xrange-(r.min.x+3)); + + if(r.min.y+3 >= Yrange) + r.max.y = Yrange; + else + r.max.y = r.min.y+3 + nrand(Yrange-(r.min.y+3)); + } + assert(r.min.x >= 0); + assert(r.max.x <= Xrange); + assert(r.min.y >= 0); + assert(r.max.y <= Yrange); + /* copy from i to tmp so we have just the replicated bits */ + nb = tmp->width*sizeof(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(; xflags |= Frepl; + i->r = r; + i->clipr = randrect(); +// fprint(2, "replicate [[%d %d] [%d %d]] [[%d %d][%d %d]]\n", r.min.x, r.min.y, r.max.x, r.max.y, +// i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y); + tmp->clipr = i->clipr; +} + +/* + * Mask is preset; do the rest + */ +void +verifyrectmaskrepl(int srcrepl, int maskrepl) +{ + Point sp, mp, tp, up; + Rectangle dr; + int x, y; + Memimage *s, *m; + +// print("verfrect %d %d\n", srcrepl, maskrepl); + src->flags &= ~Frepl; + src->r = Rect(0, 0, Xrange, Yrange); + src->clipr = src->r; + stmp->flags &= ~Frepl; + stmp->r = Rect(0, 0, Xrange, Yrange); + stmp->clipr = src->r; + mask->flags &= ~Frepl; + mask->r = Rect(0, 0, Xrange, Yrange); + mask->clipr = mask->r; + mtmp->flags &= ~Frepl; + mtmp->r = Rect(0, 0, Xrange, Yrange); + mtmp->clipr = mask->r; + + fill(dst, dstbits); + fill(src, srcbits); + + memmove(dst->data->bdata, dstbits, dst->width*sizeof(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; ydata->bdata, dst->width*sizeof(u32int)*Yrange); + + 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; yr, sp), drawrepl(mask->r, mp), y, srcrepl?stmp:nil, maskrepl?mtmp:nil); +} + +void +verifyrectrepl(int srcrepl, int maskrepl) +{ + int i; + + /* mask all ones */ + memset(maskbits, 0xFF, nbytes); + for(i=0; i>= (nhave-nwant); + return v & ((1<>24; + *r = v>>16; + *g = v>>8; + *b = v; +} + +/* + * Convert uchar channels into 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 use the next bpp bits + * in the bottom byte of v. This madness is due to having big endian bits + * but little endian bytes. + */ + npack = 8/bpp; + v >>= 8 - bpp*(pt.x%npack+1); + v &= (1<chan; c; c>>=8){ + nbits = NBITS(c); + bits = v & ((1<>= nbits; + switch(TYPE(c)){ + case CRed: + r = rbits; + break; + case CGreen: + g = rbits; + break; + case CBlue: + b = rbits; + break; + case CGrey: + r = g = b = rbits; + break; + case CAlpha: + a = rbits; + break; + case CMap: + p = img->cmap->cmap2rgb + 3*bits; + r = p[0]; + g = p[1]; + b = p[2]; + break; + case CIgnore: + break; + default: + fprint(2, "unknown channel type %lud\n", TYPE(c)); + abort(); + } + } + } + return rgbatopix(r, g, b, a); +} + +/* + * Return the greyscale equivalent of a pixel. + */ +uchar +getgrey(Memimage *img, Point pt) +{ + uchar r, g, b, a; + pixtorgba(getpixel(img, pt), &r, &g, &b, &a); + return RGB2K(r, g, b); +} + +/* + * Return the value at pt in image, if image is interpreted + * as a mask. This means the alpha channel if present, else + * the greyscale or its computed equivalent. + */ +uchar +getmask(Memimage *img, Point pt) +{ + if(img->flags&Falpha) + return getpixel(img, pt)>>24; + else + return getgrey(img, pt); +} +#undef DBG + +#define DBG if(0) +/* + * Write a pixel to img at point pt. + * + * We do this by reading a 32-bit little endian + * value from p and then writing it back + * after tweaking the appropriate bits. Because + * the data is little endian, we don't have to worry + * about what the actual depth is, as long as it is + * less than 32 bits. + */ +void +putpixel(Memimage *img, Point pt, 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.x%npack pixels, + * which is equivalent to skipping the rightmost npack - pt.x%npack - 1 pixels. + */ + npack = 8/bpp; + sh = bpp*(npack - pt.x%npack - 1); + bits = RGB2K(r,g,b); +DBG print("repl %lux 8 %d = %lux...", bits, bpp, replbits(bits, 8, bpp)); + bits = replbits(bits, 8, bpp); + mask = (1<chan; c; c>>=8){ + nbits = NBITS(c); + switch(TYPE(c)){ + case CRed: + bits = r; + break; + case CGreen: + bits = g; + break; + case CBlue: + bits = b; + break; + case CGrey: + bits = RGB2K(r, g, b); + break; + case CAlpha: + bits = a; + break; + case CIgnore: + bits = 0; + break; + case CMap: + q = img->cmap->rgb2cmap; + bits = q[(r>>4)*16*16+(g>>4)*16+(b>>4)]; + break; + default: + SET(bits); + fprint(2, "unknown channel type %lud\n", TYPE(c)); + abort(); + } + +DBG print("repl %lux 8 %d = %lux...", bits, nbits, replbits(bits, 8, nbits)); + if(TYPE(c) != CMap) + bits = replbits(bits, 8, nbits); + mask = (1<>8; + p[2] = v>>16; + p[3] = v>>24; +} +#undef DBG + +#define DBG if(0) +void +drawonepixel(Memimage *dst, Point dp, Memimage *src, Point sp, Memimage *mask, Point mp) +{ + uchar m, M, sr, sg, sb, sa, sk, dr, dg, db, da, dk; + + pixtorgba(getpixel(dst, dp), &dr, &dg, &db, &da); + pixtorgba(getpixel(src, sp), &sr, &sg, &sb, &sa); + m = getmask(mask, mp); + M = 255-(sa*m)/255; + +DBG print("dst %x %x %x %x src %x %x %x %x m %x = ", dr,dg,db,da, sr,sg,sb,sa, m); + if(dst->flags&Fgrey){ + /* + * We need to do the conversion to grey before the alpha calculation + * because the draw operator does this, and we need to be operating + * at the same precision so we get exactly the same answers. + */ + sk = RGB2K(sr, sg, sb); + dk = RGB2K(dr, dg, db); + dk = (sk*m + dk*M)/255; + dr = dg = db = dk; + da = (sa*m + da*M)/255; + }else{ + /* + * True color alpha calculation treats all channels (including alpha) + * the same. It might have been nice to use an array, but oh well. + */ + dr = (sr*m + dr*M)/255; + dg = (sg*m + dg*M)/255; + db = (sb*m + db*M)/255; + da = (sa*m + da*M)/255; + } + +DBG print("%x %x %x %x\n", dr,dg,db,da); + putpixel(dst, dp, rgbatopix(dr, dg, db, da)); +} blob - /dev/null blob + 7dc45cd6fee3dab9ea799c8b0dcdb62422018e20 (mode 644) --- /dev/null +++ src/libdraw/md-ellipse.c @@ -0,0 +1,247 @@ +#include +#include +#include +#include + +/* + * ellipse(dst, c, a, b, t, src, sp) + * draws an ellipse centered at c with semiaxes a,b>=0 + * and semithickness t>=0, or filled if t<0. point sp + * in src maps to c in dst + * + * very thick skinny ellipses are brushed with circles (slow) + * others are approximated by filling between 2 ellipses + * criterion for very thick when b 0.5*x/(1-x) + * where x = b/a + */ + +typedef struct Param Param; +typedef struct State State; + +static void bellipse(int, State*, Param*); +static void erect(int, int, int, int, Param*); +static void eline(int, int, int, int, Param*); + +struct Param { + Memimage *dst; + Memimage *src; + Point c; + int t; + Point sp; + Memimage *disc; + int op; +}; + +/* + * denote residual error by e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2 + * e(x,y) = 0 on ellipse, e(x,y) < 0 inside, e(x,y) > 0 outside + */ + +struct State { + int a; + int x; + vlong a2; /* a^2 */ + vlong b2; /* b^2 */ + vlong b2x; /* b^2 * x */ + vlong a2y; /* a^2 * y */ + vlong c1; + vlong c2; /* test criteria */ + vlong ee; /* ee = e(x+1/2,y-1/2) - (a^2+b^2)/4 */ + vlong dxe; + vlong dye; + vlong d2xe; + vlong d2ye; +}; + +static +State* +newstate(State *s, int a, int b) +{ + s->x = 0; + s->a = a; + s->a2 = (vlong)(a*a); + s->b2 = (vlong)(b*b); + s->b2x = (vlong)0; + s->a2y = s->a2*(vlong)b; + s->c1 = -((s->a2>>2) + (vlong)(a&1) + s->b2); + s->c2 = -((s->b2>>2) + (vlong)(b&1)); + s->ee = -s->a2y; + s->dxe = (vlong)0; + s->dye = s->ee<<1; + s->d2xe = s->b2<<1; + s->d2ye = s->a2<<1; + return s; +} + +/* + * return x coord of rightmost pixel on next scan line + */ +static +int +step(State *s) +{ + while(s->x < s->a) { + if(s->ee+s->b2x <= s->c1 || /* e(x+1,y-1/2) <= 0 */ + s->ee+s->a2y <= s->c2) { /* e(x+1/2,y) <= 0 (rare) */ + s->dxe += s->d2xe; + s->ee += s->dxe; + s->b2x += s->b2; + s->x++; + continue; + } + s->dye += s->d2ye; + s->ee += s->dye; + s->a2y -= s->a2; + if(s->ee-s->a2y <= s->c2) { /* e(x+1/2,y-1) <= 0 */ + s->dxe += s->d2xe; + s->ee += s->dxe; + s->b2x += s->b2; + return s->x++; + } + break; + } + return s->x; +} + +void +memellipse(Memimage *dst, Point c, int a, int b, int t, Memimage *src, Point sp, int op) +{ + State in, out; + int y, inb, inx, outx, u; + Param p; + + if(a < 0) + a = -a; + if(b < 0) + b = -b; + p.dst = dst; + p.src = src; + p.c = c; + p.t = t; + p.sp = subpt(sp, c); + p.disc = nil; + p.op = op; + + u = (t<<1)*(a-b); + if(bb*b || aa*a) { +/* if(bb*b/a || aa*a/b) # very thick */ + bellipse(b, newstate(&in, a, b), &p); + return; + } + + if(t < 0) { + inb = -1; + newstate(&out, a, y = b); + } else { + inb = b - t; + newstate(&out, a+t, y = b+t); + } + if(t > 0) + newstate(&in, a-t, inb); + inx = 0; + for( ; y>=0; y--) { + outx = step(&out); + if(y > inb) { + erect(-outx, y, outx, y, &p); + if(y != 0) + erect(-outx, -y, outx, -y, &p); + continue; + } + if(t > 0) { + inx = step(&in); + if(y == inb) + inx = 0; + } else if(inx > outx) + inx = outx; + erect(inx, y, outx, y, &p); + if(y != 0) + erect(inx, -y, outx, -y, &p); + erect(-outx, y, -inx, y, &p); + if(y != 0) + erect(-outx, -y, -inx, -y, &p); + inx = outx + 1; + } +} + +static Point p00 = {0, 0}; + +/* + * a brushed ellipse + */ +static +void +bellipse(int y, State *s, Param *p) +{ + int t, ox, oy, x, nx; + + t = p->t; + p->disc = allocmemimage(Rect(-t,-t,t+1,t+1), GREY1); + if(p->disc == nil) + return; + memfillcolor(p->disc, DTransparent); + memellipse(p->disc, p00, t, t, -1, memopaque, p00, p->op); + oy = y; + ox = 0; + nx = x = step(s); + do { + while(nx==x && y-->0) + nx = step(s); + y++; + eline(-x,-oy,-ox, -y, p); + eline(ox,-oy, x, -y, p); + eline(-x, y,-ox, oy, p); + eline(ox, y, x, oy, p); + ox = x+1; + x = nx; + y--; + oy = y; + } while(oy > 0); +} + +/* + * a rectangle with closed (not half-open) coordinates expressed + * relative to the center of the ellipse + */ +static +void +erect(int x0, int y0, int x1, int y1, Param *p) +{ + Rectangle r; + +/* print("R %d,%d %d,%d\n", x0, y0, x1, y1); */ + r = Rect(p->c.x+x0, p->c.y+y0, p->c.x+x1+1, p->c.y+y1+1); + memdraw(p->dst, r, p->src, addpt(p->sp, r.min), memopaque, p00, p->op); +} + +/* + * a brushed point similarly specified + */ +static +void +epoint(int x, int y, Param *p) +{ + Point p0; + Rectangle r; + +/* print("P%d %d,%d\n", p->t, x, y); */ + p0 = Pt(p->c.x+x, p->c.y+y); + r = Rpt(addpt(p0, p->disc->r.min), addpt(p0, p->disc->r.max)); + memdraw(p->dst, r, p->src, addpt(p->sp, r.min), p->disc, p->disc->r.min, p->op); +} + +/* + * a brushed horizontal or vertical line similarly specified + */ +static +void +eline(int x0, int y0, int x1, int y1, Param *p) +{ +/* print("L%d %d,%d %d,%d\n", p->t, x0, y0, x1, y1); */ + if(x1 > x0+1) + erect(x0+1, y0-p->t, x1-1, y1+p->t, p); + else if(y1 > y0+1) + erect(x0-p->t, y0+1, x1+p->t, y1-1, p); + epoint(x0, y0, p); + if(x1-x0 || y1-y0) + epoint(x1, y1, p); +} blob - /dev/null blob + 928ae1e196c163e079213643514072a5c617c8db (mode 644) --- /dev/null +++ src/libdraw/md-fillpoly.c @@ -0,0 +1,524 @@ +#include +#include +#include +#include + +typedef struct Seg Seg; + +struct Seg +{ + Point p0; + Point p1; + long num; + long den; + long dz; + long dzrem; + long z; + long zerr; + long d; +}; + +static void zsort(Seg **seg, Seg **ep); +static int ycompare(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 nseg, int wind, Memimage *src, Point sp, int, int, int, int); +static void yscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src, Point sp, int, int); + +#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, int op) +{ + Rectangle r; + + r.min.x = left; + r.min.y = y; + r.max.x = right; + r.max.y = y+1; + p.x += left; + p.y += y; + memdraw(dst, r, src, p, memopaque, p, op); +} + +static void +fillpoint(Memimage *dst, int x, int y, Memimage *src, Point p, int op) +{ + Rectangle r; + + r.min.x = x; + r.min.y = y; + r.max.x = x+1; + r.max.y = y+1; + p.x += x; + p.y += y; + memdraw(dst, r, src, p, memopaque, p, op); +} + +void +memfillpoly(Memimage *dst, Point *vert, int nvert, int w, Memimage *src, Point sp, int op) +{ + _memfillpolysc(dst, vert, nvert, w, src, sp, 0, 0, 0, op); +} + +void +_memfillpolysc(Memimage *dst, Point *vert, int nvert, int w, Memimage *src, Point sp, int detail, int fixshift, int clipped, int op) +{ + Seg **seg, *segtab; + Point p0; + int i; + + if(nvert == 0) + return; + + seg = malloc((nvert+2)*sizeof(Seg*)); + if(seg == nil) + return; + segtab = malloc((nvert+1)*sizeof(Seg)); + if(segtab == nil) { + free(seg); + return; + } + + sp.x = (sp.x - vert[0].x) >> fixshift; + sp.y = (sp.y - vert[0].y) >> fixshift; + p0 = vert[nvert-1]; + if(!fixshift) { + p0.x <<= 1; + p0.y <<= 1; + } + for(i = 0; i < nvert; i++) { + segtab[i].p0 = p0; + p0 = vert[i]; + if(!fixshift) { + p0.x <<= 1; + p0.y <<= 1; + } + segtab[i].p1 = p0; + segtab[i].d = 1; + } + if(!fixshift) + fixshift = 1; + + xscan(dst, seg, segtab, nvert, w, src, sp, detail, fixshift, clipped, op); + if(detail) + yscan(dst, seg, segtab, nvert, w, src, sp, fixshift, op); + + free(seg); + free(segtab); +} + +static long +mod(long x, long y) +{ + long z; + + z = x%y; + if((long)(((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, Point sp, int detail, int fixshift, int clipped, int op) +{ + long y, maxy, x, x2, xerr, xden, onehalf; + Seg **ep, **next, **p, **q, *s; + long n, i, iy, cnt, ix, ix2, minx, maxx; + Point pt; + void (*fill)(Memimage*, int, int, int, Memimage*, Point, int); + + fill = fillline; +/* + * This can only work on 8-bit destinations, since fillcolor is + * just using memset on sp.x. + * + * I'd rather not even enable it then, since then if the general + * code is too slow, someone will come up with a better improvement + * than this sleazy hack. -rsc + * + if(clipped && (src->flags&Frepl) && src->depth==8 && Dx(src->r)==1 && Dy(src->r)==1) { + fill = fillcolor; + sp.x = membyteval(src); + } + * + */ + USED(clipped); + + + for(i=0, s=segtab, p=seg; ip0.y == s->p1.y) + continue; + if(s->p0.y > s->p1.y) { + pt = s->p0; + s->p0 = s->p1; + s->p1 = pt; + s->d = -s->d; + } + s->num = s->p1.x - s->p0.x; + s->den = s->p1.y - s->p0.y; + s->dz = sdiv(s->num, s->den) << fixshift; + s->dzrem = mod(s->num, s->den) << fixshift; + s->dz += sdiv(s->dzrem, s->den); + s->dzrem = mod(s->dzrem, s->den); + p++; + } + n = p-seg; + if(n == 0) + return; + *p = 0; + qsort(seg, p-seg , sizeof(Seg*), ycompare); + + onehalf = 0; + if(fixshift) + onehalf = 1 << (fixshift-1); + + minx = dst->clipr.min.x; + maxx = dst->clipr.max.x; + + y = seg[0]->p0.y; + if(y < (dst->clipr.min.y << fixshift)) + y = dst->clipr.min.y << fixshift; + iy = (y + onehalf) >> fixshift; + y = (iy << fixshift) + onehalf; + maxy = dst->clipr.max.y << fixshift; + + ep = next = seg; + + while(yp1.y < y) + continue; + s->z += s->dz; + s->zerr += s->dzrem; + if(s->zerr >= s->den) { + s->z++; + s->zerr -= s->den; + if(s->zerr < 0 || s->zerr >= s->den) + print("bad ratzerr1: %ld den %ld dzrem %ld\n", s->zerr, s->den, s->dzrem); + } + *q++ = s; + } + + for(p = next; *p; p++) { + s = *p; + if(s->p0.y >= y) + break; + if(s->p1.y < y) + continue; + s->z = s->p0.x; + s->z += smuldivmod(y - s->p0.y, s->num, s->den, &s->zerr); + if(s->zerr < 0 || s->zerr >= s->den) + print("bad ratzerr2: %ld den %ld ratdzrem %ld\n", s->zerr, s->den, s->dzrem); + *q++ = s; + } + ep = q; + next = p; + + if(ep == seg) { + if(*next == 0) + break; + iy = (next[0]->p0.y + onehalf) >> fixshift; + y = (iy << fixshift) + onehalf; + continue; + } + + zsort(seg, ep); + + for(p = seg; p < ep; p++) { + cnt = 0; + x = p[0]->z; + xerr = p[0]->zerr; + xden = p[0]->den; + ix = (x + onehalf) >> fixshift; + if(ix >= maxx) + break; + if(ix < minx) + ix = minx; + cnt += p[0]->d; + p++; + for(;;) { + if(p == ep) { + print("xscan: fill to infinity"); + return; + } + cnt += p[0]->d; + if((cnt&wind) == 0) + break; + p++; + } + x2 = p[0]->z; + ix2 = (x2 + onehalf) >> fixshift; + if(ix2 <= minx) + continue; + if(ix2 > maxx) + ix2 = maxx; + if(ix == ix2 && detail) { + if(xerr*p[0]->den + p[0]->zerr*xden > p[0]->den*xden) + x++; + ix = (x + x2) >> (fixshift+1); + ix2 = ix+1; + } + (*fill)(dst, ix, ix2, iy, src, sp, op); + } + y += (1<p0.x == s->p1.x) + continue; + if(s->p0.x > s->p1.x) { + pt = s->p0; + s->p0 = s->p1; + s->p1 = pt; + s->d = -s->d; + } + s->num = s->p1.y - s->p0.y; + s->den = s->p1.x - s->p0.x; + s->dz = sdiv(s->num, s->den) << fixshift; + s->dzrem = mod(s->num, s->den) << fixshift; + s->dz += sdiv(s->dzrem, s->den); + s->dzrem = mod(s->dzrem, s->den); + p++; + } + n = p-seg; + if(n == 0) + return; + *p = 0; + qsort(seg, n , sizeof(Seg*), xcompare); + + onehalf = 0; + if(fixshift) + onehalf = 1 << (fixshift-1); + + miny = dst->clipr.min.y; + maxy = dst->clipr.max.y; + + x = seg[0]->p0.x; + if(x < (dst->clipr.min.x << fixshift)) + x = dst->clipr.min.x << fixshift; + ix = (x + onehalf) >> fixshift; + x = (ix << fixshift) + onehalf; + maxx = dst->clipr.max.x << fixshift; + + ep = next = seg; + + while(xp1.x < x) + continue; + s->z += s->dz; + s->zerr += s->dzrem; + if(s->zerr >= s->den) { + s->z++; + s->zerr -= s->den; + if(s->zerr < 0 || s->zerr >= s->den) + print("bad ratzerr1: %ld den %ld ratdzrem %ld\n", s->zerr, s->den, s->dzrem); + } + *q++ = s; + } + + for(p = next; *p; p++) { + s = *p; + if(s->p0.x >= x) + break; + if(s->p1.x < x) + continue; + s->z = s->p0.y; + s->z += smuldivmod(x - s->p0.x, s->num, s->den, &s->zerr); + if(s->zerr < 0 || s->zerr >= s->den) + print("bad ratzerr2: %ld den %ld ratdzrem %ld\n", s->zerr, s->den, s->dzrem); + *q++ = s; + } + ep = q; + next = p; + + if(ep == seg) { + if(*next == 0) + break; + ix = (next[0]->p0.x + onehalf) >> fixshift; + x = (ix << fixshift) + onehalf; + continue; + } + + zsort(seg, ep); + + for(p = seg; p < ep; p++) { + cnt = 0; + y = p[0]->z; + yerr = p[0]->zerr; + yden = p[0]->den; + iy = (y + onehalf) >> fixshift; + if(iy >= maxy) + break; + if(iy < miny) + iy = miny; + cnt += p[0]->d; + p++; + for(;;) { + if(p == ep) { + print("yscan: fill to infinity"); + return; + } + cnt += p[0]->d; + if((cnt&wind) == 0) + break; + p++; + } + y2 = p[0]->z; + iy2 = (y2 + onehalf) >> fixshift; + if(iy2 <= miny) + continue; + if(iy2 > maxy) + iy2 = maxy; + if(iy == iy2) { + if(yerr*p[0]->den + p[0]->zerr*yden > p[0]->den*yden) + y++; + iy = (y + y2) >> (fixshift+1); + fillpoint(dst, ix, iy, src, sp, op); + } + } + x += (1<z > p[1]->z) { + s = p[0]; + p[0] = p[1]; + p[1] = s; + done = 0; + } + } + } while(!done); + } else { + q = ep-1; + for(p = seg; p < q; p++) { + if(p[0]->z > p[1]->z) { + qsort(seg, ep-seg, sizeof(Seg*), zcompare); + break; + } + } + } +} + +static int +ycompare(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; +} blob - /dev/null blob + 3f36250f22ec43c8bf6cb3d064bc2cc8e01333e5 (mode 644) --- /dev/null +++ src/libdraw/md-hwdraw.c @@ -0,0 +1,12 @@ +#include +#include +#include +#include + +int +hwdraw(Memdrawparam *p) +{ + USED(p); + return 0; /* could not satisfy request */ +} + blob - /dev/null blob + 923b6b448c8e075b6c258e5777eb21a24de21cdd (mode 644) --- /dev/null +++ src/libdraw/md-iprint.c @@ -0,0 +1,12 @@ +#include +#include +#include +#include + +int +iprint(char *fmt,...) +{ + USED(fmt); + return -1; +} + blob - /dev/null blob + 632e82385919aeab2525903b0cb38df0591f09cd (mode 644) --- /dev/null +++ src/libdraw/md-line.c @@ -0,0 +1,484 @@ +#include +#include +#include +#include + +enum +{ + Arrow1 = 8, + Arrow2 = 10, + Arrow3 = 3, +}; + +static +int +lmin(int a, int b) +{ + if(a < b) + return a; + return b; +} + +static +int +lmax(int a, int b) +{ + if(a > b) + return a; + return b; +} + +#ifdef NOTUSED +/* + * Rather than line clip, we run the Bresenham loop over the full line, + * and clip on each pixel. This is more expensive but means that + * lines look the same regardless of how the windowing has tiled them. + * For speed, we check for clipping outside the loop and make the + * test easy when possible. + */ + +static +void +horline1(Memimage *dst, Point p0, Point p1, int srcval, Rectangle clipr) +{ + int x, y, dy, deltay, deltax, maxx; + int dd, easy, e, bpp, m, m0; + uchar *d; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + dd = dst->width*sizeof(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 0){ + y += dy; + d += dd; + e += deltax; + }else + e += deltay; + d++; + m >>= bpp; + if(m == 0) + m = m0; + } +} + +static +void +verline1(Memimage *dst, Point p0, Point p1, int srcval, Rectangle clipr) +{ + int x, y, deltay, deltax, maxy; + int easy, e, bpp, m, m0, dd; + uchar *d; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + dd = 1; + if(deltax < 0){ + dd = -1; + deltax = -deltax; + } + maxy = lmin(p1.y, clipr.max.y-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + m = m0 >> (p0.x&(7/dst->depth))*bpp; + easy = ptinrect(p0, clipr) && ptinrect(p1, clipr); + e = 2*deltax - deltay; + x = p0.x; + d = byteaddr(dst, p0); + deltax *= 2; + deltay = deltax - 2*deltay; + for(y=p0.y; y<=maxy; y++){ + if(easy || (clipr.min.y<=y && clipr.min.x<=x && x 0){ + x += dd; + d += dd; + e += deltay; + }else + e += deltax; + d += dst->width*sizeof(u32int); + m >>= bpp; + if(m == 0) + m = m0; + } +} + +static +void +horliner(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangle clipr) +{ + int x, y, sx, sy, deltay, deltax, minx, maxx; + int bpp, m, m0; + uchar *d, *s; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + sx = drawreplxy(src->r.min.x, src->r.max.x, p0.x+dsrc.x); + minx = lmax(p0.x, clipr.min.x); + maxx = lmin(p1.x, clipr.max.x-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + m = m0 >> (minx&(7/dst->depth))*bpp; + for(x=minx; x<=maxx; x++){ + y = p0.y + (deltay*(x-p0.x)+deltax/2)/deltax; + if(clipr.min.y<=y && yr.min.y, src->r.max.y, y+dsrc.y); + s = byteaddr(src, Pt(sx, sy)); + *d ^= (*d^*s) & m; + } + if(++sx >= src->r.max.x) + sx = src->r.min.x; + m >>= bpp; + if(m == 0) + m = m0; + } +} + +static +void +verliner(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangle clipr) +{ + int x, y, sx, sy, deltay, deltax, miny, maxy; + int bpp, m, m0; + uchar *d, *s; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + sy = drawreplxy(src->r.min.y, src->r.max.y, p0.y+dsrc.y); + miny = lmax(p0.y, clipr.min.y); + maxy = lmin(p1.y, clipr.max.y-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + for(y=miny; y<=maxy; y++){ + if(deltay == 0) /* degenerate line */ + x = p0.x; + else + x = p0.x + (deltax*(y-p0.y)+deltay/2)/deltay; + if(clipr.min.x<=x && x> (x&(7/dst->depth))*bpp; + d = byteaddr(dst, Pt(x, y)); + sx = drawreplxy(src->r.min.x, src->r.max.x, x+dsrc.x); + s = byteaddr(src, Pt(sx, sy)); + *d ^= (*d^*s) & m; + } + if(++sy >= src->r.max.y) + sy = src->r.min.y; + } +} + +static +void +horline(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangle clipr) +{ + int x, y, deltay, deltax, minx, maxx; + int bpp, m, m0; + uchar *d, *s; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + minx = lmax(p0.x, clipr.min.x); + maxx = lmin(p1.x, clipr.max.x-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + m = m0 >> (minx&(7/dst->depth))*bpp; + for(x=minx; x<=maxx; x++){ + y = p0.y + (deltay*(x-p0.x)+deltay/2)/deltax; + if(clipr.min.y<=y && y>= bpp; + if(m == 0) + m = m0; + } +} + +static +void +verline(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangle clipr) +{ + int x, y, deltay, deltax, miny, maxy; + int bpp, m, m0; + uchar *d, *s; + + deltax = p1.x - p0.x; + deltay = p1.y - p0.y; + miny = lmax(p0.y, clipr.min.y); + maxy = lmin(p1.y, clipr.max.y-1); + bpp = dst->depth; + m0 = 0xFF^(0xFF>>bpp); + for(y=miny; y<=maxy; y++){ + if(deltay == 0) /* degenerate line */ + x = p0.x; + else + x = p0.x + deltax*(y-p0.y)/deltay; + if(clipr.min.x<=x && x> (x&(7/dst->depth))*bpp; + d = byteaddr(dst, Pt(x, y)); + s = byteaddr(src, addpt(dsrc, Pt(x, y))); + *d ^= (*d^*s) & m; + } + } +} +#endif /* NOTUSED */ + +static Memimage* +membrush(int radius) +{ + static Memimage *brush; + static int brushradius; + + if(brush==nil || brushradius!=radius){ + freememimage(brush); + brush = allocmemimage(Rect(0, 0, 2*radius+1, 2*radius+1), memopaque->chan); + if(brush != nil){ + memfillcolor(brush, DTransparent); /* zeros */ + memellipse(brush, Pt(radius, radius), radius, radius, -1, memopaque, Pt(radius, radius), S); + } + brushradius = radius; + } + return brush; +} + +static +void +discend(Point p, int radius, Memimage *dst, Memimage *src, Point dsrc, int op) +{ + Memimage *disc; + Rectangle r; + + disc = membrush(radius); + if(disc != nil){ + r.min.x = p.x - radius; + r.min.y = p.y - radius; + r.max.x = p.x + radius+1; + r.max.y = p.y + radius+1; + memdraw(dst, r, src, addpt(r.min, dsrc), disc, Pt(0,0), op); + } +} + +static +void +arrowend(Point tip, Point *pp, int end, int sin, int cos, int radius) +{ + int x1, x2, x3; + + /* before rotation */ + if(end == Endarrow){ + x1 = Arrow1; + x2 = Arrow2; + x3 = Arrow3; + }else{ + x1 = (end>>5) & 0x1FF; /* distance along line from end of line to tip */ + x2 = (end>>14) & 0x1FF; /* distance along line from barb to tip */ + x3 = (end>>23) & 0x1FF; /* distance perpendicular from edge of line to barb */ + } + + /* comments follow track of right-facing arrowhead */ + pp->x = tip.x+((2*radius+1)*sin/2-x1*cos); /* upper side of shaft */ + pp->y = tip.y-((2*radius+1)*cos/2+x1*sin); + pp++; + pp->x = tip.x+((2*radius+2*x3+1)*sin/2-x2*cos); /* upper barb */ + pp->y = tip.y-((2*radius+2*x3+1)*cos/2+x2*sin); + pp++; + pp->x = tip.x; + pp->y = tip.y; + pp++; + pp->x = tip.x+(-(2*radius+2*x3+1)*sin/2-x2*cos); /* lower barb */ + pp->y = tip.y-(-(2*radius+2*x3+1)*cos/2+x2*sin); + pp++; + pp->x = tip.x+(-(2*radius+1)*sin/2-x1*cos); /* lower side of shaft */ + pp->y = tip.y+((2*radius+1)*cos/2-x1*sin); +} + +void +_memimageline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, Rectangle clipr, int op) +{ + /* + * BUG: We should really really pick off purely horizontal and purely + * vertical lines and handle them separately with calls to memimagedraw + * on rectangles. + */ + + int hor; + int sin, cos, dx, dy, t; + Rectangle oclipr, r; + Point q, pts[10], *pp, d; + + if(radius < 0) + return; + if(rectclip(&clipr, dst->r) == 0) + return; + if(rectclip(&clipr, dst->clipr) == 0) + return; + d = subpt(sp, p0); + if(rectclip(&clipr, rectsubpt(src->clipr, d)) == 0) + return; + if((src->flags&Frepl)==0 && rectclip(&clipr, rectsubpt(src->r, d))==0) + return; + /* this means that only verline() handles degenerate lines (p0==p1) */ + hor = (abs(p1.x-p0.x) > abs(p1.y-p0.y)); + /* + * Clipping is a little peculiar. We can't use Sutherland-Cohen + * clipping because lines are wide. But this is probably just fine: + * we do all math with the original p0 and p1, but clip when deciding + * what pixels to draw. This means the layer code can call this routine, + * using clipr to define the region being written, and get the same set + * of pixels regardless of the dicing. + */ + if((hor && p0.x>p1.x) || (!hor && p0.y>p1.y)){ + q = p0; + p0 = p1; + p1 = q; + t = end0; + end0 = end1; + end1 = t; + } + + if((p0.x == p1.x || p0.y == p1.y) && (end0&0x1F) == Endsquare && (end1&0x1F) == Endsquare){ + r.min = p0; + r.max = p1; + if(p0.x == p1.x){ + r.min.x -= radius; + r.max.x += radius+1; + } + else{ + r.min.y -= radius; + r.max.y += radius+1; + } + oclipr = dst->clipr; + dst->clipr = clipr; + memimagedraw(dst, r, src, sp, memopaque, sp, op); + dst->clipr = oclipr; + return; + } + +/* Hard: */ + /* draw thick line using polygon fill */ + icossin2(p1.x-p0.x, p1.y-p0.y, &cos, &sin); + dx = (sin*(2*radius+1))/2; + dy = (cos*(2*radius+1))/2; + pp = pts; + oclipr = dst->clipr; + dst->clipr = clipr; + q.x = ICOSSCALE*p0.x+ICOSSCALE/2-cos/2; + q.y = ICOSSCALE*p0.y+ICOSSCALE/2-sin/2; + switch(end0 & 0x1F){ + case Enddisc: + discend(p0, radius, dst, src, d, op); + /* fall through */ + case Endsquare: + default: + pp->x = q.x-dx; + pp->y = q.y+dy; + pp++; + pp->x = q.x+dx; + pp->y = q.y-dy; + pp++; + break; + case Endarrow: + arrowend(q, pp, end0, -sin, -cos, radius); + _memfillpolysc(dst, pts, 5, ~0, src, addpt(pts[0], mulpt(d, ICOSSCALE)), 1, 10, 1, op); + pp[1] = pp[4]; + pp += 2; + } + q.x = ICOSSCALE*p1.x+ICOSSCALE/2+cos/2; + q.y = ICOSSCALE*p1.y+ICOSSCALE/2+sin/2; + switch(end1 & 0x1F){ + case Enddisc: + discend(p1, radius, dst, src, d, op); + /* fall through */ + case Endsquare: + default: + pp->x = q.x+dx; + pp->y = q.y-dy; + pp++; + pp->x = q.x-dx; + pp->y = q.y+dy; + pp++; + break; + case Endarrow: + arrowend(q, pp, end1, sin, cos, radius); + _memfillpolysc(dst, pp, 5, ~0, src, addpt(pts[0], mulpt(d, ICOSSCALE)), 1, 10, 1, op); + pp[1] = pp[4]; + pp += 2; + } + _memfillpolysc(dst, pts, pp-pts, ~0, src, addpt(pts[0], mulpt(d, ICOSSCALE)), 0, 10, 1, op); + dst->clipr = oclipr; + return; +} + +void +memimageline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, int op) +{ + _memimageline(dst, p0, p1, end0, end1, radius, src, sp, dst->clipr, op); +} + +/* + * Simple-minded conservative code to compute bounding box of line. + * Result is probably a little larger than it needs to be. + */ +static +void +addbbox(Rectangle *r, Point p) +{ + if(r->min.x > p.x) + r->min.x = p.x; + if(r->min.y > p.y) + r->min.y = p.y; + if(r->max.x < p.x+1) + r->max.x = p.x+1; + if(r->max.y < p.y+1) + r->max.y = p.y+1; +} + +int +memlineendsize(int end) +{ + int x3; + + if((end&0x3F) != Endarrow) + return 0; + if(end == Endarrow) + x3 = Arrow3; + else + x3 = (end>>23) & 0x1FF; + return x3; +} + +Rectangle +memlinebbox(Point p0, Point p1, int end0, int end1, int radius) +{ + Rectangle r, r1; + int extra; + + r.min.x = 10000000; + r.min.y = 10000000; + r.max.x = -10000000; + r.max.y = -10000000; + extra = lmax(memlineendsize(end0), memlineendsize(end1)); + r1 = insetrect(canonrect(Rpt(p0, p1)), -(radius+extra)); + addbbox(&r, r1.min); + addbbox(&r, r1.max); + return r; +} blob - /dev/null blob + 6788fa90b3a78b55526c106beac8620ff9269e7b (mode 644) --- /dev/null +++ src/libdraw/md-load.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include + +int +_loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) +{ + int y, l, lpart, rpart, mx, m, mr; + uchar *q; + + if(!rectinrect(r, i->r)) + return -1; + l = bytesperline(r, i->depth); + if(ndata < l*Dy(r)) + return -1; + ndata = l*Dy(r); + q = byteaddr(i, r.min); + mx = 7/i->depth; + lpart = (r.min.x & mx) * i->depth; + rpart = (r.max.x & mx) * i->depth; + m = 0xFF >> lpart; + /* may need to do bit insertion on edges */ + if(l == 1){ /* all in one byte */ + if(rpart) + m ^= 0xFF >> rpart; + for(y=r.min.y; ywidth*sizeof(u32int); + data++; + } + return ndata; + } + if(lpart==0 && rpart==0){ /* easy case */ + for(y=r.min.y; ywidth*sizeof(u32int); + data += l; + } + return ndata; + } + mr = 0xFF ^ (0xFF >> rpart); + if(lpart!=0 && rpart==0){ + for(y=r.min.y; y 1) + memmove(q+1, data+1, l-1); + q += i->width*sizeof(u32int); + data += l; + } + return ndata; + } + if(lpart==0 && rpart!=0){ + for(y=r.min.y; y 1) + memmove(q, data, l-1); + q[l-1] ^= (data[l-1]^q[l-1]) & mr; + q += i->width*sizeof(u32int); + data += l; + } + return ndata; + } + for(y=r.min.y; y 2) + memmove(q+1, data+1, l-2); + q[l-1] ^= (data[l-1]^q[l-1]) & mr; + q += i->width*sizeof(u32int); + data += l; + } + return ndata; +} blob - /dev/null blob + e8d5efc394486da4f81a362839441ed54c1f5030 (mode 644) --- /dev/null +++ src/libdraw/md-mkcmap.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include + +/* +struct Memcmap +{ + uchar cmap2rgb[3*256]; + uchar rgb2cmap[16*16*16]; +}; +*/ + +static Memcmap* +mkcmap(void) +{ + static Memcmap def; + + int i, rgb, r, g, b; + + for(i=0; i<256; i++){ + rgb = cmap2rgb(i); + r = (rgb>>16)&0xff; + g = (rgb>>8)&0xff; + b = rgb&0xff; + def.cmap2rgb[3*i] = r; + def.cmap2rgb[3*i+1] = g; + def.cmap2rgb[3*i+2] = b; + } + + for(r=0; r<16; r++) + for(g=0; g<16; g++) + for(b=0; b<16; b++) + def.rgb2cmap[r*16*16+g*16+b] = rgb2cmap(r*0x11, g*0x11, b*0x11); + return &def; +} + +void +main(int argc, char **argv) +{ + Memcmap *c; + int i, j, inferno; + + inferno = 0; + ARGBEGIN{ + case 'i': + inferno = 1; + }ARGEND + + memimageinit(); + c = mkcmap(); + if(!inferno) + print("#include \n#include \n"); + else + print("#include \"lib9.h\"\n"); + print("#include \n"); + print("#include \n\n"); + print("static Memcmap def = {\n"); + print("/* cmap2rgb */ {\n"); + for(i=0; icmap2rgb); ){ + print("\t"); + for(j=0; j<16; j++, i++) + print("0x%2.2ux,", c->cmap2rgb[i]); + print("\n"); + } + print("},\n"); + print("/* rgb2cmap */ {\n"); + for(i=0; irgb2cmap);){ + print("\t"); + for(j=0; j<16; j++, i++) + print("0x%2.2ux,", c->rgb2cmap[i]); + print("\n"); + } + print("}\n"); + print("};\n"); + print("Memcmap *memdefcmap = &def;\n"); + print("void _memmkcmap(void){}\n"); + exits(0); +} blob - /dev/null blob + c8d926e4a69378605dcfb59f83ebbc8e8c24f700 (mode 644) --- /dev/null +++ src/libdraw/md-openmemsubfont.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +Memsubfont* +openmemsubfont(char *name) +{ + Memsubfont *sf; + Memimage *i; + Fontchar *fc; + int fd, n; + char hdr[3*12+4+1]; + uchar *p; + + fd = open(name, OREAD); + if(fd < 0) + return nil; + p = nil; + i = readmemimage(fd); + if(i == nil) + goto Err; + if(read(fd, hdr, 3*12) != 3*12){ + werrstr("openmemsubfont: header read error: %r"); + goto Err; + } + n = atoi(hdr); + p = malloc(6*(n+1)); + if(p == nil) + goto Err; + if(read(fd, p, 6*(n+1)) != 6*(n+1)){ + werrstr("openmemsubfont: fontchar read error: %r"); + goto Err; + } + fc = malloc(sizeof(Fontchar)*(n+1)); + if(fc == nil) + goto Err; + _unpackinfo(fc, p, n); + sf = allocmemsubfont(name, n, atoi(hdr+12), atoi(hdr+24), fc, i); + if(sf == nil){ + free(fc); + goto Err; + } + free(p); + return sf; +Err: + close(fd); + if (i != nil) + freememimage(i); + if (p != nil) + free(p); + return nil; +} blob - /dev/null blob + d16c0a92a7bb0407aa5be4a7553b84a8a82ae966 (mode 644) --- /dev/null +++ src/libdraw/md-poly.c @@ -0,0 +1,23 @@ +#include +#include +#include +#include + +void +mempoly(Memimage *dst, Point *vert, int nvert, int end0, int end1, int radius, Memimage *src, Point sp, int op) +{ + int i, e0, e1; + Point d; + + if(nvert < 2) + return; + d = subpt(sp, vert[0]); + for(i=1; i +#include +#include +#include + +Memimage* +readmemimage(int fd) +{ + char hdr[5*12+1]; + int dy; + 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 +#include +#include +#include + +Point +memimagestring(Memimage *b, Point p, Memimage *color, Point cp, Memsubfont *f, char *cs) +{ + int w, width; + uchar *s; + Rune c; + Fontchar *i; + + s = (uchar*)cs; + for(; c=*s; p.x+=width, cp.x+=width){ + width = 0; + if(c < Runeself) + s++; + else{ + w = chartorune(&c, (char*)s); + if(w == 0){ + s++; + continue; + } + s += w; + } + if(c >= f->n) + continue; + i = f->info+c; + width = i->width; + memdraw(b, Rect(p.x+i->left, p.y+i->top, p.x+i->left+(i[1].x-i[0].x), p.y+i->bottom), + color, cp, f->bits, Pt(i->x, i->top), SoverD); + } + return p; +} + +Point +memsubfontwidth(Memsubfont *f, char *cs) +{ + Rune c; + Point p; + uchar *s; + Fontchar *i; + int w, width; + + p = Pt(0, f->height); + s = (uchar*)cs; + for(; c=*s; p.x+=width){ + width = 0; + if(c < Runeself) + s++; + else{ + w = chartorune(&c, (char*)s); + if(w == 0){ + s++; + continue; + } + s += w; + } + if(c >= f->n) + continue; + i = f->info+c; + width = i->width; + } + return p; +} blob - /dev/null blob + e2bdee5c02d1af5f0551f015329c5ff6278ee58c (mode 644) --- /dev/null +++ src/libdraw/md-subfont.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +Memsubfont* +allocmemsubfont(char *name, int n, int height, int ascent, Fontchar *info, Memimage *i) +{ + Memsubfont *f; + + f = malloc(sizeof(Memsubfont)); + if(f == 0) + return 0; + f->n = n; + f->height = height; + f->ascent = ascent; + f->info = info; + f->bits = i; + if(name) + f->name = strdup(name); + else + f->name = 0; + return f; +} + +void +freememsubfont(Memsubfont *f) +{ + if(f == 0) + return; + free(f->info); /* note: f->info must have been malloc'ed! */ + freememimage(f->bits); + free(f); +} blob - /dev/null blob + 86835757b3dcf34a83621d1c04b04594c8e397e5 (mode 644) --- /dev/null +++ src/libdraw/md-unload.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +int +_unloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) +{ + int y, l; + uchar *q; + + if(!rectinrect(r, i->r)) + return -1; + l = bytesperline(r, i->depth); + if(ndata < l*Dy(r)) + return -1; + ndata = l*Dy(r); + q = byteaddr(i, r.min); + for(y=r.min.y; ywidth*sizeof(u32int); + data += l; + } + return ndata; +} blob - /dev/null blob + c03c58e8728711e66d2a458c8341e737df9f3a71 (mode 644) --- /dev/null +++ src/libdraw/md-write.c @@ -0,0 +1,183 @@ +#include +#include +#include +#include + +#define CHUNK 8000 + +#define HSHIFT 3 /* HSHIFT==5 runs slightly faster, but hash table is 64x bigger */ +#define NHASH (1<<(HSHIFT*NMATCH)) +#define HMASK (NHASH-1) +#define hupdate(h, c) ((((h)<r; + bpl = bytesperline(r, i->depth); + n = Dy(r)*bpl; + data = malloc(n); + ncblock = _compblocksize(r, i->depth); + outbuf = malloc(ncblock); + hash = malloc(NHASH*sizeof(Hlist)); + chain = malloc(NMEM*sizeof(Hlist)); + if(data == 0 || outbuf == 0 || hash == 0 || chain == 0){ + ErrOut: + free(data); + free(outbuf); + free(hash); + free(chain); + return -1; + } + for(miny = r.min.y; miny != r.max.y; miny += dy){ + dy = r.max.y-miny; + if(dy*bpl > CHUNK) + dy = CHUNK/bpl; + nb = unloadmemimage(i, Rect(r.min.x, miny, r.max.x, miny+dy), + data+(miny-r.min.y)*bpl, dy*bpl); + if(nb != dy*bpl) + goto ErrOut; + } + sprint(hdr, "compressed\n%11s %11d %11d %11d %11d ", + chantostr(cbuf, i->chan), r.min.x, r.min.y, r.max.x, r.max.y); + if(write(fd, hdr, 11+5*12) != 11+5*12) + goto ErrOut; + edata = data+n; + eout = outbuf+ncblock; + line = data; + r.max.y = r.min.y; + while(line != edata){ + memset(hash, 0, NHASH*sizeof(Hlist)); + memset(chain, 0, NMEM*sizeof(Hlist)); + cp = chain; + h = 0; + outp = outbuf; + for(n = 0; n != NMATCH; n++) + h = hupdate(h, line[n]); + loutp = outbuf; + while(line != edata){ + ndump = 0; + eline = line+bpl; + for(p = line; p != eline; ){ + if(eline-p < NRUN) + es = eline; + else + es = p+NRUN; + q = 0; + runlen = 0; + for(hp = hash[h].next; hp; hp = hp->next){ + s = p + runlen; + if(s >= es) + continue; + t = hp->s + runlen; + for(; s >= p; s--) + if(*s != *t--) + goto matchloop; + t += runlen+2; + s += runlen+2; + for(; s < es; s++) + if(*s != *t++) + break; + n = s-p; + if(n > runlen){ + runlen = n; + q = hp->s; + if(n == NRUN) + break; + } + matchloop: ; + } + if(runlen < NMATCH){ + if(ndump == NDUMP){ + if(eout-outp < ndump+1) + goto Bfull; + *outp++ = ndump-1+128; + memmove(outp, dumpbuf, ndump); + outp += ndump; + ndump = 0; + } + dumpbuf[ndump++] = *p; + runlen = 1; + } + else{ + if(ndump != 0){ + if(eout-outp < ndump+1) + goto Bfull; + *outp++ = ndump-1+128; + memmove(outp, dumpbuf, ndump); + outp += ndump; + ndump = 0; + } + offs = p-q-1; + if(eout-outp < 2) + goto Bfull; + *outp++ = ((runlen-NMATCH)<<2) + (offs>>8); + *outp++ = offs&255; + } + for(q = p+runlen; p != q; p++){ + if(cp->prev) + cp->prev->next = 0; + cp->next = hash[h].next; + cp->prev = &hash[h]; + if(cp->next) + cp->next->prev = cp; + cp->prev->next = cp; + cp->s = p; + if(++cp == &chain[NMEM]) + cp = chain; + if(edata-p > NMATCH) + h = hupdate(h, p[NMATCH]); + } + } + if(ndump != 0){ + if(eout-outp < ndump+1) + goto Bfull; + *outp++ = ndump-1+128; + memmove(outp, dumpbuf, ndump); + outp += ndump; + } + line = eline; + loutp = outp; + r.max.y++; + } + Bfull: + if(loutp == outbuf) + goto ErrOut; + n = loutp-outbuf; + sprint(hdr, "%11d %11ld ", r.max.y, n); + write(fd, hdr, 2*12); + write(fd, outbuf, n); + r.min.y = r.max.y; + } + free(data); + free(outbuf); + free(hash); + free(chain); + return 0; +} blob - /dev/null blob + 08784ce92540af3156204a8190ee46eab1e4ab11 (mode 644) --- /dev/null +++ src/libdraw/memdraw.h @@ -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; word-aligned */ + int ref; /* number of Memimages using this data */ + void* imref; + int allocd; /* is this malloc'd? */ +}; + +enum { + Frepl = 1<<0, /* is replicated */ + Fsimple = 1<<1, /* is 1x1 */ + Fgrey = 1<<2, /* is grey */ + Falpha = 1<<3, /* has explicit alpha */ + Fcmap = 1<<4, /* has cmap channel */ + Fbytes = 1<<5, /* has only 8-bit channels */ +}; + +struct Memimage +{ + Rectangle r; /* rectangle in data area, local coords */ + Rectangle clipr; /* clipping region */ + int depth; /* number of bits of storage per pixel */ + int nchan; /* number of channels */ + u32int chan; /* channel descriptions */ + Memcmap *cmap; + + Memdata *data; /* pointer to data; shared by windows in this image */ + int zero; /* data->bdata+zero==&byte containing (0,0) */ + u32int width; /* width in words of a single scan line */ + 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 baseline */ + Fontchar *info; /* n+1 character descriptors */ + Memimage *bits; /* of font */ +}; + +/* + * Encapsulated parameters and information for sub-draw routines. + */ +enum { + Simplesrc=1<<0, + Simplemask=1<<1, + Replsrc=1<<2, + Replmask=1<<3, + Fullmask=1<<4, +}; +struct Memdrawparam +{ + Memimage *dst; + Rectangle r; + Memimage *src; + Rectangle sr; + Memimage *mask; + Rectangle mr; + int op; + + 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*, Memimage*); +extern Memsubfont* openmemsubfont(char*); +extern void freememsubfont(Memsubfont*); +extern Point memsubfontwidth(Memsubfont*, char*); +extern Memsubfont* getmemdefont(void); + +/* + * Predefined + */ +extern Memimage* memwhite; +extern Memimage* memblack; +extern Memimage* memopaque; +extern Memimage* memtransparent; +extern Memcmap* memdefcmap; + +/* + * Kernel interface + */ +void memimagemove(void*, void*); + +/* + * Kernel cruft + */ +extern void rdb(void); +extern int iprint(char*, ...); +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*); blob - /dev/null blob + 36d87767db6a4a0d1294cdb813854f5d866cafa0 (mode 644) --- /dev/null +++ src/libdraw/memlayer.h @@ -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 drawn */ + Memimage *fill; /* if non-zero, picture to use when repainting */ +}; + +struct Memlayer +{ + Rectangle screenr; /* true position of layer on screen */ + Point delta; /* add delta to go from image coords to screen */ + Memscreen *screen; /* screen this layer belongs to */ + Memimage *front; /* window in front of this one */ + Memimage *rear; /* window behind this one*/ + int clear; /* layer is fully visible */ + Memimage *save; /* save area for obscured parts */ + Refreshfn refreshfn; /* function to call to refresh obscured parts if save==nil */ + void *refreshptr; /* argument to refreshfn */ +}; + +/* + * These functions accept local coordinates + */ +int memload(Memimage*, Rectangle, uchar*, int, int); +int memunload(Memimage*, Rectangle, uchar*, int); + +/* + * All these functions accept screen coordinates, not local ones. + */ +void _memlayerop(void (*fn)(Memimage*, Rectangle, Rectangle, void*, int), Memimage*, Rectangle, Rectangle, void*); +Memimage* memlalloc(Memscreen*, Rectangle, Refreshfn, void*, 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*); blob - /dev/null blob + 883043380494df0b6470278ba067c9e5177647aa (mode 644) --- /dev/null +++ src/libdraw/menuhit.c @@ -0,0 +1,277 @@ +#include +#include +#include +#include +#include + +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); /* dark green */ + bord = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedgreen); /* not as dark green */ + 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, Image *save, Image *restore) +{ + 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 lasti, Image *save) +{ + 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; ichan, 1, DDarkgreen); /* border color; BUG? */ + 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)), -Margin); + r = rectsubpt(r, Pt(wid/2, lasti*(font->height+Vspacing)+font->height/2)); + 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.xr.min.x) + pt.x = screen->r.min.x-r.min.x; + if(r.min.yr.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-1)))){ + if(scrolling && ptinrect(mc->m.xy, scrollr)){ + noff = ((mc->m.xy.y-scrollr.min.y)*nitem)/Dy(scrollr); + 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, nitemdrawn); + menuscrollpaint(b, scrollr, off, nitem, nitemdrawn); + } + } + 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; +} blob - /dev/null blob + bb99a25ab4d41b82f60096fe078cf4374b5fa108 (mode 644) --- /dev/null +++ src/libdraw/mkfile @@ -0,0 +1 @@ +<../libutf/mkfile blob - /dev/null blob + c352a0b2c3fbceaab100c4d5af5f7ad829dd0921 (mode 644) --- /dev/null +++ src/libdraw/ml-draw.c @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include + +struct Draw +{ + Point deltas; + Point deltam; + Memlayer *dstlayer; + Memimage *src; + Memimage *mask; + int op; +}; + +static +void +ldrawop(Memimage *dst, Rectangle screenr, Rectangle clipr, void *etc, int insave) +{ + struct Draw *d; + Point p0, p1; + Rectangle oclipr, srcr, r, mr; + int ok; + + d = etc; + if(insave && d->dstlayer->save==nil) + return; + + p0 = addpt(screenr.min, d->deltas); + p1 = addpt(screenr.min, d->deltam); + + if(insave){ + r = rectsubpt(screenr, d->dstlayer->delta); + clipr = rectsubpt(clipr, d->dstlayer->delta); + }else + r = screenr; + + /* now in logical coordinates */ + + /* clipr may have narrowed what we should draw on, so clip if necessary */ + if(!rectinrect(r, clipr)){ + oclipr = dst->clipr; + dst->clipr = clipr; + ok = drawclip(dst, &r, d->src, &p0, d->mask, &p1, &srcr, &mr); + dst->clipr = oclipr; + if(!ok) + return; + } + memdraw(dst, r, d->src, p0, d->mask, p1, d->op); +} + +void +memdraw(Memimage *dst, Rectangle r, Memimage *src, Point p0, Memimage *mask, Point p1, int op) +{ + struct Draw d; + Rectangle srcr, tr, mr; + Memlayer *dl, *sl; + + if(drawdebug) + iprint("memdraw %p %R %p %P %p %P\n", dst, r, src, p0, mask, p1); + + if(mask == nil) + mask = memopaque; + + if(mask->layer){ +if(drawdebug) iprint("mask->layer != nil\n"); + return; /* too hard, at least for now */ + } + + Top: + if(dst->layer==nil && src->layer==nil){ + memimagedraw(dst, r, src, p0, mask, p1, op); + return; + } + + if(drawclip(dst, &r, src, &p0, mask, &p1, &srcr, &mr) == 0){ +if(drawdebug) iprint("drawclip dstcr %R srccr %R maskcr %R\n", dst->clipr, src->clipr, mask->clipr); + return; + } + + /* + * Convert to screen coordinates. + */ + dl = dst->layer; + if(dl != nil){ + r.min.x += dl->delta.x; + r.min.y += dl->delta.y; + r.max.x += dl->delta.x; + r.max.y += dl->delta.y; + } + Clearlayer: + if(dl!=nil && dl->clear){ + if(src == dst){ + p0.x += dl->delta.x; + p0.y += dl->delta.y; + src = dl->screen->image; + } + dst = dl->screen->image; + goto Top; + } + + sl = src->layer; + if(sl != nil){ + p0.x += sl->delta.x; + p0.y += sl->delta.y; + srcr.min.x += sl->delta.x; + srcr.min.y += sl->delta.y; + srcr.max.x += sl->delta.x; + srcr.max.y += sl->delta.y; + } + + /* + * Now everything is in screen coordinates. + * mask is an image. dst and src are images or obscured layers. + */ + + /* + * if dst and src are the same layer, just draw in save area and expose. + */ + if(dl!=nil && dst==src){ + if(dl->save == nil) + return; /* refresh function makes this case unworkable */ + if(rectXrect(r, srcr)){ + tr = r; + if(srcr.min.x < tr.min.x){ + p1.x += tr.min.x - srcr.min.x; + tr.min.x = srcr.min.x; + } + if(srcr.min.y < tr.min.y){ + p1.y += tr.min.x - srcr.min.x; + tr.min.y = srcr.min.y; + } + if(srcr.max.x > tr.max.x) + tr.max.x = srcr.max.x; + if(srcr.max.y > tr.max.y) + tr.max.y = srcr.max.y; + memlhide(dst, tr); + }else{ + memlhide(dst, r); + memlhide(dst, srcr); + } + memdraw(dl->save, rectsubpt(r, dl->delta), dl->save, + subpt(srcr.min, src->layer->delta), mask, p1, op); + memlexpose(dst, r); + return; + } + + if(sl){ + if(sl->clear){ + src = sl->screen->image; + if(dl != nil){ + r.min.x -= dl->delta.x; + r.min.y -= dl->delta.y; + r.max.x -= dl->delta.x; + r.max.y -= dl->delta.y; + } + goto Top; + } + /* relatively rare case; use save area */ + if(sl->save == nil) + return; /* refresh function makes this case unworkable */ + memlhide(src, srcr); + /* convert back to logical coordinates */ + p0.x -= sl->delta.x; + p0.y -= sl->delta.y; + srcr.min.x -= sl->delta.x; + srcr.min.y -= sl->delta.y; + srcr.max.x -= sl->delta.x; + srcr.max.y -= sl->delta.y; + src = src->layer->save; + } + + /* + * src is now an image. dst may be an image or a clear layer + */ + if(dst->layer==nil) + goto Top; + if(dst->layer->clear) + goto Clearlayer; + + /* + * dst is an obscured layer + */ + d.deltas = subpt(p0, r.min); + d.deltam = subpt(p1, r.min); + d.dstlayer = dl; + d.src = src; + d.op = op; + d.mask = mask; + _memlayerop(ldrawop, dst, r, r, &d); +} blob - /dev/null blob + 17d934d319271a9fc2e8d1c44faebad413d7339b (mode 644) --- /dev/null +++ src/libdraw/ml-lalloc.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include + +Memimage* +memlalloc(Memscreen *s, Rectangle screenr, Refreshfn refreshfn, void *refreshptr, u32int val) +{ + Memlayer *l; + Memimage *n; + static Memimage *paint; + + if(paint == nil){ + paint = allocmemimage(Rect(0,0,1,1), RGBA32); + if(paint == nil) + return nil; + paint->flags |= Frepl; + paint->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); + } + + n = allocmemimaged(screenr, s->image->chan, s->image->data, 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 area */ + if(val != DNofill) + memfillcolor(l->save, val); + } + l->refreshfn = refreshfn; + l->refreshptr = nil; /* don't set it until we're done */ + l->screenr = screenr; + l->delta = Pt(0,0); + + n->data->ref++; + n->zero = s->image->zero; + n->width = s->image->width; + n->layer = l; + + /* start with new window behind all existing ones */ + l->front = s->rearmost; + l->rear = nil; + if(s->rearmost) + s->rearmost->layer->rear = n; + s->rearmost = n; + if(s->frontmost == nil) + s->frontmost = n; + l->clear = 0; + + /* now pull new window to front */ + _memltofrontfill(n, val != DNofill); + l->refreshptr = refreshptr; + + /* + * paint with requested color; previously exposed areas are already right + * if this window has backing store, but just painting the whole thing is simplest. + */ + if(val != DNofill){ + memsetchan(paint, n->chan); + memfillcolor(paint, val); + memdraw(n, n->r, paint, n->r.min, nil, n->r.min, S); + } + return n; +} blob - /dev/null blob + 27fdcfc56b8a889a8455d20cc7f7f7df4b7d57d2 (mode 644) --- /dev/null +++ src/libdraw/ml-layerop.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include + +#define RECUR(a,b,c,d) _layerop(fn, i, Rect(a.x, b.y, c.x, d.y), clipr, etc, front->layer->rear); + +static void +_layerop( + void (*fn)(Memimage*, Rectangle, Rectangle, void*, int), + Memimage *i, + Rectangle r, + Rectangle clipr, + void *etc, + Memimage *front) +{ + Rectangle fr; + + Top: + if(front == i){ + /* no one is in front of this part of window; use the screen */ + fn(i->layer->screen->image, r, clipr, etc, 0); + return; + } + fr = front->layer->screenr; + if(rectXrect(r, fr) == 0){ + /* r doesn't touch this window; continue on next rearmost */ + // assert(front && front->layer && front->layer->screen && front->layer->rear); + front = front->layer->rear; + goto Top; + } + if(fr.max.y < r.max.y){ + RECUR(r.min, fr.max, r.max, r.max); + r.max.y = fr.max.y; + } + if(r.min.y < fr.min.y){ + RECUR(r.min, r.min, r.max, fr.min); + r.min.y = fr.min.y; + } + if(fr.max.x < r.max.x){ + RECUR(fr.max, r.min, r.max, r.max); + r.max.x = fr.max.x; + } + if(r.min.x < fr.min.x){ + RECUR(r.min, r.min, fr.min, r.max); + r.min.x = fr.min.x; + } + /* r is covered by front, so put in save area */ + (*fn)(i->layer->save, r, clipr, etc, 1); +} + +/* + * Assumes incoming rectangle has already been clipped to i's logical r and clipr + */ +void +_memlayerop( + void (*fn)(Memimage*, Rectangle, Rectangle, void*, int), + Memimage *i, + Rectangle screenr, /* clipped to window boundaries */ + Rectangle clipr, /* clipped also to clipping rectangles of hierarchy */ + void *etc) +{ + Memlayer *l; + Rectangle r, scr; + + l = i->layer; + if(!rectclip(&screenr, l->screenr)) + return; + if(l->clear){ + fn(l->screen->image, screenr, clipr, etc, 0); + return; + } + r = screenr; + scr = l->screen->image->clipr; + + /* + * Do the piece on the screen + */ + if(rectclip(&screenr, scr)) + _layerop(fn, i, screenr, clipr, etc, l->screen->frontmost); + if(rectinrect(r, scr)) + return; + + /* + * Do the piece off the screen + */ + if(!rectXrect(r, scr)){ + /* completely offscreen; easy */ + fn(l->save, r, clipr, etc, 1); + return; + } + if(r.min.y < scr.min.y){ + /* above screen */ + fn(l->save, Rect(r.min.x, r.min.y, r.max.x, scr.min.y), clipr, etc, 1); + r.min.y = scr.min.y; + } + if(r.max.y > scr.max.y){ + /* below screen */ + fn(l->save, Rect(r.min.x, scr.max.y, r.max.x, r.max.y), clipr, etc, 1); + r.max.y = scr.max.y; + } + if(r.min.x < scr.min.x){ + /* left of screen */ + fn(l->save, Rect(r.min.x, r.min.y, scr.min.x, r.max.y), clipr, etc, 1); + r.min.x = scr.min.x; + } + if(r.max.x > scr.max.x){ + /* right of screen */ + fn(l->save, Rect(scr.max.x, r.min.y, r.max.x, r.max.y), clipr, etc, 1); + } +} blob - /dev/null blob + 34cd6eadbc2a9d1c8ab46f5dd8caa286945d88be (mode 644) --- /dev/null +++ src/libdraw/ml-ldelete.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +void +memldelete(Memimage *i) +{ + Memscreen *s; + Memlayer *l; + + l = i->layer; + /* free backing store and disconnect refresh, to make pushback fast */ + freememimage(l->save); + l->save = nil; + l->refreshptr = nil; + memltorear(i); + + /* window is now the rearmost; clean up screen structures and deallocate */ + s = i->layer->screen; + if(s->fill){ + i->clipr = i->r; + memdraw(i, i->r, s->fill, i->r.min, nil, i->r.min, S); + } + if(l->front){ + l->front->layer->rear = nil; + s->rearmost = l->front; + }else{ + s->frontmost = nil; + s->rearmost = nil; + } + free(l); + freememimage(i); +} + +/* + * Just free the data structures, don't do graphics + */ +void +memlfree(Memimage *i) +{ + Memlayer *l; + + l = i->layer; + freememimage(l->save); + free(l); + freememimage(i); +} + +void +_memlsetclear(Memscreen *s) +{ + Memimage *i, *j; + Memlayer *l; + + for(i=s->rearmost; i; i=i->layer->front){ + l = i->layer; + l->clear = rectinrect(l->screenr, l->screen->image->clipr); + if(l->clear) + for(j=l->front; j; j=j->layer->front) + if(rectXrect(l->screenr, j->layer->screenr)){ + l->clear = 0; + break; + } + } +} blob - /dev/null blob + d6aaa55fe2ce93dc01b060d7b567135f73b43f4e (mode 644) --- /dev/null +++ src/libdraw/ml-lhide.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +/* + * Hide puts that portion of screenr now on the screen into the window's save area. + * Expose puts that portion of screenr now in the save area onto the screen. + * + * Hide and Expose both require that the layer structures in the screen + * match the geometry they are being asked to update, that is, they update the + * save area (hide) or screen (expose) based on what those structures tell them. + * This means they must be called at the correct time during window shuffles. + */ + +static +void +lhideop(Memimage *src, Rectangle screenr, Rectangle clipr, void *etc, int insave) +{ + Rectangle r; + Memlayer *l; + + USED(clipr.min.x); + USED(insave); + l = etc; + if(src != l->save){ /* do nothing if src is already in save area */ + r = rectsubpt(screenr, l->delta); + memdraw(l->save, r, src, screenr.min, nil, screenr.min, S); + } +} + +void +memlhide(Memimage *i, Rectangle screenr) +{ + if(i->layer->save == nil) + return; + if(rectclip(&screenr, i->layer->screen->image->r) == 0) + return; + _memlayerop(lhideop, i, screenr, screenr, i->layer); +} + +static +void +lexposeop(Memimage *dst, Rectangle screenr, Rectangle clipr, void *etc, int insave) +{ + Memlayer *l; + Rectangle r; + + USED(clipr.min.x); + if(insave) /* if dst is save area, don't bother */ + return; + l = etc; + r = rectsubpt(screenr, l->delta); + if(l->save) + memdraw(dst, screenr, l->save, r.min, nil, r.min, S); + else + l->refreshfn(dst, r, l->refreshptr); +} + +void +memlexpose(Memimage *i, Rectangle screenr) +{ + if(rectclip(&screenr, i->layer->screen->image->r) == 0) + return; + _memlayerop(lexposeop, i, screenr, screenr, i->layer); +} blob - /dev/null blob + 8c09a53592f42b750bc77df0bd156bb543c53b1a (mode 644) --- /dev/null +++ src/libdraw/ml-line.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include + +struct Lline +{ + Point p0; + Point p1; + Point delta; + int end0; + int end1; + int radius; + Point sp; + Memlayer *dstlayer; + Memimage *src; + int op; +}; + +static void llineop(Memimage*, Rectangle, Rectangle, void*, int); + +static +void +_memline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, Rectangle clipr, int op) +{ + Rectangle r; + struct Lline ll; + Point d; + int srcclipped; + Memlayer *dl; + + if(radius < 0) + return; + if(src->layer) /* can't draw line with layered source */ + return; + srcclipped = 0; + + Top: + dl = dst->layer; + if(dl == nil){ + _memimageline(dst, p0, p1, end0, end1, radius, src, sp, clipr, op); + return; + } + if(!srcclipped){ + d = subpt(sp, p0); + if(rectclip(&clipr, rectsubpt(src->clipr, d)) == 0) + return; + if((src->flags&Frepl)==0 && rectclip(&clipr, rectsubpt(src->r, d))==0) + return; + srcclipped = 1; + } + + /* dst is known to be a layer */ + p0.x += dl->delta.x; + p0.y += dl->delta.y; + p1.x += dl->delta.x; + p1.y += dl->delta.y; + clipr.min.x += dl->delta.x; + clipr.min.y += dl->delta.y; + clipr.max.x += dl->delta.x; + clipr.max.y += dl->delta.y; + if(dl->clear){ + dst = dst->layer->screen->image; + goto Top; + } + + /* XXX */ + /* this is not the correct set of tests */ +// if(log2[dst->depth] != log2[src->depth] || log2[dst->depth]!=3) +// return; + + /* can't use sutherland-cohen clipping because lines are wide */ + r = memlinebbox(p0, p1, end0, end1, radius); + /* + * r is now a bounding box for the line; + * use it as a clipping rectangle for subdivision + */ + if(rectclip(&r, clipr) == 0) + return; + ll.p0 = p0; + ll.p1 = p1; + ll.end0 = end0; + ll.end1 = end1; + ll.sp = sp; + ll.dstlayer = dst->layer; + ll.src = src; + ll.radius = radius; + ll.delta = dl->delta; + ll.op = op; + _memlayerop(llineop, dst, r, r, &ll); +} + +static +void +llineop(Memimage *dst, Rectangle screenr, Rectangle clipr, void *etc, int insave) +{ + struct Lline *ll; + Point p0, p1; + + USED(screenr.min.x); + ll = etc; + if(insave && ll->dstlayer->save==nil) + return; + if(!rectclip(&clipr, screenr)) + return; + if(insave){ + p0 = subpt(ll->p0, ll->delta); + p1 = subpt(ll->p1, ll->delta); + clipr = rectsubpt(clipr, ll->delta); + }else{ + p0 = ll->p0; + p1 = ll->p1; + } + _memline(dst, p0, p1, ll->end0, ll->end1, ll->radius, ll->src, ll->sp, clipr, ll->op); +} + +void +memline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, int op) +{ + _memline(dst, p0, p1, end0, end1, radius, src, sp, dst->clipr, op); +} blob - /dev/null blob + d211564be0c81883cfa425d4e8a6f1c14edf8e95 (mode 644) --- /dev/null +++ src/libdraw/ml-load.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include + +int +memload(Memimage *dst, Rectangle r, uchar *data, int n, int iscompressed) +{ + int (*loadfn)(Memimage*, Rectangle, uchar*, int); + Memimage *tmp; + Memlayer *dl; + Rectangle lr; + int dx; + + loadfn = loadmemimage; + if(iscompressed) + loadfn = cloadmemimage; + + Top: + dl = dst->layer; + if(dl == nil) + return loadfn(dst, r, data, n); + + /* + * Convert to screen coordinates. + */ + lr = r; + r.min.x += dl->delta.x; + r.min.y += dl->delta.y; + r.max.x += dl->delta.x; + r.max.y += dl->delta.y; + dx = dl->delta.x&(7/dst->depth); + if(dl->clear && dx==0){ + dst = dl->screen->image; + goto Top; + } + + /* + * dst is an obscured layer or data is unaligned + */ + if(dl->save && dx==0){ + n = loadfn(dl->save, lr, data, n); + if(n > 0) + memlexpose(dst, r); + return n; + } + tmp = allocmemimage(lr, dst->chan); + if(tmp == nil) + return -1; + n = loadfn(tmp, lr, data, n); + memdraw(dst, lr, tmp, lr.min, nil, lr.min, S); + freememimage(tmp); + return n; +} blob - /dev/null blob + 0926ee8d216bd66119a4dc3c1a2932e2bb587287 (mode 644) --- /dev/null +++ src/libdraw/ml-lorigin.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include + +/* + * Place i so i->r.min = log, i->layer->screenr.min == scr. +*/ +int +memlorigin(Memimage *i, Point log, Point scr) +{ + Memlayer *l; + Memscreen *s; + Memimage *t, *shad, *nsave; + Rectangle x, newr, oldr; + Point delta; + int overlap, eqlog, eqscr, wasclear; + + l = i->layer; + s = l->screen; + oldr = l->screenr; + newr = Rect(scr.x, scr.y, scr.x+Dx(oldr), scr.y+Dy(oldr)); + eqscr = eqpt(scr, oldr.min); + eqlog = eqpt(log, i->r.min); + if(eqscr && eqlog) + return 0; + nsave = nil; + if(eqlog==0 && l->save!=nil){ + nsave = allocmemimage(Rect(log.x, log.y, log.x+Dx(oldr), log.y+Dy(oldr)), i->chan); + if(nsave == nil) + return -1; + } + + /* + * Bring it to front and move logical coordinate system. + */ + memltofront(i); + wasclear = l->clear; + if(nsave){ + if(!wasclear) + memimagedraw(nsave, nsave->r, l->save, l->save->r.min, nil, Pt(0,0), S); + freememimage(l->save); + l->save = nsave; + } + delta = subpt(log, i->r.min); + i->r = rectaddpt(i->r, delta); + i->clipr = rectaddpt(i->clipr, delta); + l->delta = subpt(l->screenr.min, i->r.min); + if(eqscr) + return 0; + + /* + * To clean up old position, make a shadow window there, don't paint it, + * push it behind this one, and (later) delete it. Because the refresh function + * for this fake window is a no-op, this will cause no graphics action except + * to restore the background and expose the windows previously hidden. + */ + shad = memlalloc(s, oldr, memlnorefresh, nil, DNofill); + if(shad == nil) + return -1; + s->frontmost = i; + if(s->rearmost == i) + s->rearmost = shad; + else + l->rear->layer->front = shad; + shad->layer->front = i; + shad->layer->rear = l->rear; + l->rear = shad; + l->front = nil; + shad->layer->clear = 0; + + /* + * Shadow is now holding down the fort at the old position. + * Move the window and hide things obscured by new position. + */ + for(t=l->rear->layer->rear; t!=nil; t=t->layer->rear){ + x = newr; + overlap = rectclip(&x, t->layer->screenr); + if(overlap){ + memlhide(t, x); + t->layer->clear = 0; + } + } + l->screenr = newr; + l->delta = subpt(scr, i->r.min); + l->clear = rectinrect(newr, l->screen->image->clipr); + + /* + * Everything's covered. Copy to new position and delete shadow window. + */ + if(wasclear) + memdraw(s->image, newr, s->image, oldr.min, nil, Pt(0,0), S); + else + memlexpose(i, newr); + memldelete(shad); + + return 1; +} + +void +memlnorefresh(Memimage *l, Rectangle r, void *v) +{ + USED(l); + USED(r.min.x); + USED(v); +} blob - /dev/null blob + 44079be2bf1175c6912c480d38aa45011231f8eb (mode 644) --- /dev/null +++ src/libdraw/ml-lsetrefresh.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include + +int +memlsetrefresh(Memimage *i, Refreshfn fn, void *ptr) +{ + Memlayer *l; + + l = i->layer; + if(l->refreshfn!=nil && fn!=nil){ /* just change functions */ + l->refreshfn = fn; + l->refreshptr = ptr; + return 1; + } + + if(l->refreshfn == nil){ /* is using backup image; just free it */ + freememimage(l->save); + l->save = nil; + l->refreshfn = fn; + l->refreshptr = ptr; + return 1; + } + + l->save = allocmemimage(i->r, i->chan); + if(l->save == nil) + return 0; + /* easiest way is just to update the entire save area */ + l->refreshfn(i, i->r, l->refreshptr); + l->refreshfn = nil; + l->refreshptr = nil; + return 1; +} blob - /dev/null blob + 447b40bdfdf51ccccab7932bdb72c2df30b26c00 (mode 644) --- /dev/null +++ src/libdraw/ml-ltofront.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include + +/* + * Pull i towards top of screen, just behind front +*/ +static +void +_memltofront(Memimage *i, Memimage *front, int fill) +{ + Memlayer *l; + Memscreen *s; + Memimage *f, *ff, *rr; + Rectangle x; + int overlap; + + l = i->layer; + s = l->screen; + while(l->front != front){ + f = l->front; + x = l->screenr; + overlap = rectclip(&x, f->layer->screenr); + if(overlap){ + memlhide(f, x); + f->layer->clear = 0; + } + /* swap l and f in screen's list */ + ff = f->layer->front; + rr = l->rear; + if(ff == nil) + s->frontmost = i; + else + ff->layer->rear = i; + if(rr == nil) + s->rearmost = f; + else + rr->layer->front = f; + l->front = ff; + l->rear = f; + f->layer->front = i; + f->layer->rear = rr; + if(overlap && fill) + memlexpose(i, x); + } +} + +void +_memltofrontfill(Memimage *i, int fill) +{ + _memltofront(i, nil, fill); + _memlsetclear(i->layer->screen); +} + +void +memltofront(Memimage *i) +{ + _memltofront(i, nil, 1); + _memlsetclear(i->layer->screen); +} + +void +memltofrontn(Memimage **ip, int n) +{ + Memimage *i, *front; + Memscreen *s; + + if(n == 0) + return; + front = nil; + while(--n >= 0){ + i = *ip++; + _memltofront(i, front, 1); + front = i; + } + s = front->layer->screen; + _memlsetclear(s); +} blob - /dev/null blob + d53e8cc95e3a3c757c886690a9f47b2c007990d7 (mode 644) --- /dev/null +++ src/libdraw/ml-ltorear.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +void +_memltorear(Memimage *i, Memimage *rear) +{ + Memlayer *l; + Memscreen *s; + Memimage *f, *r, *rr; + Rectangle x; + int overlap; + + l = i->layer; + s = l->screen; + while(l->rear != rear){ + r = l->rear; + x = l->screenr; + overlap = rectclip(&x, r->layer->screenr); + if(overlap){ + memlhide(i, x); + l->clear = 0; + } + /* swap l and r in screen's list */ + rr = r->layer->rear; + f = l->front; + if(rr == nil) + s->rearmost = i; + else + rr->layer->front = i; + if(f == nil) + s->frontmost = r; + else + f->layer->rear = r; + l->rear = rr; + l->front = r; + r->layer->rear = i; + r->layer->front = f; + if(overlap) + memlexpose(r, x); + } +} + +void +memltorear(Memimage *i) +{ + _memltorear(i, nil); + _memlsetclear(i->layer->screen); +} + +void +memltorearn(Memimage **ip, int n) +{ + Memimage *i, *rear; + Memscreen *s; + + if(n == 0) + return; + rear = nil; + while(--n >= 0){ + i = *ip++; + _memltorear(i, rear); + rear = i; + } + s = rear->layer->screen; + _memlsetclear(s); +} blob - /dev/null blob + 23a8b290bc190b67048be4b2662397ab02fdc137 (mode 644) --- /dev/null +++ src/libdraw/ml-unload.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +int +memunload(Memimage *src, Rectangle r, uchar *data, int n) +{ + Memimage *tmp; + Memlayer *dl; + Rectangle lr; + int dx; + + Top: + dl = src->layer; + if(dl == nil) + return unloadmemimage(src, r, data, n); + + /* + * Convert to screen coordinates. + */ + lr = r; + r.min.x += dl->delta.x; + r.min.y += dl->delta.y; + r.max.x += dl->delta.x; + r.max.y += dl->delta.y; + dx = dl->delta.x&(7/src->depth); + if(dl->clear && dx==0){ + src = dl->screen->image; + goto Top; + } + + /* + * src is an obscured layer or data is unaligned + */ + if(dl->save && dx==0){ + if(dl->refreshfn != nil) + return -1; /* can't unload window if it's not Refbackup */ + if(n > 0) + memlhide(src, r); + n = unloadmemimage(dl->save, lr, data, n); + return n; + } + tmp = allocmemimage(lr, src->chan); + if(tmp == nil) + return -1; + memdraw(tmp, lr, src, lr.min, nil, lr.min, S); + n = unloadmemimage(tmp, lr, data, n); + freememimage(tmp); + return n; +} blob - /dev/null blob + e7b8f8909b0e7f219f43776a83c5ab44bd46dc06 (mode 644) --- /dev/null +++ src/libdraw/mouse.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include +#include + +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 exit here */ + 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 wrong value if we block during send. + * This means that programs should receive into mc->Mouse (see readmouse() above) if + * 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); + } +} blob - /dev/null blob + f0a0f697523863e3910a405bf6528a9f06aab175 (mode 644) --- /dev/null +++ src/libdraw/mouse.h @@ -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 action before handling resize */ + + 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*); blob - /dev/null blob + dc16e379b744c75f93ae55af57276e668c5fa783 (mode 644) --- /dev/null +++ src/libdraw/openfont.c @@ -0,0 +1,32 @@ +#include +#include +#include + +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; +} blob - /dev/null blob + 6eb8ee26ecf7cb47e2a5a0dc81ec408fee39e89a (mode 644) --- /dev/null +++ src/libdraw/readcolmap.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include + +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); +} blob - /dev/null blob + 1d2717b0d105f731b00e21310c09f626ce31c2bf (mode 644) --- /dev/null +++ src/libdraw/readimage.c @@ -0,0 +1,118 @@ +#include +#include +#include + +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 +#include +#include + +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); +} blob - /dev/null blob + 4dfb27faa5d15e00ce525ac8c9dc2947f42aab14 (mode 644) --- /dev/null +++ src/libdraw/string.c @@ -0,0 +1,137 @@ +#include +#include +#include + +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, SoverD); +} + +Point +stringop(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, Drawop op) +{ + return _string(dst, pt, src, sp, f, s, nil, 1<<24, dst->clipr, nil, ZP, op); +} + +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, SoverD); +} + +Point +stringnop(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, int len, Drawop op) +{ + return _string(dst, pt, src, sp, f, s, nil, len, dst->clipr, nil, ZP, op); +} + +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, SoverD); +} + +Point +runestringop(Image *dst, Point pt, Image *src, Point sp, Font *f, Rune *r, Drawop op) +{ + return _string(dst, pt, src, sp, f, nil, r, 1<<24, dst->clipr, nil, ZP, op); +} + +Point +runestringn(Image *dst, Point pt, Image *src, Point sp, Font *f, Rune *r, int len) +{ + return _string(dst, pt, src, sp, f, nil, r, len, dst->clipr, nil, ZP, SoverD); +} + +Point +runestringnop(Image *dst, Point pt, Image *src, Point sp, Font *f, Rune *r, int len, Drawop op) +{ + return _string(dst, pt, src, sp, f, nil, r, len, dst->clipr, nil, ZP, op); +} + +Point +_string(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, Rune *r, int len, Rectangle clipr, Image *bg, Point bgp, Drawop op) +{ + 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; cdisplay, subfontname) == 0){ + def = f->display->defaultfont; + if(def && f!=def) + f = def; + else + break; + } + } + } + return pt; +} blob - /dev/null blob + c4877912fe5e8246c8afa7ce2160f542f8ccf5c7 (mode 644) --- /dev/null +++ src/libdraw/stringwidth.c @@ -0,0 +1,97 @@ +#include +#include +#include + +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, &subfontname)) <= 0){ + 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 rune 0x%.4ux in %s\n", rune, name); + 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); +} blob - /dev/null blob + 61838b0548190d1e622ea654e7a5acc9c9cc3acb (mode 644) --- /dev/null +++ src/libdraw/subfont.c @@ -0,0 +1,28 @@ +#include +#include +#include + +Subfont* +allocsubfont(char *name, int n, int height, int ascent, Fontchar *info, Image *i) +{ + 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; +} blob - /dev/null blob + eb0bdba89c446355cbeed7005548d11c13a62405 (mode 644) --- /dev/null +++ src/libdraw/subfontcache.c @@ -0,0 +1,39 @@ +#include +#include +#include + +/* + * Easy versions of the cache routines; may be substituted by fancier ones for other purposes + */ + +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->display){ + 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; that's your business */ +} + +void +uninstallsubfont(Subfont *subfont) +{ + if(subfont == lastsubfont){ + lastname = 0; + lastsubfont = 0; + } +} blob - /dev/null blob + bcb4e3a1ef7f49020f50ab0852c4501683de9d5d (mode 644) --- /dev/null +++ src/libdraw/subfontname.c @@ -0,0 +1,44 @@ +#include +#include +#include + +/* + * 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; +} blob - /dev/null blob + 2203b0876e47137bc3ca1d18e7ce7e879f7e09db (mode 644) --- /dev/null +++ src/libdraw/unix.c @@ -0,0 +1,16 @@ +#include + +#include +#include +#include + +vlong +flength(int fd) +{ + struct stat s; + + if(fstat(fd, &s) < 0) + return -1; + return s.st_size; +} + blob - /dev/null blob + c203b9c293823ae9257313b9126eeee86ea85aa9 (mode 644) --- /dev/null +++ src/libdraw/unloadimage.c @@ -0,0 +1,53 @@ +#include +#include +#include + +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; +} blob - /dev/null blob + 30b026f95ddfd76fd7829f3c97e6899080e7b29b (mode 644) --- /dev/null +++ src/libdraw/writecolmap.c @@ -0,0 +1,35 @@ +#include +#include +#include + +/* + * 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"); +} blob - /dev/null blob + 19475c747818d4a7b05b110e632f7dc164d34729 (mode 644) --- /dev/null +++ src/libdraw/x11-alloc.c @@ -0,0 +1,120 @@ +#include "x11-inc.h" + +#include +#include +#include +#include +#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); +} + blob - /dev/null blob + 25a325c62a620e81793a140fff542176f01b04b5 (mode 644) --- /dev/null +++ src/libdraw/x11-cload.c @@ -0,0 +1,19 @@ +#include "x11-inc.h" + +#include +#include +#include +#include +#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; +} + blob - /dev/null blob + 33b92c8743046d7419eb0acf6b5ef59bf58bf4f9 (mode 644) --- /dev/null +++ src/libdraw/x11-draw.c @@ -0,0 +1,143 @@ +#include "x11-inc.h" + +#include +#include +#include +#include +#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; +} + blob - /dev/null blob + 9a23547ae78d282eae03c4345e45215ba99dbf50 (mode 644) --- /dev/null +++ src/libdraw/x11-event.c @@ -0,0 +1,136 @@ +#include "x11-inc.h" + +#include +#include +#include +#include +#include + +#include +#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); +} + blob - /dev/null blob + ff0b2e86527813a32cc37367cbf08b71c2f43600 (mode 644) --- /dev/null +++ src/libdraw/x11-fill.c @@ -0,0 +1,57 @@ +#include "x11-inc.h" + +#include +#include +#include +#include +#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)); +} + + blob - /dev/null blob + feed46c67221e5a6d3f08442c9271de6a60667cb (mode 644) --- /dev/null +++ src/libdraw/x11-get.c @@ -0,0 +1,110 @@ +#include "x11-inc.h" + +#include +#include +#include +#include +#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; ydirty = 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; ypixmap, gc, xi, xdelta.x, xdelta.y, delta.x, delta.y, + Dx(r), Dy(r)); + + if(_x.usetable && m->chan==CMAP8){ + for(y=r.min.y; yX; + if(xm == nil) + return; + xm->dirty = 1; + addrect(&xm->dirtyr, r); +} + + + blob - /dev/null blob + 4baf4b1afca1b06cee1cbe12d5a32c2b33449bc5 (mode 644) --- /dev/null +++ src/libdraw/x11-inc.h @@ -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 +#include +#include +#include +#include +#include + +#undef Colormap +#undef Cursor +#undef Display +#undef Drawable +#undef Font +#undef GC +#undef Point +#undef Rectangle +#undef Screen +#undef Visual +#undef Window + blob - /dev/null blob + 3100124094cbd38ee10822905255833d775becc0 (mode 644) --- /dev/null +++ src/libdraw/x11-init.c @@ -0,0 +1,584 @@ +/* + * Some of the stuff in this file is not X-dependent and should be elsewhere. + */ +#include "x11-inc.h" +#include +#include +#include +#include +#include +#include +#include +#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; iclass != 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.depth); + _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 grey shades */ + cr = ((255.0/7.0)*v7)+0.5; + cg = cr; + cb = cr; + } + else { + num=17*15*(4*den+v7*2)/14; + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + _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 values */ + 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, AllocAll); + 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-bit map"); + 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.min.y, + 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.depth); + 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; +} + blob - /dev/null blob + 49ee3fbba8f20d2e01d7d7e6181d6d168e3a0100 (mode 644) --- /dev/null +++ src/libdraw/x11-itrans.c @@ -0,0 +1,258 @@ +/* input event and data structure translation */ + +#include "x11-inc.h" + +#include +#include +#include +#include +#include +#include +#include +#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.x, -c->offset.y); + 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); +} + blob - /dev/null blob + 188e52bf467d1fd352b4755cdd4f9f42d5aa2716 (mode 644) --- /dev/null +++ src/libdraw/x11-keyboard.c @@ -0,0 +1,71 @@ +#include "x11-inc.h" +#include +#include +#include +#include +#include +#include +#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; +} + blob - /dev/null blob + e4e32bf2419d2bbb9d58d22c8fd1f40195ea9004 (mode 644) --- /dev/null +++ src/libdraw/x11-load.c @@ -0,0 +1,19 @@ +#include "x11-inc.h" + +#include +#include +#include +#include +#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; +} + blob - /dev/null blob + c8234b2090a676c42b190b08cd6b50a179aa6df8 (mode 644) --- /dev/null +++ src/libdraw/x11-memdraw.h @@ -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) + blob - /dev/null blob + c6a2685107163432d19f99a2ba4f5783a3997c8f (mode 644) --- /dev/null +++ src/libdraw/x11-mouse.c @@ -0,0 +1,107 @@ +#include "x11-inc.h" +#include +#include +#include +#include +#include +#include +#include +#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|StructureNotifyMask); + XWindowEvent(_x.mousecon, _x.drawable, MouseMask|ExposureMask|StructureNotifyMask, &xevent); + 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 wrong value if we block during send. + * This means that programs should receive into mc->Mouse (see readmouse() above) if + * 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); +} + blob - /dev/null blob + 8635b0ba456d044c252552f6971bced7df24bf11 (mode 644) --- /dev/null +++ src/libdraw/x11-pixelbits.c @@ -0,0 +1,17 @@ +#include "x11-inc.h" + +#include +#include +#include +#include +#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); +} + + blob - /dev/null blob + 3e8a635c1bc04d579c4fab8acb331156a6229fd6 (mode 644) --- /dev/null +++ src/libdraw/x11-unload.c @@ -0,0 +1,16 @@ +#include "x11-inc.h" + +#include +#include +#include +#include +#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); +} + blob - /dev/null blob + 2f391bff61ed15b45eee2fa4e5a195187bf92a7f (mode 644) --- /dev/null +++ src/libthread/386.c @@ -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 */ +} + blob - /dev/null blob + 624518e07715768157dd775b8f66c60a5114a02b (mode 644) --- /dev/null +++ src/libthread/FreeBSD-386.s @@ -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 + blob - /dev/null blob + a5d7d87d79f0af66f7a5f54320edabb411b0e803 (mode 644) --- /dev/null +++ src/libthread/LICENSE @@ -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. + blob - /dev/null blob + 14b8d4e7925867693d110c320d9c5a67dcd16758 (mode 644) --- /dev/null +++ src/libthread/Make.Darwin-PowerMacintosh @@ -0,0 +1,6 @@ +CC=gcc +CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I${PREFIX}/include +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O blob - /dev/null blob + 087ed3abab5ea1bb549989f2c6abbae4ce024922 (mode 644) --- /dev/null +++ src/libthread/Make.FreeBSD-386 @@ -0,0 +1,7 @@ +CC=gcc +CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME) +NAN=nan64.$O blob - /dev/null blob + edbdc111a2a0c008975e297c7ca60e46199397a9 (mode 644) --- /dev/null +++ src/libthread/Make.HP-UX-9000 @@ -0,0 +1,6 @@ +CC=cc +CFLAGS=-O -c -Ae -I. +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O blob - /dev/null blob + 74b0252c6eae6331b48ede7fcfa1ed760609d1a3 (mode 644) --- /dev/null +++ src/libthread/Make.Linux-386 @@ -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 blob - /dev/null blob + 087ed3abab5ea1bb549989f2c6abbae4ce024922 (mode 644) --- /dev/null +++ src/libthread/Make.NetBSD-386 @@ -0,0 +1,7 @@ +CC=gcc +CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME) +NAN=nan64.$O blob - /dev/null blob + 3d45279bba28dbe69c00f89568c444ab83a4ac72 (mode 644) --- /dev/null +++ src/libthread/Make.OSF1-alpha @@ -0,0 +1,6 @@ +CC=cc +CFLAGS+=-g -c -I. +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O blob - /dev/null blob + c5fe67b82d86c731a56c101e522e804390e5bd5a (mode 644) --- /dev/null +++ src/libthread/Make.SunOS-sun4u @@ -0,0 +1,2 @@ +include Make.SunOS-sun4u-$(CC) +NAN=nan64.$O blob - /dev/null blob + 829301de8649d40632eb809ae512598af11d48c5 (mode 644) --- /dev/null +++ src/libthread/Make.SunOS-sun4u-cc @@ -0,0 +1,6 @@ +CC=cc +CFLAGS+=-g -c -I. -O +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O blob - /dev/null blob + 5c41594812a4a27eb848d384cbc356d206c0a44b (mode 644) --- /dev/null +++ src/libthread/Make.SunOS-sun4u-gcc @@ -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 blob - /dev/null blob + ebccc1ac5a6c06a482021fe8841d515f0bc64574 (mode 644) --- /dev/null +++ src/libthread/Makefile @@ -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.ports $(TGZFILES) $(NAME)-$(VERSION) + 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 rsc@amsterdam.lcs.mit.edu: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=="---" { ofile=$$2; next} {if(ofile) print >ofile}') + (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 rsc@amsterdam.lcs.mit.edu:public_html/software + +.phony: all clean nuke install tgz rpm ports blob - /dev/null blob + 05c34167b53ca8db6547c98e126ccd4f943b54a2 (mode 644) --- /dev/null +++ src/libthread/Makefile.MID @@ -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 + blob - /dev/null blob + 8bf69d6af9b8b0844389840746e10387e789f7b4 (mode 644) --- /dev/null +++ src/libthread/NOTICE @@ -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 ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. +*/ + +This is a Unix port of the Plan 9 thread library. + +Please send comments about the packaging +to Russ Cox . + blob - /dev/null blob + 8bf69d6af9b8b0844389840746e10387e789f7b4 (mode 644) --- /dev/null +++ src/libthread/README @@ -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 ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. +*/ + +This is a Unix port of the Plan 9 thread library. + +Please send comments about the packaging +to Russ Cox . + blob - /dev/null blob + 7cad85cc70b800972d718bd7e66983e8ee588280 (mode 644) --- /dev/null +++ src/libthread/asm-FreeBSD-386.s @@ -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 + blob - /dev/null blob + 75d965bf758bcb35d7fdd6de8ba70e317664ba79 (mode 644) --- /dev/null +++ src/libthread/asm-Linux-386.s @@ -0,0 +1 @@ +.include "asm-FreeBSD-386.s" blob - /dev/null blob + adfeb9047f3905f9daa169cccbbfe6691bdffb4c (mode 644) --- /dev/null +++ src/libthread/bundle.ports @@ -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= rsc@post.harvard.edu + +MAN3= print.3 fmtinstall.3 +MLINKS= XXX +USE_REINPLACE= XXX (wkj says yes) + +.include + +post-patch: + ${REINPLACE_CMD} -e 's,@@LOCAL@@,${PREFIX},g' ${WRKSRC}/Makefile + +.include + +--- 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 +rsc@post.harvard.edu +--- 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. blob - /dev/null blob + 384f23fd01d78ce9028d12f0ae6cf0fda9bb2c8d (mode 644) --- /dev/null +++ src/libthread/channel.c @@ -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; inentry; i++) + if(c->qentry[i] && c->qentry[i]->op==otherop && *c->qentry[i]->tag==nil){ + _threaddebug(DBGCHAN, "can rendez alt %p chan %p", a, c); + 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 back */ + 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 %d", + 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; inentry; 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; inentry; i++) + if(c->qentry[i]==a){ + _threaddebug(DBGCHAN, "Dequeuing alt %p from channel %p", a, a->c); + 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; inentry; i++) + if(c->qentry[i] && c->qentry[i]->op==otherop && *c->qentry[i]->tag==nil) + if(nrand(++n) == 0) + b = c->qentry[i]; + if(b != nil){ + _threaddebug(DBGCHAN, "rendez %s alt %p chan %p alt %p", a->op==CHANRCV?"recv":"send", a, c, b); + 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; +} blob - /dev/null blob + af9e81033061329d811a58d8a46543ad2624a698 (mode 644) --- /dev/null +++ src/libthread/chanprint.c @@ -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; +} blob - /dev/null blob + ab803a2cf87c2ecb28375788fec009d3dd0491b0 (mode 644) --- /dev/null +++ src/libthread/create.c @@ -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 grp) +{ + 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, name); + 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, rforkflag); + 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, threadgetgrp()); +} + +/* + * 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, int rforkflag) +{ + 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; +} + blob - /dev/null blob + 63e2e1b5506525e0b02e2d406297a10a798c1653 (mode 644) --- /dev/null +++ src/libthread/debug.c @@ -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(); +} blob - /dev/null blob + 5a37e34cbd7c68adee2acb94da20ab8feab03d3e (mode 644) --- /dev/null +++ src/libthread/exec-unix.c @@ -0,0 +1,124 @@ +#include +#include +#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){ /* exec failed */ + 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); +} blob - /dev/null blob + bcf2080285eade1f75c0d9cd0ac4b465f2f754b6 (mode 644) --- /dev/null +++ src/libthread/exec.c @@ -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){ /* exec failed */ + 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); +} + blob - /dev/null blob + ddf700143ac55e0cc7c538621ce673c186cafdb4 (mode 644) --- /dev/null +++ src/libthread/exit.c @@ -0,0 +1,63 @@ +#include "threadimpl.h" +#include + +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", _threadexitsallstatus); + 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 + +int +_threadgetpid(void) +{ + return getpid(); +} blob - /dev/null blob + 727798d38510545277c3887f605bc03ce2a0f062 (mode 644) --- /dev/null +++ src/libthread/id.c @@ -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) + abort(); + lock(&privlock); + privmask &= ~(1<thread->udata[i]; +} blob - /dev/null blob + d9cf9d04eeee76ee27a2057c03c88ffc61cfcc7c (mode 644) --- /dev/null +++ src/libthread/iocall.c @@ -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; +} blob - /dev/null blob + fbaabb7c9e6341c711175a5e3655291cfb2ce25e (mode 644) --- /dev/null +++ src/libthread/ioclose.c @@ -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); +} blob - /dev/null blob + 8171156c5cbfb50b258844cc9c760ee388175260 (mode 644) --- /dev/null +++ src/libthread/iodial.c @@ -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); +} blob - /dev/null blob + efca4ca6ab9bf2980bea8b6c488d703172c9bf13 (mode 644) --- /dev/null +++ src/libthread/ioopen.c @@ -0,0 +1,20 @@ +#include +#include +#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); +} blob - /dev/null blob + 1cada2de268c8218fb38b23db5efcb1abd370348 (mode 644) --- /dev/null +++ src/libthread/ioproc.3 @@ -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 +#include +#include +.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) + blob - /dev/null blob + 2b2a602526ef5b6c9c82359ae1ab93bdd3fed583 (mode 644) --- /dev/null +++ src/libthread/ioproc.c @@ -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); +} blob - /dev/null blob + 62b1be0306d57ee00e8916eb7d1c402f6f617ef9 (mode 644) --- /dev/null +++ src/libthread/ioread.c @@ -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); +} blob - /dev/null blob + b843f6033ed831dae0f4c7cd946f84c3d911420d (mode 644) --- /dev/null +++ src/libthread/ioreadn.c @@ -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); +} blob - /dev/null blob + 5575645440eb0c32ed9f83c1418f7580bec69a6d (mode 644) --- /dev/null +++ src/libthread/iosleep.c @@ -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); +} blob - /dev/null blob + 664a84bf207460c9983a99b2800c8318bdf0db59 (mode 644) --- /dev/null +++ src/libthread/iowrite.c @@ -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); +} blob - /dev/null blob + a9392ead787e9bd9f327eee9da438e68be96c161 (mode 644) --- /dev/null +++ src/libthread/kill.c @@ -0,0 +1,89 @@ +#include "threadimpl.h" +#include + +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; + } +} blob - /dev/null blob + 0c9f3030b5f7526220c8549c100f28335a7d2abd (mode 644) --- /dev/null +++ src/libthread/label.h @@ -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 + + blob - /dev/null blob + 86e7506e8ba26bc7ce4bc3f6ba69bb5f846c626a (mode 644) --- /dev/null +++ src/libthread/lib.c @@ -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); +} blob - /dev/null blob + 1acd8348bb1099998262866f7e35d4c6369230ea (mode 644) --- /dev/null +++ src/libthread/main.c @@ -0,0 +1,124 @@ +#include "threadimpl.h" +#include + +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); +} blob - /dev/null blob + dd74fb6cc87c58f2814dcd4f3d9a445274f0e771 (mode 644) --- /dev/null +++ src/libthread/memset.c @@ -0,0 +1,8 @@ +#include "threadimpl.h" +#include + +void +_threadmemset(void *v, int c, int n) +{ + memset(v, c, n); +} blob - /dev/null blob + ef3f96050591da7967260351de718de504215df9 (mode 644) --- /dev/null +++ src/libthread/memsetd.c @@ -0,0 +1,8 @@ +#include "threadimpl.h" +#include + +void +_threaddebugmemset(void *v, int c, int n) +{ + memset(v, c, n); +} blob - /dev/null blob + 703f6b06d8e3b134af7b4f1b01b739e1b5a86593 (mode 644) --- /dev/null +++ src/libthread/mkfile @@ -0,0 +1,2 @@ +<../libutf/mkfile + blob - /dev/null blob + b7f4b13752ae11ec0a7327b53553dc5ba1751724 (mode 644) --- /dev/null +++ src/libthread/note.c @@ -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; ipending) + return; + + p->pending = 0; + for(n=notes; nproc == p){ + for(i=0; ipid || (fn = onnote[i])==nil) + continue; + if((*fn)(v, n->s)) + break; + } + if(i==NFN){ + _threaddebug(DBGNOTE, "Unhandled note %s, proc %p\n", n->s, p); + 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", _threadexitsallstatus); +// _exits(_threadexitsallstatus); +// } + + if(strcmp(s, "threadint")==0) + noted(NCONT); + + p = _threadgetproc(); + if(p == nil) + noted(NDFLT); + + for(n=notes; ninuse)) + 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); +*/ +} + blob - /dev/null blob + 4029652a7ca372cb42f00f89554707f71e993bde (mode 644) --- /dev/null +++ src/libthread/proctab.c @@ -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); +} blob - /dev/null blob + 9b78e63e6f4b8ffad00f2af24936e1d932f4ee5f (mode 644) --- /dev/null +++ src/libthread/ref.c @@ -0,0 +1,13 @@ +#include "threadimpl.h" + +void +incref(Ref *r) +{ + _xinc(&r->ref); +} + +long +decref(Ref *r) +{ + return _xdec(&r->ref); +} blob - /dev/null blob + 62b825b5eeaa12a6f491c20359b13a6390d2910a (mode 644) --- /dev/null +++ src/libthread/rendez.c @@ -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", t->proc->pid, t->id); + *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; irendbreak){ + *l = t->rendhash; + finish(t, ~0); + }else + l=&t->rendhash; + } + } + unlock(&_threadrgrp.lock); +} blob - /dev/null blob + fdc72de213dd4f0e8ac0d1b66599d4a26b808adf (mode 644) --- /dev/null +++ src/libthread/rpm.spec @@ -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 +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 blob - /dev/null blob + 8609833d32561fe90ae1506dd77b3682d556273d (mode 644) --- /dev/null +++ src/libthread/sched.c @@ -0,0 +1,192 @@ +#include "threadimpl.h" +#include + +//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", _threadexitsallstatus); + 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 references? */ + 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, (ulong)t->stk); + 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(); +} + blob - /dev/null blob + 9ba86827512eb251afd27c0edeaef43fb4a8b165 (mode 644) --- /dev/null +++ src/libthread/texec.c @@ -0,0 +1,34 @@ +#include +#include +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->time[2], w->msg); + threadexits(nil); +} blob - /dev/null blob + 3009ac8478712c799d758bfb93e7ef67825cdf5c (mode 644) --- /dev/null +++ src/libthread/thread.3 @@ -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 +#include +#include +.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) blob - /dev/null blob + 10aac284824e67af1bff25a3ed7d25090769550d (mode 644) --- /dev/null +++ src/libthread/thread.h @@ -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 (may be zero) + unsigned int f; // Extraction point (insertion pt: (f + n) % s) + unsigned int n; // Number of values in the channel + int e; // Element size + int freed; // Set when channel is being deleted + volatile Alt **qentry; // Receivers/senders waiting (malloc) + volatile int nentry; // # of entries malloc-ed + unsigned char v[1]; // Array of s values in the channel +}; + + +/* 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 now zero */ +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 stacksize); +int procrfork(void (*f)(void *arg), void *arg, unsigned int stacksize, int flag); +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 stacksize); +void** threaddata(void); +void threadexits(char *); +void threadexitsall(char *); +int threadgetgrp(void); /* return thread group of current thread */ +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_ */ blob - /dev/null blob + 24d9b214d3c5d2ab1bb05010c5df248bd22b3e2d (mode 644) --- /dev/null +++ src/libthread/threadimpl.h @@ -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 +#include +#include +#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 data structure */ + 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 address of stack) */ + uint stksize; /* stack size */ + Thread *next; /* next on ready queue */ + + Proc *proc; /* proc of this thread */ + Thread *nextt; /* next on list of threads in this proc */ + Thread *prevt; /* prev on list of threads in this proc */ + int ret; /* return value for Exec, Fork */ + + 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 current */ + Alt *alt; /* pointer to current alt structure (debugging) */ + + 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 created thread */ + Proc *next; /* linked list of Procs */ + + void *arg; /* passed between shared and unshared stk */ + char str[ERRMAX]; /* used by threadexits to avoid malloc */ + 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); + blob - /dev/null blob + 52d6f05e21063ad5b8df8754e0e3ed6433fbbd03 (mode 755) Binary files /dev/null and src/libthread/tprimes differ blob - /dev/null blob + 9426bdfad7ac69a191256ee27ce4d3dc3569463f (mode 644) --- /dev/null +++ src/libthread/tprimes.c @@ -0,0 +1,62 @@ +#include +#include + +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); +}