Initial revision

This commit is contained in:
rsc
2003-09-30 17:47:42 +00:00
parent ed7c8e8d02
commit 76193d7cb0
223 changed files with 32479 additions and 0 deletions

150
man/man7/regexp9.7 Normal file
View File

@@ -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)

91
man/man7/utf.7 Normal file
View File

@@ -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" .

258
src/cmd/mk/LICENSE Normal file
View File

@@ -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.

View File

@@ -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

View File

@@ -0,0 +1,6 @@
CC=cc
CFLAGS=-O -c -Ae -I.
O=o
AR=ar
ARFLAGS=rvc
NAN=nan64.$O

View File

@@ -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

View File

@@ -0,0 +1,6 @@
CC=cc
CFLAGS+=-g -c -I.
O=o
AR=ar
ARFLAGS=rvc
NAN=nan64.$O

View File

@@ -0,0 +1,2 @@
include Make.SunOS-sun4u-$(CC)
NAN=nan64.$O

View File

@@ -0,0 +1,6 @@
CC=cc
CFLAGS+=-g -c -I. -O
O=o
AR=ar
ARFLAGS=rvc
NAN=nan64.$O

View File

@@ -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

117
src/cmd/mk/Makefile Normal file
View File

@@ -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

45
src/cmd/mk/Makefile.MID Normal file
View File

@@ -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

52
src/cmd/mk/arc.c Normal file
View File

@@ -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);
}

180
src/cmd/mk/archive.c Normal file
View File

@@ -0,0 +1,180 @@
#include "mk.h"
#define ARMAG "!<arch>\n"
#define SARMAG 8
#define ARFMAG "`\n"
#define SARNAME 16
struct ar_hdr
{
char name[SARNAME];
char date[12];
char uid[6];
char gid[6];
char mode[8];
char size[10];
char fmag[2];
};
#define SAR_HDR (SARNAME+44)
static int dolong;
static void atimes(char *);
static char *split(char*, char**);
long
atimeof(int force, char *name)
{
Symtab *sym;
long t;
char *archive, *member, buf[512];
archive = split(name, &member);
if(archive == 0)
Exit();
t = mtime(archive);
sym = symlook(archive, S_AGG, 0);
if(sym){
if(force || (t > (long)sym->value)){
atimes(archive);
sym->value = (void *)t;
}
}
else{
atimes(archive);
/* mark the aggegate as having been done */
symlook(strdup(archive), S_AGG, "")->value = (void *)t;
}
/* truncate long member name to sizeof of name field in 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;
}

46
src/cmd/mk/bundle.ports Normal file
View File

@@ -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 <bsd.port.pre.mk>
post-patch:
${REINPLACE_CMD} -e 's,$$(PREFIX),${PREFIX},g' ${WRKSRC}/Makefile
.include <bsd.port.post.mk>
--- pkg-comment ---
Streamlined replacement for make
--- pkg-descr ---
Mk is a streamlined replacement for make, written for
Tenth Edition Research Unix by Andrew Hume.
WWW: http://pdos.lcs.mit.edu/~rsc/software/#mk
Russ Cox
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.

149
src/cmd/mk/env.c Normal file
View File

@@ -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&REGEXP)
envupd("stem",newword(""));
else
envupd("stem", newword(j->stem));
envupd("prereq", wdup(j->p));
sprint(buf, "%d", getpid());
envupd("pid", newword(buf));
sprint(buf, "%d", slot);
envupd("nproc", newword(buf));
envupd("newprereq", wdup(j->np));
envupd("alltarget", wdup(j->at));
l = &v;
v = w = wdup(j->np);
while(w){
cp = strchr(w->s, '(');
if(cp){
qp = strchr(cp+1, ')');
if(qp){
*qp = 0;
strcpy(w->s, cp+1);
l = &w->next;
w = w->next;
continue;
}
}
*l = w->next;
free(w->s);
free(w);
w = *l;
}
envupd("newmember", v);
/* update stem0 -> stem9 */
for(p = myenv; *p; p++)
if(strcmp(*p, "stem0") == 0)
break;
for(i = 0; *p; i++, p++){
if((j->r->attr&REGEXP) && j->match[i])
envupd(*p, newword(j->match[i]));
else
envupd(*p, newword(""));
}
return envy;
}

90
src/cmd/mk/file.c Normal file
View File

@@ -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;
}
}
}

84
src/cmd/mk/fns.h Normal file
View File

@@ -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);

279
src/cmd/mk/graph.c Normal file
View File

@@ -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&REGEXP){
stem[0] = 0;
patrule = r;
memset((char*)rmatch, 0, sizeof(rmatch));
if(regexec(r->pat, node->name, rmatch, NREGEXP) == 0)
continue;
} else {
if(!match(node->name, r->target, stem)) continue;
}
if(cnt[r->rule] >= nreps) continue;
cnt[r->rule]++;
/* if(r->attr&VIR)
* node->flags |= VIRTUAL;
* if(r->attr&NOREC)
* node->flags |= NORECIPE;
* if(r->attr&DEL)
* node->flags |= DELETE;
*/
if(!r->tail || !r->tail->s || !*r->tail->s) {
a->next = newarc((Node *)0, r, stem, rmatch);
a = a->next;
} else
for(w = r->tail; w; w = w->next){
if(r->attr&REGEXP)
regsub(w->s, buf, sizeof buf, rmatch, NREGEXP);
else
subst(stem, w->s, buf);
a->next = newarc(applyrules(buf, cnt), r, stem, rmatch);
a = a->next;
}
cnt[r->rule]--;
}
a->next = node->prereqs;
node->prereqs = head.next;
return(node);
}
static void
togo(Node *node)
{
Arc *la, *a;
/* delete them now */
la = 0;
for(a = node->prereqs; a; la = a, a = a->next)
if(a->flag&TOGO){
if(a == node->prereqs)
node->prereqs = a->next;
else
la->next = a->next, a = la;
}
}
static int
vacuous(Node *node)
{
Arc *la, *a;
int vac = !(node->flags&PROBABLE);
if(node->flags&READY)
return(node->flags&VACUOUS);
node->flags |= READY;
for(a = node->prereqs; a; a = a->next)
if(a->n && vacuous(a->n) && (a->r->attr&META))
a->flag |= TOGO;
else
vac = 0;
/* if a rule generated arcs that DON'T go; no others from that rule go */
for(a = node->prereqs; a; a = a->next)
if((a->flag&TOGO) == 0)
for(la = node->prereqs; la; la = la->next)
if((la->flag&TOGO) && (la->r == a->r)){
la->flag &= ~TOGO;
}
togo(node);
if(vac)
node->flags |= VACUOUS;
return(vac);
}
static Node *
newnode(char *name)
{
register Node *node;
node = (Node *)Malloc(sizeof(Node));
symlook(name, S_NODE, (void *)node);
node->name = name;
node->time = timeof(name, 0);
node->prereqs = 0;
node->flags = node->time? PROBABLE : 0;
node->next = 0;
return(node);
}
void
dumpn(char *s, Node *n)
{
char buf[1024];
Arc *a;
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;
}

147
src/cmd/mk/lex.c Normal file
View File

@@ -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;
}

285
src/cmd/mk/main.c Normal file
View File

@@ -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();
}

49
src/cmd/mk/match.c Normal file
View File

@@ -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;
}

665
src/cmd/mk/mk.1 Normal file
View File

@@ -0,0 +1,665 @@
.TH MK 1
.de EX
.nf
.ft B
..
.de EE
.fi
.ft R
..
.de LR
.if t .BR \\$1 \\$2
.if n .RB ` \\$1 '\\$2
..
.de L
.nh
.if t .B \\$1
.if n .RB ` \\$1 '
..
.SH NAME
mk \- maintain (make) related files
.SH SYNOPSIS
.B mk
[
.B -f
.I mkfile
] ...
[
.I option ...
]
[
.I target ...
]
.SH DESCRIPTION
.I Mk
uses the dependency rules specified in
.I mkfile
to control the update (usually by compilation) of
.I targets
(usually files)
from the source files upon which they depend.
The
.I mkfile
(default
.LR mkfile )
contains a
.I rule
for each target that identifies the files and other
targets upon which it depends and an
.IR sh (1)
script, a
.IR recipe ,
to update the target.
The script is run if the target does not exist
or if it is older than any of the files it depends on.
.I Mkfile
may also contain
.I meta-rules
that define actions for updating implicit targets.
If no
.I target
is specified, the target of the first rule (not meta-rule) in
.I mkfile
is updated.
.PP
The environment variable
.B $NPROC
determines how many targets may be updated simultaneously;
Some operating systems, e.g., Plan 9, set
.B $NPROC
automatically to the number of CPUs on the current machine.
.PP
Options are:
.TP \w'\fL-d[egp]\ 'u
.B -a
Assume all targets to be out of date.
Thus, everything is updated.
.PD 0
.TP
.BR -d [ egp ]
Produce debugging output
.RB ( p
is for parsing,
.B g
for graph building,
.B e
for execution).
.TP
.B -e
Explain why each target is made.
.TP
.B -i
Force any missing intermediate targets to be made.
.TP
.B -k
Do as much work as possible in the face of errors.
.TP
.B -n
Print, but do not execute, the commands
needed to update the targets.
.TP
.B -s
Make the command line arguments sequentially rather than in parallel.
.TP
.B -t
Touch (update the modified date of) file targets, without
executing any recipes.
.TP
.BI -w target1 , target2,...
Pretend the modify time for each
.I target
is the current time; useful in conjunction with
.B -n
to learn what updates would be triggered by
modifying the
.IR targets .
.PD
.SS The \fLmkfile\fP
A
.I mkfile
consists of
.I assignments
(described under `Environment') and
.IR rules .
A rule contains
.I targets
and a
.IR tail .
A target is a literal string
and is normally a file name.
The tail contains zero or more
.I prerequisites
and an optional
.IR recipe ,
which is an
.B shell
script.
Each line of the recipe must begin with white space.
A rule takes the form
.IP
.EX
target: prereq1 prereq2
\f2recipe using\fP prereq1, prereq2 \f2to build\fP target
.EE
.PP
When the recipe is executed,
the first character on every line is elided.
.PP
After the colon on the target line, a rule may specify
.IR attributes ,
described below.
.PP
A
.I meta-rule
has a target of the form
.IB A % B
where
.I A
and
.I B
are (possibly empty) strings.
A meta-rule acts as a rule for any potential target whose
name matches
.IB A % B
with
.B %
replaced by an arbitrary string, called the
.IR stem .
In interpreting a meta-rule,
the stem is substituted for all occurrences of
.B %
in the prerequisite names.
In the recipe of a meta-rule, the environment variable
.B $stem
contains the string matched by the
.BR % .
For example, a meta-rule to compile a C program using
.IR cc (1)
might be:
.IP
.EX
%: %.c
cc -c $stem.c
cc -o $stem $stem.o
.EE
.PP
Meta-rules may contain an ampersand
.B &
rather than a percent sign
.BR % .
A
.B %
matches a maximal length string of any characters;
an
.B &
matches a maximal length string of any characters except period
or slash.
.PP
The text of the
.I mkfile
is processed as follows.
Lines beginning with
.B <
followed by a file name are replaced by the contents of the named
file.
Lines beginning with
.B "<|"
followed by a file name are replaced by the output
of the execution of the named
file.
Blank lines and comments, which run from unquoted
.B #
characters to the following newline, are deleted.
The character sequence backslash-newline is deleted,
so long lines in
.I mkfile
may be folded.
Non-recipe lines are processed by substituting for
.BI `{ command }
the output of the
.I command
when run by
.IR sh .
References to variables are replaced by the variables' values.
Special characters may be quoted using single quotes
.BR \&''
as in
.IR sh (1).
.PP
Assignments and rules are distinguished by
the first unquoted occurrence of
.B :
(rule)
or
.B =
(assignment).
.PP
A later rule may modify or override an existing rule under the
following conditions:
.TP
\-
If the targets of the rules exactly match and one rule
contains only a prerequisite clause and no recipe, the
clause is added to the prerequisites of the other rule.
If either or both targets are virtual, the recipe is
always executed.
.TP
\-
If the targets of the rules match exactly and the
prerequisites do not match and both rules
contain recipes,
.I mk
reports an ``ambiguous recipe'' error.
.TP
\-
If the target and prerequisites of both rules match exactly,
the second rule overrides the first.
.SS Environment
Rules may make use of
shell
environment variables.
A legal reference of the form
.B $OBJ
or
.B ${name}
is expanded as in
.IR sh (1).
A reference of the form
.BI ${name: A % B = C\fL%\fID\fL}\fR,
where
.I A, B, C, D
are (possibly empty) strings,
has the value formed by expanding
.B $name
and substituting
.I C
for
.I A
and
.I D
for
.I B
in each word in
.B $name
that matches pattern
.IB A % B\f1.
.PP
Variables can be set by
assignments of the form
.I
var\fL=\fR[\fIattr\fL=\fR]\fIvalue\fR
.br
Blanks in the
.I value
break it into words.
Such variables are exported
to the environment of
recipes as they are executed, unless
.BR U ,
the only legal attribute
.IR attr ,
is present.
The initial value of a variable is
taken from (in increasing order of precedence)
the default values below,
.I mk's
environment, the
.IR mkfiles ,
and any command line assignment as an argument to
.IR mk .
A variable assignment argument overrides the first (but not any subsequent)
assignment to that variable.
The variable
.B MKFLAGS
contains all the option arguments (arguments starting with
.L -
or containing
.LR = )
and
.B MKARGS
contains all the targets in the call to
.IR mk .
.PP
Dynamic information may be included in the mkfile by using a line of the form
.IP
\fR<|\fIcommand\fR \fIargs\fR
.LP
This runs the command
.I command
with the given arguments
.I args
and pipes its standard output to
.I mk
to be included as part of the mkfile. For instance, the Inferno kernels
use this technique
to run a shell command with an awk script and a configuration
file as arguments in order for
the
.I awk
script to process the file and output a set of variables and their values.
.SS Execution
.PP
During execution,
.I mk
determines which targets must be updated, and in what order,
to build the
.I names
specified on the command line.
It then runs the associated recipes.
.PP
A target is considered up to date if it has no prerequisites or
if all its prerequisites are up to date and it is newer
than all its prerequisites.
Once the recipe for a target has executed, the target is
considered up to date.
.PP
The date stamp
used to determine if a target is up to date is computed
differently for different types of targets.
If a target is
.I virtual
(the target of a rule with the
.B V
attribute),
its date stamp is initially zero; when the target is
updated the date stamp is set to
the most recent date stamp of its prerequisites.
Otherwise, if a target does not exist as a file,
its date stamp is set to the most recent date stamp of its prerequisites,
or zero if it has no prerequisites.
Otherwise, the target is the name of a file and
the target's date stamp is always that file's modification date.
The date stamp is computed when the target is needed in
the execution of a rule; it is not a static value.
.PP
Nonexistent targets that have prerequisites
and are themselves prerequisites are treated specially.
Such a target
.I t
is given the date stamp of its most recent prerequisite
and if this causes all the targets which have
.I t
as a prerequisite to be up to date,
.I t
is considered up to date.
Otherwise,
.I t
is made in the normal fashion.
The
.B -i
flag overrides this special treatment.
.PP
Files may be made in any order that respects
the preceding restrictions.
.PP
A recipe is executed by supplying the recipe as standard input to
the command
.BR /bin/sh .
(Note that unlike
.IR make ,
.I mk
feeds the entire recipe to the shell rather than running each line
of the recipe separately.)
The environment is augmented by the following variables:
.TP 14
.B $alltarget
all the targets of this rule.
.TP
.B $newprereq
the prerequisites that caused this rule to execute.
.TP
.B $newmember
the prerequisites that are members of an aggregate
that caused this rule to execute.
When the prerequisites of a rule are members of an
aggregate,
.B $newprereq
contains the name of the aggregate and out of date
members, while
.B $newmember
contains only the name of the members.
.TP
.B $nproc
the process slot for this recipe.
It satisfies
.RB 0 $nproc < $NPROC .
.TP
.B $pid
the process id for the
.I mk
executing the recipe.
.TP
.B $prereq
all the prerequisites for this rule.
.TP
.B $stem
if this is a meta-rule,
.B $stem
is the string that matched
.B %
or
.BR & .
Otherwise, it is empty.
For regular expression meta-rules (see below), the variables
.LR stem0 ", ...,"
.L stem9
are set to the corresponding subexpressions.
.TP
.B $target
the targets for this rule that need to be remade.
.PP
These variables are available only during the execution of a recipe,
not while evaluating the
.IR mkfile .
.PP
Unless the rule has the
.B Q
attribute,
the recipe is printed prior to execution
with recognizable environment variables expanded.
Commands returning error status
cause
.I mk
to terminate.
.PP
Recipes and backquoted
.B rc
commands in places such as assignments
execute in a copy of
.I mk's
environment; changes they make to
environment variables are not visible from
.IR mk .
.PP
Variable substitution in a rule is done when
the rule is read; variable substitution in the recipe is done
when the recipe is executed. For example:
.IP
.EX
bar=a.c
foo: $bar
$CC -o foo $bar
bar=b.c
.EE
.PP
will compile
.B b.c
into
.BR foo ,
if
.B a.c
is newer than
.BR foo .
.SS Aggregates
Names of the form
.IR a ( b )
refer to member
.I b
of the aggregate
.IR a .
Currently, the only aggregates supported are
.IR ar (1)
archives.
.SS Attributes
The colon separating the target from the prerequisites
may be
immediately followed by
.I attributes
and another colon.
The attributes are:
.TP
.B D
If the recipe exits with a non-null status, the target is deleted.
.TP
.B E
Continue execution if the recipe draws errors.
.TP
.B N
If there is no recipe, the target has its time updated.
.TP
.B n
The rule is a meta-rule that cannot be a target of a virtual rule.
Only files match the pattern in the target.
.TP
.B P
The characters after the
.B P
until the terminating
.B :
are taken as a program name.
It will be invoked as
.B "sh -c prog 'arg1' 'arg2'"
and should return a zero exit status
if and only if arg1 is up to date with respect to arg2.
Date stamps are still propagated in the normal way.
.TP
.B Q
The recipe is not printed prior to execution.
.TP
.B R
The rule is a meta-rule using regular expressions.
In the rule,
.B %
has no special meaning.
The target is interpreted as a regular expression as defined in
.IR regexp (6).
The prerequisites may contain references
to subexpressions in form
.BI \e n\f1,
as in the substitute command of
.IR sed (1).
.TP
.B U
The targets are considered to have been updated
even if the recipe did not do so.
.TP
.B V
The targets of this rule are marked as virtual.
They are distinct from files of the same name.
.PD
.SH EXAMPLES
A simple mkfile to compile a program:
.IP
.EX
.ta 8n +8n +8n +8n +8n +8n +8n
</$objtype/mkfile
prog: a.$O b.$O c.$O
$LD $LDFLAGS -o $target $prereq
%.$O: %.c
$CC $CFLAGS $stem.c
.EE
.PP
Override flag settings in the mkfile:
.IP
.EX
% mk target 'CFLAGS=-S -w'
.EE
.PP
Maintain a library:
.IP
.EX
libc.a(%.$O):N: %.$O
libc.a: libc.a(abs.$O) libc.a(access.$O) libc.a(alarm.$O) ...
ar r libc.a $newmember
.EE
.PP
String expression variables to derive names from a master list:
.IP
.EX
NAMES=alloc arc bquote builtins expand main match mk var word
OBJ=${NAMES:%=%.$O}
.EE
.PP
Regular expression meta-rules:
.IP
.EX
([^/]*)/(.*)\e.$O:R: \e1/\e2.c
cd $stem1; $CC $CFLAGS $stem2.c
.EE
.PP
A correct way to deal with
.IR yacc (1)
grammars.
The file
.B lex.c
includes the file
.B x.tab.h
rather than
.B y.tab.h
in order to reflect changes in content, not just modification time.
.IP
.EX
lex.$O: x.tab.h
x.tab.h: y.tab.h
cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h
y.tab.c y.tab.h: gram.y
$YACC -d gram.y
.EE
.PP
The above example could also use the
.B P
attribute for the
.B x.tab.h
rule:
.IP
.EX
x.tab.h:Pcmp -s: y.tab.h
cp y.tab.h x.tab.h
.EE
.SH SEE ALSO
.IR sh (1),
.IR regexp9 (7)
.PP
A. Hume,
``Mk: a Successor to Make''
(Tenth Edition Research Unix Manuals).
.PP
Andrew G. Hume and Bob Flandrena,
``Maintaining Files on Plan 9 with Mk''.
DOCPREFIX/doc/mk.pdf
.SH HISTORY
Andrew Hume wrote
.I mk
for Tenth Edition Research Unix.
It was later ported to Plan 9.
This software is a port of the Plan 9 version back to Unix.
.SH BUGS
Identical recipes for regular expression meta-rules only have one target.
.br
Seemingly appropriate input like
.B CFLAGS=-DHZ=60
is parsed as an erroneous attribute; correct it by inserting
a space after the first
.LR = .
.br
The recipes printed by
.I mk
before being passed to
.I sh
for execution are sometimes erroneously expanded
for printing. Don't trust what's printed; rely
on what
.I sh
does.

226
src/cmd/mk/mk.c Normal file
View File

@@ -0,0 +1,226 @@
#include "mk.h"
int runerrs;
void
mk(char *target)
{
Node *node;
int did = 0;
nproc(); /* it can be updated dynamically */
nrep(); /* it can be updated dynamically */
runerrs = 0;
node = graph(target);
if(DEBUG(D_GRAPH)){
dumpn("new target\n", node);
Bflush(&bout);
}
clrmade(node);
while(node->flags&NOTMADE){
if(work(node, (Node *)0, (Arc *)0))
did = 1; /* found something to do */
else {
if(waitup(1, (int *)0) > 0){
if(node->flags&(NOTMADE|BEINGMADE)){
assert("must be run errors", runerrs);
break; /* nothing more waiting */
}
}
}
}
if(node->flags&BEINGMADE)
waitup(-1, (int *)0);
while(jobs)
waitup(-2, (int *)0);
assert("target didn't get done", runerrs || (node->flags&MADE));
if(did == 0)
Bprint(&bout, "mk: '%s' is up to date\n", node->name);
}
void
clrmade(Node *n)
{
Arc *a;
n->flags &= ~(CANPRETEND|PRETENDING);
if(strchr(n->name, '(') ==0 || n->time)
n->flags |= CANPRETEND;
MADESET(n, NOTMADE);
for(a = n->prereqs; a; a = a->next)
if(a->n)
clrmade(a->n);
}
static void
unpretend(Node *n)
{
MADESET(n, NOTMADE);
n->flags &= ~(CANPRETEND|PRETENDING);
n->time = 0;
}
int
work(Node *node, Node *p, Arc *parc)
{
Arc *a, *ra;
int weoutofdate;
int ready;
int did = 0;
/*print("work(%s) flags=0x%x time=%ld\n", node->name, node->flags, 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;
}

203
src/cmd/mk/mk.h Normal file
View File

@@ -0,0 +1,203 @@
#include <utf.h>
#include <fmt.h>
#include <setjmp.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <bio.h>
#include <regexp9.h>
#include <time.h>
#define uchar _mkuchar
#define ushort _mkushort
#define uint _mkuint
#define ulong _mkulong
#define vlong _mkvlong
#define uvlong _mkuvlong
#define nil ((void*)0)
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
#define nelem(x) (sizeof(x)/sizeof((x)[0]))
#define OREAD O_RDONLY
#define OWRITE O_WRONLY
#define ORDWR O_RDWR
#define USED(x) if(x);else
#define remove unlink
#define seek lseek
#define exits(s) exit((s) && ((char*)s)[0] ? 1 : 0)
#define create(name, mode, perm) creat(name, perm)
#define ERRMAX 256
#undef assert
#define assert mkassert
extern Biobuf bout;
typedef struct Bufblock
{
struct Bufblock *next;
char *start;
char *end;
char *current;
} Bufblock;
typedef struct Word
{
char *s;
struct Word *next;
} Word;
typedef struct Envy
{
char *name;
Word *values;
} Envy;
extern Envy *envy;
typedef struct Rule
{
char *target; /* one target */
Word *tail; /* constituents of targets */
char *recipe; /* do it ! */
short attr; /* attributes */
short line; /* source line */
char *file; /* source file */
Word *alltargets; /* all the targets */
int rule; /* rule number */
Reprog *pat; /* reg exp goo */
char *prog; /* to use in out of date */
struct Rule *chain; /* hashed per target */
struct Rule *next;
} Rule;
extern Rule *rules, *metarules, *patrule;
/* Rule.attr */
#define META 0x0001
#define UNUSED 0x0002
#define UPD 0x0004
#define QUIET 0x0008
#define VIR 0x0010
#define REGEXP 0x0020
#define NOREC 0x0040
#define DEL 0x0080
#define NOVIRT 0x0100
#define NREGEXP 10
typedef struct Arc
{
short flag;
struct Node *n;
Rule *r;
char *stem;
char *prog;
char *match[NREGEXP];
struct Arc *next;
} Arc;
/* Arc.flag */
#define TOGO 1
typedef struct Node
{
char *name;
long time;
unsigned short flags;
Arc *prereqs;
struct Node *next; /* list for a rule */
} Node;
/* Node.flags */
#define VIRTUAL 0x0001
#define CYCLE 0x0002
#define READY 0x0004
#define CANPRETEND 0x0008
#define PRETENDING 0x0010
#define NOTMADE 0x0020
#define BEINGMADE 0x0040
#define MADE 0x0080
#define MADESET(n,m) n->flags = (n->flags&~(NOTMADE|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"

9
src/cmd/mk/mkfile Normal file
View File

@@ -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

5
src/cmd/mk/mkfile.test Normal file
View File

@@ -0,0 +1,5 @@
a: b
cp b a
c:V:
echo hello world

307
src/cmd/mk/parse.c Normal file
View File

@@ -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&REGEXP)){
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);
}

175
src/cmd/mk/rc.c Normal file
View File

@@ -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;
}

117
src/cmd/mk/recipe.c Normal file
View File

@@ -0,0 +1,117 @@
#include "mk.h"
int
dorecipe(Node *node)
{
char buf[BIGBLOCK];
register Node *n;
Rule *r = 0;
Arc *a, *aa;
Word head, ahead, lp, ln, *w, *ww, *aw;
Symtab *s;
int did = 0;
aa = 0;
/*
pick up the rule
*/
for(a = node->prereqs; a; a = a->next)
if(*a->r->recipe)
r = (aa = a)->r;
/*
no recipe? go to buggery!
*/
if(r == 0){
if(!(node->flags&VIRTUAL) && !(node->flags&NORECIPE)){
fprint(2, "mk: no recipe to make '%s'\n", node->name);
Exit();
}
if(strchr(node->name, '(') && node->time == 0)
MADESET(node, MADE);
else
update(0, node);
if(tflag){
if(!(node->flags&VIRTUAL))
touch(node->name);
else if(explain)
Bprint(&bout, "no touch of virtual '%s'\n", node->name);
}
return(did);
}
/*
build the node list
*/
node->next = 0;
head.next = 0;
ww = &head;
ahead.next = 0;
aw = &ahead;
if(r->attr&REGEXP){
ww->next = newword(node->name);
aw->next = newword(node->name);
} else {
for(w = r->alltargets; w; w = w->next){
if(r->attr&META)
subst(aa->stem, w->s, buf);
else
strcpy(buf, w->s);
aw->next = newword(buf);
aw = aw->next;
if((s = symlook(buf, S_NODE, 0)) == 0)
continue; /* not a node we are interested in */
n = (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);
}

29
src/cmd/mk/rpm.spec Normal file
View File

@@ -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 <rsc@post.harvard.edu>
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

107
src/cmd/mk/rule.c Normal file
View File

@@ -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&REGEXP) || charin(head, "%&")){
r->attr |= META;
if(reuse)
return;
if(attr&REGEXP){
patrule = r;
r->pat = regcomp(head);
}
if(metarules == 0)
metarules = lmr = r;
else {
lmr->next = r;
lmr = r;
}
} else {
if(reuse)
return;
r->pat = 0;
if(rules == 0)
rules = lr = r;
else {
lr->next = r;
lr = r;
}
}
}
void
dumpr(char *s, Rule *r)
{
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);
}

296
src/cmd/mk/run.c Normal file
View File

@@ -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]);
}

189
src/cmd/mk/sh.c Normal file
View File

@@ -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;
}

123
src/cmd/mk/shprint.c Normal file
View File

@@ -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);
}

95
src/cmd/mk/symtab.c Normal file
View File

@@ -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);
}

306
src/cmd/mk/unix.c Normal file
View File

@@ -0,0 +1,306 @@
#include "mk.h"
#include <sys/wait.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/time.h>
char *shell = "/bin/sh";
char *shellname = "sh";
extern char **environ;
static void
mkperror(char *s)
{
fprint(2, "%s: %r\n", s);
}
void
readenv(void)
{
char **p, *s;
Word *w;
for(p = environ; *p; p++){
s = shname(*p);
if(*s == '=') {
*s = 0;
w = newword(s+1);
} else
w = newword("");
if (symlook(*p, S_INTERNAL, 0))
continue;
s = strdup(*p);
setvar(s, (void *)w);
symlook(s, S_EXPORTED, (void*)"")->value = (void*)"";
}
}
/*
* done on child side of fork, so parent's env is not affected
* and we don't care about freeing memory because we're going
* to exec immediately after this.
*/
void
exportenv(Envy *e)
{
int i;
char **p;
char buf[4096];
p = 0;
for(i = 0; e->name; e++, i++) {
p = (char**) Realloc(p, (i+2)*sizeof(char*));
if(e->values)
sprint(buf, "%s=%s", e->name, wtos(e->values, IWS));
else
sprint(buf, "%s=", e->name);
p[i] = strdup(buf);
}
p[i] = 0;
environ = p;
}
int
waitfor(char *msg)
{
int status;
int pid;
*msg = 0;
pid = wait(&status);
if(pid > 0) {
if(status&0x7f) {
if(status&0x80)
snprint(msg, ERRMAX, "signal %d, core dumped", 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;
}

41
src/cmd/mk/var.c Normal file
View File

@@ -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;
}

256
src/cmd/mk/varsub.c Normal file
View File

@@ -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;
}

180
src/cmd/mk/word.c Normal file
View File

@@ -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');
}

258
src/cmd/sam/LICENSE Normal file
View File

@@ -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.

18
src/cmd/sam/Makefile Normal file
View File

@@ -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

240
src/cmd/sam/address.c Normal file
View File

@@ -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; i<file.nused; i++){
f = file.filepptr[i];
if(f == cmd)
continue;
if(filematch(f, r)){
if(match)
error(Emanyfiles);
match = f;
}
}
if(!match)
error(Efsearch);
return match;
}
int
filematch(File *f, String *r)
{
char *c, buf[STRSIZE+100];
String *t;
c = Strtoc(&f->name);
sprint(buf, "%c%c%c %s\n", " '"[f->mod],
"-+"[f->rasp!=0], " ."[f==curfile], c);
free(c);
t = tmpcstr(buf);
Strduplstr(&genstr, t);
freetmpstr(t);
/* A little dirty... */
if(menu == 0)
menu = fileopen();
bufreset(menu);
bufinsert(menu, 0, genstr.s, genstr.n);
compile(r);
return execute(menu, 0, menu->_.nc);
}
Address
charaddr(Posn l, Address addr, int sign)
{
if(sign == 0)
addr.r.p1 = addr.r.p2 = l;
else if(sign < 0)
addr.r.p2 = addr.r.p1-=l;
else if(sign > 0)
addr.r.p1 = addr.r.p2+=l;
if(addr.r.p1<0 || addr.r.p2>addr.f->_.nc)
error(Erange);
return addr;
}
Address
lineaddr(Posn l, Address addr, int sign)
{
int n;
int c;
File *f = addr.f;
Address a;
Posn p;
a.f = f;
if(sign >= 0){
if(l == 0){
if(sign==0 || addr.r.p2==0){
a.r.p1 = a.r.p2 = 0;
return a;
}
a.r.p1 = addr.r.p2;
p = addr.r.p2-1;
}else{
if(sign==0 || addr.r.p2==0){
p = (Posn)0;
n = 1;
}else{
p = addr.r.p2-1;
n = filereadc(f, p++)=='\n';
}
while(n < l){
if(p >= f->_.nc)
error(Erange);
if(filereadc(f, p++) == '\n')
n++;
}
a.r.p1 = p;
}
while(p < f->_.nc && filereadc(f, p++)!='\n')
;
a.r.p2 = p;
}else{
p = addr.r.p1;
if(l == 0)
a.r.p2 = addr.r.p1;
else{
for(n = 0; n<l; ){ /* always runs once */
if(p == 0){
if(++n != l)
error(Erange);
}else{
c = filereadc(f, p-1);
if(c != '\n' || ++n != l)
p--;
}
}
a.r.p2 = p;
if(p > 0)
p--;
}
while(p > 0 && filereadc(f, p-1)!='\n') /* lines start after a newline */
p--;
a.r.p1 = p;
}
return a;
}

302
src/cmd/sam/buff.c Normal file
View File

@@ -0,0 +1,302 @@
#include "sam.h"
enum
{
Slop = 100, /* room to grow with reallocation */
};
static
void
sizecache(Buffer *b, uint n)
{
if(n <= b->cmax)
return;
b->cmax = n+Slop;
b->c = runerealloc(b->c, b->cmax);
}
static
void
addblock(Buffer *b, uint i, uint n)
{
if(i > b->nbl)
panic("internal error: addblock");
b->bl = realloc(b->bl, (b->nbl+1)*sizeof b->bl[0]);
if(i < b->nbl)
memmove(b->bl+i+1, b->bl+i, (b->nbl-i)*sizeof(Block*));
b->bl[i] = disknewblock(disk, n);
b->nbl++;
}
static
void
delblock(Buffer *b, uint i)
{
if(i >= b->nbl)
panic("internal error: delblock");
diskrelease(disk, b->bl[i]);
b->nbl--;
if(i < b->nbl)
memmove(b->bl+i, b->bl+i+1, (b->nbl-i)*sizeof(Block*));
b->bl = realloc(b->bl, b->nbl*sizeof b->bl[0]);
}
/*
* Move cache so b->cq <= q0 < b->cq+b->cnc.
* If at very end, q0 will fall on end of cache block.
*/
static
void
flush(Buffer *b)
{
if(b->cdirty || b->cnc==0){
if(b->cnc == 0)
delblock(b, b->cbi);
else
diskwrite(disk, &b->bl[b->cbi], b->c, b->cnc);
b->cdirty = FALSE;
}
}
static
void
setcache(Buffer *b, uint q0)
{
Block **blp, *bl;
uint i, q;
if(q0 > b->nc)
panic("internal error: setcache");
/*
* flush and reload if q0 is not in cache.
*/
if(b->nc == 0 || (b->cq<=q0 && q0<b->cq+b->cnc))
return;
/*
* if q0 is at end of file and end of cache, continue to grow this 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;
}

594
src/cmd/sam/cmd.c Normal file
View File

@@ -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'<c) /* no number defaults to 1 */
return sign;
while('0'<=(c=getch()) && c<='9')
n = n*10 + (c-'0');
ungetch();
return sign*n;
}
int
skipbl(void)
{
int c;
do
c = getch();
while(c==' ' || c=='\t');
if(c >= 0)
ungetch();
return c;
}
void
termcommand(void)
{
Posn p;
for(p=cmdpt; p<cmd->_.nc; p++){
if(terminp >= &termline[BLOCKSIZE]){
cmdpt = cmd->_.nc;
error(Etoolong);
}
*terminp++ = filereadc(cmd, p);
}
cmdpt = cmd->_.nc;
}
void
cmdloop(void)
{
Cmd *cmdp;
File *ocurfile;
int loaded;
for(;;){
if(!downloaded && curfile && curfile->unread)
load(curfile);
if((cmdp = parsecmd(0))==0){
if(downloaded){
rescue();
exits("eof");
}
break;
}
ocurfile = curfile;
loaded = curfile && !curfile->unread;
if(cmdexec(curfile, cmdp) == 0)
break;
freecmd();
cmdupdate();
update();
if(downloaded && curfile &&
(ocurfile!=curfile || (!loaded && !curfile->unread)))
outTs(Hcurrent, curfile->tag);
/* don't allow type ahead on files that aren't bound */
if(downloaded && curfile && curfile->rasp == 0)
terminp = termoutp;
}
}
Cmd *
newcmd(void){
Cmd *p;
p = emalloc(sizeof(Cmd));
inslist(&cmdlist, cmdlist.nused, (long)p);
return p;
}
Addr*
newaddr(void)
{
Addr *p;
p = emalloc(sizeof(Addr));
inslist(&addrlist, addrlist.nused, (long)p);
return p;
}
String*
newre(void)
{
String *p;
p = emalloc(sizeof(String));
inslist(&relist, relist.nused, (long)p);
Strinit(p);
return p;
}
String*
newstring(void)
{
String *p;
p = emalloc(sizeof(String));
inslist(&stringlist, stringlist.nused, (long)p);
Strinit(p);
return p;
}
void
freecmd(void)
{
int i;
while(cmdlist.nused > 0)
free(cmdlist.ucharpptr[--cmdlist.nused]);
while(addrlist.nused > 0)
free(addrlist.ucharpptr[--addrlist.nused]);
while(relist.nused > 0){
i = --relist.nused;
Strclose(relist.stringpptr[i]);
free(relist.stringpptr[i]);
}
while(stringlist.nused>0){
i = --stringlist.nused;
Strclose(stringlist.stringpptr[i]);
free(stringlist.stringpptr[i]);
}
}
int
lookup(int c)
{
int i;
for(i=0; cmdtab[i].cmdc; i++)
if(cmdtab[i].cmdc == c)
return i;
return -1;
}
void
okdelim(int c)
{
if(c=='\\' || ('a'<=c && c<='z')
|| ('A'<=c && c<='Z') || ('0'<=c && c<='9'))
error_c(Edelim, c);
}
void
atnl(void)
{
skipbl();
if(getch() != '\n')
error(Enewline);
}
void
getrhs(String *s, int delim, int cmd)
{
int c;
while((c = getch())>0 && c!=delim && c!='\n'){
if(c == '\\'){
if((c=getch()) <= 0)
error(Ebadrhs);
if(c == '\n'){
ungetch();
c='\\';
}else if(c == 'n')
c='\n';
else if(c!=delim && (cmd=='s' || c!='\\')) /* s 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;
}

122
src/cmd/sam/disk.c Normal file
View File

@@ -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");
}

144
src/cmd/sam/error.c Normal file
View File

@@ -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));
}

65
src/cmd/sam/errors.h Normal file
View File

@@ -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;

631
src/cmd/sam/file.c Normal file
View File

@@ -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; i<f->ntext; i++)
if(f->text[i] == t)
goto Found;
panic("can't find text in filedeltext");
Found:
f->ntext--;
if(f->ntext == 0){
fileclose(f);
return;
}
memmove(f->text+i, f->text+i+1, (f->ntext-i)*sizeof(Text*));
if(f->curtext == t)
f->curtext = f->text[0];
}
#endif
void
fileinsert(File *f, uint p0, Rune *s, uint ns)
{
if(p0 > f->_.nc)
panic("internal error: fileinsert");
if(f->seq > 0)
fileuninsert(f, &f->delta, p0, ns);
bufinsert(f, p0, s, ns);
if(ns)
f->mod = TRUE;
}
void
fileuninsert(File *f, Buffer *delta, uint p0, uint ns)
{
Undo u;
/* undo an insertion by deleting */
u.type = Delete;
u.mod = f->mod;
u.seq = f->seq;
u.p0 = p0;
u.n = ns;
bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
}
void
filedelete(File *f, uint p0, uint p1)
{
if(!(p0<=p1 && p0<=f->_.nc && p1<=f->_.nc))
panic("internal error: filedelete");
if(f->seq > 0)
fileundelete(f, &f->delta, p0, p1);
bufdelete(f, p0, p1);
if(p1 > p0)
f->mod = TRUE;
}
void
fileundelete(File *f, Buffer *delta, uint p0, uint p1)
{
Undo u;
Rune *buf;
uint i, n;
/* undo a deletion by inserting */
u.type = Insert;
u.mod = f->mod;
u.seq = f->seq;
u.p0 = p0;
u.n = p1-p0;
buf = fbufalloc();
for(i=p0; i<p1; i+=n){
n = p1 - i;
if(n > RBUFSIZE)
n = RBUFSIZE;
bufread(f, i, buf, n);
bufinsert(delta, delta->nc, buf, n);
}
fbuffree(buf);
bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
}
int
filereadc(File *f, uint q)
{
Rune r;
if(q >= f->_.nc)
return -1;
bufread(f, q, &r, 1);
return r;
}
void
filesetname(File *f, String *s)
{
if(!f->unread) /* This is setting initial file name */
fileunsetname(f, &f->delta);
Strduplstr(&f->name, s);
sortname(f);
f->unread = TRUE;
}
void
fileunsetname(File *f, Buffer *delta)
{
String s;
Undo u;
/* undo a file name change by restoring old name */
u.type = Filename;
u.mod = f->mod;
u.seq = f->seq;
u.p0 = 0; /* unused */
Strinit(&s);
Strduplstr(&s, &f->name);
fullname(&s);
u.n = s.n;
if(s.n)
bufinsert(delta, delta->nc, s.s, s.n);
bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
Strclose(&s);
}
void
fileunsetdot(File *f, Buffer *delta, Range dot)
{
Undo u;
u.type = Dot;
u.mod = f->mod;
u.seq = f->seq;
u.p0 = dot.p1;
u.n = dot.p2 - dot.p1;
bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
}
void
fileunsetmark(File *f, Buffer *delta, Range mark)
{
Undo u;
u.type = Mark;
u.mod = f->mod;
u.seq = f->seq;
u.p0 = mark.p1;
u.n = mark.p2 - mark.p1;
bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
}
uint
fileload(File *f, uint p0, int fd, int *nulls)
{
if(f->seq > 0)
panic("undo in file.load unimplemented");
return bufload(f, p0, fd, nulls);
}
int
fileupdate(File *f, int notrans, int toterm)
{
uint p1, p2;
int mod;
if(f->rescuing)
return FALSE;
flushmerge();
/*
* fix the modification bit
* subtle point: don't save it away in the log.
*
* if another change is made, the correct f->mod
* state is saved in the undo log by filemark
* when setting the dot and mark.
*
* if the change is undone, the correct state is
* saved from f in the fileun... routines.
*/
mod = f->mod;
f->mod = f->prevmod;
if(f == cmd)
notrans = TRUE;
else{
fileunsetdot(f, &f->delta, f->prevdot);
fileunsetmark(f, &f->delta, f->prevmark);
}
f->dot = f->ndot;
fileundo(f, FALSE, !notrans, &p1, &p2, toterm);
f->mod = mod;
if(f->delta.nc == 0)
f->seq = 0;
if(f == cmd)
return FALSE;
if(f->mod){
f->closeok = 0;
quitok = 0;
}else
f->closeok = 1;
return TRUE;
}
long
prevseq(Buffer *b)
{
Undo u;
uint up;
up = b->nc;
if(up == 0)
return 0;
up -= Undosize;
bufread(b, up, (Rune*)&u, Undosize);
return u.seq;
}
long
undoseq(File *f, int isundo)
{
if(isundo)
return f->seq;
return prevseq(&f->epsilon);
}
void
fileundo(File *f, int isundo, int canredo, uint *q0p, uint *q1p, int flag)
{
Undo u;
Rune *buf;
uint i, n, up;
uint stop;
Buffer *delta, *epsilon;
if(isundo){
/* undo; reverse delta onto epsilon, seq decreases */
delta = &f->delta;
epsilon = &f->epsilon;
stop = f->seq;
}else{
/* redo; reverse epsilon onto delta, seq increases */
delta = &f->epsilon;
epsilon = &f->delta;
stop = 0; /* don't know yet */
}
raspstart(f);
while(delta->nc > 0){
up = delta->nc-Undosize;
bufread(delta, up, (Rune*)&u, Undosize);
if(isundo){
if(u.seq < stop){
f->seq = u.seq;
raspdone(f, flag);
return;
}
}else{
if(stop == 0)
stop = u.seq;
if(u.seq > stop){
raspdone(f, flag);
return;
}
}
switch(u.type){
default:
panic("undo unknown u.type");
break;
case Delete:
f->seq = u.seq;
if(canredo)
fileundelete(f, epsilon, u.p0, u.p0+u.n);
f->mod = u.mod;
bufdelete(f, u.p0, u.p0+u.n);
raspdelete(f, u.p0, u.p0+u.n, flag);
*q0p = u.p0;
*q1p = u.p0;
break;
case Insert:
f->seq = u.seq;
if(canredo)
fileuninsert(f, epsilon, u.p0, u.n);
f->mod = u.mod;
up -= u.n;
buf = fbufalloc();
for(i=0; i<u.n; i+=n){
n = u.n - i;
if(n > RBUFSIZE)
n = RBUFSIZE;
bufread(delta, up+i, buf, n);
bufinsert(f, u.p0+i, buf, n);
raspinsert(f, u.p0+i, buf, n, flag);
}
fbuffree(buf);
*q0p = u.p0;
*q1p = u.p0+u.n;
break;
case Filename:
f->seq = u.seq;
if(canredo)
fileunsetname(f, epsilon);
f->mod = u.mod;
up -= u.n;
Strinsure(&f->name, u.n+1);
bufread(delta, up, f->name.s, u.n);
f->name.s[u.n] = 0;
f->name.n = u.n;
fixname(&f->name);
sortname(f);
break;
case Dot:
f->seq = u.seq;
if(canredo)
fileunsetdot(f, epsilon, f->dot.r);
f->mod = u.mod;
f->dot.r.p1 = u.p0;
f->dot.r.p2 = u.p0 + u.n;
break;
case Mark:
f->seq = u.seq;
if(canredo)
fileunsetmark(f, epsilon, f->mark);
f->mod = u.mod;
f->mark.p1 = u.p0;
f->mark.p2 = u.p0 + u.n;
break;
}
bufdelete(delta, up, delta->nc);
}
if(isundo)
f->seq = 0;
raspdone(f, flag);
}
void
filereset(File *f)
{
bufreset(&f->delta);
bufreset(&f->epsilon);
f->seq = 0;
}
void
fileclose(File *f)
{
Strclose(&f->name);
bufclose(f);
bufclose(&f->delta);
bufclose(&f->epsilon);
if(f->rasp)
listfree(f->rasp);
free(f);
}
void
filemark(File *f)
{
if(f->unread)
return;
if(f->epsilon.nc)
bufdelete(&f->epsilon, 0, f->epsilon.nc);
if(f != cmd){
f->prevdot = f->dot.r;
f->prevmark = f->mark;
f->prevseq = f->seq;
f->prevmod = f->mod;
}
f->ndot = f->dot;
f->seq = seq;
f->hiposn = 0;
}

262
src/cmd/sam/io.c Normal file
View File

@@ -0,0 +1,262 @@
#include "sam.h"
#define NSYSFILE 3
#define NOFILE 128
void
checkqid(File *f)
{
int i, w;
File *g;
w = whichmenu(f);
for(i=1; i<file.nused; i++){
g = file.filepptr[i];
if(w == i)
continue;
if(f->dev==g->dev && f->qidpath==g->qidpath)
warn_SS(Wdupfile, &f->name, &g->name);
}
}
void
writef(File *f)
{
Posn n;
char *name;
int i, samename, newfile;
ulong dev;
uvlong qid;
long mtime, appendonly, length;
newfile = 0;
samename = Strcmp(&genstr, &f->name) == 0;
name = Strtoc(&f->name);
i = statfile(name, &dev, &qid, &mtime, 0, 0);
if(i == -1)
newfile++;
else if(samename &&
(f->dev!=dev || f->qidpath!=qid || f->mtime<mtime)){
f->dev = dev;
f->qidpath = qid;
f->mtime = mtime;
warn_S(Wdate, &genstr);
return;
}
if(genc)
free(genc);
genc = Strtoc(&genstr);
if((io=create(genc, 1, 0666L)) < 0)
error_r(Ecreate, genc);
dprint("%s: ", genc);
if(statfd(io, 0, 0, 0, &length, &appendonly) > 0 && appendonly && 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);
}

47
src/cmd/sam/list.c Normal file
View File

@@ -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);
}

821
src/cmd/sam/mesg.c Normal file
View File

@@ -0,0 +1,821 @@
#include "sam.h"
Header h;
uchar indata[DATASIZE];
uchar outdata[2*DATASIZE+3]; /* room for overflow message */
uchar *inp;
uchar *outp;
uchar *outmsg = outdata;
Posn cmdpt;
Posn cmdptadv;
Buffer snarfbuf;
int waitack;
int noflush;
int tversion;
long inlong(void);
long invlong(void);
int inshort(void);
int inmesg(Tmesg);
void setgenstr(File*, Posn, Posn);
#ifdef DEBUG
char *hname[] = {
[Hversion] "Hversion",
[Hbindname] "Hbindname",
[Hcurrent] "Hcurrent",
[Hnewname] "Hnewname",
[Hmovname] "Hmovname",
[Hgrow] "Hgrow",
[Hcheck0] "Hcheck0",
[Hcheck] "Hcheck",
[Hunlock] "Hunlock",
[Hdata] "Hdata",
[Horigin] "Horigin",
[Hunlockfile] "Hunlockfile",
[Hsetdot] "Hsetdot",
[Hgrowdata] "Hgrowdata",
[Hmoveto] "Hmoveto",
[Hclean] "Hclean",
[Hdirty] "Hdirty",
[Hcut] "Hcut",
[Hsetpat] "Hsetpat",
[Hdelname] "Hdelname",
[Hclose] "Hclose",
[Hsetsnarf] "Hsetsnarf",
[Hsnarflen] "Hsnarflen",
[Hack] "Hack",
[Hexit] "Hexit",
[Hplumb] "Hplumb",
};
char *tname[] = {
[Tversion] "Tversion",
[Tstartcmdfile] "Tstartcmdfile",
[Tcheck] "Tcheck",
[Trequest] "Trequest",
[Torigin] "Torigin",
[Tstartfile] "Tstartfile",
[Tworkfile] "Tworkfile",
[Ttype] "Ttype",
[Tcut] "Tcut",
[Tpaste] "Tpaste",
[Tsnarf] "Tsnarf",
[Tstartnewfile] "Tstartnewfile",
[Twrite] "Twrite",
[Tclose] "Tclose",
[Tlook] "Tlook",
[Tsearch] "Tsearch",
[Tsend] "Tsend",
[Tdclick] "Tdclick",
[Tstartsnarf] "Tstartsnarf",
[Tsetsnarf] "Tsetsnarf",
[Tack] "Tack",
[Texit] "Texit",
[Tplumb] "Tplumb",
};
void
journal(int out, char *s)
{
static int fd = 0;
if(fd <= 0)
fd = create("/tmp/sam.out", 1, 0666L);
fprint(fd, "%s%s\n", out? "out: " : "in: ", s);
}
void
journaln(int out, long n)
{
char buf[32];
sprint(buf, "%ld", n);
journal(out, buf);
}
#else
#define journal(a, b)
#define journaln(a, b)
#endif
int
rcvchar(void){
static uchar buf[64];
static i, nleft = 0;
if(nleft <= 0){
nleft = read(0, (char *)buf, sizeof buf);
if(nleft <= 0)
return -1;
i = 0;
}
--nleft;
return buf[i++];
}
int
rcv(void){
int c;
static state = 0;
static count = 0;
static i = 0;
while((c=rcvchar()) != -1)
switch(state){
case 0:
h.type = c;
state++;
break;
case 1:
h.count0 = c;
state++;
break;
case 2:
h.count1 = c;
count = h.count0|(h.count1<<8);
i = 0;
if(count > DATASIZE)
panic("count>DATASIZE");
if(count == 0)
goto zerocount;
state++;
break;
case 3:
indata[i++] = c;
if(i == count){
zerocount:
indata[i] = 0;
state = count = 0;
return inmesg(h.type);
}
break;
}
return 0;
}
File *
whichfile(int tag)
{
int i;
for(i = 0; i<file.nused; i++)
if(file.filepptr[i]->tag==tag)
return file.filepptr[i];
hiccough((char *)0);
return 0;
}
int
inmesg(Tmesg type)
{
Rune buf[1025];
char cbuf[64];
int i, m;
short s;
long l, l1;
File *f;
Posn p0, p1, p;
Range r;
String *str;
char *c, *wdir;
Rune *rp;
Plumbmsg *pm;
if(type > TMAX)
panic("inmesg");
journal(0, tname[type]);
inp = indata;
switch(type){
case -1:
panic("rcv error");
default:
fprint(2, "unknown type %d\n", type);
panic("rcv unknown");
case Tversion:
tversion = inshort();
journaln(0, tversion);
break;
case Tstartcmdfile:
l = invlong(); /* for 64-bit pointers */
journaln(0, l);
Strdupl(&genstr, samname);
cmd = newfile();
cmd->unread = 0;
outTsv(Hbindname, cmd->tag, l);
outTs(Hcurrent, cmd->tag);
logsetname(cmd, &genstr);
cmd->rasp = emalloc(sizeof(List));
cmd->mod = 0;
if(cmdstr.n){
loginsert(cmd, 0L, cmdstr.s, cmdstr.n);
Strdelete(&cmdstr, 0L, (Posn)cmdstr.n);
}
fileupdate(cmd, FALSE, TRUE);
outT0(Hunlock);
break;
case Tcheck:
/* go through whichfile to check the tag */
outTs(Hcheck, whichfile(inshort())->tag);
break;
case Trequest:
f = whichfile(inshort());
p0 = inlong();
p1 = p0+inshort();
journaln(0, p0);
journaln(0, p1-p0);
if(f->unread)
panic("Trequest: unread");
if(p1>f->_.nc)
p1 = f->_.nc;
if(p0>f->_.nc) /* can happen e.g. scrolling during command */
p0 = f->_.nc;
if(p0 == p1){
i = 0;
r.p1 = r.p2 = p0;
}else{
r = rdata(f->rasp, p0, p1-p0);
i = r.p2-r.p1;
bufread(f, r.p1, buf, i);
}
buf[i]=0;
outTslS(Hdata, f->tag, r.p1, tmprstr(buf, i+1));
break;
case Torigin:
s = inshort();
l = inlong();
l1 = inlong();
journaln(0, l1);
lookorigin(whichfile(s), l, l1);
break;
case Tstartfile:
termlocked++;
f = whichfile(inshort());
if(!f->rasp) /* this might be a duplicate message */
f->rasp = emalloc(sizeof(List));
current(f);
outTsv(Hbindname, f->tag, invlong()); /* for 64-bit 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; l<snarfbuf.nc; l+=m){
m = snarfbuf.nc-l;
if(m>BLOCKSIZE)
m = BLOCKSIZE;
bufread(&snarfbuf, l, genbuf, m);
loginsert(f, p0, tmprstr(genbuf, m)->s, m);
}
if(fileupdate(f, FALSE, TRUE))
seq++;
f->dot.r.p1 = p0;
f->dot.r.p2 = p0+snarfbuf.nc;
f->tdot.p1 = -1; /* force telldot to tell (arguably a BUG) */
telldot(f);
outTs(Hunlockfile, f->tag);
break;
case Tsnarf:
i = inshort();
p0 = inlong();
p1 = inlong();
snarf(whichfile(i), p0, p1, &snarfbuf, 0);
break;
case Tstartnewfile:
l = invlong();
Strdupl(&genstr, empty);
f = newfile();
f->rasp = emalloc(sizeof(List));
outTsv(Hbindname, f->tag, l);
logsetname(f, &genstr);
outTs(Hcurrent, f->tag);
current(f);
load(f);
break;
case Twrite:
termlocked++;
i = inshort();
journaln(0, i);
f = whichfile(i);
addr.r.p1 = 0;
addr.r.p2 = f->_.nc;
if(f->name.s[0] == 0)
error(Enoname);
Strduplstr(&genstr, &f->name);
writef(f);
break;
case Tclose:
termlocked++;
i = inshort();
journaln(0, i);
f = whichfile(i);
current(f);
trytoclose(f);
/* if trytoclose fails, will error out */
delete(f);
break;
case Tlook:
f = whichfile(inshort());
termlocked++;
p0 = inlong();
p1 = inlong();
journaln(0, p0);
journaln(0, p1);
setgenstr(f, p0, p1);
for(l = 0; l<genstr.n; l++){
i = genstr.s[l];
if(utfrune(".*+?(|)\\[]^$", i))
Strinsert(&genstr, tmpcstr("\\"), l++);
}
Straddc(&genstr, '\0');
nextmatch(f, &genstr, p1, 1);
moveto(f, sel.p[0]);
break;
case Tsearch:
termlocked++;
if(curfile == 0)
error(Enofile);
if(lastpat.s[0] == 0)
panic("Tsearch");
nextmatch(curfile, &lastpat, curfile->dot.r.p2, 1);
moveto(curfile, sel.p[0]);
break;
case Tsend:
termlocked++;
inshort(); /* ignored */
p0 = inlong();
p1 = inlong();
setgenstr(cmd, p0, p1);
bufreset(&snarfbuf);
bufinsert(&snarfbuf, (Posn)0, genstr.s, genstr.n);
outTl(Hsnarflen, genstr.n);
if(genstr.s[genstr.n-1] != '\n')
Straddc(&genstr, '\n');
loginsert(cmd, cmd->_.nc, genstr.s, genstr.n);
fileupdate(cmd, FALSE, TRUE);
cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->_.nc;
telldot(cmd);
termcommand();
break;
case Tdclick:
f = whichfile(inshort());
p1 = inlong();
doubleclick(f, p1);
f->tdot.p1 = f->tdot.p2 = p1;
telldot(f);
outTs(Hunlockfile, f->tag);
break;
case Tstartsnarf:
if (snarfbuf.nc <= 0) { /* nothing to export */
outTs(Hsetsnarf, 0);
break;
}
c = 0;
i = 0;
m = snarfbuf.nc;
if(m > SNARFSIZE) {
m = SNARFSIZE;
dprint("?warning: snarf buffer truncated\n");
}
rp = malloc(m*sizeof(Rune));
if(rp){
bufread(&snarfbuf, 0, rp, m);
c = Strtoc(tmprstr(rp, m));
free(rp);
i = strlen(c);
}
outTs(Hsetsnarf, i);
if(c){
Write(1, c, i);
free(c);
} else
dprint("snarf buffer too long\n");
break;
case Tsetsnarf:
m = inshort();
if(m > SNARFSIZE)
error(Etoolong);
c = malloc(m+1);
if(c){
for(i=0; i<m; i++)
c[i] = rcvchar();
c[m] = 0;
str = tmpcstr(c);
free(c);
bufreset(&snarfbuf);
bufinsert(&snarfbuf, (Posn)0, str->s, str->n);
freetmpstr(str);
outT0(Hunlock);
}
break;
case Tack:
waitack = 0;
break;
case Tplumb:
f = whichfile(inshort());
p0 = inlong();
p1 = inlong();
pm = emalloc(sizeof(Plumbmsg));
pm->src = strdup("sam");
pm->dst = 0;
/* construct current directory */
c = Strtoc(&f->name);
if(c[0] == '/')
pm->wdir = c;
else{
wdir = emalloc(1024);
getwd(wdir, 1024);
pm->wdir = emalloc(1024);
snprint(pm->wdir, 1024, "%s/%s", wdir, c);
cleanname(pm->wdir);
free(wdir);
free(c);
}
c = strrchr(pm->wdir, '/');
if(c)
*c = '\0';
pm->type = strdup("text");
if(p1 > p0)
pm->attr = nil;
else{
p = p0;
while(p0>0 && (i=filereadc(f, p0 - 1))!=' ' && i!='\t' && i!='\n')
p0--;
while(p1<f->_.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; l<p2; l+=i){
i = p2-l>BLOCKSIZE? BLOCKSIZE : p2-l;
bufread(f, l, genbuf, i);
bufinsert(buf, buf->nc, tmprstr(genbuf, i)->s, i);
}
}
int
inshort(void)
{
ushort n;
n = inp[0] | (inp[1]<<8);
inp += 2;
return n;
}
long
inlong(void)
{
ulong n;
n = inp[0] | (inp[1]<<8) | (inp[2]<<16) | (inp[3]<<24);
inp += 4;
return n;
}
long
invlong(void)
{
ulong n;
n = (inp[7]<<24) | (inp[6]<<16) | (inp[5]<<8) | inp[4];
n = (n<<16) | (inp[3]<<8) | inp[2];
n = (n<<16) | (inp[1]<<8) | inp[0];
inp += 8;
return n;
}
void
setgenstr(File *f, Posn p0, Posn p1)
{
if(p0 != p1){
if(p1-p0 >= TBLOCKSIZE)
error(Etoolong);
Strinsure(&genstr, p1-p0);
bufread(f, p0, genbuf, p1-p0);
memmove(genstr.s, genbuf, RUNESIZE*(p1-p0));
genstr.n = p1-p0;
}else{
if(snarfbuf.nc == 0)
error(Eempty);
if(snarfbuf.nc > TBLOCKSIZE)
error(Etoolong);
bufread(&snarfbuf, (Posn)0, genbuf, snarfbuf.nc);
Strinsure(&genstr, snarfbuf.nc);
memmove(genstr.s, genbuf, RUNESIZE*snarfbuf.nc);
genstr.n = snarfbuf.nc;
}
}
void
outT0(Hmesg type)
{
outstart(type);
outsend();
}
void
outTl(Hmesg type, long l)
{
outstart(type);
outlong(l);
outsend();
}
void
outTs(Hmesg type, int s)
{
outstart(type);
journaln(1, s);
outshort(s);
outsend();
}
void
outS(String *s)
{
char *c;
int i;
c = Strtoc(s);
i = strlen(c);
outcopy(i, c);
if(i > 99)
c[99] = 0;
journaln(1, i);
journal(1, c);
free(c);
}
void
outTsS(Hmesg type, int s1, String *s)
{
outstart(type);
outshort(s1);
outS(s);
outsend();
}
void
outTslS(Hmesg type, int s1, Posn l1, String *s)
{
outstart(type);
outshort(s1);
journaln(1, s1);
outlong(l1);
journaln(1, l1);
outS(s);
outsend();
}
void
outTS(Hmesg type, String *s)
{
outstart(type);
outS(s);
outsend();
}
void
outTsllS(Hmesg type, int s1, Posn l1, Posn l2, String *s)
{
outstart(type);
outshort(s1);
outlong(l1);
outlong(l2);
journaln(1, l1);
journaln(1, l2);
outS(s);
outsend();
}
void
outTsll(Hmesg type, int s, Posn l1, Posn l2)
{
outstart(type);
outshort(s);
outlong(l1);
outlong(l2);
journaln(1, l1);
journaln(1, l2);
outsend();
}
void
outTsl(Hmesg type, int s, Posn l)
{
outstart(type);
outshort(s);
outlong(l);
journaln(1, l);
outsend();
}
void
outTsv(Hmesg type, int s, Posn l)
{
outstart(type);
outshort(s);
outvlong((void*)l);
journaln(1, l);
outsend();
}
void
outstart(Hmesg type)
{
journal(1, hname[type]);
outmsg[0] = type;
outp = outmsg+3;
}
void
outcopy(int count, void *data)
{
memmove(outp, data, count);
outp += count;
}
void
outshort(int s)
{
*outp++ = s;
*outp++ = s>>8;
}
void
outlong(long l)
{
*outp++ = l;
*outp++ = l>>8;
*outp++ = l>>16;
*outp++ = l>>24;
}
void
outvlong(void *v)
{
int i;
ulong l;
l = (ulong) v;
for(i = 0; i < 8; i++, l >>= 8)
*outp++ = l;
}
void
outsend(void)
{
int outcount;
outcount = outp-outmsg;
outcount -= 3;
outmsg[1] = outcount;
outmsg[2] = outcount>>8;
outmsg = outp;
if(!noflush){
outcount = outmsg-outdata;
if (write(1, (char*) outdata, outcount) != outcount)
rescue();
outmsg = outdata;
return;
}
if(outmsg < outdata+DATASIZE)
return;
outflush();
}
void
outflush(void)
{
if(outmsg == outdata)
return;
noflush = 0;
outT0(Hack);
waitack = 1;
do
if(rcv() == 0){
rescue();
exits("eof");
}
while(waitack);
outmsg = outdata;
noflush = 1;
}

131
src/cmd/sam/mesg.h Normal file
View File

@@ -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")
*
*/

40
src/cmd/sam/mkfile Normal file
View File

@@ -0,0 +1,40 @@
</$objtype/mkfile
TARG=sam
OFILES=sam.$O\
address.$O\
buff.$O\
cmd.$O\
disk.$O\
error.$O\
file.$O\
io.$O\
list.$O\
mesg.$O\
moveto.$O\
multi.$O\
plan9.$O\
rasp.$O\
regexp.$O\
shell.$O\
string.$O\
sys.$O\
util.$O\
xec.$O\
HFILES=sam.h\
errors.h\
mesg.h\
BIN=/$objtype/bin
</sys/src/cmd/mkone
address.$O cmd.$O parse.$O xec.$O unix.$O: parse.h
safeinstall: $O.out
mv $BIN/$TARG $BIN/o$TARG
cp $prereq $BIN/$TARG
safeinstallall:V:
for (objtype in $CPUS)
mk safeinstall

173
src/cmd/sam/moveto.c Normal file
View File

@@ -0,0 +1,173 @@
#include "sam.h"
void
moveto(File *f, Range r)
{
Posn p1 = r.p1, p2 = r.p2;
f->dot.r.p1 = p1;
f->dot.r.p2 = p2;
if(f->rasp){
telldot(f);
outTsl(Hmoveto, f->tag, f->dot.r.p1);
}
}
void
telldot(File *f)
{
if(f->rasp == 0)
panic("telldot");
if(f->dot.r.p1==f->tdot.p1 && f->dot.r.p2==f->tdot.p2)
return;
outTsll(Hsetdot, f->tag, f->dot.r.p1, f->dot.r.p2);
f->tdot = f->dot.r;
}
void
tellpat(void)
{
outTS(Hsetpat, &lastpat);
patset = FALSE;
}
#define CHARSHIFT 128
void
lookorigin(File *f, Posn p0, Posn ls)
{
int nl, nc, c;
Posn p, oldp0;
if(p0 > f->_.nc)
p0 = f->_.nc;
oldp0 = p0;
p = p0;
for(nl=nc=c=0; c!=-1 && nl<ls && nc<ls*CHARSHIFT; nc++)
if((c=filereadc(f, --p)) == '\n'){
nl++;
oldp0 = p0-nc;
}
if(c == -1)
p0 = 0;
else if(nl==0){
if(p0>=CHARSHIFT/2)
p0-=CHARSHIFT/2;
else
p0 = 0;
}else
p0 = oldp0;
outTsl(Horigin, f->tag, p0);
}
int
alnum(int c)
{
/*
* Hard to get absolutely right. Use what we know about ASCII
* and assume anything above the Latin control characters is
* potentially an alphanumeric.
*/
if(c<=' ')
return 0;
if(0x7F<=c && c<=0xA0)
return 0;
if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
return 0;
return 1;
}
int
clickmatch(File *f, int cl, int cr, int dir, Posn *p)
{
int c;
int nest = 1;
for(;;){
if(dir > 0){
if(*p >= f->_.nc)
break;
c = filereadc(f, (*p)++);
}else{
if(*p == 0)
break;
c = filereadc(f, --(*p));
}
if(c == cr){
if(--nest==0)
return 1;
}else if(c == cl)
nest++;
}
return cl=='\n' && nest==1;
}
Rune*
strrune(Rune *s, Rune c)
{
Rune c1;
if(c == 0) {
while(*s++)
;
return s-1;
}
while(c1 = *s++)
if(c1 == c)
return s-1;
return 0;
}
void
doubleclick(File *f, Posn p1)
{
int c, i;
Rune *r, *l;
Posn p;
if(p1 > f->_.nc)
return;
f->dot.r.p1 = f->dot.r.p2 = p1;
for(i=0; left[i]; i++){
l = left[i];
r = right[i];
/* try left match */
p = p1;
if(p1 == 0)
c = '\n';
else
c = filereadc(f, p - 1);
if(strrune(l, c)){
if(clickmatch(f, c, r[strrune(l, c)-l], 1, &p)){
f->dot.r.p1 = p1;
f->dot.r.p2 = p-(c!='\n');
}
return;
}
/* try right match */
p = p1;
if(p1 == f->_.nc)
c = '\n';
else
c = filereadc(f, p);
if(strrune(r, c)){
if(clickmatch(f, c, l[strrune(r, c)-r], -1, &p)){
f->dot.r.p1 = p;
if(c!='\n' || p!=0 || filereadc(f, 0)=='\n')
f->dot.r.p1++;
f->dot.r.p2 = p1+(p1<f->_.nc && c=='\n');
}
return;
}
}
/* try filling out word to right */
p = p1;
while(p < f->_.nc && alnum(filereadc(f, p++)))
f->dot.r.p2++;
/* try filling out word to left */
p = p1;
while(--p >= 0 && alnum(filereadc(f, p)))
f->dot.r.p1--;
}

123
src/cmd/sam/multi.c Normal file
View File

@@ -0,0 +1,123 @@
#include "sam.h"
List file;
ushort tag;
File *
newfile(void)
{
File *f;
f = fileopen();
inslist(&file, 0, (long)f);
f->tag = tag++;
if(downloaded)
outTs(Hnewname, f->tag);
/* already sorted; file name is "" */
return f;
}
int
whichmenu(File *f)
{
int i;
for(i=0; i<file.nused; i++)
if(file.filepptr[i]==f)
return i;
return -1;
}
void
delfile(File *f)
{
int w = whichmenu(f);
if(w < 0) /* e.g. x/./D */
return;
if(downloaded)
outTs(Hdelname, f->tag);
dellist(&file, w);
fileclose(f);
}
void
fullname(String *name)
{
if(name->n > 0 && name->s[0]!='/' && name->s[0]!=0)
Strinsert(name, &curwd, (Posn)0);
}
void
fixname(String *name)
{
String *t;
char *s;
fullname(name);
s = Strtoc(name);
if(strlen(s) > 0)
s = cleanname(s);
t = tmpcstr(s);
Strduplstr(name, t);
free(s);
freetmpstr(t);
if(Strispre(&curwd, name))
Strdelete(name, 0, curwd.n);
}
void
sortname(File *f)
{
int i, cmp, w;
int dupwarned;
w = whichmenu(f);
dupwarned = FALSE;
dellist(&file, w);
if(f == cmd)
i = 0;
else{
for(i=0; i<file.nused; i++){
cmp = Strcmp(&f->name, &file.filepptr[i]->name);
if(cmp==0 && !dupwarned){
dupwarned = TRUE;
warn_S(Wdupname, &f->name);
}else if(cmp<0 && (i>0 || cmd==0))
break;
}
}
inslist(&file, i, (long)f);
if(downloaded)
outTsS(Hmovname, f->tag, &f->name);
}
void
state(File *f, int cleandirty)
{
if(f == cmd)
return;
f->unread = FALSE;
if(downloaded && whichmenu(f)>=0){ /* else flist or menu */
if(f->mod && cleandirty!=Dirty)
outTs(Hclean, f->tag);
else if(!f->mod && cleandirty==Dirty)
outTs(Hdirty, f->tag);
}
if(cleandirty == Clean)
f->mod = FALSE;
else
f->mod = TRUE;
}
File *
lookfile(String *s)
{
int i;
for(i=0; i<file.nused; i++)
if(Strcmp(&file.filepptr[i]->name, s) == 0)
return file.filepptr[i];
return 0;
}

68
src/cmd/sam/parse.h Normal file
View File

@@ -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*);

185
src/cmd/sam/plan9.c Normal file
View File

@@ -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;
}

9
src/cmd/sam/plumb.c Normal file
View File

@@ -0,0 +1,9 @@
#include <u.h>
#include "plumb.h"
/* XXX - Can we do better than this? */
char *cleanname(char *s) { return s; }
char *plumbunpackattr(char *cbuf) { return 0; }
char *plumbpack(Plumbmsg *pm, int *i) { return 0; }
int plumbfree(Plumbmsg *pm) { return 0; }

325
src/cmd/sam/rasp.c Normal file
View File

@@ -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 && p1<cmdpt){
if(p2 <= cmdpt)
cmdpt -= n;
else
cmdpt = p1;
}
if(toterm){
if(grown){
outTsll(Hgrow, f->tag, growpos, grown);
grown = 0;
}else if(shrunk && shrinkpos!=p1 && shrinkpos!=p2){
outTsll(Hcut, f->tag, shrinkpos, shrunk);
shrunk = 0;
}
if(!shrunk || shrinkpos==p2)
shrinkpos = p1;
shrunk += n;
}
rcut(f->rasp, p1, p2);
}
void
raspinsert(File *f, uint p1, Rune *buf, uint n, int toterm)
{
Range r;
if(n == 0)
return;
if(p1 < f->dot.r.p1){
f->dot.r.p1 += n;
f->dot.r.p2 += n;
}
if(p1 < f->mark.p1){
f->mark.p1 += n;
f->mark.p2 += n;
}
if(f->rasp == nil)
return;
if(f==cmd && p1<cmdpt)
cmdpt += n;
if(toterm){
if(shrunk){
outTsll(Hcut, f->tag, shrinkpos, shrunk);
shrunk = 0;
}
if(n>GROWDATASIZE || !rterm(f->rasp, p1)){
rgrow(f->rasp, p1, n);
if(grown && growpos+grown!=p1 && growpos!=p1){
outTsll(Hgrow, f->tag, growpos, grown);
grown = 0;
}
if(!grown)
growpos = p1;
grown += n;
}else{
if(grown){
outTsll(Hgrow, f->tag, growpos, grown);
grown = 0;
}
rgrow(f->rasp, p1, n);
r = rdata(f->rasp, p1, n);
if(r.p1!=p1 || r.p2!=p1+n)
panic("rdata in toterminal");
outTsllS(Hgrowdata, f->tag, p1, n, tmprstr(buf, n));
}
}else{
rgrow(f->rasp, p1, n);
r = rdata(f->rasp, p1, n);
if(r.p1!=p1 || r.p2!=p1+n)
panic("rdata in toterminal");
}
}
#define M 0x80000000L
#define P(i) r->longptr[i]
#define T(i) (P(i)&M) /* in terminal */
#define L(i) (P(i)&~M) /* length of this piece */
void
rcut(List *r, Posn p1, Posn p2)
{
Posn p, x;
int i;
if(p1 == p2)
panic("rcut 0");
for(p=0,i=0; i<r->nused && p+L(i)<=p1; p+=L(i++))
;
if(i == r->nused)
panic("rcut 1");
if(p < p1){ /* chop this piece */
if(p+L(i) < p2){
x = p1-p;
p += L(i);
}else{
x = L(i)-(p2-p1);
p = p2;
}
if(T(i))
P(i) = x|M;
else
P(i) = x;
i++;
}
while(i<r->nused && p+L(i)<=p2){
p += L(i);
dellist(r, i);
}
if(p < p2){
if(i == r->nused)
panic("rcut 2");
x = L(i)-(p2-p);
if(T(i))
P(i) = x|M;
else
P(i) = x;
}
/* can we merge i and i-1 ? */
if(i>0 && i<r->nused && T(i-1)==T(i)){
x = L(i-1)+L(i);
dellist(r, i--);
if(T(i))
P(i)=x|M;
else
P(i)=x;
}
}
void
rgrow(List *r, Posn p1, Posn n)
{
Posn p;
int i;
if(n == 0)
panic("rgrow 0");
for(p=0,i=0; i<r->nused && p+L(i)<=p1; p+=L(i++))
;
if(i == r->nused){ /* stick on end of file */
if(p!=p1)
panic("rgrow 1");
if(i>0 && !T(i-1))
P(i-1)+=n;
else
inslist(r, i, n);
}else if(!T(i)) /* goes in this empty piece */
P(i)+=n;
else if(p==p1 && i>0 && !T(i-1)) /* special case; simplifies 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; i<r->nused && p+L(i)<=p1; p+=L(i++))
;
if(i==r->nused && (i==0 || !T(i-1)))
return 0;
return T(i);
}
Range
rdata(List *r, Posn p1, Posn n)
{
Posn p;
int i;
Range rg;
if(n==0)
panic("rdata 0");
for(p = 0,i = 0; i<r->nused && p+L(i)<=p1; p+=L(i++))
;
if(i==r->nused)
panic("rdata 1");
if(T(i)){
n-=L(i)-(p1-p);
if(n<=0){
rg.p1 = rg.p2 = p1;
return rg;
}
p+=L(i++);
p1 = p;
}
if(T(i) || i==r->nused)
panic("rdata 2");
if(p+L(i)<p1+n)
n = L(i)-(p1-p);
rg.p1 = p1;
rg.p2 = p1+n;
if(p!=p1){
inslist(r, i+1, L(i)-(p1-p));
P(i)=p1-p;
i++;
}
if(L(i)!=n){
inslist(r, i+1, L(i)-n);
P(i)=n;
}
P(i)|=M;
/* now i is set; can we merge? */
if(i<r->nused-1 && T(i+1)){
P(i)=(n+=L(i+1))|M;
dellist(r, i+1);
}
if(i>0 && T(i-1)){
P(i)=(n+L(i-1))|M;
dellist(r, i-1);
}
return rg;
}

801
src/cmd/sam/regexp.c Normal file
View File

@@ -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; i<nclass; i++)
free(class[i]);
nclass = 0;
progp = program;
backwards = FALSE;
startinst = realcompile(s->s);
optimize(program);
oprogp = progp;
backwards = TRUE;
bstartinst = realcompile(s->s);
optimize(oprogp);
Strduplstr(&lastregexp, s);
}
void
operand(int t)
{
Inst *i;
if(lastwasand)
operator(CAT); /* catenate is implicit */
i = newinst(t);
if(t == CCLASS){
if(negateclass)
i->type = NCCLASS; /* UGH */
i->rclass = nclass-1; /* UGH */
}
pushand(i, i);
lastwasand = TRUE;
}
void
operator(int t)
{
if(t==RBRA && --nbra<0)
regerror(Erightpar);
if(t==LBRA){
/*
* if(++cursubid >= NSUBEXP)
* regerror(Esubexp);
*/
cursubid++; /* silently ignored */
nbra++;
if(lastwasand)
operator(CAT);
}else
evaluntil(t);
if(t!=RBRA)
pushator(t);
lastwasand = FALSE;
if(t==STAR || t==QUEST || t==PLUS || t==RBRA)
lastwasand = TRUE; /* these look like operands */
}
void
cant(char *s)
{
char buf[100];
sprint(buf, "regexp: can't happen: %s", s);
panic(buf);
}
void
pushand(Inst *f, Inst *l)
{
if(andp >= &andstack[NSTACK])
cant("operand stack overflow");
andp->first = f;
andp->last = l;
andp++;
}
void
pushator(int t)
{
if(atorp >= &atorstack[NSTACK])
cant("operator stack overflow");
*atorp++=t;
if(cursubid >= NSUBEXP)
*subidp++= -1;
else
*subidp++=cursubid;
}
Node *
popand(int op)
{
if(andp <= &andstack[0])
if(op)
regerror_c(Emissop, op);
else
regerror(Ebadregexp);
return --andp;
}
int
popator(void)
{
if(atorp <= &atorstack[0])
cant("operator stack underflow");
--subidp;
return *--atorp;
}
void
evaluntil(int pri)
{
Node *op1, *op2, *t;
Inst *inst1, *inst2;
while(pri==RBRA || atorp[-1]>=pri){
switch(popator()){
case LBRA:
op1 = popand('(');
inst2 = newinst(RBRA);
inst2->subid = *subidp;
op1->last->next = inst2;
inst1 = newinst(LBRA);
inst1->subid = *subidp;
inst1->next = op1->first;
pushand(inst1, inst2);
return; /* must have been RBRA */
default:
panic("unknown regexp operator");
break;
case OR:
op2 = popand('|');
op1 = popand('|');
inst2 = newinst(NOP);
op2->last->next = inst2;
op1->last->next = inst2;
inst1 = newinst(OR);
inst1->right = op1->first;
inst1->left = op2->first;
pushand(inst1, inst2);
break;
case CAT:
op2 = popand(0);
op1 = popand(0);
if(backwards && op2->first->type!=END)
t = op1, op1 = op2, op2 = t;
op1->last->next = op2->first;
pushand(op1->first, op2->last);
break;
case STAR:
op2 = popand('*');
inst1 = newinst(OR);
op2->last->next = inst1;
inst1->right = op2->first;
pushand(inst1, inst1);
break;
case PLUS:
op2 = popand('+');
inst1 = newinst(OR);
op2->last->next = inst1;
inst1->right = op2->first;
pushand(op2->first, inst1);
break;
case QUEST:
op2 = popand('?');
inst1 = newinst(OR);
inst2 = newinst(NOP);
inst1->left = inst2;
inst1->right = op2->first;
op2->last->next = inst2;
pushand(inst1, inst2);
break;
}
}
}
void
optimize(Inst *start)
{
Inst *inst, *target;
for(inst=start; inst->type!=END; inst++){
target = inst->next;
while(target->type == NOP)
target = target->next;
inst->next = target;
}
}
#ifdef DEBUG
void
dumpstack(void){
Node *stk;
int *ip;
dprint("operators\n");
for(ip = atorstack; ip<atorp; ip++)
dprint("0%o\n", *ip);
dprint("operands\n");
for(stk = andstack; stk<andp; stk++)
dprint("0%o\t0%o\n", stk->first->type, stk->last->type);
}
void
dump(void){
Inst *l;
l = program;
do{
dprint("%d:\t0%o\t%d\t%d\n", l-program, l->type,
l->left-program, l->right-program);
}while(l++->type);
}
#endif
void
startlex(Rune *s)
{
exprp = s;
nbra = 0;
}
int
lex(void){
int c= *exprp++;
switch(c){
case '\\':
if(*exprp)
if((c= *exprp++)=='n')
c='\n';
break;
case 0:
c = END;
--exprp; /* In case we come here again */
break;
case '*':
c = STAR;
break;
case '?':
c = QUEST;
break;
case '+':
c = PLUS;
break;
case '|':
c = OR;
break;
case '.':
c = ANY;
break;
case '(':
c = LBRA;
break;
case ')':
c = RBRA;
break;
case '^':
c = BOL;
break;
case '$':
c = EOL;
break;
case '[':
c = CCLASS;
bldcclass();
break;
}
return c;
}
long
nextrec(void){
if(exprp[0]==0 || (exprp[0]=='\\' && exprp[1]==0))
regerror(Ebadclass);
if(exprp[0] == '\\'){
exprp++;
if(*exprp=='n'){
exprp++;
return '\n';
}
return *exprp++|0x10000;
}
return *exprp++;
}
void
bldcclass(void)
{
long c1, c2, n, na;
Rune *classp;
classp = emalloc(DCLASS*RUNESIZE);
n = 0;
na = DCLASS;
/* we have already seen the '[' */
if(*exprp == '^'){
classp[n++] = '\n'; /* don't match newline in negate 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->type<OPERATOR? startinst->type : 0;
list[0][0].inst = list[1][0].inst = 0;
sel.p[0].p1 = -1;
/* Execute machine once for each character */
for(;;p++){
doloop:
c = filereadc(f, p);
if(p>=eof || c<0){
switch(wrapped++){
case 0: /* let loop run one more click */
case 2:
break;
case 1: /* expired; wrap to beginning */
if(sel.p[0].p1>=0 || eof!=INFINITY)
goto Return;
list[0][0].inst = list[1][0].inst = 0;
p = 0;
goto doloop;
default:
goto Return;
}
}else if(((wrapped && p>=startp) || sel.p[0].p1>0) && nnl==0)
break;
/* fast check for first char */
if(startchar && nnl==0 && c!=startchar)
continue;
tl = list[flag];
nl = list[flag^=1];
nl->inst = 0;
ntl = nnl;
nnl = 0;
if(sel.p[0].p1<0 && (!wrapped || p<startp || startp==eof)){
/* Add first instruction to this list */
if(++ntl >= NLIST)
Overflow:
error(Eoverflow);
sempty.p[0].p1 = p;
addinst(tl, startinst, &sempty);
}
/* Execute machine until this list is empty */
for(tlp = tl; inst = tlp->inst; tlp++){ /* assignment = */
Switchstmt:
switch(inst->type){
default: /* regular character */
if(inst->type==c){
Addinst:
if(++nnl >= NLIST)
goto Overflow;
addinst(nl, inst->next, &tlp->se);
}
break;
case LBRA:
if(inst->subid>=0)
tlp->se.p[inst->subid].p1 = p;
inst = inst->next;
goto Switchstmt;
case RBRA:
if(inst->subid>=0)
tlp->se.p[inst->subid].p2 = p;
inst = inst->next;
goto Switchstmt;
case ANY:
if(c!='\n')
goto Addinst;
break;
case BOL:
if(p==0 || filereadc(f, p - 1)=='\n'){
Step:
inst = inst->next;
goto Switchstmt;
}
break;
case EOL:
if(c == '\n')
goto Step;
break;
case CCLASS:
if(c>=0 && classmatch(inst->rclass, c, 0))
goto Addinst;
break;
case NCCLASS:
if(c>=0 && classmatch(inst->rclass, c, 1))
goto Addinst;
break;
case OR:
/* evaluate right choice later */
if(++ntl >= NLIST)
goto Overflow;
addinst(tlp, inst->right, &tlp->se);
/* efficiency: advance and re-evaluate */
inst = inst->left;
goto Switchstmt;
case END: /* Match! */
tlp->se.p[0].p2 = p;
newmatch(&tlp->se);
break;
}
}
}
Return:
return sel.p[0].p1>=0;
}
void
newmatch(Rangeset *sp)
{
int i;
if(sel.p[0].p1<0 || sp->p[0].p1<sel.p[0].p1 ||
(sp->p[0].p1==sel.p[0].p1 && sp->p[0].p2>sel.p[0].p2))
for(i = 0; i<NSUBEXP; i++)
sel.p[i] = sp->p[i];
}
int
bexecute(File *f, Posn startp)
{
int flag = 0;
Inst *inst;
Ilist *tlp;
Posn p = startp;
int nnl = 0, ntl;
int c;
int wrapped = 0;
int startchar = bstartinst->type<OPERATOR? bstartinst->type : 0;
list[0][0].inst = list[1][0].inst = 0;
sel.p[0].p1= -1;
/* Execute machine once for each character, including terminal NUL */
for(;;--p){
doloop:
if((c = filereadc(f, p - 1))==-1){
switch(wrapped++){
case 0: /* let loop run one more click */
case 2:
break;
case 1: /* expired; wrap to end */
if(sel.p[0].p1>=0)
case 3:
goto Return;
list[0][0].inst = list[1][0].inst = 0;
p = f->_.nc;
goto doloop;
default:
goto Return;
}
}else if(((wrapped && p<=startp) || sel.p[0].p1>0) && nnl==0)
break;
/* fast check for first char */
if(startchar && nnl==0 && c!=startchar)
continue;
tl = list[flag];
nl = list[flag^=1];
nl->inst = 0;
ntl = nnl;
nnl = 0;
if(sel.p[0].p1<0 && (!wrapped || p>startp)){
/* Add first instruction to this list */
if(++ntl >= NLIST)
Overflow:
error(Eoverflow);
/* the minus is so the optimizations in addinst work */
sempty.p[0].p1 = -p;
addinst(tl, bstartinst, &sempty);
}
/* Execute machine until this list is empty */
for(tlp = tl; inst = tlp->inst; tlp++){ /* assignment = */
Switchstmt:
switch(inst->type){
default: /* regular character */
if(inst->type == c){
Addinst:
if(++nnl >= NLIST)
goto Overflow;
addinst(nl, inst->next, &tlp->se);
}
break;
case LBRA:
if(inst->subid>=0)
tlp->se.p[inst->subid].p1 = p;
inst = inst->next;
goto Switchstmt;
case RBRA:
if(inst->subid >= 0)
tlp->se.p[inst->subid].p2 = p;
inst = inst->next;
goto Switchstmt;
case ANY:
if(c != '\n')
goto Addinst;
break;
case BOL:
if(c=='\n' || p==0){
Step:
inst = inst->next;
goto Switchstmt;
}
break;
case EOL:
if(p==f->_.nc || filereadc(f, p)=='\n')
goto Step;
break;
case CCLASS:
if(c>=0 && classmatch(inst->rclass, c, 0))
goto Addinst;
break;
case NCCLASS:
if(c>=0 && classmatch(inst->rclass, c, 1))
goto Addinst;
break;
case OR:
/* evaluate right choice later */
if(++ntl >= NLIST)
goto Overflow;
addinst(tlp, inst->right, &tlp->se);
/* efficiency: advance and re-evaluate */
inst = inst->left;
goto Switchstmt;
case END: /* Match! */
tlp->se.p[0].p1 = -tlp->se.p[0].p1; /* minus 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].p2<sel.p[0].p1))
for(i = 0; i<NSUBEXP; i++){ /* note the reversal; p1<=p2 */
sel.p[i].p1 = sp->p[i].p2;
sel.p[i].p2 = sp->p[i].p1;
}
}

BIN
src/cmd/sam/sam Executable file

Binary file not shown.

739
src/cmd/sam/sam.c Normal file
View File

@@ -0,0 +1,739 @@
#include "sam.h"
Rune genbuf[BLOCKSIZE];
int io;
int panicking;
int rescuing;
String genstr;
String rhs;
String curwd;
String cmdstr;
Rune empty[] = { 0 };
char *genc;
File *curfile;
File *flist;
File *cmd;
jmp_buf mainloop;
List tempfile;
int quitok = TRUE;
int downloaded;
int dflag;
int Rflag;
char *machine;
char *home;
int bpipeok;
int termlocked;
char *samterm = SAMTERM;
char *rsamname = RSAM;
File *lastfile;
Disk *disk;
long seq;
Rune baddir[] = { '<', 'b', 'a', 'd', 'd', 'i', 'r', '>', '\n'};
void usage(void);
int main(int argc, char *argv[])
{
int i;
String *t;
char **ap, **arg;
{ // libfmt-2.0 uses %lu where we need %lud
extern int __flagfmt(Fmt*);
fmtinstall('u', __flagfmt);
}
arg = argv++;
ap = argv;
while(argc>1 && argv[0] && argv[0][0]=='-'){
switch(argv[0][1]){
case 'd':
dflag++;
break;
case 'r':
--argc, argv++;
if(argc == 1)
usage();
machine = *argv;
break;
case 'R':
Rflag++;
break;
case 't':
--argc, argv++;
if(argc == 1)
usage();
samterm = *argv;
break;
case 's':
--argc, argv++;
if(argc == 1)
usage();
rsamname = *argv;
break;
case 'x': /* x11 option - strip the x */
strcpy(*argv+1, *argv+2);
*ap++ = *argv++;
*ap++ = *argv;
argc--;
break;
default:
dprint("sam: unknown flag %c\n", argv[0][1]);
exits("usage");
}
--argc, argv++;
}
Strinit(&cmdstr);
Strinit0(&lastpat);
Strinit0(&lastregexp);
Strinit0(&genstr);
Strinit0(&rhs);
Strinit0(&curwd);
tempfile.listptr = emalloc(1); /* so it can be freed later */
Strinit0(&plan9cmd);
home = getenv(HOME);
disk = diskinit();
if(home == 0)
home = "/";
if(!dflag)
startup(machine, Rflag, arg, ap);
notify(notifyf);
getcurwd();
if(argc>1){
for(i=0; i<argc-1; i++){
if(!setjmp(mainloop)){
t = tmpcstr(argv[i]);
Straddc(t, '\0');
Strduplstr(&genstr, t);
freetmpstr(t);
fixname(&genstr);
logsetname(newfile(), &genstr);
}
}
}else if(!downloaded)
newfile();
seq++;
if(file.nused)
current(file.filepptr[0]);
setjmp(mainloop);
cmdloop();
trytoquit(); /* if we already q'ed, quitok will be TRUE */
exits(0);
}
void
usage(void)
{
dprint("usage: sam [-d] [-t samterm] [-s sam name] -r machine\n");
exits("usage");
}
void
rescue(void)
{
int i, nblank = 0;
File *f;
char *c;
char buf[256];
if(rescuing++)
return;
io = -1;
for(i=0; i<file.nused; i++){
f = file.filepptr[i];
if(f==cmd || f->_.nc==0 || !fileisdirty(f))
continue;
if(io == -1){
sprint(buf, "%s/sam.save", home);
io = create(buf, 1, 0777);
if(io<0)
return;
}
if(f->name.s[0]){
c = Strtoc(&f->name);
strncpy(buf, c, sizeof buf-1);
buf[sizeof buf-1] = 0;
free(c);
}else
sprint(buf, "nameless.%d", nblank++);
fprint(io, "#!%s '%s' $* <<'---%s'\n", SAMSAVECMD, buf, buf);
addr.r.p1 = 0, addr.r.p2 = f->_.nc;
writeio(f);
fprint(io, "\n---%s\n", (char *)buf);
}
}
void
panic(char *s)
{
int wasd;
if(!panicking++ && !setjmp(mainloop)){
wasd = downloaded;
downloaded = 0;
dprint("sam: panic: %s: %r\n", s);
if(wasd)
fprint(2, "sam: panic: %s: %r\n", s);
rescue();
abort();
}
}
void
hiccough(char *s)
{
File *f;
int i;
if(rescuing)
exits("rescue");
if(s)
dprint("%s\n", s);
resetcmd();
resetxec();
resetsys();
if(io > 0)
close(io);
/*
* back out any logged changes & restore old sequences
*/
for(i=0; i<file.nused; i++){
f = file.filepptr[i];
if(f==cmd)
continue;
if(f->seq==seq){
bufdelete(&f->epsilon, 0, f->epsilon.nc);
f->seq = f->prevseq;
f->dot.r = f->prevdot;
f->mark = f->prevmark;
state(f, f->prevmod ? Dirty: Clean);
}
}
update();
if (curfile) {
if (curfile->unread)
curfile->unread = FALSE;
else if (downloaded)
outTs(Hcurrent, curfile->tag);
}
longjmp(mainloop, 1);
}
void
intr(void)
{
error(Eintr);
}
void
trytoclose(File *f)
{
char *t;
char buf[256];
if(f == cmd) /* possible? */
return;
if(f->deleted)
return;
if(fileisdirty(f) && !f->closeok){
f->closeok = TRUE;
if(f->name.s[0]){
t = Strtoc(&f->name);
strncpy(buf, t, sizeof buf-1);
free(t);
}else
strcpy(buf, "nameless file");
error_s(Emodified, buf);
}
f->deleted = TRUE;
}
void
trytoquit(void)
{
int c;
File *f;
if(!quitok){
for(c = 0; c<file.nused; c++){
f = file.filepptr[c];
if(f!=cmd && fileisdirty(f)){
quitok = TRUE;
eof = FALSE;
error(Echanges);
}
}
}
}
void
load(File *f)
{
Address saveaddr;
Strduplstr(&genstr, &f->name);
filename(f);
if(f->name.s[0]){
saveaddr = addr;
edit(f, 'I');
addr = saveaddr;
}else{
f->unread = 0;
f->cleanseq = f->seq;
}
fileupdate(f, TRUE, TRUE);
}
void
cmdupdate(void)
{
if(cmd && cmd->seq!=0){
fileupdate(cmd, FALSE, downloaded);
cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->_.nc;
telldot(cmd);
}
}
void
delete(File *f)
{
if(downloaded && f->rasp)
outTs(Hclose, f->tag);
delfile(f);
if(f == curfile)
current(0);
}
void
update(void)
{
int i, anymod;
File *f;
settempfile();
for(anymod = i=0; i<tempfile.nused; i++){
f = tempfile.filepptr[i];
if(f==cmd) /* cmd gets done in main() */
continue;
if(f->deleted) {
delete(f);
continue;
}
if(f->seq==seq && fileupdate(f, FALSE, downloaded))
anymod++;
if(f->rasp)
telldot(f);
}
if(anymod)
seq++;
}
File *
current(File *f)
{
return curfile = f;
}
void
edit(File *f, int cmd)
{
int empty = TRUE;
Posn p;
int nulls;
if(cmd == 'r')
logdelete(f, addr.r.p1, addr.r.p2);
if(cmd=='e' || cmd=='I'){
logdelete(f, (Posn)0, f->_.nc);
addr.r.p2 = f->_.nc;
}else if(f->_.nc!=0 || (f->name.s[0] && Strcmp(&genstr, &f->name)!=0))
empty = FALSE;
if((io = open(genc, OREAD))<0) {
if (curfile && curfile->unread)
curfile->unread = FALSE;
error_r(Eopen, genc);
}
p = readio(f, &nulls, empty, TRUE);
closeio((cmd=='e' || cmd=='I')? -1 : p);
if(cmd == 'r')
f->ndot.r.p1 = addr.r.p2, f->ndot.r.p2 = addr.r.p2+p;
else
f->ndot.r.p1 = f->ndot.r.p2 = 0;
f->closeok = empty;
if (quitok)
quitok = empty;
else
quitok = FALSE;
state(f, empty && !nulls? Clean : Dirty);
if(empty && !nulls)
f->cleanseq = f->seq;
if(cmd == 'e')
filename(f);
}
int
getname(File *f, String *s, int save)
{
int c, i;
Strzero(&genstr);
if(genc){
free(genc);
genc = 0;
}
if(s==0 || (c = s->s[0])==0){ /* no name provided */
if(f)
Strduplstr(&genstr, &f->name);
goto Return;
}
if(c!=' ' && c!='\t')
error(Eblank);
for(i=0; (c=s->s[i])==' ' || c=='\t'; i++)
;
while(s->s[i] > ' ')
Straddc(&genstr, s->s[i++]);
if(s->s[i])
error(Enewline);
fixname(&genstr);
if(f && (save || f->name.s[0]==0)){
logsetname(f, &genstr);
if(Strcmp(&f->name, &genstr)){
quitok = f->closeok = FALSE;
f->qidpath = 0;
f->mtime = 0;
state(f, Dirty); /* if it's 'e', fix later */
}
}
Return:
genc = Strtoc(&genstr);
i = genstr.n;
if(i && genstr.s[i-1]==0)
i--;
return i; /* strlen(name) */
}
void
filename(File *f)
{
if(genc)
free(genc);
genc = Strtoc(&genstr);
dprint("%c%c%c %s\n", " '"[f->mod],
"-+"[f->rasp!=0], " ."[f==curfile], genc);
}
void
undostep(File *f, int isundo)
{
uint p1, p2;
int mod;
mod = f->mod;
fileundo(f, isundo, 1, &p1, &p2, TRUE);
f->ndot = f->dot;
if(f->mod){
f->closeok = 0;
quitok = 0;
}else
f->closeok = 1;
if(f->mod != mod){
f->mod = mod;
if(mod)
mod = Clean;
else
mod = Dirty;
state(f, mod);
}
}
int
undo(int isundo)
{
File *f;
int i;
Mod max;
max = undoseq(curfile, isundo);
if(max == 0)
return 0;
settempfile();
for(i = 0; i<tempfile.nused; i++){
f = tempfile.filepptr[i];
if(f!=cmd && undoseq(f, isundo)==max)
undostep(f, isundo);
}
return 1;
}
int
readcmd(String *s)
{
int retcode;
if(flist != 0)
fileclose(flist);
flist = fileopen();
addr.r.p1 = 0, addr.r.p2 = flist->_.nc;
retcode = plan9(flist, '<', s, FALSE);
fileupdate(flist, FALSE, FALSE);
flist->seq = 0;
if (flist->_.nc > BLOCKSIZE)
error(Etoolong);
Strzero(&genstr);
Strinsure(&genstr, flist->_.nc);
bufread(flist, (Posn)0, genbuf, flist->_.nc);
memmove(genstr.s, genbuf, flist->_.nc*RUNESIZE);
genstr.n = flist->_.nc;
Straddc(&genstr, '\0');
return retcode;
}
void
getcurwd(void)
{
String *t;
char buf[256];
buf[0] = 0;
getwd(buf, sizeof(buf));
t = tmpcstr(buf);
Strduplstr(&curwd, t);
freetmpstr(t);
if(curwd.n == 0)
warn(Wpwd);
else if(curwd.s[curwd.n-1] != '/')
Straddc(&curwd, '/');
}
void
cd(String *str)
{
int i, fd;
char *s;
File *f;
String owd;
getcurwd();
if(getname((File *)0, str, FALSE))
s = genc;
else
s = home;
if(chdir(s))
syserror("chdir");
fd = open("/dev/wdir", OWRITE);
if(fd > 0)
write(fd, s, strlen(s));
dprint("!\n");
Strinit(&owd);
Strduplstr(&owd, &curwd);
getcurwd();
settempfile();
for(i=0; i<tempfile.nused; i++){
f = tempfile.filepptr[i];
if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){
Strinsert(&f->name, &owd, (Posn)0);
fixname(&f->name);
sortname(f);
}else if(f != cmd && Strispre(&curwd, &f->name)){
fixname(&f->name);
sortname(f);
}
}
Strclose(&owd);
}
int
loadflist(String *s)
{
int c, i;
c = s->s[0];
for(i = 0; s->s[i]==' ' || s->s[i]=='\t'; i++)
;
if((c==' ' || c=='\t') && s->s[i]!='\n'){
if(s->s[i]=='<'){
Strdelete(s, 0L, (long)i+1);
readcmd(s);
}else{
Strzero(&genstr);
while((c = s->s[i++]) && c!='\n')
Straddc(&genstr, c);
Straddc(&genstr, '\0');
}
}else{
if(c != '\n')
error(Eblank);
Strdupl(&genstr, empty);
}
if(genc)
free(genc);
genc = Strtoc(&genstr);
return genstr.s[0];
}
File *
readflist(int readall, int delete)
{
Posn i;
int c;
File *f;
String t;
Strinit(&t);
for(i=0,f=0; f==0 || readall || delete; i++){ /* ++ skips blank */
Strdelete(&genstr, (Posn)0, i);
for(i=0; (c = genstr.s[i])==' ' || c=='\t' || c=='\n'; i++)
;
if(i >= genstr.n)
break;
Strdelete(&genstr, (Posn)0, i);
for(i=0; (c=genstr.s[i]) && c!=' ' && c!='\t' && c!='\n'; i++)
;
if(i == 0)
break;
genstr.s[i] = 0;
Strduplstr(&t, tmprstr(genstr.s, i+1));
fixname(&t);
f = lookfile(&t);
if(delete){
if(f == 0)
warn_S(Wfile, &t);
else
trytoclose(f);
}else if(f==0 && readall)
logsetname(f = newfile(), &t);
}
Strclose(&t);
return f;
}
File *
tofile(String *s)
{
File *f;
if(s->s[0] != ' ')
error(Eblank);
if(loadflist(s) == 0){
f = lookfile(&genstr); /* empty string ==> nameless 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<addr.r.p2; p+=ni){
ni = addr.r.p2-p;
if(ni > BLOCKSIZE)
ni = BLOCKSIZE;
bufread(f, p, genbuf, ni);
loginsert(addr2.f, addr2.r.p2, tmprstr(genbuf, ni)->s, ni);
}
addr2.f->ndot.r.p2 = addr2.r.p2+(f->dot.r.p2-f->dot.r.p1);
addr2.f->ndot.r.p1 = addr2.r.p2;
}
void
move(File *f, Address addr2)
{
if(addr.r.p2 <= addr2.r.p2){
logdelete(f, addr.r.p1, addr.r.p2);
copy(f, addr2);
}else if(addr.r.p1 >= addr2.r.p2){
copy(f, addr2);
logdelete(f, addr.r.p1, addr.r.p2);
}else
error(Eoverlap);
}
Posn
nlcount(File *f, Posn p0, Posn p1)
{
Posn nl = 0;
while(p0 < p1)
if(filereadc(f, p0++)=='\n')
nl++;
return nl;
}
void
printposn(File *f, int charsonly)
{
Posn l1, l2;
if(!charsonly){
l1 = 1+nlcount(f, (Posn)0, addr.r.p1);
l2 = l1+nlcount(f, addr.r.p1, addr.r.p2);
/* check if addr ends with '\n' */
if(addr.r.p2>0 && addr.r.p2>addr.r.p1 && filereadc(f, addr.r.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*));
}

407
src/cmd/sam/sam.h Normal file
View File

@@ -0,0 +1,407 @@
#include <u.h>
#include <libc.h>
#include <plumb.h>
#include "errors.h"
/*
* BLOCKSIZE is relatively small to keep memory consumption down.
*/
#define BLOCKSIZE 2048
#define RUNESIZE sizeof(Rune)
#define NDISC 5
#define NBUFFILES 3+2*NDISC /* plan 9+undo+snarf+NDISC*(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);

152
src/cmd/sam/shell.c Normal file
View File

@@ -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; l<plan9buf.nc; l+=m){
m = plan9buf.nc-l;
if(m>BLOCKSIZE-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);
}

272
src/cmd/sam/unix.c Normal file
View File

@@ -0,0 +1,272 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <pwd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include "sam.h"
Rune samname[] = { '~', '~', 's', 'a', 'm', '~', '~', 0 };
static Rune l1[] = { '{', '[', '(', '<', 0253, 0};
static Rune l2[] = { '\n', 0};
static Rune l3[] = { '\'', '"', '`', 0};
Rune *left[]= { l1, l2, l3, 0};
static Rune r1[] = {'}', ']', ')', '>', 0273, 0};
static Rune r2[] = {'\n', 0};
static Rune r3[] = {'\'', '"', '`', 0};
Rune *right[]= { r1, r2, r3, 0};
#ifndef SAMTERMNAME
#define SAMTERMNAME "/usr/local/bin/samterm"
#endif
#ifndef TMPDIRNAME
#define TMPDIRNAME "/tmp"
#endif
#ifndef SHNAME
#define SHNAME "rc"
#endif
#ifndef SHPATHNAME
#define SHPATHNAME "/bin/rc"
#endif
#ifndef RXNAME
#define RXNAME "ssh"
#endif
#ifndef RXPATHNAME
#define RXPATHNAME "/usr/local/bin/ssh"
#endif
#ifndef SAMSAVECMDNAME
#define SAMSAVECMDNAME "/bin/rc\n/usr/local/bin/samsave"
#endif
char RSAM[] = "sam";
char SAMTERM[] = SAMTERMNAME;
char HOME[] = "HOME";
char TMPDIR[] = TMPDIRNAME;
char SH[] = SHNAME;
char SHPATH[] = SHPATHNAME;
char RX[] = RXNAME;
char RXPATH[] = RXPATHNAME;
char SAMSAVECMD[] = SAMSAVECMDNAME;
void
dprint(char *z, ...)
{
char buf[BLOCKSIZE];
va_list arg;
va_start(arg, z);
vseprint(buf, &buf[BLOCKSIZE], z, arg);
va_end(arg);
termwrite(buf);
}
void
print_ss(char *s, String *a, String *b)
{
dprint("?warning: %s: `%.*S' and `%.*S'\n", s, a->n, a->s, b->n, b->s);
}
void
print_s(char *s, String *a)
{
dprint("?warning: %s `%.*S'\n", s, a->n, a->s);
}
char*
getuser(void)
{
static char user[64];
if(user[0] == 0){
struct passwd *pw = getpwuid(getuid());
strcpy(user, pw ? pw->pw_name : "nobody");
}
return user;
}
int
statfile(char *name, ulong *dev, uvlong *id, long *time, long *length, long *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;
}

508
src/cmd/sam/xec.c Normal file
View File

@@ -0,0 +1,508 @@
#include "sam.h"
#include "parse.h"
int Glooping;
int nest;
int append(File*, Cmd*, Posn);
int display(File*);
void looper(File*, Cmd*, int);
void filelooper(Cmd*, int);
void linelooper(File*, Cmd*);
void
resetxec(void)
{
Glooping = nest = 0;
}
int
cmdexec(File *f, Cmd *cp)
{
int i;
Addr *ap;
Address a;
if(f && f->unread)
load(f);
if(f==0 && (cp->addr==0 || cp->addr->type!='"') &&
!utfrune("bBnqUXY!", cp->cmdc) &&
cp->cmdc!=('c'|0x100) && !(cp->cmdc=='D' && cp->ctext))
error(Enofile);
i = lookup(cp->cmdc);
if(i >= 0 && cmdtab[i].defaddr != aNo){
if((ap=cp->addr)==0 && cp->cmdc!='\n'){
cp->addr = ap = newaddr();
ap->type = '.';
if(cmdtab[i].defaddr == aAll)
ap->type = '*';
}else if(ap && ap->type=='"' && ap->next==0 && cp->cmdc!='\n'){
ap->next = newaddr();
ap->next->type = '.';
if(cmdtab[i].defaddr == aAll)
ap->next->type = '*';
}
if(cp->addr){ /* may be false for '\n' (only) */
static Address none = {0,0,0};
if(f)
addr = address(ap, f->dot, 0);
else /* a " */
addr = address(ap, none, 0);
f = addr.f;
}
}
current(f);
switch(cp->cmdc){
case '{':
a = cp->addr? address(cp->addr, f->dot, 0): f->dot;
for(cp = cp->ccmd; cp; cp = cp->next){
a.f->dot = a;
cmdexec(a.f, cp);
}
break;
default:
i=(*cmdtab[i].fn)(f, cp);
return i;
}
return 1;
}
int
a_cmd(File *f, Cmd *cp)
{
return append(f, cp, addr.r.p2);
}
int
b_cmd(File *f, Cmd *cp)
{
USED(f);
f = cp->cmdc=='b'? tofile(cp->ctext) : getfile(cp->ctext);
if(f->unread)
load(f);
else if(nest == 0)
filename(f);
return TRUE;
}
int
c_cmd(File *f, Cmd *cp)
{
logdelete(f, addr.r.p1, addr.r.p2);
f->ndot.r.p1 = f->ndot.r.p2 = addr.r.p2;
return append(f, cp, addr.r.p2);
}
int
d_cmd(File *f, Cmd *cp)
{
USED(cp);
logdelete(f, addr.r.p1, addr.r.p2);
f->ndot.r.p1 = f->ndot.r.p2 = addr.r.p1;
return TRUE;
}
int
D_cmd(File *f, Cmd *cp)
{
closefiles(f, cp->ctext);
return TRUE;
}
int
e_cmd(File *f, Cmd *cp)
{
if(getname(f, cp->ctext, cp->cmdc=='e')==0)
error(Enoname);
edit(f, cp->cmdc);
return TRUE;
}
int
f_cmd(File *f, Cmd *cp)
{
getname(f, cp->ctext, TRUE);
filename(f);
return TRUE;
}
int
g_cmd(File *f, Cmd *cp)
{
if(f!=addr.f)panic("g_cmd f!=addr.f");
compile(cp->re);
if(execute(f, addr.r.p1, addr.r.p2) ^ cp->cmdc=='v'){
f->dot = addr;
return cmdexec(f, cp->ccmd);
}
return TRUE;
}
int
i_cmd(File *f, Cmd *cp)
{
return append(f, cp, addr.r.p1);
}
int
k_cmd(File *f, Cmd *cp)
{
USED(cp);
f->mark = addr.r;
return TRUE;
}
int
m_cmd(File *f, Cmd *cp)
{
Address addr2;
addr2 = address(cp->caddr, f->dot, 0);
if(cp->cmdc=='m')
move(f, addr2);
else
copy(f, addr2);
return TRUE;
}
int
n_cmd(File *f, Cmd *cp)
{
int i;
USED(f);
USED(cp);
for(i = 0; i<file.nused; i++){
if(file.filepptr[i] == cmd)
continue;
f = file.filepptr[i];
Strduplstr(&genstr, &f->name);
filename(f);
}
return TRUE;
}
int
p_cmd(File *f, Cmd *cp)
{
USED(cp);
return display(f);
}
int
q_cmd(File *f, Cmd *cp)
{
USED(cp);
USED(f);
trytoquit();
if(downloaded){
outT0(Hexit);
return TRUE;
}
return FALSE;
}
int
s_cmd(File *f, Cmd *cp)
{
int i, j, c, n;
Posn p1, op, didsub = 0, delta = 0;
n = cp->num;
op= -1;
compile(cp->re);
for(p1 = addr.r.p1; p1<=addr.r.p2 && execute(f, p1, addr.r.p2); ){
if(sel.p[0].p1==sel.p[0].p2){ /* empty match? */
if(sel.p[0].p1==op){
p1++;
continue;
}
p1 = sel.p[0].p2+1;
}else
p1 = sel.p[0].p2;
op = sel.p[0].p2;
if(--n>0)
continue;
Strzero(&genstr);
for(i = 0; i<cp->ctext->n; i++)
if((c = cp->ctext->s[i])=='\\' && i<cp->ctext->n-1){
c = cp->ctext->s[++i];
if('1'<=c && c<='9') {
j = c-'0';
if(sel.p[j].p2-sel.p[j].p1>BLOCKSIZE)
error(Elongtag);
bufread(f, sel.p[j].p1, genbuf, sel.p[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; p = a3.r.p2){
a3.r.p1 = a3.r.p2;
/*pjw if(p!=r.p1 || (linesel = lineaddr((Posn)0, a3, 1)).r.p2==p)*/
if(p!=r.p1 || (a = lineaddr((Posn)0, a3, 1), linesel = a.r, linesel.p2==p)){
a = lineaddr((Posn)1, a3, 1);
linesel = a.r;
}
if(linesel.p1 >= r.p2)
break;
if(linesel.p2 >= r.p2)
linesel.p2 = r.p2;
if(linesel.p2 > linesel.p1)
if(linesel.p1>=a3.r.p2 && linesel.p2>a3.r.p2){
f->dot.r = linesel;
cmdexec(f, cp->ccmd);
a3.r = linesel;
continue;
}
break;
}
--nest;
}
void
filelooper(Cmd *cp, int XY)
{
File *f, *cur;
int i;
if(Glooping++)
error(EnestXY);
nest++;
settempfile();
cur = curfile;
for(i = 0; i<tempfile.nused; i++){
f = tempfile.filepptr[i];
if(f==cmd)
continue;
if(cp->re==0 || filematch(f, cp->re)==XY)
cmdexec(f, cp->ccmd);
}
if(cur && whichmenu(cur)>=0) /* check that cur is still a file */
current(cur);
--Glooping;
--nest;
}

0
src/libdraw/BOT Normal file
View File

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,6 @@
CC=cc
CFLAGS=-O -c -Ae -I.
O=o
AR=ar
ARFLAGS=rvc
NAN=nan64.$O

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,6 @@
CC=cc
CFLAGS+=-g -c -I.
O=o
AR=ar
ARFLAGS=rvc
NAN=nan64.$O

View File

@@ -0,0 +1,2 @@
include Make.SunOS-sun4u-$(CC)
NAN=nan64.$O

View File

@@ -0,0 +1,6 @@
CC=cc
CFLAGS+=-g -c -I. -O
O=o
AR=ar
ARFLAGS=rvc
NAN=nan64.$O

View File

@@ -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

194
src/libdraw/Makefile Normal file
View File

@@ -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

123
src/libdraw/Makefile.MID Normal file
View File

@@ -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

237
src/libdraw/alloc.c Normal file
View File

@@ -0,0 +1,237 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
Image*
allocimage(Display *d, Rectangle r, u32int chan, int repl, u32int val)
{
return _allocimage(nil, d, r, chan, repl, val, 0, 0);
}
Image*
_allocimage(Image *ai, Display *d, Rectangle r, u32int chan, int repl, u32int 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;
}

206
src/libdraw/arith.c Normal file
View File

@@ -0,0 +1,206 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
Point
Pt(int x, int y)
{
Point p;
p.x = x;
p.y = y;
return p;
}
Rectangle
Rect(int x, int y, int bx, int by)
{
Rectangle r;
r.min.x = x;
r.min.y = y;
r.max.x = bx;
r.max.y = by;
return r;
}
Rectangle
Rpt(Point min, Point max)
{
Rectangle r;
r.min = min;
r.max = max;
return r;
}
Point
addpt(Point a, Point b)
{
a.x += b.x;
a.y += b.y;
return a;
}
Point
subpt(Point a, Point b)
{
a.x -= b.x;
a.y -= b.y;
return a;
}
Rectangle
insetrect(Rectangle r, int n)
{
r.min.x += n;
r.min.y += n;
r.max.x -= n;
r.max.y -= n;
return r;
}
Point
divpt(Point a, int b)
{
a.x /= b;
a.y /= b;
return a;
}
Point
mulpt(Point a, int b)
{
a.x *= b;
a.y *= b;
return a;
}
Rectangle
rectsubpt(Rectangle r, Point p)
{
r.min.x -= p.x;
r.min.y -= p.y;
r.max.x -= p.x;
r.max.y -= p.y;
return r;
}
Rectangle
rectaddpt(Rectangle r, Point p)
{
r.min.x += p.x;
r.min.y += p.y;
r.max.x += p.x;
r.max.y += p.y;
return r;
}
int
eqpt(Point p, Point q)
{
return p.x==q.x && p.y==q.y;
}
int
eqrect(Rectangle r, Rectangle s)
{
return r.min.x==s.min.x && r.max.x==s.max.x &&
r.min.y==s.min.y && r.max.y==s.max.y;
}
int
rectXrect(Rectangle r, Rectangle s)
{
return r.min.x<s.max.x && s.min.x<r.max.x &&
r.min.y<s.max.y && s.min.y<r.max.y;
}
int
rectinrect(Rectangle r, Rectangle s)
{
return s.min.x<=r.min.x && r.max.x<=s.max.x && s.min.y<=r.min.y && r.max.y<=s.max.y;
}
int
ptinrect(Point p, Rectangle r)
{
return p.x>=r.min.x && p.x<r.max.x &&
p.y>=r.min.y && p.y<r.max.y;
}
Rectangle
canonrect(Rectangle r)
{
int t;
if (r.max.x < r.min.x) {
t = r.min.x;
r.min.x = r.max.x;
r.max.x = t;
}
if (r.max.y < r.min.y) {
t = r.min.y;
r.min.y = r.max.y;
r.max.y = t;
}
return r;
}
void
combinerect(Rectangle *r1, Rectangle r2)
{
if(r1->min.x > r2.min.x)
r1->min.x = r2.min.x;
if(r1->min.y > r2.min.y)
r1->min.y = r2.min.y;
if(r1->max.x < r2.max.x)
r1->max.x = r2.max.x;
if(r1->max.y < r2.max.y)
r1->max.y = r2.max.y;
}
u32int
drawld2chan[] = {
GREY1,
GREY2,
GREY4,
CMAP8,
};
int log2[] = { -1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4, -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);
}

141
src/libdraw/buildfont.c Normal file
View File

@@ -0,0 +1,141 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
static char*
skip(char *s)
{
while(*s==' ' || *s=='\n' || *s=='\t')
s++;
return s;
}
Font*
buildfont(Display *d, char *buf, char *name)
{
Font *fnt;
Cachefont *c;
char *s, *t;
ulong min, max;
int offset;
char badform[] = "bad font format: number expected (char position %d)";
s = buf;
fnt = malloc(sizeof(Font));
if(fnt == 0)
return 0;
memset(fnt, 0, sizeof(Font));
fnt->display = d;
fnt->name = strdup(name);
fnt->ncache = NFCACHE+NFLOOK;
fnt->nsubf = NFSUBF;
fnt->cache = malloc(fnt->ncache * sizeof(fnt->cache[0]));
fnt->subf = malloc(fnt->nsubf * sizeof(fnt->subf[0]));
if(fnt->name==0 || fnt->cache==0 || fnt->subf==0){
Err2:
free(fnt->name);
free(fnt->cache);
free(fnt->subf);
free(fnt->sub);
free(fnt);
return 0;
}
fnt->height = strtol(s, &s, 0);
s = skip(s);
fnt->ascent = strtol(s, &s, 0);
s = skip(s);
if(fnt->height<=0 || fnt->ascent<=0){
werrstr("bad height or ascent in font file");
goto Err2;
}
fnt->width = 0;
fnt->nsub = 0;
fnt->sub = 0;
memset(fnt->subf, 0, fnt->nsubf * sizeof(fnt->subf[0]));
memset(fnt->cache, 0, fnt->ncache*sizeof(fnt->cache[0]));
fnt->age = 1;
do{
/* must be looking at a number now */
if(*s<'0' || '9'<*s){
werrstr(badform, s-buf);
goto Err3;
}
min = strtol(s, &s, 0);
s = skip(s);
/* must be looking at a number now */
if(*s<'0' || '9'<*s){
werrstr(badform, s-buf);
goto Err3;
}
max = strtol(s, &s, 0);
s = skip(s);
if(*s==0 || min>=65536 || max>=65536 || min>max){
werrstr("illegal subfont range");
Err3:
freefont(fnt);
return 0;
}
t = s;
offset = strtol(s, &t, 0);
if(t>s && (*t==' ' || *t=='\t' || *t=='\n'))
s = skip(t);
else
offset = 0;
fnt->sub = realloc(fnt->sub, (fnt->nsub+1)*sizeof(Cachefont*));
if(fnt->sub == 0){
/* realloc manual says fnt->sub may have been 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; i<f->nsub; i++){
c = f->sub[i];
free(c->subfontname);
free(c->name);
free(c);
}
for(i=0; i<f->nsubf; i++){
s = f->subf[i].f;
if(s && s!=display->defaultsubfont)
freesubfont(s);
}
freeimage(f->cacheimage);
free(f->name);
free(f->cache);
free(f->subf);
free(f->sub);
free(f);
}

View File

@@ -0,0 +1,34 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
static
int
unitsperline(Rectangle r, int d, int bitsperunit)
{
ulong l, t;
if(d <= 0 || d > 32) /* being called wrong. d is image depth. */
abort();
if(r.min.x >= 0){
l = (r.max.x*d+bitsperunit-1)/bitsperunit;
l -= (r.min.x*d)/bitsperunit;
}else{ /* make positive before divide */
t = (-r.min.x*d+bitsperunit-1)/bitsperunit;
l = t+(r.max.x*d+bitsperunit-1)/bitsperunit;
}
return l;
}
int
wordsperline(Rectangle r, int d)
{
return unitsperline(r, d, 8*sizeof(ulong));
}
int
bytesperline(Rectangle r, int d)
{
return unitsperline(r, d, 8);
}

77
src/libdraw/chan.c Normal file
View File

@@ -0,0 +1,77 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
static char channames[] = "rgbkamx";
char*
chantostr(char *buf, u32int cc)
{
u32int c, rc;
char *p;
if(chantodepth(cc) == 0)
return nil;
/* reverse the channel descriptor so we can easily generate the string 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;
}

113
src/libdraw/creadimage.c Normal file
View File

@@ -0,0 +1,113 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
Image *
creadimage(Display *d, int fd, int dolock)
{
char hdr[5*12+1];
Rectangle r;
int m, nb, miny, maxy, new, ldepth, ncblock;
uchar *buf, *a;
Image *i;
u32int chan;
if(readn(fd, hdr, 5*12) != 5*12)
return nil;
/*
* distinguish new channel descriptor from old ldepth.
* channel descriptors have letters as well as numbers,
* while ldepths are a single digit formatted as %-11d.
*/
new = 0;
for(m=0; m<10; m++){
if(hdr[m] != ' '){
new = 1;
break;
}
}
if(hdr[11] != ' '){
werrstr("creadimage: bad format");
return nil;
}
if(new){
hdr[11] = '\0';
if((chan = strtochan(hdr)) == 0){
werrstr("creadimage: bad channel string %s", hdr);
return nil;
}
}else{
ldepth = ((int)hdr[10])-'0';
if(ldepth<0 || ldepth>3){
werrstr("creadimage: bad ldepth %d", ldepth);
return nil;
}
chan = drawld2chan[ldepth];
}
r.min.x=atoi(hdr+1*12);
r.min.y=atoi(hdr+2*12);
r.max.x=atoi(hdr+3*12);
r.max.y=atoi(hdr+4*12);
if(r.min.x>r.max.x || r.min.y>r.max.y){
werrstr("creadimage: bad rectangle");
return nil;
}
if(dolock)
lockdisplay(d);
i = allocimage(d, r, chan, 0, 0);
if(dolock)
unlockdisplay(d);
if(i == nil)
return nil;
ncblock = _compblocksize(r, i->depth);
buf = malloc(ncblock);
if(buf == nil)
goto Errout;
miny = r.min.y;
while(miny != r.max.y){
if(readn(fd, hdr, 2*12) != 2*12){
Errout:
if(dolock)
lockdisplay(d);
Erroutlock:
freeimage(i);
if(dolock)
unlockdisplay(d);
free(buf);
return nil;
}
maxy = atoi(hdr+0*12);
nb = atoi(hdr+1*12);
if(maxy<=miny || r.max.y<maxy){
werrstr("creadimage: bad maxy %d", maxy);
goto Errout;
}
if(nb<=0 || ncblock<nb){
werrstr("creadimage: bad count %d", nb);
goto Errout;
}
if(readn(fd, buf, nb)!=nb)
goto Errout;
if(dolock)
lockdisplay(d);
a = bufimage(i->display, 21+nb);
if(a == nil)
goto Erroutlock;
a[0] = 'Y';
BPLONG(a+1, i->id);
BPLONG(a+5, r.min.x);
BPLONG(a+9, miny);
BPLONG(a+13, r.max.x);
BPLONG(a+17, maxy);
if(!new) /* old image: flip the data bits */
_twiddlecompressed(buf, nb);
memmove(a+21, buf, nb);
if(dolock)
unlockdisplay(d);
miny = maxy;
}
free(buf);
return i;
}

7
src/libdraw/cursor.h Normal file
View File

@@ -0,0 +1,7 @@
typedef struct Cursor Cursor;
struct Cursor
{
Point offset;
uchar clr[2*16];
uchar set[2*16];
};

1587
src/libdraw/devdraw.c Normal file

File diff suppressed because it is too large Load Diff

520
src/libdraw/draw.h Normal file
View File

@@ -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<<LOG2NFCACHE), /* #chars cached */
NFLOOK = 5, /* #chars to scan in cache */
NFSUBF = 2, /* #subfonts to cache */
/* max value */
MAXFCACHE = 1024+NFLOOK, /* upper limit */
MAXSUBF = 50, /* generous upper limit */
/* deltas */
DSUBF = 4,
/* expiry ages */
SUBFAGE = 10000,
CACHEAGE = 10000
};
struct Cachefont
{
Rune min; /* lowest rune value to be taken from subfont */
Rune max; /* highest rune value+1 to be taken from subfont */
int offset; /* position in subfont of character at min */
char *name; /* stored in font */
char *subfontname; /* to access subfont */
};
struct Cacheinfo
{
ushort x; /* left edge of bits */
uchar width; /* width of baseline */
schar left; /* offset of baseline */
Rune value; /* value of character at this slot in cache */
ushort age;
};
struct Cachesubf
{
u32int age; /* for replacement */
Cachefont *cf; /* font info that owns us */
Subfont *f; /* attached subfont */
};
struct Font
{
char *name;
Display *display;
short height; /* max height of image, interline spacing */
short ascent; /* top of image to baseline */
short width; /* widest so far; used in caching only */
short nsub; /* number of subfonts */
u32int age; /* increasing counter; used for LRU */
int maxdepth; /* maximum depth of all loaded subfonts */
int ncache; /* size of cache */
int nsubf; /* size of subfont list */
Cacheinfo *cache;
Cachesubf *subf;
Cachefont **sub; /* as read from file */
Image *cacheimage;
};
#define Dx(r) ((r).max.x-(r).min.x)
#define Dy(r) ((r).max.y-(r).min.y)
/*
* Image management
*/
extern Image* _allocimage(Image*, Display*, Rectangle, u32int, int, u32int, int, int);
extern Image* allocimage(Display*, Rectangle, u32int, int, u32int);
extern uchar* bufimage(Display*, int);
extern int bytesperline(Rectangle, int);
extern void closedisplay(Display*);
extern void drawerror(Display*, char*);
extern int flushimage(Display*, int);
extern int freeimage(Image*);
extern int _freeimage1(Image*);
extern int geninitdraw(char*, void(*)(Display*, char*), char*, char*, char*, int);
extern int initdraw(void(*)(Display*, char*), char*, char*);
extern int newwindow(char*);
extern int loadimage(Image*, Rectangle, uchar*, int);
extern int cloadimage(Image*, Rectangle, uchar*, int);
extern int getwindow(Display*, int);
extern int gengetwindow(Display*, char*, Image**, Screen**, int);
extern Image* readimage(Display*, int, int);
extern Image* creadimage(Display*, int, int);
extern int unloadimage(Image*, Rectangle, uchar*, int);
extern int wordsperline(Rectangle, int);
extern int writeimage(int, Image*, int);
extern Image* namedimage(Display*, char*);
extern int nameimage(Image*, char*, int);
extern Image* allocimagemix(Display*, u32int, u32int);
/*
* Colors
*/
extern void readcolmap(Display*, RGB*);
extern void writecolmap(Display*, RGB*);
extern u32int setalpha(u32int, uchar);
/*
* Windows
*/
extern Screen* allocscreen(Image*, Image*, int);
extern Image* _allocwindow(Image*, Screen*, Rectangle, int, u32int);
extern Image* allocwindow(Screen*, Rectangle, int, u32int);
extern void bottomnwindows(Image**, int);
extern void bottomwindow(Image*);
extern int freescreen(Screen*);
extern Screen* publicscreen(Display*, int, u32int);
extern void topnwindows(Image**, int);
extern void topwindow(Image*);
extern int originwindow(Image*, Point, Point);
/*
* Geometry
*/
extern Point Pt(int, int);
extern Rectangle Rect(int, int, int, int);
extern Rectangle Rpt(Point, Point);
extern Point addpt(Point, Point);
extern Point subpt(Point, Point);
extern Point divpt(Point, int);
extern Point mulpt(Point, int);
extern int eqpt(Point, Point);
extern int eqrect(Rectangle, Rectangle);
extern Rectangle insetrect(Rectangle, int);
extern Rectangle rectaddpt(Rectangle, Point);
extern Rectangle rectsubpt(Rectangle, Point);
extern Rectangle canonrect(Rectangle);
extern int rectXrect(Rectangle, Rectangle);
extern int rectinrect(Rectangle, Rectangle);
extern void combinerect(Rectangle*, Rectangle);
extern int rectclip(Rectangle*, Rectangle);
extern int ptinrect(Point, Rectangle);
extern void replclipr(Image*, int, Rectangle);
extern int drawreplxy(int, int, int); /* used to be drawsetxy */
extern Point drawrepl(Rectangle, Point);
extern int rgb2cmap(int, int, int);
extern int cmap2rgb(int);
extern int cmap2rgba(int);
extern void icossin(int, int*, int*);
extern void icossin2(int, int, int*, int*);
/*
* Graphics
*/
extern void draw(Image*, Rectangle, Image*, Image*, Point);
extern void drawop(Image*, Rectangle, Image*, Image*, Point, Drawop);
extern void gendraw(Image*, Rectangle, Image*, Point, Image*, Point);
extern void gendrawop(Image*, Rectangle, Image*, Point, Image*, Point, Drawop);
extern void line(Image*, Point, Point, int, int, int, Image*, Point);
extern void lineop(Image*, Point, Point, int, int, int, Image*, Point, Drawop);
extern void poly(Image*, Point*, int, int, int, int, Image*, Point);
extern void polyop(Image*, Point*, int, int, int, int, Image*, Point, Drawop);
extern void fillpoly(Image*, Point*, int, int, Image*, Point);
extern void fillpolyop(Image*, Point*, int, int, Image*, Point, Drawop);
extern Point string(Image*, Point, Image*, Point, Font*, char*);
extern Point stringop(Image*, Point, Image*, Point, Font*, char*, Drawop);
extern Point stringn(Image*, Point, Image*, Point, Font*, char*, int);
extern Point stringnop(Image*, Point, Image*, Point, Font*, char*, int, Drawop);
extern Point runestring(Image*, Point, Image*, Point, Font*, Rune*);
extern Point runestringop(Image*, Point, Image*, Point, Font*, Rune*, Drawop);
extern Point runestringn(Image*, Point, Image*, Point, Font*, Rune*, int);
extern Point runestringnop(Image*, Point, Image*, Point, Font*, Rune*, int, Drawop);
extern Point stringbg(Image*, Point, Image*, Point, Font*, char*, Image*, Point);
extern Point stringbgop(Image*, Point, Image*, Point, Font*, char*, Image*, Point, Drawop);
extern Point stringnbg(Image*, Point, Image*, Point, Font*, char*, int, Image*, Point);
extern Point stringnbgop(Image*, Point, Image*, Point, Font*, char*, int, Image*, Point, Drawop);
extern Point runestringbg(Image*, Point, Image*, Point, Font*, Rune*, Image*, Point);
extern Point runestringbgop(Image*, Point, Image*, Point, Font*, Rune*, Image*, Point, Drawop);
extern Point runestringnbg(Image*, Point, Image*, Point, Font*, Rune*, int, Image*, Point);
extern Point runestringnbgop(Image*, Point, Image*, Point, Font*, Rune*, int, Image*, Point, Drawop);
extern Point _string(Image*, Point, Image*, Point, Font*, char*, Rune*, int, Rectangle, Image*, Point, Drawop);
extern Point stringsubfont(Image*, Point, Image*, Subfont*, char*);
extern int bezier(Image*, Point, Point, Point, Point, int, int, int, Image*, Point);
extern int bezierop(Image*, Point, Point, Point, Point, int, int, int, Image*, Point, Drawop);
extern int bezspline(Image*, Point*, int, int, int, int, Image*, Point);
extern int bezsplineop(Image*, Point*, int, int, int, int, Image*, Point, Drawop);
extern int bezsplinepts(Point*, int, Point**);
extern int fillbezier(Image*, Point, Point, Point, Point, int, Image*, Point);
extern int fillbezierop(Image*, Point, Point, Point, Point, int, Image*, Point, Drawop);
extern int fillbezspline(Image*, Point*, int, int, Image*, Point);
extern int fillbezsplineop(Image*, Point*, int, int, Image*, Point, Drawop);
extern void ellipse(Image*, Point, int, int, int, Image*, Point);
extern void ellipseop(Image*, Point, int, int, int, Image*, Point, Drawop);
extern void fillellipse(Image*, Point, int, int, Image*, Point);
extern void fillellipseop(Image*, Point, int, int, Image*, Point, Drawop);
extern void arc(Image*, Point, int, int, int, Image*, Point, int, int);
extern void arcop(Image*, Point, int, int, int, Image*, Point, int, int, Drawop);
extern void fillarc(Image*, Point, int, int, Image*, Point, int, int);
extern void fillarcop(Image*, Point, int, int, Image*, Point, int, int, Drawop);
extern void border(Image*, Rectangle, int, Image*, Point);
extern void borderop(Image*, Rectangle, int, Image*, Point, Drawop);
/*
* Font management
*/
extern Font* openfont(Display*, char*);
extern Font* buildfont(Display*, char*, char*);
extern void freefont(Font*);
extern Font* mkfont(Subfont*, Rune);
extern int cachechars(Font*, char**, Rune**, ushort*, int, int*, char**);
extern void agefont(Font*);
extern Subfont* allocsubfont(char*, int, int, int, Fontchar*, Image*);
extern Subfont* lookupsubfont(Display*, char*);
extern void installsubfont(char*, Subfont*);
extern void uninstallsubfont(Subfont*);
extern void freesubfont(Subfont*);
extern Subfont* readsubfont(Display*, char*, int, int);
extern Subfont* readsubfonti(Display*, char*, int, Image*, int);
extern int writesubfont(int, Subfont*);
extern void _unpackinfo(Fontchar*, uchar*, int);
extern Point stringsize(Font*, char*);
extern int stringwidth(Font*, char*);
extern int stringnwidth(Font*, char*, int);
extern Point runestringsize(Font*, Rune*);
extern int runestringwidth(Font*, Rune*);
extern int runestringnwidth(Font*, Rune*, int);
extern Point strsubfontwidth(Subfont*, char*);
extern int loadchar(Font*, Rune, Cacheinfo*, int, int, char**);
extern char* subfontname(char*, char*, int);
extern Subfont* _getsubfont(Display*, char*);
extern Subfont* getdefont(Display*);
extern void lockdisplay(Display*);
extern void unlockdisplay(Display*);
extern int drawlsetrefresh(u32int, int, void*, void*);
/*
* Predefined
*/
extern uchar defontdata[];
extern int sizeofdefont;
extern Point ZP;
extern Rectangle ZR;
/*
* Set up by initdraw()
*/
extern Display *display;
extern Font *font;
extern Image *screen;
extern Screen *_screen;
extern int _cursorfd;
extern int _drawdebug; /* set to 1 to see errors from flushimage */
extern void _setdrawop(Display*, Drawop);
extern Display *_initdisplay(void(*)(Display*,char*), char*);
#define BGSHORT(p) (((p)[0]<<0) | ((p)[1]<<8))
#define BGLONG(p) ((BGSHORT(p)<<0) | (BGSHORT(p+2)<<16))
#define BPSHORT(p, v) ((p)[0]=(v), (p)[1]=((v)>>8))
#define BPLONG(p, v) (BPSHORT(p, (v)), BPSHORT(p+2, (v)>>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);

82
src/libdraw/ellipse.c Normal file
View File

@@ -0,0 +1,82 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
static
void
doellipse(int cmd, Image *dst, Point *c, int xr, int yr, int thick, Image *src, 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);
}

271
src/libdraw/emenuhit.c Normal file
View File

@@ -0,0 +1,271 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
enum
{
Margin = 4, /* outside to text */
Border = 2, /* outside to selection boxes */
Blackborder = 2, /* width of outlining border */
Vspacing = 2, /* extra spacing between lines of text */
Maxunscroll = 25, /* maximum #entries before scrolling turns on */
Nscroll = 20, /* number entries in scrolling part */
Scrollwid = 14, /* width of scroll bar */
Gap = 4, /* between text and scroll bar */
};
static Image *menutxt;
static Image *back;
static Image *high;
static Image *bord;
static Image *text;
static Image *htext;
static
void
menucolors(void)
{
/* Main tone is greenish, with negative selection */
back = allocimagemix(display, DPalegreen, DWhite);
high = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DDarkgreen); /* 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; i<nitemdrawn; i++)
paintitem(menu, textr, off, i, 0, nil, nil);
}
static void
menuscrollpaint(Rectangle scrollr, int off, int nitem, int nitemdrawn)
{
Rectangle r;
draw(screen, scrollr, back, nil, ZP);
r.min.x = scrollr.min.x;
r.max.x = scrollr.max.x;
r.min.y = scrollr.min.y + (Dy(scrollr)*off)/nitem;
r.max.y = scrollr.min.y + (Dy(scrollr)*(off+nitemdrawn))/nitem;
if(r.max.y < r.min.y+2)
r.max.y = r.min.y+2;
border(screen, r, 1, bord, ZP);
if(menutxt == 0)
menutxt = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, DDarkgreen);
if(menutxt)
draw(screen, insetrect(r, 1), menutxt, nil, ZP);
}
int
emenuhit(int but, Mouse *m, Menu *menu)
{
int i, nitem, nitemdrawn, maxwid, lasti, off, noff, wid, screenitem;
int scrolling;
Rectangle r, menur, sc, textr, scrollr;
Image *b, *save;
Point pt;
char *item;
if(back == nil)
menucolors();
sc = screen->clipr;
replclipr(screen, 0, screen->r);
maxwid = 0;
for(nitem = 0;
item = menu->item? menu->item[nitem] : (*menu->gen)(nitem);
nitem++){
i = stringwidth(font, item);
if(i > maxwid)
maxwid = i;
}
if(menu->lasthit<0 || menu->lasthit>=nitem)
menu->lasthit = 0;
screenitem = (Dy(screen->r)-10)/(font->height+Vspacing);
if(nitem>Maxunscroll || nitem>screenitem){
scrolling = 1;
nitemdrawn = Nscroll;
if(nitemdrawn > screenitem)
nitemdrawn = screenitem;
wid = maxwid + Gap + Scrollwid;
off = menu->lasthit - nitemdrawn/2;
if(off < 0)
off = 0;
if(off > nitem-nitemdrawn)
off = nitem-nitemdrawn;
lasti = menu->lasthit-off;
}else{
scrolling = 0;
nitemdrawn = nitem;
wid = maxwid;
off = 0;
lasti = menu->lasthit;
}
r = insetrect(Rect(0, 0, wid, nitemdrawn*(font->height+Vspacing)), -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.x<screen->r.min.x)
pt.x = screen->r.min.x-r.min.x;
if(r.min.y<screen->r.min.y)
pt.y = screen->r.min.y-r.min.y;
menur = rectaddpt(r, pt);
textr.max.x = menur.max.x-Margin;
textr.min.x = textr.max.x-maxwid;
textr.min.y = menur.min.y+Margin;
textr.max.y = textr.min.y + nitemdrawn*(font->height+Vspacing);
if(scrolling){
scrollr = insetrect(menur, Border);
scrollr.max.x = scrollr.min.x+Scrollwid;
}else
scrollr = Rect(0, 0, 0, 0);
b = allocimage(display, menur, screen->chan, 0, 0);
if(b == 0)
b = screen;
draw(b, menur, screen, nil, menur.min);
draw(screen, menur, back, nil, ZP);
border(screen, menur, Blackborder, bord, ZP);
save = allocimage(display, menurect(textr, 0), screen->chan, 0, -1);
r = menurect(textr, lasti);
emoveto(divpt(addpt(r.min, r.max), 2));
menupaint(menu, textr, off, nitemdrawn);
if(scrolling)
menuscrollpaint(scrollr, off, nitem, nitemdrawn);
while(m->buttons & (1<<(but-1))){
lasti = menuscan(menu, but, m, textr, off, lasti, save);
if(lasti >= 0)
break;
while(!ptinrect(m->xy, textr) && (m->buttons & (1<<(but-1)))){
if(scrolling && ptinrect(m->xy, scrollr)){
noff = ((m->xy.y-scrollr.min.y)*nitem)/Dy(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;
}

486
src/libdraw/event.c Normal file
View File

@@ -0,0 +1,486 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <cursor.h>
#include <event.h>
typedef struct Slave Slave;
typedef struct Ebuf Ebuf;
struct Slave
{
int pid;
Ebuf *head; /* ueue of messages for this 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; i<nslave; i++)
if((keys & (1<<i)) && eslave[i].head){
id = 1<<i;
if(i == Smouse)
e->mouse = emouse();
else if(i == Skeyboard)
e->kbdc = ekbd();
else if(i == Stimer)
eslave[i].head = 0;
else{
eb = ebread(&eslave[i]);
e->n = eb->n;
if(eslave[i].fn)
id = (*eslave[i].fn)(id, e, eb->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; i<nslave; i++)
if((keys & (1<<i)) && eslave[i].head)
return 1;
d = dirfstat(epipe[0]);
if(d == nil)
drawerror(display, "events: ecanread stat error");
l = d->length;
free(d);
if(l == 0)
return 0;
extract();
}
return -1;
}
ulong
estartfn(ulong key, int fd, int n, int (*fn)(int, Event*, uchar*, int))
{
char buf[EMAXMSG+1];
int i, r;
if(fd < 0)
drawerror(display, "events: bad file descriptor");
if(n <= 0 || n > EMAXMSG)
n = EMAXMSG;
i = eforkslave(key);
if(i < MAXSLAVE){
eslave[i].fn = fn;
return 1<<i;
}
buf[0] = i - MAXSLAVE;
while((r = read(fd, buf+1, n))>0)
if(write(epipe[1], buf, r+1)!=r+1)
break;
buf[0] = MAXSLAVE;
write(epipe[1], buf, 1);
_exits(0);
return 0;
}
ulong
estart(ulong key, int fd, int n)
{
return estartfn(key, fd, n, nil);
}
ulong
etimer(ulong key, int n)
{
char t[2];
if(Stimer != -1)
drawerror(display, "events: timer started twice");
Stimer = eforkslave(key);
if(Stimer < MAXSLAVE)
return 1<<Stimer;
if(n <= 0)
n = 1000;
t[0] = t[1] = Stimer - MAXSLAVE;
do
sleep(n);
while(write(epipe[1], t, 2) == 2);
t[0] = MAXSLAVE;
write(epipe[1], t, 1);
_exits(0);
return 0;
}
static void
ekeyslave(int fd)
{
Rune r;
char t[3], k[10];
int kr, kn, w;
if(eforkslave(Ekeyboard) < MAXSLAVE)
return;
kn = 0;
t[0] = Skeyboard;
for(;;){
while(!fullrune(k, kn)){
kr = read(fd, k+kn, sizeof k - kn);
if(kr <= 0)
goto breakout;
kn += kr;
}
w = chartorune(&r, k);
kn -= w;
memmove(k, &k[w], kn);
t[1] = r;
t[2] = r>>8;
if(write(epipe[1], t, 3) != 3)
break;
}
breakout:;
t[0] = MAXSLAVE;
write(epipe[1], t, 1);
_exits(0);
}
void
einit(ulong keys)
{
int ctl, fd;
char buf[256];
parentpid = getpid();
if(pipe(epipe) < 0)
drawerror(display, "events: einit pipe");
atexit(ekill);
atnotify(enote, 1);
snprint(buf, sizeof buf, "%s/mouse", display->devdir);
mousefd = open(buf, ORDWR|OCEXEC);
if(mousefd < 0)
drawerror(display, "einit: can't open mouse\n");
snprint(buf, sizeof buf, "%s/cursor", display->devdir);
cursorfd = open(buf, ORDWR|OCEXEC);
if(cursorfd < 0)
drawerror(display, "einit: can't open cursor\n");
if(keys&Ekeyboard){
snprint(buf, sizeof buf, "%s/cons", display->devdir);
fd = open(buf, OREAD);
if(fd < 0)
drawerror(display, "events: can't open console");
snprint(buf, sizeof buf, "%s/consctl", display->devdir);
ctl = open("/dev/consctl", OWRITE|OCEXEC);
if(ctl < 0)
drawerror(display, "events: can't open consctl");
write(ctl, "rawon", 5);
for(Skeyboard=0; Ekeyboard & ~(1<<Skeyboard); Skeyboard++)
;
ekeyslave(fd);
}
if(keys&Emouse){
estart(Emouse, mousefd, 1+4*12);
for(Smouse=0; Emouse & ~(1<<Smouse); Smouse++)
;
}
}
static void
extract(void)
{
Slave *s;
Ebuf *eb;
int i, n;
uchar ebuf[EMAXMSG+1];
/* avoid generating a message if there's nothing to show. */
/* this test isn't perfect, though; could do flushimage(display, 0) then call extract */
/* also: make sure we don't interfere if we're multiprocessing the display */
if(display->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; i<MAXSLAVE; i++)
if((key & ~(1<<i)) == 0 && eslave[i].pid == 0){
if(nslave <= i)
nslave = i + 1;
/*
* share the file descriptors so the last child
* out closes all connections to the window server.
*/
switch(pid = rfork(RFPROC)){
case 0:
return MAXSLAVE+i;
case -1:
fprint(2, "events: fork error\n");
exits("fork");
}
eslave[i].pid = pid;
eslave[i].head = eslave[i].tail = 0;
return i;
}
drawerror(display, "events: bad slave assignment");
return 0;
}
static int
enote(void *v, char *s)
{
char t[1];
int i, pid;
USED(v, s);
pid = getpid();
if(pid != parentpid){
for(i=0; i<nslave; i++){
if(pid == eslave[i].pid){
t[0] = MAXSLAVE;
write(epipe[1], t, 1);
break;
}
}
return 0;
}
close(epipe[0]);
epipe[0] = -1;
close(epipe[1]);
epipe[1] = -1;
for(i=0; i<nslave; i++){
if(pid == eslave[i].pid)
continue; /* don't kill myself */
postnote(PNPROC, eslave[i].pid, "die");
}
return 0;
}
static void
ekill(void)
{
enote(0, 0);
}
Mouse
emouse(void)
{
Mouse m;
Ebuf *eb;
static but[2];
int b;
if(Smouse < 0)
drawerror(display, "events: mouse not initialized");
eb = ebread(&eslave[Smouse]);
m.xy.x = atoi((char*)eb->buf+1+0*12);
m.xy.y = atoi((char*)eb->buf+1+1*12);
b = atoi((char*)eb->buf+1+2*12);
m.buttons = b&7;
m.msec = atoi((char*)eb->buf+1+3*12);
if (logfid)
fprint(logfid, "b: %d xy: %P\n", m.buttons, m.xy);
free(eb);
return m;
}
int
ekbd(void)
{
Ebuf *eb;
int c;
if(Skeyboard < 0)
drawerror(display, "events: keyboard not initialzed");
eb = ebread(&eslave[Skeyboard]);
c = eb->buf[0] + (eb->buf[1]<<8);
free(eb);
return c;
}
void
emoveto(Point pt)
{
char buf[2*12+2];
int n;
n = sprint(buf, "m%d %d", pt.x, pt.y);
write(mousefd, buf, n);
}
void
esetcursor(Cursor *c)
{
uchar curs[2*4+2*2*16];
if(c == 0)
write(cursorfd, curs, 0);
else{
BPLONG(curs+0*4, c->offset.x);
BPLONG(curs+1*4, c->offset.y);
memmove(curs+2*4, c->clr, 2*2*16);
write(cursorfd, curs, sizeof curs);
}
}
int
ereadmouse(Mouse *m)
{
int n;
char buf[128];
do{
n = read(mousefd, buf, sizeof(buf));
if(n < 0) /* probably interrupted */
return -1;
n = eatomouse(m, buf, n);
}while(n == 0);
return n;
}
int
eatomouse(Mouse *m, char *buf, int n)
{
if(n != 1+4*12){
werrstr("atomouse: bad count");
return -1;
}
if(buf[0] == 'r')
eresized(1);
m->xy.x = atoi(buf+1+0*12);
m->xy.y = atoi(buf+1+1*12);
m->buttons = atoi(buf+1+2*12);
m->msec = atoi(buf+1+3*12);
return n;
}

63
src/libdraw/event.h Normal file
View File

@@ -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);

401
src/libdraw/font.c Normal file
View File

@@ -0,0 +1,401 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
static int fontresize(Font*, int, int, int);
#if 0
static int freeup(Font*);
#endif
#define PJW 0 /* use NUL==pjw for invisible characters */
static Rune empty[] = { 0 };
int
cachechars(Font *f, char **ss, Rune **rr, ushort *cp, int max, int *wp, char **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) && i<max; sp+=w, rp+=rw){
if(ss){
r = *(uchar*)sp;
if(r < Runeself)
w = 1;
else{
w = chartorune(&vr, sp);
r = vr;
}
rw = 0;
}else{
r = *rp;
w = 0;
rw = 1;
}
sh = (17 * (uint)r) & (f->ncache-NFLOOK-1);
c = &f->cache[sh];
ec = c+NFLOOK;
h = sh;
while(c < ec){
if(c->value==r && c->age)
goto Found;
c++;
h++;
}
/*
* Not found; toss out oldest entry
*/
a = ~0;
th = sh;
tc = &f->cache[th];
while(tc < ec){
if(tc->age < a){
a = tc->age;
h = th;
c = tc;
}
tc++;
th++;
}
if(a && (f->age-a)<500){ /* kicking out too recent; 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->age<SUBFAGE && s->cf->name != nil){
/* clean up */
if(s->f != display->defaultsubfont)
freesubfont(s->f);
s->cf = nil;
s->f = nil;
s->age = 0;
}else{
s->age >>= 2;
s->age++;
}
}
s++;
}
f->age = (65536>>2) + 1;
}
}
static Subfont*
cf2subfont(Cachefont *cf, Font *f)
{
int depth;
char *name;
Subfont *sf;
name = cf->subfontname;
if(name == nil){
depth = 0;
if(f->display){
if(f->display->screenimage)
depth = f->display->screenimage->depth;
}
name = subfontname(cf->name, f->name, depth);
if(name == nil)
return nil;
cf->subfontname = name;
}
sf = lookupsubfont(f->display, name);
return sf;
}
/* return 1 if load succeeded, 0 if failed, -1 if must retry */
int
loadchar(Font *f, Rune r, Cacheinfo *c, int h, int noflush, char **subfontname)
{
int i, oi, wid, top, bottom;
Rune pic;
Fontchar *fi;
Cachefont *cf;
Cachesubf *subf, *of;
uchar *b;
pic = r;
Again:
for(i=0; i<f->nsub; i++){
cf = f->sub[i];
if(cf->min<=pic && pic<=cf->max)
goto Found;
}
TryPJW:
if(pic != PJW){
pic = PJW;
goto Again;
}
return 0;
Found:
/*
* Choose exact or oldest
*/
oi = 0;
subf = &f->subf[0];
for(i=0; i<f->nsubf; i++){
if(cf == subf->cf)
goto Found2;
if(subf->age < f->subf[oi].age)
oi = i;
subf++;
}
subf = &f->subf[oi];
if(subf->f){
if(f->age-subf->age>SUBFAGE || f->nsubf>MAXSUBF){
Toss:
/* ancient data; toss */
freesubfont(subf->f);
subf->cf = nil;
subf->f = nil;
subf->age = 0;
}else{ /* too recent; grow 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; i<subf->f->n; i++){
t = subf->f->info[i].top-d;
if(t < 0)
t = 0;
subf->f->info[i].top = t;
t = subf->f->info[i].bottom-d;
if(t < 0)
t = 0;
subf->f->info[i].bottom = t;
}
subf->f->ascent = f->ascent;
}
Found2:
subf->age = f->age;
pic += cf->offset;
if(pic-cf->min >= subf->f->n)
goto TryPJW;
fi = &subf->f->info[pic - cf->min];
if(fi->width == 0)
goto TryPJW;
wid = (fi+1)->x - fi->x;
if(f->width < wid || f->width == 0 || f->maxdepth < subf->f->bits->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;
}

36
src/libdraw/getsubfont.c Normal file
View File

@@ -0,0 +1,36 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
/*
* Default version: treat as file name
*/
Subfont*
_getsubfont(Display *d, char *name)
{
int fd;
Subfont *f;
fd = open(name, OREAD);
if(fd < 0){
fprint(2, "getsubfont: can't open %s: %r\n", name);
return 0;
}
/*
* unlock display so i/o happens with display released, unless
* user is doing his own locking, in which case this could break 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;
}

203
src/libdraw/init.c Normal file
View File

@@ -0,0 +1,203 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
Display *display;
Font *font;
Image *screen;
int _drawdebug;
static char deffontname[] = "*default*";
Screen *_screen;
int debuglockdisplay = 0;
static void
drawshutdown(void)
{
Display *d;
d = display;
if(d){
display = nil;
closedisplay(d);
}
}
int
initdraw(void (*error)(Display*, char*), char *fontname, char *label)
{
Subfont *df;
char buf[128];
display = _initdisplay(error, label); /* sets screen too */
if(display == nil)
return -1;
display->image = display->screenimage;
screen = display->screenimage;
/*
* Set up default font
*/
df = getdefont(display);
display->defaultsubfont = df;
if(df == nil){
fprint(2, "imageinit: can't open default subfont: %r\n");
Error:
closedisplay(display);
display = nil;
return -1;
}
if(fontname == nil)
fontname = getenv("font"); /* leak */
/*
* Build fonts with caches==depth of screen, for speed.
* If conversion were faster, we'd use 0 and save memory.
*/
if(fontname == nil){
snprint(buf, sizeof buf, "%d %d\n0 %d\t%s\n", df->height, df->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;
}

102
src/libdraw/keyboard.c Normal file
View File

@@ -0,0 +1,102 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <keyboard.h>
void
closekeyboard(Keyboardctl *kc)
{
if(kc == nil)
return;
postnote(PNPROC, kc->pid, "kill");
#ifdef BUG
/* Drain the channel */
while(?kc->c)
<-kc->c;
#endif
close(kc->ctlfd);
close(kc->consfd);
free(kc->file);
free(kc->c);
free(kc);
}
static
void
_ioproc(void *arg)
{
int m, n;
char buf[20];
Rune r;
Keyboardctl *kc;
kc = arg;
threadsetname("kbdproc");
kc->pid = getpid();
n = 0;
for(;;){
while(n>0 && fullrune(buf, n)){
m = chartorune(&r, buf);
n -= m;
memmove(buf, buf+m, n);
send(kc->c, &r);
}
m = read(kc->consfd, buf+n, sizeof buf-n);
if(m <= 0){
yield(); /* if error is due to exiting, we'll 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));
}

36
src/libdraw/keyboard.h Normal file
View File

@@ -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,
};

1
src/libdraw/libdraw.x Normal file
View File

@@ -0,0 +1 @@
!<arch>

200
src/libdraw/md-alloc.c Normal file
View File

@@ -0,0 +1,200 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#define poolalloc(a, b) malloc(b)
#define poolfree(a, b) free(b)
void
memimagemove(void *from, void *to)
{
Memdata *md;
md = *(Memdata**)to;
if(md->base != from){
print("compacted data not right: #%p\n", md->base);
abort();
}
md->base = to;
/* if allocmemimage changes this must change too */
md->bdata = (uchar*)&md->base[2];
}
Memimage*
allocmemimaged(Rectangle r, u32int chan, Memdata *md, void *X)
{
int d;
u32int l;
Memimage *i;
if(Dx(r) <= 0 || Dy(r) <= 0){
werrstr("bad rectangle %R", r);
return nil;
}
if((d = chantodepth(chan)) == 0) {
werrstr("bad channel descriptor %.8lux", chan);
return nil;
}
l = wordsperline(r, d);
i = mallocz(sizeof(Memimage), 1);
if(i == nil)
return nil;
i->X = X;
i->data = md;
i->zero = sizeof(u32int)*l*r.min.y;
if(r.min.x >= 0)
i->zero += (r.min.x*d)/8;
else
i->zero -= (-r.min.x*d+7)/8;
i->zero = -i->zero;
i->width = l;
i->r = r;
i->clipr = r;
i->flags = 0;
i->layer = nil;
i->cmap = memdefcmap;
if(memsetchan(i, chan) < 0){
free(i);
return nil;
}
return i;
}
Memimage*
_allocmemimage(Rectangle r, u32int chan)
{
int d;
u32int l, nw;
Memdata *md;
Memimage *i;
if((d = chantodepth(chan)) == 0) {
werrstr("bad channel descriptor %.8lux", chan);
return nil;
}
l = wordsperline(r, d);
nw = l*Dy(r);
md = malloc(sizeof(Memdata));
if(md == nil)
return nil;
md->ref = 1;
md->base = poolalloc(imagmem, (2+nw)*sizeof(u32int));
if(md->base == nil){
free(md);
return nil;
}
md->base[0] = (u32int)md;
md->base[1] = getcallerpc(&r);
/* if this changes, memimagemove must change too */
md->bdata = (uchar*)&md->base[2];
md->allocd = 1;
i = allocmemimaged(r, chan, md, nil);
if(i == nil){
poolfree(imagmem, md->base);
free(md);
return nil;
}
md->imref = i;
return i;
}
void
_freememimage(Memimage *i)
{
if(i == nil)
return;
if(i->data->ref-- == 1 && i->data->allocd){
if(i->data->base)
poolfree(imagmem, i->data->base);
free(i->data);
}
free(i);
}
/*
* Wordaddr is deprecated.
*/
u32int*
wordaddr(Memimage *i, Point p)
{
return (u32int*) ((u32int)byteaddr(i, p) & ~(sizeof(u32int)-1));
}
uchar*
byteaddr(Memimage *i, Point p)
{
uchar *a;
a = i->data->bdata+i->zero+sizeof(u32int)*p.y*i->width;
if(i->depth < 8){
/*
* We need to always round down,
* but C rounds toward zero.
*/
int np;
np = 8/i->depth;
if(p.x < 0)
return a+(p.x-np+1)/np;
else
return a+p.x/np;
}
else
return a+p.x*(i->depth/8);
}
int
memsetchan(Memimage *i, u32int chan)
{
int d;
int t, j, k;
u32int cc;
int bytes;
if((d = chantodepth(chan)) == 0) {
werrstr("bad channel descriptor");
return -1;
}
i->depth = d;
i->chan = chan;
i->flags &= ~(Fgrey|Falpha|Fcmap|Fbytes);
bytes = 1;
for(cc=chan, j=0, k=0; cc; j+=NBITS(cc), cc>>=8, k++){
t=TYPE(cc);
if(t < 0 || t >= NChan){
werrstr("bad channel string");
return -1;
}
if(t == CGrey)
i->flags |= Fgrey;
if(t == CAlpha)
i->flags |= Falpha;
if(t == CMap && i->cmap == nil){
i->cmap = memdefcmap;
i->flags |= Fcmap;
}
i->shift[t] = j;
i->mask[t] = (1<<NBITS(cc))-1;
i->nbits[t] = NBITS(cc);
if(NBITS(cc) != 8)
bytes = 0;
}
i->nchan = k;
if(bytes)
i->flags |= Fbytes;
return 0;
}

116
src/libdraw/md-arc.c Normal file
View File

@@ -0,0 +1,116 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
/*
* elarc(dst,c,a,b,t,src,sp,alpha,phi)
* draws the part of an ellipse between rays at angles alpha and alpha+phi
* measured counterclockwise from the positive x axis. other
* arguments are as for ellipse(dst,c,a,b,t,src,sp)
*/
enum
{
R, T, L, B /* right, top, left, bottom */
};
static
Point corners[] = {
{1,1},
{-1,1},
{-1,-1},
{1,-1}
};
static
Point p00;
/*
* make a "wedge" mask covering the desired angle and contained in
* a surrounding square; draw a full ellipse; intersect that with the
* wedge to make a mask through which to copy src to dst.
*/
void
memarc(Memimage *dst, Point c, int a, int b, int t, Memimage *src, Point sp, 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);
}

Some files were not shown because too many files have changed in this diff Show More