Initial revision
This commit is contained in:
150
man/man7/regexp9.7
Normal file
150
man/man7/regexp9.7
Normal 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
91
man/man7/utf.7
Normal 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
258
src/cmd/mk/LICENSE
Normal 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.
|
||||
|
||||
7
src/cmd/mk/Make.FreeBSD-386
Normal file
7
src/cmd/mk/Make.FreeBSD-386
Normal 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
|
||||
6
src/cmd/mk/Make.HP-UX-9000
Normal file
6
src/cmd/mk/Make.HP-UX-9000
Normal file
@@ -0,0 +1,6 @@
|
||||
CC=cc
|
||||
CFLAGS=-O -c -Ae -I.
|
||||
O=o
|
||||
AR=ar
|
||||
ARFLAGS=rvc
|
||||
NAN=nan64.$O
|
||||
7
src/cmd/mk/Make.Linux-386
Normal file
7
src/cmd/mk/Make.Linux-386
Normal 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
|
||||
6
src/cmd/mk/Make.OSF1-alpha
Normal file
6
src/cmd/mk/Make.OSF1-alpha
Normal file
@@ -0,0 +1,6 @@
|
||||
CC=cc
|
||||
CFLAGS+=-g -c -I.
|
||||
O=o
|
||||
AR=ar
|
||||
ARFLAGS=rvc
|
||||
NAN=nan64.$O
|
||||
2
src/cmd/mk/Make.SunOS-sun4u
Normal file
2
src/cmd/mk/Make.SunOS-sun4u
Normal file
@@ -0,0 +1,2 @@
|
||||
include Make.SunOS-sun4u-$(CC)
|
||||
NAN=nan64.$O
|
||||
6
src/cmd/mk/Make.SunOS-sun4u-cc
Normal file
6
src/cmd/mk/Make.SunOS-sun4u-cc
Normal file
@@ -0,0 +1,6 @@
|
||||
CC=cc
|
||||
CFLAGS+=-g -c -I. -O
|
||||
O=o
|
||||
AR=ar
|
||||
ARFLAGS=rvc
|
||||
NAN=nan64.$O
|
||||
6
src/cmd/mk/Make.SunOS-sun4u-gcc
Normal file
6
src/cmd/mk/Make.SunOS-sun4u-gcc
Normal 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
117
src/cmd/mk/Makefile
Normal 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
45
src/cmd/mk/Makefile.MID
Normal 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
52
src/cmd/mk/arc.c
Normal 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
180
src/cmd/mk/archive.c
Normal 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
46
src/cmd/mk/bundle.ports
Normal 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
149
src/cmd/mk/env.c
Normal 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®EXP)
|
||||
envupd("stem",newword(""));
|
||||
else
|
||||
envupd("stem", newword(j->stem));
|
||||
envupd("prereq", wdup(j->p));
|
||||
sprint(buf, "%d", getpid());
|
||||
envupd("pid", newword(buf));
|
||||
sprint(buf, "%d", slot);
|
||||
envupd("nproc", newword(buf));
|
||||
envupd("newprereq", wdup(j->np));
|
||||
envupd("alltarget", wdup(j->at));
|
||||
l = &v;
|
||||
v = w = wdup(j->np);
|
||||
while(w){
|
||||
cp = strchr(w->s, '(');
|
||||
if(cp){
|
||||
qp = strchr(cp+1, ')');
|
||||
if(qp){
|
||||
*qp = 0;
|
||||
strcpy(w->s, cp+1);
|
||||
l = &w->next;
|
||||
w = w->next;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
*l = w->next;
|
||||
free(w->s);
|
||||
free(w);
|
||||
w = *l;
|
||||
}
|
||||
envupd("newmember", v);
|
||||
/* update stem0 -> stem9 */
|
||||
for(p = myenv; *p; p++)
|
||||
if(strcmp(*p, "stem0") == 0)
|
||||
break;
|
||||
for(i = 0; *p; i++, p++){
|
||||
if((j->r->attr®EXP) && j->match[i])
|
||||
envupd(*p, newword(j->match[i]));
|
||||
else
|
||||
envupd(*p, newword(""));
|
||||
}
|
||||
return envy;
|
||||
}
|
||||
90
src/cmd/mk/file.c
Normal file
90
src/cmd/mk/file.c
Normal 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
84
src/cmd/mk/fns.h
Normal 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
279
src/cmd/mk/graph.c
Normal 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®EXP){
|
||||
stem[0] = 0;
|
||||
patrule = r;
|
||||
memset((char*)rmatch, 0, sizeof(rmatch));
|
||||
if(regexec(r->pat, node->name, rmatch, NREGEXP) == 0)
|
||||
continue;
|
||||
} else {
|
||||
if(!match(node->name, r->target, stem)) continue;
|
||||
}
|
||||
if(cnt[r->rule] >= nreps) continue;
|
||||
cnt[r->rule]++;
|
||||
|
||||
/* if(r->attr&VIR)
|
||||
* node->flags |= VIRTUAL;
|
||||
* if(r->attr&NOREC)
|
||||
* node->flags |= NORECIPE;
|
||||
* if(r->attr&DEL)
|
||||
* node->flags |= DELETE;
|
||||
*/
|
||||
|
||||
if(!r->tail || !r->tail->s || !*r->tail->s) {
|
||||
a->next = newarc((Node *)0, r, stem, rmatch);
|
||||
a = a->next;
|
||||
} else
|
||||
for(w = r->tail; w; w = w->next){
|
||||
if(r->attr®EXP)
|
||||
regsub(w->s, buf, sizeof buf, rmatch, NREGEXP);
|
||||
else
|
||||
subst(stem, w->s, buf);
|
||||
a->next = newarc(applyrules(buf, cnt), r, stem, rmatch);
|
||||
a = a->next;
|
||||
}
|
||||
cnt[r->rule]--;
|
||||
}
|
||||
a->next = node->prereqs;
|
||||
node->prereqs = head.next;
|
||||
return(node);
|
||||
}
|
||||
|
||||
static void
|
||||
togo(Node *node)
|
||||
{
|
||||
Arc *la, *a;
|
||||
|
||||
/* delete them now */
|
||||
la = 0;
|
||||
for(a = node->prereqs; a; la = a, a = a->next)
|
||||
if(a->flag&TOGO){
|
||||
if(a == node->prereqs)
|
||||
node->prereqs = a->next;
|
||||
else
|
||||
la->next = a->next, a = la;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
vacuous(Node *node)
|
||||
{
|
||||
Arc *la, *a;
|
||||
int vac = !(node->flags&PROBABLE);
|
||||
|
||||
if(node->flags&READY)
|
||||
return(node->flags&VACUOUS);
|
||||
node->flags |= READY;
|
||||
for(a = node->prereqs; a; a = a->next)
|
||||
if(a->n && vacuous(a->n) && (a->r->attr&META))
|
||||
a->flag |= TOGO;
|
||||
else
|
||||
vac = 0;
|
||||
/* if a rule generated arcs that DON'T go; no others from that rule go */
|
||||
for(a = node->prereqs; a; a = a->next)
|
||||
if((a->flag&TOGO) == 0)
|
||||
for(la = node->prereqs; la; la = la->next)
|
||||
if((la->flag&TOGO) && (la->r == a->r)){
|
||||
la->flag &= ~TOGO;
|
||||
}
|
||||
togo(node);
|
||||
if(vac)
|
||||
node->flags |= VACUOUS;
|
||||
return(vac);
|
||||
}
|
||||
|
||||
static Node *
|
||||
newnode(char *name)
|
||||
{
|
||||
register Node *node;
|
||||
|
||||
node = (Node *)Malloc(sizeof(Node));
|
||||
symlook(name, S_NODE, (void *)node);
|
||||
node->name = name;
|
||||
node->time = timeof(name, 0);
|
||||
node->prereqs = 0;
|
||||
node->flags = node->time? PROBABLE : 0;
|
||||
node->next = 0;
|
||||
return(node);
|
||||
}
|
||||
|
||||
void
|
||||
dumpn(char *s, Node *n)
|
||||
{
|
||||
char buf[1024];
|
||||
Arc *a;
|
||||
|
||||
sprint(buf, "%s ", (*s == ' ')? s:"");
|
||||
Bprint(&bout, "%s%s@%ld: time=%ld flags=0x%x next=%ld\n",
|
||||
s, n->name, n, n->time, n->flags, n->next);
|
||||
for(a = n->prereqs; a; a = a->next)
|
||||
dumpa(buf, a);
|
||||
}
|
||||
|
||||
static void
|
||||
trace(char *s, Arc *a)
|
||||
{
|
||||
fprint(2, "\t%s", s);
|
||||
while(a){
|
||||
fprint(2, " <-(%s:%d)- %s", a->r->file, a->r->line,
|
||||
a->n? a->n->name:"");
|
||||
if(a->n){
|
||||
for(a = a->n->prereqs; a; a = a->next)
|
||||
if(*a->r->recipe) break;
|
||||
} else
|
||||
a = 0;
|
||||
}
|
||||
fprint(2, "\n");
|
||||
}
|
||||
|
||||
static void
|
||||
cyclechk(Node *n)
|
||||
{
|
||||
Arc *a;
|
||||
|
||||
if((n->flags&CYCLE) && n->prereqs){
|
||||
fprint(2, "mk: cycle in graph detected at target %s\n", n->name);
|
||||
Exit();
|
||||
}
|
||||
n->flags |= CYCLE;
|
||||
for(a = n->prereqs; a; a = a->next)
|
||||
if(a->n)
|
||||
cyclechk(a->n);
|
||||
n->flags &= ~CYCLE;
|
||||
}
|
||||
|
||||
static void
|
||||
ambiguous(Node *n)
|
||||
{
|
||||
Arc *a;
|
||||
Rule *r = 0;
|
||||
Arc *la;
|
||||
int bad = 0;
|
||||
|
||||
la = 0;
|
||||
for(a = n->prereqs; a; a = a->next){
|
||||
if(a->n)
|
||||
ambiguous(a->n);
|
||||
if(*a->r->recipe == 0) continue;
|
||||
if(r == 0)
|
||||
r = a->r, la = a;
|
||||
else{
|
||||
if(r->recipe != a->r->recipe){
|
||||
if((r->attr&META) && !(a->r->attr&META)){
|
||||
la->flag |= TOGO;
|
||||
r = a->r, la = a;
|
||||
} else if(!(r->attr&META) && (a->r->attr&META)){
|
||||
a->flag |= TOGO;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(r->recipe != a->r->recipe){
|
||||
if(bad == 0){
|
||||
fprint(2, "mk: ambiguous recipes for %s:\n", n->name);
|
||||
bad = 1;
|
||||
trace(n->name, la);
|
||||
}
|
||||
trace(n->name, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(bad)
|
||||
Exit();
|
||||
togo(n);
|
||||
}
|
||||
|
||||
static void
|
||||
attribute(Node *n)
|
||||
{
|
||||
register Arc *a;
|
||||
|
||||
for(a = n->prereqs; a; a = a->next){
|
||||
if(a->r->attr&VIR)
|
||||
n->flags |= VIRTUAL;
|
||||
if(a->r->attr&NOREC)
|
||||
n->flags |= NORECIPE;
|
||||
if(a->r->attr&DEL)
|
||||
n->flags |= DELETE;
|
||||
if(a->n)
|
||||
attribute(a->n);
|
||||
}
|
||||
if(n->flags&VIRTUAL)
|
||||
n->time = 0;
|
||||
}
|
||||
147
src/cmd/mk/lex.c
Normal file
147
src/cmd/mk/lex.c
Normal 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
285
src/cmd/mk/main.c
Normal 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
49
src/cmd/mk/match.c
Normal 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
665
src/cmd/mk/mk.1
Normal 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
226
src/cmd/mk/mk.c
Normal 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
203
src/cmd/mk/mk.h
Normal 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
9
src/cmd/mk/mkfile
Normal 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
5
src/cmd/mk/mkfile.test
Normal file
@@ -0,0 +1,5 @@
|
||||
a: b
|
||||
cp b a
|
||||
|
||||
c:V:
|
||||
echo hello world
|
||||
307
src/cmd/mk/parse.c
Normal file
307
src/cmd/mk/parse.c
Normal 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®EXP)){
|
||||
for(w = head; w; w = w->next)
|
||||
if(charin(w->s, "%&"))
|
||||
break;
|
||||
if(w == 0)
|
||||
target1 = wdup(head);
|
||||
}
|
||||
for(w = head; w; w = w->next)
|
||||
addrule(w->s, tail, body, head, attr, hline, prog);
|
||||
}
|
||||
|
||||
static int
|
||||
rhead(char *line, Word **h, Word **t, int *attr, char **prog)
|
||||
{
|
||||
char *p;
|
||||
char *pp;
|
||||
int sep;
|
||||
Rune r;
|
||||
int n;
|
||||
Word *w;
|
||||
|
||||
p = charin(line,":=<");
|
||||
if(p == 0)
|
||||
return('?');
|
||||
sep = *p;
|
||||
*p++ = 0;
|
||||
if(sep == '<' && *p == '|'){
|
||||
sep = '|';
|
||||
p++;
|
||||
}
|
||||
*attr = 0;
|
||||
*prog = 0;
|
||||
if(sep == '='){
|
||||
pp = charin(p, termchars); /* termchars is shell-dependent */
|
||||
if (pp && *pp == '=') {
|
||||
while (p != pp) {
|
||||
n = chartorune(&r, p);
|
||||
switch(r)
|
||||
{
|
||||
default:
|
||||
SYNERR(-1);
|
||||
fprint(2, "unknown attribute '%c'\n",*p);
|
||||
Exit();
|
||||
case 'U':
|
||||
*attr = 1;
|
||||
break;
|
||||
}
|
||||
p += n;
|
||||
}
|
||||
p++; /* skip trailing '=' */
|
||||
}
|
||||
}
|
||||
if((sep == ':') && *p && (*p != ' ') && (*p != '\t')){
|
||||
while (*p) {
|
||||
n = chartorune(&r, p);
|
||||
if (r == ':')
|
||||
break;
|
||||
p += n;
|
||||
switch(r)
|
||||
{
|
||||
default:
|
||||
SYNERR(-1);
|
||||
fprint(2, "unknown attribute '%c'\n", p[-1]);
|
||||
Exit();
|
||||
case 'D':
|
||||
*attr |= DEL;
|
||||
break;
|
||||
case 'E':
|
||||
*attr |= NOMINUSE;
|
||||
break;
|
||||
case 'n':
|
||||
*attr |= NOVIRT;
|
||||
break;
|
||||
case 'N':
|
||||
*attr |= NOREC;
|
||||
break;
|
||||
case 'P':
|
||||
pp = utfrune(p, ':');
|
||||
if (pp == 0 || *pp == 0)
|
||||
goto eos;
|
||||
*pp = 0;
|
||||
*prog = strdup(p);
|
||||
*pp = ':';
|
||||
p = pp;
|
||||
break;
|
||||
case 'Q':
|
||||
*attr |= QUIET;
|
||||
break;
|
||||
case 'R':
|
||||
*attr |= REGEXP;
|
||||
break;
|
||||
case 'U':
|
||||
*attr |= UPD;
|
||||
break;
|
||||
case 'V':
|
||||
*attr |= VIR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*p++ != ':') {
|
||||
eos:
|
||||
SYNERR(-1);
|
||||
fprint(2, "missing trailing :\n");
|
||||
Exit();
|
||||
}
|
||||
}
|
||||
*h = w = stow(line);
|
||||
if(*w->s == 0 && sep != '<' && sep != '|') {
|
||||
SYNERR(mkinline-1);
|
||||
fprint(2, "no var on left side of assignment/rule\n");
|
||||
Exit();
|
||||
}
|
||||
*t = stow(p);
|
||||
return(sep);
|
||||
}
|
||||
|
||||
static char *
|
||||
rbody(Biobuf *in)
|
||||
{
|
||||
Bufblock *buf;
|
||||
int r, lastr;
|
||||
char *p;
|
||||
|
||||
lastr = '\n';
|
||||
buf = newbuf();
|
||||
for(;;){
|
||||
r = Bgetrune(in);
|
||||
if (r < 0)
|
||||
break;
|
||||
if (lastr == '\n') {
|
||||
if (r == '#')
|
||||
rinsert(buf, r);
|
||||
else if (r != ' ' && r != '\t') {
|
||||
Bungetrune(in);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
rinsert(buf, r);
|
||||
lastr = r;
|
||||
if (r == '\n')
|
||||
mkinline++;
|
||||
}
|
||||
insert(buf, 0);
|
||||
p = strdup(buf->start);
|
||||
freebuf(buf);
|
||||
return p;
|
||||
}
|
||||
|
||||
struct input
|
||||
{
|
||||
char *file;
|
||||
int line;
|
||||
struct input *next;
|
||||
};
|
||||
static struct input *inputs = 0;
|
||||
|
||||
void
|
||||
ipush(void)
|
||||
{
|
||||
struct input *in, *me;
|
||||
|
||||
me = (struct input *)Malloc(sizeof(*me));
|
||||
me->file = infile;
|
||||
me->line = mkinline;
|
||||
me->next = 0;
|
||||
if(inputs == 0)
|
||||
inputs = me;
|
||||
else {
|
||||
for(in = inputs; in->next; )
|
||||
in = in->next;
|
||||
in->next = me;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ipop(void)
|
||||
{
|
||||
struct input *in, *me;
|
||||
|
||||
assert("pop input list", inputs != 0);
|
||||
if(inputs->next == 0){
|
||||
me = inputs;
|
||||
inputs = 0;
|
||||
} else {
|
||||
for(in = inputs; in->next->next; )
|
||||
in = in->next;
|
||||
me = in->next;
|
||||
in->next = 0;
|
||||
}
|
||||
infile = me->file;
|
||||
mkinline = me->line;
|
||||
free((char *)me);
|
||||
}
|
||||
175
src/cmd/mk/rc.c
Normal file
175
src/cmd/mk/rc.c
Normal 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
117
src/cmd/mk/recipe.c
Normal 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®EXP){
|
||||
ww->next = newword(node->name);
|
||||
aw->next = newword(node->name);
|
||||
} else {
|
||||
for(w = r->alltargets; w; w = w->next){
|
||||
if(r->attr&META)
|
||||
subst(aa->stem, w->s, buf);
|
||||
else
|
||||
strcpy(buf, w->s);
|
||||
aw->next = newword(buf);
|
||||
aw = aw->next;
|
||||
if((s = symlook(buf, S_NODE, 0)) == 0)
|
||||
continue; /* not a node we are interested in */
|
||||
n = (Node *)s->value;
|
||||
if(aflag == 0 && n->time) {
|
||||
for(a = n->prereqs; a; a = a->next)
|
||||
if(a->n && outofdate(n, a, 0))
|
||||
break;
|
||||
if(a == 0)
|
||||
continue;
|
||||
}
|
||||
ww->next = newword(buf);
|
||||
ww = ww->next;
|
||||
if(n == node) continue;
|
||||
n->next = node->next;
|
||||
node->next = n;
|
||||
}
|
||||
}
|
||||
for(n = node; n; n = n->next)
|
||||
if((n->flags&READY) == 0)
|
||||
return(did);
|
||||
/*
|
||||
gather the params for the job
|
||||
*/
|
||||
lp.next = ln.next = 0;
|
||||
for(n = node; n; n = n->next){
|
||||
for(a = n->prereqs; a; a = a->next){
|
||||
if(a->n){
|
||||
addw(&lp, a->n->name);
|
||||
if(outofdate(n, a, 0)){
|
||||
addw(&ln, a->n->name);
|
||||
if(explain)
|
||||
fprint(1, "%s(%ld) < %s(%ld)\n",
|
||||
n->name, n->time, a->n->name, a->n->time);
|
||||
}
|
||||
} else {
|
||||
if(explain)
|
||||
fprint(1, "%s has no prerequisites\n",
|
||||
n->name);
|
||||
}
|
||||
}
|
||||
MADESET(n, BEINGMADE);
|
||||
}
|
||||
/*print("lt=%s ln=%s lp=%s\n",wtos(head.next, ' '),wtos(ln.next, ' '),wtos(lp.next, ' '));*//**/
|
||||
run(newjob(r, node, aa->stem, aa->match, lp.next, ln.next, head.next, ahead.next));
|
||||
return(1);
|
||||
}
|
||||
|
||||
void
|
||||
addw(Word *w, char *s)
|
||||
{
|
||||
Word *lw;
|
||||
|
||||
for(lw = w; w = w->next; lw = w){
|
||||
if(strcmp(s, w->s) == 0)
|
||||
return;
|
||||
}
|
||||
lw->next = newword(s);
|
||||
}
|
||||
29
src/cmd/mk/rpm.spec
Normal file
29
src/cmd/mk/rpm.spec
Normal 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
107
src/cmd/mk/rule.c
Normal 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®EXP) || charin(head, "%&")){
|
||||
r->attr |= META;
|
||||
if(reuse)
|
||||
return;
|
||||
if(attr®EXP){
|
||||
patrule = r;
|
||||
r->pat = regcomp(head);
|
||||
}
|
||||
if(metarules == 0)
|
||||
metarules = lmr = r;
|
||||
else {
|
||||
lmr->next = r;
|
||||
lmr = r;
|
||||
}
|
||||
} else {
|
||||
if(reuse)
|
||||
return;
|
||||
r->pat = 0;
|
||||
if(rules == 0)
|
||||
rules = lr = r;
|
||||
else {
|
||||
lr->next = r;
|
||||
lr = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dumpr(char *s, Rule *r)
|
||||
{
|
||||
Bprint(&bout, "%s: start=%ld\n", s, r);
|
||||
for(; r; r = r->next){
|
||||
Bprint(&bout, "\tRule %ld: %s[%d] attr=%x next=%ld chain=%ld alltarget='%s'",
|
||||
r, r->file, r->line, r->attr, r->next, r->chain, wtos(r->alltargets, ' '));
|
||||
if(r->prog)
|
||||
Bprint(&bout, " prog='%s'", r->prog);
|
||||
Bprint(&bout, "\n\ttarget=%s: %s\n", r->target, wtos(r->tail, ' '));
|
||||
Bprint(&bout, "\trecipe@%ld='%s'\n", r->recipe, r->recipe);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
rcmp(Rule *r, char *target, Word *tail)
|
||||
{
|
||||
Word *w;
|
||||
|
||||
if(strcmp(r->target, target))
|
||||
return 1;
|
||||
for(w = r->tail; w && tail; w = w->next, tail = tail->next)
|
||||
if(strcmp(w->s, tail->s))
|
||||
return 1;
|
||||
return(w || tail);
|
||||
}
|
||||
|
||||
char *
|
||||
rulecnt(void)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = Malloc(nrules);
|
||||
memset(s, 0, nrules);
|
||||
return(s);
|
||||
}
|
||||
296
src/cmd/mk/run.c
Normal file
296
src/cmd/mk/run.c
Normal 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
189
src/cmd/mk/sh.c
Normal 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
123
src/cmd/mk/shprint.c
Normal 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
95
src/cmd/mk/symtab.c
Normal 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
306
src/cmd/mk/unix.c
Normal 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
41
src/cmd/mk/var.c
Normal 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
256
src/cmd/mk/varsub.c
Normal 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
180
src/cmd/mk/word.c
Normal 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
258
src/cmd/sam/LICENSE
Normal 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
18
src/cmd/sam/Makefile
Normal 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
240
src/cmd/sam/address.c
Normal 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
302
src/cmd/sam/buff.c
Normal 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
594
src/cmd/sam/cmd.c
Normal 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
122
src/cmd/sam/disk.c
Normal 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
144
src/cmd/sam/error.c
Normal 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
65
src/cmd/sam/errors.h
Normal 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
631
src/cmd/sam/file.c
Normal 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
262
src/cmd/sam/io.c
Normal 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
47
src/cmd/sam/list.c
Normal 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
821
src/cmd/sam/mesg.c
Normal 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
131
src/cmd/sam/mesg.h
Normal 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
40
src/cmd/sam/mkfile
Normal 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
173
src/cmd/sam/moveto.c
Normal 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
123
src/cmd/sam/multi.c
Normal 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
68
src/cmd/sam/parse.h
Normal 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
185
src/cmd/sam/plan9.c
Normal 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
9
src/cmd/sam/plumb.c
Normal 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
325
src/cmd/sam/rasp.c
Normal 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
801
src/cmd/sam/regexp.c
Normal 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
BIN
src/cmd/sam/sam
Executable file
Binary file not shown.
739
src/cmd/sam/sam.c
Normal file
739
src/cmd/sam/sam.c
Normal 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
407
src/cmd/sam/sam.h
Normal 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
152
src/cmd/sam/shell.c
Normal 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
272
src/cmd/sam/unix.c
Normal 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
508
src/cmd/sam/xec.c
Normal 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
0
src/libdraw/BOT
Normal file
6
src/libdraw/Make.Darwin-PowerMacintosh
Normal file
6
src/libdraw/Make.Darwin-PowerMacintosh
Normal 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
|
||||
7
src/libdraw/Make.FreeBSD-386
Normal file
7
src/libdraw/Make.FreeBSD-386
Normal 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
|
||||
6
src/libdraw/Make.HP-UX-9000
Normal file
6
src/libdraw/Make.HP-UX-9000
Normal file
@@ -0,0 +1,6 @@
|
||||
CC=cc
|
||||
CFLAGS=-O -c -Ae -I.
|
||||
O=o
|
||||
AR=ar
|
||||
ARFLAGS=rvc
|
||||
NAN=nan64.$O
|
||||
7
src/libdraw/Make.Linux-386
Normal file
7
src/libdraw/Make.Linux-386
Normal 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
|
||||
7
src/libdraw/Make.NetBSD-386
Normal file
7
src/libdraw/Make.NetBSD-386
Normal 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
|
||||
6
src/libdraw/Make.OSF1-alpha
Normal file
6
src/libdraw/Make.OSF1-alpha
Normal file
@@ -0,0 +1,6 @@
|
||||
CC=cc
|
||||
CFLAGS+=-g -c -I.
|
||||
O=o
|
||||
AR=ar
|
||||
ARFLAGS=rvc
|
||||
NAN=nan64.$O
|
||||
2
src/libdraw/Make.SunOS-sun4u
Normal file
2
src/libdraw/Make.SunOS-sun4u
Normal file
@@ -0,0 +1,2 @@
|
||||
include Make.SunOS-sun4u-$(CC)
|
||||
NAN=nan64.$O
|
||||
6
src/libdraw/Make.SunOS-sun4u-cc
Normal file
6
src/libdraw/Make.SunOS-sun4u-cc
Normal file
@@ -0,0 +1,6 @@
|
||||
CC=cc
|
||||
CFLAGS+=-g -c -I. -O
|
||||
O=o
|
||||
AR=ar
|
||||
ARFLAGS=rvc
|
||||
NAN=nan64.$O
|
||||
6
src/libdraw/Make.SunOS-sun4u-gcc
Normal file
6
src/libdraw/Make.SunOS-sun4u-gcc
Normal 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
194
src/libdraw/Makefile
Normal 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
123
src/libdraw/Makefile.MID
Normal 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
237
src/libdraw/alloc.c
Normal 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
206
src/libdraw/arith.c
Normal 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
141
src/libdraw/buildfont.c
Normal 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);
|
||||
}
|
||||
34
src/libdraw/bytesperline.c
Normal file
34
src/libdraw/bytesperline.c
Normal 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
77
src/libdraw/chan.c
Normal 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
113
src/libdraw/creadimage.c
Normal 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
7
src/libdraw/cursor.h
Normal 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
1587
src/libdraw/devdraw.c
Normal file
File diff suppressed because it is too large
Load Diff
520
src/libdraw/draw.h
Normal file
520
src/libdraw/draw.h
Normal 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
82
src/libdraw/ellipse.c
Normal 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
271
src/libdraw/emenuhit.c
Normal 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
486
src/libdraw/event.c
Normal 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
63
src/libdraw/event.h
Normal 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
401
src/libdraw/font.c
Normal 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
36
src/libdraw/getsubfont.c
Normal 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
203
src/libdraw/init.c
Normal 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
102
src/libdraw/keyboard.c
Normal 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
36
src/libdraw/keyboard.h
Normal 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
1
src/libdraw/libdraw.x
Normal file
@@ -0,0 +1 @@
|
||||
!<arch>
|
||||
200
src/libdraw/md-alloc.c
Normal file
200
src/libdraw/md-alloc.c
Normal 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
116
src/libdraw/md-arc.c
Normal 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
Reference in New Issue
Block a user