initial import

This commit is contained in:
garbeam@wmii.de
2005-11-20 18:27:27 +02:00
commit e5f0f74b8a
323 changed files with 41871 additions and 0 deletions

287
LICENSE Normal file
View File

@@ -0,0 +1,287 @@
The rare bits touched by Anselm R. Garbe are under following LICENSE:
MIT/X Consortium License
(C)opyright MMV Anselm R. Garbe <garbeam at gmail dot com>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
===================================================================
The Plan 9 software is provided under the terms of the
Lucent Public License, Version 1.02, reproduced below,
with the following notable 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.
Other, less notable exceptions are marked in the file tree with
COPYING, COPYRIGHT, or LICENSE files.
===================================================================
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.

35
Makefile Normal file
View File

@@ -0,0 +1,35 @@
# 9base - awk basename cat cleanname echo grep rc sed seq sleep sort tee
# test touch tr uniq from Plan 9
include config.mk
SUBDIRS = lib9 yacc awk basename bc cat cleanname date echo grep rc \
sed seq sleep sort tee test touch tr uniq
all:
@echo 9base build options:
@echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
@chmod 755 yacc/9yacc
@for i in ${SUBDIRS}; do cd $$i; ${MAKE} || exit; cd ..; done;
clean:
@for i in ${SUBDIRS}; do cd $$i; ${MAKE} clean || exit; cd ..; done
@echo cleaned 9base
install: all
@for i in ${SUBDIRS}; do cd $$i; ${MAKE} install || exit; cd ..; done
@echo installed 9base to ${DESTDIR}${PREFIX}
uninstall:
@for i in ${SUBDIRS}; do cd $$i; ${MAKE} uninstall || exit; cd ..; done
@echo uninstalled 9base
dist: clean
@mkdir -p 9base-${VERSION}
@cp -R Makefile README LICENSE config.mk ${SUBDIRS} 9base-${VERSION}
@tar -cf 9base-${VERSION}.tar 9base-${VERSION}
@gzip 9base-${VERSION}.tar
@rm -rf 9base-${VERSION}
@echo created distribution 9base-${VERSION}.tar.gz

30
README Normal file
View File

@@ -0,0 +1,30 @@
Abstract
--------
This is a port of various original Plan 9 tools for Unix, based on
plan9port [1], mk-with-libs.tgz [2], and wmii [3]. See the LICENSE
file for license details.
Requirements
------------
In order to build 9base you need yacc or bison.
Installation
------------
Edit config.mk to match your local setup and execute 'make install'
(if necessary as root). By default, 9rc is installed into its own
hierarchy, /usr/local/9. This is done to avoid conflicts with the
standard tools of your system. You can then put /usr/local/9/bin at
the end of your PATH.
Credits
-------
Many thanks go to Lucent, the Bell Labs which developed this fine
stuff and to Russ Cox for his plan9port.
References
----------
[1] http://swtch.com/plan9port/
[2] http://swtch.com/plan9port/unix/
[3] http://wmii.de
--Anselm R. Garbe

47
awk/Makefile Normal file
View File

@@ -0,0 +1,47 @@
# awk - awk unix port from plan9
# Depends on ../lib9
include ../config.mk
TARG = awk
OFILES = re.o lex.o main.o parse.o proctab.o tran.o lib.o run.o y.tab.o
YFILES = awkgram.y
MANFILES = awk.1
all:
@if [ ! -f y.tab.c ]; then \
${MAKE} -f Makefile depend;\
fi
@${MAKE} -f Makefile ${TARG}
@echo built ${TARG}
depend:
@echo YACC ${YFILES}
@${YACC} -d ${YFILES}
install: ${TARG}
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/
@chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG}
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1
@chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES}
uninstall:
rm -f ${DESTDIR}${PREFIX}/bin/${TARG}
rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES}
.c.o:
@echo CC $*.c
@${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c
clean:
rm -f ${OFILES} ${TARG} y.tab.c y.tab.h
${TARG}: ${OFILES}
@echo LD ${TARG}
@${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -lm -L${PREFIX}/lib -L../lib9 -l9

527
awk/awk.1 Normal file
View File

@@ -0,0 +1,527 @@
.TH AWK 1
.SH NAME
awk \- pattern-directed scanning and processing language
.SH SYNOPSIS
.B awk
[
.BI -F fs
]
[
.BI -v
.I var=value
]
[
.BI -mr n
]
[
.BI -mf n
]
[
.B -f
.I prog
[
.I prog
]
[
.I file ...
]
.SH DESCRIPTION
.I Awk
scans each input
.I file
for lines that match any of a set of patterns specified literally in
.IR prog
or in one or more files
specified as
.B -f
.IR file .
With each pattern
there can be an associated action that will be performed
when a line of a
.I file
matches the pattern.
Each line is matched against the
pattern portion of every pattern-action statement;
the associated action is performed for each matched pattern.
The file name
.L -
means the standard input.
Any
.IR file
of the form
.I var=value
is treated as an assignment, not a file name,
and is executed at the time it would have been opened if it were a file name.
The option
.B -v
followed by
.I var=value
is an assignment to be done before
.I prog
is executed;
any number of
.B -v
options may be present.
.B \-F
.IR fs
option defines the input field separator to be the regular expression
.IR fs .
.PP
An input line is normally made up of fields separated by white space,
or by regular expression
.BR FS .
The fields are denoted
.BR $1 ,
.BR $2 ,
\&..., while
.B $0
refers to the entire line.
If
.BR FS
is null, the input line is split into one field per character.
.PP
To compensate for inadequate implementation of storage management,
the
.B \-mr
option can be used to set the maximum size of the input record,
and the
.B \-mf
option to set the maximum number of fields.
.PP
A pattern-action statement has the form
.IP
.IB pattern " { " action " }
.PP
A missing
.BI { " action " }
means print the line;
a missing pattern always matches.
Pattern-action statements are separated by newlines or semicolons.
.PP
An action is a sequence of statements.
A statement can be one of the following:
.PP
.EX
.ta \w'\fLdelete array[expression]'u
if(\fI expression \fP)\fI statement \fP\fR[ \fPelse\fI statement \fP\fR]\fP
while(\fI expression \fP)\fI statement\fP
for(\fI expression \fP;\fI expression \fP;\fI expression \fP)\fI statement\fP
for(\fI var \fPin\fI array \fP)\fI statement\fP
do\fI statement \fPwhile(\fI expression \fP)
break
continue
{\fR [\fP\fI statement ... \fP\fR] \fP}
\fIexpression\fP #\fR commonly\fP\fI var = expression\fP
print\fR [ \fP\fIexpression-list \fP\fR] \fP\fR[ \fP>\fI expression \fP\fR]\fP
printf\fI format \fP\fR[ \fP,\fI expression-list \fP\fR] \fP\fR[ \fP>\fI expression \fP\fR]\fP
return\fR [ \fP\fIexpression \fP\fR]\fP
next #\fR skip remaining patterns on this input line\fP
nextfile #\fR skip rest of this file, open next, start at top\fP
delete\fI array\fP[\fI expression \fP] #\fR delete an array element\fP
delete\fI array\fP #\fR delete all elements of array\fP
exit\fR [ \fP\fIexpression \fP\fR]\fP #\fR exit immediately; status is \fP\fIexpression\fP
.EE
.DT
.PP
Statements are terminated by
semicolons, newlines or right braces.
An empty
.I expression-list
stands for
.BR $0 .
String constants are quoted \&\fL"\ "\fR,
with the usual C escapes recognized within.
Expressions take on string or numeric values as appropriate,
and are built using the operators
.B + \- * / % ^
(exponentiation), and concatenation (indicated by white space).
The operators
.B
! ++ \-\- += \-= *= /= %= ^= > >= < <= == != ?:
are also available in expressions.
Variables may be scalars, array elements
(denoted
.IB x [ i ] )
or fields.
Variables are initialized to the null string.
Array subscripts may be any string,
not necessarily numeric;
this allows for a form of associative memory.
Multiple subscripts such as
.B [i,j,k]
are permitted; the constituents are concatenated,
separated by the value of
.BR SUBSEP .
.PP
The
.B print
statement prints its arguments on the standard output
(or on a file if
.BI > file
or
.BI >> file
is present or on a pipe if
.BI | cmd
is present), separated by the current output field separator,
and terminated by the output record separator.
.I file
and
.I cmd
may be literal names or parenthesized expressions;
identical string values in different statements denote
the same open file.
The
.B printf
statement formats its expression list according to the format
(see
.IR fprintf (2)) .
The built-in function
.BI close( expr )
closes the file or pipe
.IR expr .
The built-in function
.BI fflush( expr )
flushes any buffered output for the file or pipe
.IR expr .
.PP
The mathematical functions
.BR exp ,
.BR log ,
.BR sqrt ,
.BR sin ,
.BR cos ,
and
.BR atan2
are built in.
Other built-in functions:
.TF length
.TP
.B length
the length of its argument
taken as a string,
or of
.B $0
if no argument.
.TP
.B rand
random number on (0,1)
.TP
.B srand
sets seed for
.B rand
and returns the previous seed.
.TP
.B int
truncates to an integer value
.TP
.B utf
converts its numerical argument, a character number, to a
.SM UTF
string
.TP
.BI substr( s , " m" , " n\fL)
the
.IR n -character
substring of
.I s
that begins at position
.IR m
counted from 1.
.TP
.BI index( s , " t" )
the position in
.I s
where the string
.I t
occurs, or 0 if it does not.
.TP
.BI match( s , " r" )
the position in
.I s
where the regular expression
.I r
occurs, or 0 if it does not.
The variables
.B RSTART
and
.B RLENGTH
are set to the position and length of the matched string.
.TP
.BI split( s , " a" , " fs\fL)
splits the string
.I s
into array elements
.IB a [1]\f1,
.IB a [2]\f1,
\&...,
.IB a [ n ]\f1,
and returns
.IR n .
The separation is done with the regular expression
.I fs
or with the field separator
.B FS
if
.I fs
is not given.
An empty string as field separator splits the string
into one array element per character.
.TP
.BI sub( r , " t" , " s\fL)
substitutes
.I t
for the first occurrence of the regular expression
.I r
in the string
.IR s .
If
.I s
is not given,
.B $0
is used.
.TP
.B gsub
same as
.B sub
except that all occurrences of the regular expression
are replaced;
.B sub
and
.B gsub
return the number of replacements.
.TP
.BI sprintf( fmt , " expr" , " ...\fL)
the string resulting from formatting
.I expr ...
according to the
.I printf
format
.I fmt
.TP
.BI system( cmd )
executes
.I cmd
and returns its exit status
.TP
.BI tolower( str )
returns a copy of
.I str
with all upper-case characters translated to their
corresponding lower-case equivalents.
.TP
.BI toupper( str )
returns a copy of
.I str
with all lower-case characters translated to their
corresponding upper-case equivalents.
.PD
.PP
The ``function''
.B getline
sets
.B $0
to the next input record from the current input file;
.B getline
.BI < file
sets
.B $0
to the next record from
.IR file .
.B getline
.I x
sets variable
.I x
instead.
Finally,
.IB cmd " | getline
pipes the output of
.I cmd
into
.BR getline ;
each call of
.B getline
returns the next line of output from
.IR cmd .
In all cases,
.B getline
returns 1 for a successful input,
0 for end of file, and \-1 for an error.
.PP
Patterns are arbitrary Boolean combinations
(with
.BR "! || &&" )
of regular expressions and
relational expressions.
Regular expressions are as in
.IR regexp (6).
Isolated regular expressions
in a pattern apply to the entire line.
Regular expressions may also occur in
relational expressions, using the operators
.BR ~
and
.BR !~ .
.BI / re /
is a constant regular expression;
any string (constant or variable) may be used
as a regular expression, except in the position of an isolated regular expression
in a pattern.
.PP
A pattern may consist of two patterns separated by a comma;
in this case, the action is performed for all lines
from an occurrence of the first pattern
though an occurrence of the second.
.PP
A relational expression is one of the following:
.IP
.I expression matchop regular-expression
.br
.I expression relop expression
.br
.IB expression " in " array-name
.br
.BI ( expr , expr,... ") in " array-name
.PP
where a
.I relop
is any of the six relational operators in C,
and a
.I matchop
is either
.B ~
(matches)
or
.B !~
(does not match).
A conditional is an arithmetic expression,
a relational expression,
or a Boolean combination
of these.
.PP
The special patterns
.B BEGIN
and
.B END
may be used to capture control before the first input line is read
and after the last.
.B BEGIN
and
.B END
do not combine with other patterns.
.PP
Variable names with special meanings:
.TF FILENAME
.TP
.B CONVFMT
conversion format used when converting numbers
(default
.BR "%.6g" )
.TP
.B FS
regular expression used to separate fields; also settable
by option
.BI \-F fs\f1.
.TP
.BR NF
number of fields in the current record
.TP
.B NR
ordinal number of the current record
.TP
.B FNR
ordinal number of the current record in the current file
.TP
.B FILENAME
the name of the current input file
.TP
.B RS
input record separator (default newline)
.TP
.B OFS
output field separator (default blank)
.TP
.B ORS
output record separator (default newline)
.TP
.B OFMT
output format for numbers (default
.BR "%.6g" )
.TP
.B SUBSEP
separates multiple subscripts (default 034)
.TP
.B ARGC
argument count, assignable
.TP
.B ARGV
argument array, assignable;
non-null members are taken as file names
.TP
.B ENVIRON
array of environment variables; subscripts are names.
.PD
.PP
Functions may be defined (at the position of a pattern-action statement) thus:
.IP
.L
function foo(a, b, c) { ...; return x }
.PP
Parameters are passed by value if scalar and by reference if array name;
functions may be called recursively.
Parameters are local to the function; all other variables are global.
Thus local variables may be created by providing excess parameters in
the function definition.
.SH EXAMPLES
.TP
.L
length($0) > 72
Print lines longer than 72 characters.
.TP
.L
{ print $2, $1 }
Print first two fields in opposite order.
.PP
.EX
BEGIN { FS = ",[ \et]*|[ \et]+" }
{ print $2, $1 }
.EE
.ns
.IP
Same, with input fields separated by comma and/or blanks and tabs.
.PP
.EX
{ s += $1 }
END { print "sum is", s, " average is", s/NR }
.EE
.ns
.IP
Add up first column, print sum and average.
.TP
.L
/start/, /stop/
Print all lines between start/stop pairs.
.PP
.EX
BEGIN { # Simulate echo(1)
for (i = 1; i < ARGC; i++) printf "%s ", ARGV[i]
printf "\en"
exit }
.EE
.SH SOURCE
.B /sys/src/cmd/awk
.SH SEE ALSO
.IR sed (1),
.IR regexp (6),
.br
A. V. Aho, B. W. Kernighan, P. J. Weinberger,
.I
The AWK Programming Language,
Addison-Wesley, 1988. ISBN 0-201-07981-X
.SH BUGS
There are no explicit conversions between numbers and strings.
To force an expression to be treated as a number add 0 to it;
to force it to be treated as a string concatenate
\&\fL""\fP to it.
.br
The scope rules for variables in functions are a botch;
the syntax is worse.

184
awk/awk.h Normal file
View File

@@ -0,0 +1,184 @@
/*
Copyright (c) Lucent Technologies 1997
All Rights Reserved
*/
typedef double Awkfloat;
/* unsigned char is more trouble than it's worth */
typedef unsigned char uschar;
#define xfree(a) { if ((a) != NULL) { free((char *) a); a = NULL; } }
#define DEBUG
#ifdef DEBUG
/* uses have to be doubly parenthesized */
# define dprintf(x) if (dbg) printf x
#else
# define dprintf(x)
#endif
extern char errbuf[];
extern int compile_time; /* 1 if compiling, 0 if running */
extern int safe; /* 0 => unsafe, 1 => safe */
#define RECSIZE (8 * 1024) /* sets limit on records, fields, etc., etc. */
extern int recsize; /* size of current record, orig RECSIZE */
extern char **FS;
extern char **RS;
extern char **ORS;
extern char **OFS;
extern char **OFMT;
extern Awkfloat *NR;
extern Awkfloat *FNR;
extern Awkfloat *NF;
extern char **FILENAME;
extern char **SUBSEP;
extern Awkfloat *RSTART;
extern Awkfloat *RLENGTH;
extern char *record; /* points to $0 */
extern int lineno; /* line number in awk program */
extern int errorflag; /* 1 if error has occurred */
extern int donefld; /* 1 if record broken into fields */
extern int donerec; /* 1 if record is valid (no fld has changed */
extern char inputFS[]; /* FS at time of input, for field splitting */
extern int dbg;
extern char *patbeg; /* beginning of pattern matched */
extern int patlen; /* length of pattern matched. set in b.c */
/* Cell: all information about a variable or constant */
typedef struct Cell {
uschar ctype; /* OCELL, OBOOL, OJUMP, etc. */
uschar csub; /* CCON, CTEMP, CFLD, etc. */
char *nval; /* name, for variables only */
char *sval; /* string value */
Awkfloat fval; /* value as number */
int tval; /* type info: STR|NUM|ARR|FCN|FLD|CON|DONTFREE */
struct Cell *cnext; /* ptr to next if chained */
} Cell;
typedef struct Array { /* symbol table array */
int nelem; /* elements in table right now */
int size; /* size of tab */
Cell **tab; /* hash table pointers */
} Array;
#define NSYMTAB 50 /* initial size of a symbol table */
extern Array *symtab;
extern Cell *nrloc; /* NR */
extern Cell *fnrloc; /* FNR */
extern Cell *nfloc; /* NF */
extern Cell *rstartloc; /* RSTART */
extern Cell *rlengthloc; /* RLENGTH */
/* Cell.tval values: */
#define NUM 01 /* number value is valid */
#define STR 02 /* string value is valid */
#define DONTFREE 04 /* string space is not freeable */
#define CON 010 /* this is a constant */
#define ARR 020 /* this is an array */
#define FCN 040 /* this is a function name */
#define FLD 0100 /* this is a field $1, $2, ... */
#define REC 0200 /* this is $0 */
/* function types */
#define FLENGTH 1
#define FSQRT 2
#define FEXP 3
#define FLOG 4
#define FINT 5
#define FSYSTEM 6
#define FRAND 7
#define FSRAND 8
#define FSIN 9
#define FCOS 10
#define FATAN 11
#define FTOUPPER 12
#define FTOLOWER 13
#define FFLUSH 14
#define FUTF 15
/* Node: parse tree is made of nodes, with Cell's at bottom */
typedef struct Node {
int ntype;
struct Node *nnext;
int lineno;
int nobj;
struct Node *narg[1]; /* variable: actual size set by calling malloc */
} Node;
#define NIL ((Node *) 0)
extern Node *winner;
extern Node *nullstat;
extern Node *nullnode;
/* ctypes */
#define OCELL 1
#define OBOOL 2
#define OJUMP 3
/* Cell subtypes: csub */
#define CFREE 7
#define CCOPY 6
#define CCON 5
#define CTEMP 4
#define CNAME 3
#define CVAR 2
#define CFLD 1
#define CUNK 0
/* bool subtypes */
#define BTRUE 11
#define BFALSE 12
/* jump subtypes */
#define JEXIT 21
#define JNEXT 22
#define JBREAK 23
#define JCONT 24
#define JRET 25
#define JNEXTFILE 26
/* node types */
#define NVALUE 1
#define NSTAT 2
#define NEXPR 3
extern int pairstack[], paircnt;
#define notlegal(n) (n <= FIRSTTOKEN || n >= LASTTOKEN || proctab[n-FIRSTTOKEN] == nullproc)
#define isvalue(n) ((n)->ntype == NVALUE)
#define isexpr(n) ((n)->ntype == NEXPR)
#define isjump(n) ((n)->ctype == OJUMP)
#define isexit(n) ((n)->csub == JEXIT)
#define isbreak(n) ((n)->csub == JBREAK)
#define iscont(n) ((n)->csub == JCONT)
#define isnext(n) ((n)->csub == JNEXT)
#define isnextfile(n) ((n)->csub == JNEXTFILE)
#define isret(n) ((n)->csub == JRET)
#define isrec(n) ((n)->tval & REC)
#define isfld(n) ((n)->tval & FLD)
#define isstr(n) ((n)->tval & STR)
#define isnum(n) ((n)->tval & NUM)
#define isarr(n) ((n)->tval & ARR)
#define isfcn(n) ((n)->tval & FCN)
#define istrue(n) ((n)->csub == BTRUE)
#define istemp(n) ((n)->csub == CTEMP)
#define isargument(n) ((n)->nobj == ARG)
/* #define freeable(p) (!((p)->tval & DONTFREE)) */
#define freeable(p) ( ((p)->tval & (STR|DONTFREE)) == STR )
#include "proto.h"

488
awk/awkgram.y Normal file
View File

@@ -0,0 +1,488 @@
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name Lucent Technologies or any of
its entities not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
****************************************************************/
%{
#include <stdio.h>
#include <string.h>
#include "awk.h"
#define makedfa(a,b) compre(a)
void checkdup(Node *list, Cell *item);
int yywrap(void) { return(1); }
Node *beginloc = 0;
Node *endloc = 0;
int infunc = 0; /* = 1 if in arglist or body of func */
int inloop = 0; /* = 1 if in while, for, do */
char *curfname = 0; /* current function name */
Node *arglist = 0; /* list of args for current function */
%}
%union {
Node *p;
Cell *cp;
int i;
char *s;
}
%token <i> FIRSTTOKEN /* must be first */
%token <p> PROGRAM PASTAT PASTAT2 XBEGIN XEND
%token <i> NL ',' '{' '(' '|' ';' '/' ')' '}' '[' ']'
%token <i> ARRAY
%token <i> MATCH NOTMATCH MATCHOP
%token <i> FINAL DOT ALL CCL NCCL CHAR OR STAR QUEST PLUS
%token <i> AND BOR APPEND EQ GE GT LE LT NE IN
%token <i> ARG BLTIN BREAK CLOSE CONTINUE DELETE DO EXIT FOR FUNC
%token <i> SUB GSUB IF INDEX LSUBSTR MATCHFCN NEXT NEXTFILE
%token <i> ADD MINUS MULT DIVIDE MOD
%token <i> ASSIGN ASGNOP ADDEQ SUBEQ MULTEQ DIVEQ MODEQ POWEQ
%token <i> PRINT PRINTF SPRINTF
%token <p> ELSE INTEST CONDEXPR
%token <i> POSTINCR PREINCR POSTDECR PREDECR
%token <cp> VAR IVAR VARNF CALL NUMBER STRING
%token <s> REGEXPR
%type <p> pas pattern ppattern plist pplist patlist prarg term re
%type <p> pa_pat pa_stat pa_stats
%type <s> reg_expr
%type <p> simple_stmt opt_simple_stmt stmt stmtlist
%type <p> var varname funcname varlist
%type <p> for if else while
%type <i> do st
%type <i> pst opt_pst lbrace rbrace rparen comma nl opt_nl and bor
%type <i> subop print
%right ASGNOP
%right '?'
%right ':'
%left BOR
%left AND
%left GETLINE
%nonassoc APPEND EQ GE GT LE LT NE MATCHOP IN '|'
%left ARG BLTIN BREAK CALL CLOSE CONTINUE DELETE DO EXIT FOR FUNC
%left GSUB IF INDEX LSUBSTR MATCHFCN NEXT NUMBER
%left PRINT PRINTF RETURN SPLIT SPRINTF STRING SUB SUBSTR
%left REGEXPR VAR VARNF IVAR WHILE '('
%left CAT
%left '+' '-'
%left '*' '/' '%'
%left NOT UMINUS
%right POWER
%right DECR INCR
%left INDIRECT
%token LASTTOKEN /* must be last */
%%
program:
pas { if (errorflag==0)
winner = (Node *)stat3(PROGRAM, beginloc, $1, endloc); }
| error { yyclearin; bracecheck(); SYNTAX("bailing out"); }
;
and:
AND | and NL
;
bor:
BOR | bor NL
;
comma:
',' | comma NL
;
do:
DO | do NL
;
else:
ELSE | else NL
;
for:
FOR '(' opt_simple_stmt ';' opt_nl pattern ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt
{ --inloop; $$ = stat4(FOR, $3, notnull($6), $9, $12); }
| FOR '(' opt_simple_stmt ';' ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt
{ --inloop; $$ = stat4(FOR, $3, NIL, $7, $10); }
| FOR '(' varname IN varname rparen {inloop++;} stmt
{ --inloop; $$ = stat3(IN, $3, makearr($5), $8); }
;
funcname:
VAR { setfname($1); }
| CALL { setfname($1); }
;
if:
IF '(' pattern rparen { $$ = notnull($3); }
;
lbrace:
'{' | lbrace NL
;
nl:
NL | nl NL
;
opt_nl:
/* empty */ { $$ = 0; }
| nl
;
opt_pst:
/* empty */ { $$ = 0; }
| pst
;
opt_simple_stmt:
/* empty */ { $$ = 0; }
| simple_stmt
;
pas:
opt_pst { $$ = 0; }
| opt_pst pa_stats opt_pst { $$ = $2; }
;
pa_pat:
pattern { $$ = notnull($1); }
;
pa_stat:
pa_pat { $$ = stat2(PASTAT, $1, stat2(PRINT, rectonode(), NIL)); }
| pa_pat lbrace stmtlist '}' { $$ = stat2(PASTAT, $1, $3); }
| pa_pat ',' pa_pat { $$ = pa2stat($1, $3, stat2(PRINT, rectonode(), NIL)); }
| pa_pat ',' pa_pat lbrace stmtlist '}' { $$ = pa2stat($1, $3, $5); }
| lbrace stmtlist '}' { $$ = stat2(PASTAT, NIL, $2); }
| XBEGIN lbrace stmtlist '}'
{ beginloc = linkum(beginloc, $3); $$ = 0; }
| XEND lbrace stmtlist '}'
{ endloc = linkum(endloc, $3); $$ = 0; }
| FUNC funcname '(' varlist rparen {infunc++;} lbrace stmtlist '}'
{ infunc--; curfname=0; defn((Cell *)$2, $4, $8); $$ = 0; }
;
pa_stats:
pa_stat
| pa_stats opt_pst pa_stat { $$ = linkum($1, $3); }
;
patlist:
pattern
| patlist comma pattern { $$ = linkum($1, $3); }
;
ppattern:
var ASGNOP ppattern { $$ = op2($2, $1, $3); }
| ppattern '?' ppattern ':' ppattern %prec '?'
{ $$ = op3(CONDEXPR, notnull($1), $3, $5); }
| ppattern bor ppattern %prec BOR
{ $$ = op2(BOR, notnull($1), notnull($3)); }
| ppattern and ppattern %prec AND
{ $$ = op2(AND, notnull($1), notnull($3)); }
| ppattern MATCHOP reg_expr { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); }
| ppattern MATCHOP ppattern
{ if (constnode($3))
$$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0));
else
$$ = op3($2, (Node *)1, $1, $3); }
| ppattern IN varname { $$ = op2(INTEST, $1, makearr($3)); }
| '(' plist ')' IN varname { $$ = op2(INTEST, $2, makearr($5)); }
| ppattern term %prec CAT { $$ = op2(CAT, $1, $2); }
| re
| term
;
pattern:
var ASGNOP pattern { $$ = op2($2, $1, $3); }
| pattern '?' pattern ':' pattern %prec '?'
{ $$ = op3(CONDEXPR, notnull($1), $3, $5); }
| pattern bor pattern %prec BOR
{ $$ = op2(BOR, notnull($1), notnull($3)); }
| pattern and pattern %prec AND
{ $$ = op2(AND, notnull($1), notnull($3)); }
| pattern EQ pattern { $$ = op2($2, $1, $3); }
| pattern GE pattern { $$ = op2($2, $1, $3); }
| pattern GT pattern { $$ = op2($2, $1, $3); }
| pattern LE pattern { $$ = op2($2, $1, $3); }
| pattern LT pattern { $$ = op2($2, $1, $3); }
| pattern NE pattern { $$ = op2($2, $1, $3); }
| pattern MATCHOP reg_expr { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); }
| pattern MATCHOP pattern
{ if (constnode($3))
$$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0));
else
$$ = op3($2, (Node *)1, $1, $3); }
| pattern IN varname { $$ = op2(INTEST, $1, makearr($3)); }
| '(' plist ')' IN varname { $$ = op2(INTEST, $2, makearr($5)); }
| pattern '|' GETLINE var {
if (safe) SYNTAX("cmd | getline is unsafe");
else $$ = op3(GETLINE, $4, itonp($2), $1); }
| pattern '|' GETLINE {
if (safe) SYNTAX("cmd | getline is unsafe");
else $$ = op3(GETLINE, (Node*)0, itonp($2), $1); }
| pattern term %prec CAT { $$ = op2(CAT, $1, $2); }
| re
| term
;
plist:
pattern comma pattern { $$ = linkum($1, $3); }
| plist comma pattern { $$ = linkum($1, $3); }
;
pplist:
ppattern
| pplist comma ppattern { $$ = linkum($1, $3); }
;
prarg:
/* empty */ { $$ = rectonode(); }
| pplist
| '(' plist ')' { $$ = $2; }
;
print:
PRINT | PRINTF
;
pst:
NL | ';' | pst NL | pst ';'
;
rbrace:
'}' | rbrace NL
;
re:
reg_expr
{ $$ = op3(MATCH, NIL, rectonode(), (Node*)makedfa($1, 0)); }
| NOT re { $$ = op1(NOT, notnull($2)); }
;
reg_expr:
'/' {startreg();} REGEXPR '/' { $$ = $3; }
;
rparen:
')' | rparen NL
;
simple_stmt:
print prarg '|' term {
if (safe) SYNTAX("print | is unsafe");
else $$ = stat3($1, $2, itonp($3), $4); }
| print prarg APPEND term {
if (safe) SYNTAX("print >> is unsafe");
else $$ = stat3($1, $2, itonp($3), $4); }
| print prarg GT term {
if (safe) SYNTAX("print > is unsafe");
else $$ = stat3($1, $2, itonp($3), $4); }
| print prarg { $$ = stat3($1, $2, NIL, NIL); }
| DELETE varname '[' patlist ']' { $$ = stat2(DELETE, makearr($2), $4); }
| DELETE varname { $$ = stat2(DELETE, makearr($2), 0); }
| pattern { $$ = exptostat($1); }
| error { yyclearin; SYNTAX("illegal statement"); }
;
st:
nl
| ';' opt_nl
;
stmt:
BREAK st { if (!inloop) SYNTAX("break illegal outside of loops");
$$ = stat1(BREAK, NIL); }
| CLOSE pattern st { $$ = stat1(CLOSE, $2); }
| CONTINUE st { if (!inloop) SYNTAX("continue illegal outside of loops");
$$ = stat1(CONTINUE, NIL); }
| do {inloop++;} stmt {--inloop;} WHILE '(' pattern ')' st
{ $$ = stat2(DO, $3, notnull($7)); }
| EXIT pattern st { $$ = stat1(EXIT, $2); }
| EXIT st { $$ = stat1(EXIT, NIL); }
| for
| if stmt else stmt { $$ = stat3(IF, $1, $2, $4); }
| if stmt { $$ = stat3(IF, $1, $2, NIL); }
| lbrace stmtlist rbrace { $$ = $2; }
| NEXT st { if (infunc)
SYNTAX("next is illegal inside a function");
$$ = stat1(NEXT, NIL); }
| NEXTFILE st { if (infunc)
SYNTAX("nextfile is illegal inside a function");
$$ = stat1(NEXTFILE, NIL); }
| RETURN pattern st { $$ = stat1(RETURN, $2); }
| RETURN st { $$ = stat1(RETURN, NIL); }
| simple_stmt st
| while {inloop++;} stmt { --inloop; $$ = stat2(WHILE, $1, $3); }
| ';' opt_nl { $$ = 0; }
;
stmtlist:
stmt
| stmtlist stmt { $$ = linkum($1, $2); }
;
subop:
SUB | GSUB
;
term:
term '/' ASGNOP term { $$ = op2(DIVEQ, $1, $4); }
| term '+' term { $$ = op2(ADD, $1, $3); }
| term '-' term { $$ = op2(MINUS, $1, $3); }
| term '*' term { $$ = op2(MULT, $1, $3); }
| term '/' term { $$ = op2(DIVIDE, $1, $3); }
| term '%' term { $$ = op2(MOD, $1, $3); }
| term POWER term { $$ = op2(POWER, $1, $3); }
| '-' term %prec UMINUS { $$ = op1(UMINUS, $2); }
| '+' term %prec UMINUS { $$ = $2; }
| NOT term %prec UMINUS { $$ = op1(NOT, notnull($2)); }
| BLTIN '(' ')' { $$ = op2(BLTIN, itonp($1), rectonode()); }
| BLTIN '(' patlist ')' { $$ = op2(BLTIN, itonp($1), $3); }
| BLTIN { $$ = op2(BLTIN, itonp($1), rectonode()); }
| CALL '(' ')' { $$ = op2(CALL, celltonode($1,CVAR), NIL); }
| CALL '(' patlist ')' { $$ = op2(CALL, celltonode($1,CVAR), $3); }
| DECR var { $$ = op1(PREDECR, $2); }
| INCR var { $$ = op1(PREINCR, $2); }
| var DECR { $$ = op1(POSTDECR, $1); }
| var INCR { $$ = op1(POSTINCR, $1); }
| GETLINE var LT term { $$ = op3(GETLINE, $2, itonp($3), $4); }
| GETLINE LT term { $$ = op3(GETLINE, NIL, itonp($2), $3); }
| GETLINE var { $$ = op3(GETLINE, $2, NIL, NIL); }
| GETLINE { $$ = op3(GETLINE, NIL, NIL, NIL); }
| INDEX '(' pattern comma pattern ')'
{ $$ = op2(INDEX, $3, $5); }
| INDEX '(' pattern comma reg_expr ')'
{ SYNTAX("index() doesn't permit regular expressions");
$$ = op2(INDEX, $3, (Node*)$5); }
| '(' pattern ')' { $$ = $2; }
| MATCHFCN '(' pattern comma reg_expr ')'
{ $$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa($5, 1)); }
| MATCHFCN '(' pattern comma pattern ')'
{ if (constnode($5))
$$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa(strnode($5), 1));
else
$$ = op3(MATCHFCN, (Node *)1, $3, $5); }
| NUMBER { $$ = celltonode($1, CCON); }
| SPLIT '(' pattern comma varname comma pattern ')' /* string */
{ $$ = op4(SPLIT, $3, makearr($5), $7, (Node*)STRING); }
| SPLIT '(' pattern comma varname comma reg_expr ')' /* const /regexp/ */
{ $$ = op4(SPLIT, $3, makearr($5), (Node*)makedfa($7, 1), (Node *)REGEXPR); }
| SPLIT '(' pattern comma varname ')'
{ $$ = op4(SPLIT, $3, makearr($5), NIL, (Node*)STRING); } /* default */
| SPRINTF '(' patlist ')' { $$ = op1($1, $3); }
| STRING { $$ = celltonode($1, CCON); }
| subop '(' reg_expr comma pattern ')'
{ $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, rectonode()); }
| subop '(' pattern comma pattern ')'
{ if (constnode($3))
$$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, rectonode());
else
$$ = op4($1, (Node *)1, $3, $5, rectonode()); }
| subop '(' reg_expr comma pattern comma var ')'
{ $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, $7); }
| subop '(' pattern comma pattern comma var ')'
{ if (constnode($3))
$$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, $7);
else
$$ = op4($1, (Node *)1, $3, $5, $7); }
| SUBSTR '(' pattern comma pattern comma pattern ')'
{ $$ = op3(SUBSTR, $3, $5, $7); }
| SUBSTR '(' pattern comma pattern ')'
{ $$ = op3(SUBSTR, $3, $5, NIL); }
| var
;
var:
varname
| varname '[' patlist ']' { $$ = op2(ARRAY, makearr($1), $3); }
| IVAR { $$ = op1(INDIRECT, celltonode($1, CVAR)); }
| INDIRECT term { $$ = op1(INDIRECT, $2); }
;
varlist:
/* nothing */ { arglist = $$ = 0; }
| VAR { arglist = $$ = celltonode($1,CVAR); }
| varlist comma VAR {
checkdup($1, $3);
arglist = $$ = linkum($1,celltonode($3,CVAR)); }
;
varname:
VAR { $$ = celltonode($1, CVAR); }
| ARG { $$ = op1(ARG, itonp($1)); }
| VARNF { $$ = op1(VARNF, (Node *) $1); }
;
while:
WHILE '(' pattern rparen { $$ = notnull($3); }
;
%%
void setfname(Cell *p)
{
if (isarr(p))
SYNTAX("%s is an array, not a function", p->nval);
else if (isfcn(p))
SYNTAX("you can't define function %s more than once", p->nval);
curfname = p->nval;
}
int constnode(Node *p)
{
return isvalue(p) && ((Cell *) (p->narg[0]))->csub == CCON;
}
char *strnode(Node *p)
{
return ((Cell *)(p->narg[0]))->sval;
}
Node *notnull(Node *n)
{
switch (n->nobj) {
case LE: case LT: case EQ: case NE: case GT: case GE:
case BOR: case AND: case NOT:
return n;
default:
return op2(NE, n, nullnode);
}
}
void checkdup(Node *vl, Cell *cp) /* check if name already in list */
{
char *s = cp->nval;
for ( ; vl; vl = vl->nnext) {
if (strcmp(s, ((Cell *)(vl->narg[0]))->nval) == 0) {
SYNTAX("duplicate argument %s", s);
break;
}
}
}

569
awk/lex.c Normal file
View File

@@ -0,0 +1,569 @@
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name Lucent Technologies or any of
its entities not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
****************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "awk.h"
#include "y.tab.h"
extern YYSTYPE yylval;
extern int infunc;
int lineno = 1;
int bracecnt = 0;
int brackcnt = 0;
int parencnt = 0;
typedef struct Keyword {
char *word;
int sub;
int type;
} Keyword;
Keyword keywords[] ={ /* keep sorted: binary searched */
{ "BEGIN", XBEGIN, XBEGIN },
{ "END", XEND, XEND },
{ "NF", VARNF, VARNF },
{ "atan2", FATAN, BLTIN },
{ "break", BREAK, BREAK },
{ "close", CLOSE, CLOSE },
{ "continue", CONTINUE, CONTINUE },
{ "cos", FCOS, BLTIN },
{ "delete", DELETE, DELETE },
{ "do", DO, DO },
{ "else", ELSE, ELSE },
{ "exit", EXIT, EXIT },
{ "exp", FEXP, BLTIN },
{ "fflush", FFLUSH, BLTIN },
{ "for", FOR, FOR },
{ "func", FUNC, FUNC },
{ "function", FUNC, FUNC },
{ "getline", GETLINE, GETLINE },
{ "gsub", GSUB, GSUB },
{ "if", IF, IF },
{ "in", IN, IN },
{ "index", INDEX, INDEX },
{ "int", FINT, BLTIN },
{ "length", FLENGTH, BLTIN },
{ "log", FLOG, BLTIN },
{ "match", MATCHFCN, MATCHFCN },
{ "next", NEXT, NEXT },
{ "nextfile", NEXTFILE, NEXTFILE },
{ "print", PRINT, PRINT },
{ "printf", PRINTF, PRINTF },
{ "rand", FRAND, BLTIN },
{ "return", RETURN, RETURN },
{ "sin", FSIN, BLTIN },
{ "split", SPLIT, SPLIT },
{ "sprintf", SPRINTF, SPRINTF },
{ "sqrt", FSQRT, BLTIN },
{ "srand", FSRAND, BLTIN },
{ "sub", SUB, SUB },
{ "substr", SUBSTR, SUBSTR },
{ "system", FSYSTEM, BLTIN },
{ "tolower", FTOLOWER, BLTIN },
{ "toupper", FTOUPPER, BLTIN },
{ "while", WHILE, WHILE },
{ "utf", FUTF, BLTIN },
};
#define DEBUG
#ifdef DEBUG
#define RET(x) { if(dbg)printf("lex %s\n", tokname(x)); return(x); }
#else
#define RET(x) return(x)
#endif
int peek(void)
{
int c = input();
unput(c);
return c;
}
int gettok(char **pbuf, int *psz) /* get next input token */
{
int c;
char *buf = *pbuf;
int sz = *psz;
char *bp = buf;
c = input();
if (c == 0)
return 0;
buf[0] = c;
buf[1] = 0;
if (!isalnum(c) && c != '.' && c != '_')
return c;
*bp++ = c;
if (isalpha(c) || c == '_') { /* it's a varname */
for ( ; (c = input()) != 0; ) {
if (bp-buf >= sz)
if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, 0))
FATAL( "out of space for name %.10s...", buf );
if (isalnum(c) || c == '_')
*bp++ = c;
else {
*bp = 0;
unput(c);
break;
}
}
} else { /* it's a number */
char *rem;
/* read input until can't be a number */
for ( ; (c = input()) != 0; ) {
if (bp-buf >= sz)
if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, 0))
FATAL( "out of space for number %.10s...", buf );
if (isdigit(c) || c == 'e' || c == 'E'
|| c == '.' || c == '+' || c == '-')
*bp++ = c;
else {
unput(c);
break;
}
}
*bp = 0;
strtod(buf, &rem); /* parse the number */
unputstr(rem); /* put rest back for later */
rem[0] = 0;
}
*pbuf = buf;
*psz = sz;
return buf[0];
}
int word(char *);
int string(void);
int regexpr(void);
int sc = 0; /* 1 => return a } right now */
int reg = 0; /* 1 => return a REGEXPR now */
int yylex(void)
{
int c;
static char *buf = 0;
static int bufsize = 500;
if (buf == 0 && (buf = (char *) malloc(bufsize)) == NULL)
FATAL( "out of space in yylex" );
if (sc) {
sc = 0;
RET('}');
}
if (reg) {
reg = 0;
return regexpr();
}
for (;;) {
c = gettok(&buf, &bufsize);
if (c == 0)
return 0;
if (isalpha(c) || c == '_')
return word(buf);
if (isdigit(c) || c == '.') {
yylval.cp = setsymtab(buf, tostring(buf), atof(buf), CON|NUM, symtab);
/* should this also have STR set? */
RET(NUMBER);
}
yylval.i = c;
switch (c) {
case '\n': /* {EOL} */
RET(NL);
case '\r': /* assume \n is coming */
case ' ': /* {WS}+ */
case '\t':
break;
case '#': /* #.* strip comments */
while ((c = input()) != '\n' && c != 0)
;
unput(c);
break;
case ';':
RET(';');
case '\\':
if (peek() == '\n') {
input();
} else if (peek() == '\r') {
input(); input(); /* \n */
lineno++;
} else {
RET(c);
}
break;
case '&':
if (peek() == '&') {
input(); RET(AND);
} else
RET('&');
case '|':
if (peek() == '|') {
input(); RET(BOR);
} else
RET('|');
case '!':
if (peek() == '=') {
input(); yylval.i = NE; RET(NE);
} else if (peek() == '~') {
input(); yylval.i = NOTMATCH; RET(MATCHOP);
} else
RET(NOT);
case '~':
yylval.i = MATCH;
RET(MATCHOP);
case '<':
if (peek() == '=') {
input(); yylval.i = LE; RET(LE);
} else {
yylval.i = LT; RET(LT);
}
case '=':
if (peek() == '=') {
input(); yylval.i = EQ; RET(EQ);
} else {
yylval.i = ASSIGN; RET(ASGNOP);
}
case '>':
if (peek() == '=') {
input(); yylval.i = GE; RET(GE);
} else if (peek() == '>') {
input(); yylval.i = APPEND; RET(APPEND);
} else {
yylval.i = GT; RET(GT);
}
case '+':
if (peek() == '+') {
input(); yylval.i = INCR; RET(INCR);
} else if (peek() == '=') {
input(); yylval.i = ADDEQ; RET(ASGNOP);
} else
RET('+');
case '-':
if (peek() == '-') {
input(); yylval.i = DECR; RET(DECR);
} else if (peek() == '=') {
input(); yylval.i = SUBEQ; RET(ASGNOP);
} else
RET('-');
case '*':
if (peek() == '=') { /* *= */
input(); yylval.i = MULTEQ; RET(ASGNOP);
} else if (peek() == '*') { /* ** or **= */
input(); /* eat 2nd * */
if (peek() == '=') {
input(); yylval.i = POWEQ; RET(ASGNOP);
} else {
RET(POWER);
}
} else
RET('*');
case '/':
RET('/');
case '%':
if (peek() == '=') {
input(); yylval.i = MODEQ; RET(ASGNOP);
} else
RET('%');
case '^':
if (peek() == '=') {
input(); yylval.i = POWEQ; RET(ASGNOP);
} else
RET(POWER);
case '$':
/* BUG: awkward, if not wrong */
c = gettok(&buf, &bufsize);
if (c == '(' || c == '[' || (infunc && isarg(buf) >= 0)) {
unputstr(buf);
RET(INDIRECT);
} else if (isalpha(c)) {
if (strcmp(buf, "NF") == 0) { /* very special */
unputstr("(NF)");
RET(INDIRECT);
}
yylval.cp = setsymtab(buf, "", 0.0, STR|NUM, symtab);
RET(IVAR);
} else {
unputstr(buf);
RET(INDIRECT);
}
case '}':
if (--bracecnt < 0)
SYNTAX( "extra }" );
sc = 1;
RET(';');
case ']':
if (--brackcnt < 0)
SYNTAX( "extra ]" );
RET(']');
case ')':
if (--parencnt < 0)
SYNTAX( "extra )" );
RET(')');
case '{':
bracecnt++;
RET('{');
case '[':
brackcnt++;
RET('[');
case '(':
parencnt++;
RET('(');
case '"':
return string(); /* BUG: should be like tran.c ? */
default:
RET(c);
}
}
}
int string(void)
{
int c, n;
char *s, *bp;
static char *buf = 0;
static int bufsz = 500;
if (buf == 0 && (buf = (char *) malloc(bufsz)) == NULL)
FATAL("out of space for strings");
for (bp = buf; (c = input()) != '"'; ) {
if (!adjbuf(&buf, &bufsz, bp-buf+2, 500, &bp, 0))
FATAL("out of space for string %.10s...", buf);
switch (c) {
case '\n':
case '\r':
case 0:
SYNTAX( "non-terminated string %.10s...", buf );
lineno++;
break;
case '\\':
c = input();
switch (c) {
case '"': *bp++ = '"'; break;
case 'n': *bp++ = '\n'; break;
case 't': *bp++ = '\t'; break;
case 'f': *bp++ = '\f'; break;
case 'r': *bp++ = '\r'; break;
case 'b': *bp++ = '\b'; break;
case 'v': *bp++ = '\v'; break;
case 'a': *bp++ = '\007'; break;
case '\\': *bp++ = '\\'; break;
case '0': case '1': case '2': /* octal: \d \dd \ddd */
case '3': case '4': case '5': case '6': case '7':
n = c - '0';
if ((c = peek()) >= '0' && c < '8') {
n = 8 * n + input() - '0';
if ((c = peek()) >= '0' && c < '8')
n = 8 * n + input() - '0';
}
*bp++ = n;
break;
case 'x': /* hex \x0-9a-fA-F + */
{ char xbuf[100], *px;
for (px = xbuf; (c = input()) != 0 && px-xbuf < 100-2; ) {
if (isdigit(c)
|| (c >= 'a' && c <= 'f')
|| (c >= 'A' && c <= 'F'))
*px++ = c;
else
break;
}
*px = 0;
unput(c);
sscanf(xbuf, "%x", &n);
*bp++ = n;
break;
}
default:
*bp++ = c;
break;
}
break;
default:
*bp++ = c;
break;
}
}
*bp = 0;
s = tostring(buf);
*bp++ = ' '; *bp++ = 0;
yylval.cp = setsymtab(buf, s, 0.0, CON|STR|DONTFREE, symtab);
RET(STRING);
}
int binsearch(char *w, Keyword *kp, int n)
{
int cond, low, mid, high;
low = 0;
high = n - 1;
while (low <= high) {
mid = (low + high) / 2;
if ((cond = strcmp(w, kp[mid].word)) < 0)
high = mid - 1;
else if (cond > 0)
low = mid + 1;
else
return mid;
}
return -1;
}
int word(char *w)
{
Keyword *kp;
int c, n;
n = binsearch(w, keywords, sizeof(keywords)/sizeof(keywords[0]));
kp = keywords + n;
if (n != -1) { /* found in table */
yylval.i = kp->sub;
switch (kp->type) { /* special handling */
case FSYSTEM:
if (safe)
SYNTAX( "system is unsafe" );
RET(kp->type);
case FUNC:
if (infunc)
SYNTAX( "illegal nested function" );
RET(kp->type);
case RETURN:
if (!infunc)
SYNTAX( "return not in function" );
RET(kp->type);
case VARNF:
yylval.cp = setsymtab("NF", "", 0.0, NUM, symtab);
RET(VARNF);
default:
RET(kp->type);
}
}
c = peek(); /* look for '(' */
if (c != '(' && infunc && (n=isarg(w)) >= 0) {
yylval.i = n;
RET(ARG);
} else {
yylval.cp = setsymtab(w, "", 0.0, STR|NUM|DONTFREE, symtab);
if (c == '(') {
RET(CALL);
} else {
RET(VAR);
}
}
}
void startreg(void) /* next call to yyles will return a regular expression */
{
reg = 1;
}
int regexpr(void)
{
int c;
static char *buf = 0;
static int bufsz = 500;
char *bp;
if (buf == 0 && (buf = (char *) malloc(bufsz)) == NULL)
FATAL("out of space for rex expr");
bp = buf;
for ( ; (c = input()) != '/' && c != 0; ) {
if (!adjbuf(&buf, &bufsz, bp-buf+3, 500, &bp, 0))
FATAL("out of space for reg expr %.10s...", buf);
if (c == '\n') {
SYNTAX( "newline in regular expression %.10s...", buf );
unput('\n');
break;
} else if (c == '\\') {
*bp++ = '\\';
*bp++ = input();
} else {
*bp++ = c;
}
}
*bp = 0;
yylval.s = tostring(buf);
unput('/');
RET(REGEXPR);
}
/* low-level lexical stuff, sort of inherited from lex */
char ebuf[300];
char *ep = ebuf;
char yysbuf[100]; /* pushback buffer */
char *yysptr = yysbuf;
FILE *yyin = 0;
int input(void) /* get next lexical input character */
{
int c;
extern char *lexprog;
if (yysptr > yysbuf)
c = *--yysptr;
else if (lexprog != NULL) { /* awk '...' */
if ((c = *lexprog) != 0)
lexprog++;
} else /* awk -f ... */
c = pgetc();
if (c == '\n')
lineno++;
else if (c == EOF)
c = 0;
if (ep >= ebuf + sizeof ebuf)
ep = ebuf;
return *ep++ = c;
}
void unput(int c) /* put lexical character back on input */
{
if (c == '\n')
lineno--;
if (yysptr >= yysbuf + sizeof(yysbuf))
FATAL("pushed back too much: %.20s...", yysbuf);
*yysptr++ = c;
if (--ep < ebuf)
ep = ebuf + sizeof(ebuf) - 1;
}
void unputstr(char *s) /* put a string back on input */
{
int i;
for (i = strlen(s)-1; i >= 0; i--)
unput(s[i]);
}

686
awk/lib.c Normal file
View File

@@ -0,0 +1,686 @@
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name Lucent Technologies or any of
its entities not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
****************************************************************/
#define DEBUG
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
#include "awk.h"
#include "y.tab.h"
FILE *infile = NULL;
char *file = "";
char *record;
int recsize = RECSIZE;
char *fields;
int fieldssize = RECSIZE;
Cell **fldtab; /* pointers to Cells */
char inputFS[100] = " ";
#define MAXFLD 200
int nfields = MAXFLD; /* last allocated slot for $i */
int donefld; /* 1 = implies rec broken into fields */
int donerec; /* 1 = record is valid (no flds have changed) */
int lastfld = 0; /* last used field */
int argno = 1; /* current input argument number */
extern Awkfloat *ARGC;
static Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE };
static Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE };
void recinit(unsigned int n)
{
record = (char *) malloc(n);
fields = (char *) malloc(n);
fldtab = (Cell **) malloc((nfields+1) * sizeof(Cell *));
if (record == NULL || fields == NULL || fldtab == NULL)
FATAL("out of space for $0 and fields");
fldtab[0] = (Cell *) malloc(sizeof (Cell));
*fldtab[0] = dollar0;
fldtab[0]->sval = record;
fldtab[0]->nval = tostring("0");
makefields(1, nfields);
}
void makefields(int n1, int n2) /* create $n1..$n2 inclusive */
{
char temp[50];
int i;
for (i = n1; i <= n2; i++) {
fldtab[i] = (Cell *) malloc(sizeof (struct Cell));
if (fldtab[i] == NULL)
FATAL("out of space in makefields %d", i);
*fldtab[i] = dollar1;
sprintf(temp, "%d", i);
fldtab[i]->nval = tostring(temp);
}
}
void initgetrec(void)
{
int i;
char *p;
for (i = 1; i < *ARGC; i++) {
if (!isclvar(p = getargv(i))) { /* find 1st real filename */
setsval(lookup("FILENAME", symtab), getargv(i));
return;
}
setclvar(p); /* a commandline assignment before filename */
argno++;
}
infile = stdin; /* no filenames, so use stdin */
}
int getrec(char **pbuf, int *pbufsize, int isrecord) /* get next input record */
{ /* note: cares whether buf == record */
int c;
static int firsttime = 1;
char *buf = *pbuf;
int bufsize = *pbufsize;
if (firsttime) {
firsttime = 0;
initgetrec();
}
dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
*RS, *FS, *ARGC, *FILENAME) );
if (isrecord) {
donefld = 0;
donerec = 1;
}
buf[0] = 0;
while (argno < *ARGC || infile == stdin) {
dprintf( ("argno=%d, file=|%s|\n", argno, file) );
if (infile == NULL) { /* have to open a new file */
file = getargv(argno);
if (*file == '\0') { /* it's been zapped */
argno++;
continue;
}
if (isclvar(file)) { /* a var=value arg */
setclvar(file);
argno++;
continue;
}
*FILENAME = file;
dprintf( ("opening file %s\n", file) );
if (*file == '-' && *(file+1) == '\0')
infile = stdin;
else if ((infile = fopen(file, "r")) == NULL)
FATAL("can't open file %s", file);
setfval(fnrloc, 0.0);
}
c = readrec(&buf, &bufsize, infile);
if (c != 0 || buf[0] != '\0') { /* normal record */
if (isrecord) {
if (freeable(fldtab[0]))
xfree(fldtab[0]->sval);
fldtab[0]->sval = buf; /* buf == record */
fldtab[0]->tval = REC | STR | DONTFREE;
if (is_number(fldtab[0]->sval)) {
fldtab[0]->fval = atof(fldtab[0]->sval);
fldtab[0]->tval |= NUM;
}
}
setfval(nrloc, nrloc->fval+1);
setfval(fnrloc, fnrloc->fval+1);
*pbuf = buf;
*pbufsize = bufsize;
return 1;
}
/* EOF arrived on this file; set up next */
if (infile != stdin)
fclose(infile);
infile = NULL;
argno++;
}
*pbuf = buf;
*pbufsize = bufsize;
return 0; /* true end of file */
}
void nextfile(void)
{
if (infile != stdin)
fclose(infile);
infile = NULL;
argno++;
}
int readrec(char **pbuf, int *pbufsize, FILE *inf) /* read one record into buf */
{
int sep, c;
char *rr, *buf = *pbuf;
int bufsize = *pbufsize;
if (strlen(*FS) >= sizeof(inputFS))
FATAL("field separator %.10s... is too long", *FS);
strcpy(inputFS, *FS); /* for subsequent field splitting */
if ((sep = **RS) == 0) {
sep = '\n';
while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */
;
if (c != EOF)
ungetc(c, inf);
}
for (rr = buf; ; ) {
for (; (c=getc(inf)) != sep && c != EOF; ) {
if (rr-buf+1 > bufsize)
if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1"))
FATAL("input record `%.30s...' too long", buf);
*rr++ = c;
}
if (**RS == sep || c == EOF)
break;
if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
break;
if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2"))
FATAL("input record `%.30s...' too long", buf);
*rr++ = '\n';
*rr++ = c;
}
if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
FATAL("input record `%.30s...' too long", buf);
*rr = 0;
dprintf( ("readrec saw <%s>, returns %d\n", buf, c == EOF && rr == buf ? 0 : 1) );
*pbuf = buf;
*pbufsize = bufsize;
return c == EOF && rr == buf ? 0 : 1;
}
char *getargv(int n) /* get ARGV[n] */
{
Cell *x;
char *s, temp[50];
extern Array *ARGVtab;
sprintf(temp, "%d", n);
x = setsymtab(temp, "", 0.0, STR, ARGVtab);
s = getsval(x);
dprintf( ("getargv(%d) returns |%s|\n", n, s) );
return s;
}
void setclvar(char *s) /* set var=value from s */
{
char *p;
Cell *q;
for (p=s; *p != '='; p++)
;
*p++ = 0;
p = qstring(p, '\0');
q = setsymtab(s, p, 0.0, STR, symtab);
setsval(q, p);
if (is_number(q->sval)) {
q->fval = atof(q->sval);
q->tval |= NUM;
}
dprintf( ("command line set %s to |%s|\n", s, p) );
}
void fldbld(void) /* create fields from current record */
{
/* this relies on having fields[] the same length as $0 */
/* the fields are all stored in this one array with \0's */
char *r, *fr, sep;
Cell *p;
int i, j, n;
if (donefld)
return;
if (!isstr(fldtab[0]))
getsval(fldtab[0]);
r = fldtab[0]->sval;
n = strlen(r);
if (n > fieldssize) {
xfree(fields);
if ((fields = (char *) malloc(n+1)) == NULL)
FATAL("out of space for fields in fldbld %d", n);
fieldssize = n;
}
fr = fields;
i = 0; /* number of fields accumulated here */
if (strlen(inputFS) > 1) { /* it's a regular expression */
i = refldbld(r, inputFS);
} else if ((sep = *inputFS) == ' ') { /* default whitespace */
for (i = 0; ; ) {
while (*r == ' ' || *r == '\t' || *r == '\n')
r++;
if (*r == 0)
break;
i++;
if (i > nfields)
growfldtab(i);
if (freeable(fldtab[i]))
xfree(fldtab[i]->sval);
fldtab[i]->sval = fr;
fldtab[i]->tval = FLD | STR | DONTFREE;
do
*fr++ = *r++;
while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
*fr++ = 0;
}
*fr = 0;
} else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */
for (i = 0; *r != 0; r++) {
char buf[2];
i++;
if (i > nfields)
growfldtab(i);
if (freeable(fldtab[i]))
xfree(fldtab[i]->sval);
buf[0] = *r;
buf[1] = 0;
fldtab[i]->sval = tostring(buf);
fldtab[i]->tval = FLD | STR;
}
*fr = 0;
} else if (*r != 0) { /* if 0, it's a null field */
for (;;) {
i++;
if (i > nfields)
growfldtab(i);
if (freeable(fldtab[i]))
xfree(fldtab[i]->sval);
fldtab[i]->sval = fr;
fldtab[i]->tval = FLD | STR | DONTFREE;
while (*r != sep && *r != '\n' && *r != '\0') /* \n is always a separator */
*fr++ = *r++;
*fr++ = 0;
if (*r++ == 0)
break;
}
*fr = 0;
}
if (i > nfields)
FATAL("record `%.30s...' has too many fields; can't happen", r);
cleanfld(i+1, lastfld); /* clean out junk from previous record */
lastfld = i;
donefld = 1;
for (j = 1; j <= lastfld; j++) {
p = fldtab[j];
if(is_number(p->sval)) {
p->fval = atof(p->sval);
p->tval |= NUM;
}
}
setfval(nfloc, (Awkfloat) lastfld);
if (dbg) {
for (j = 0; j <= lastfld; j++) {
p = fldtab[j];
printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
}
}
}
void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */
{ /* nvals remain intact */
Cell *p;
int i;
for (i = n1; i <= n2; i++) {
p = fldtab[i];
if (freeable(p))
xfree(p->sval);
p->sval = "";
p->tval = FLD | STR | DONTFREE;
}
}
void newfld(int n) /* add field n after end of existing lastfld */
{
if (n > nfields)
growfldtab(n);
cleanfld(lastfld+1, n);
lastfld = n;
setfval(nfloc, (Awkfloat) n);
}
Cell *fieldadr(int n) /* get nth field */
{
if (n < 0)
FATAL("trying to access field %d", n);
if (n > nfields) /* fields after NF are empty */
growfldtab(n); /* but does not increase NF */
return(fldtab[n]);
}
void growfldtab(int n) /* make new fields up to at least $n */
{
int nf = 2 * nfields;
if (n > nf)
nf = n;
fldtab = (Cell **) realloc(fldtab, (nf+1) * (sizeof (struct Cell *)));
if (fldtab == NULL)
FATAL("out of space creating %d fields", nf);
makefields(nfields+1, nf);
nfields = nf;
}
int refldbld(char *rec, char *fs) /* build fields from reg expr in FS */
{
/* this relies on having fields[] the same length as $0 */
/* the fields are all stored in this one array with \0's */
char *fr;
void *p;
int i, n;
n = strlen(rec);
if (n > fieldssize) {
xfree(fields);
if ((fields = (char *) malloc(n+1)) == NULL)
FATAL("out of space for fields in refldbld %d", n);
fieldssize = n;
}
fr = fields;
*fr = '\0';
if (*rec == '\0')
return 0;
p = compre(fs);
dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
for (i = 1; ; i++) {
if (i > nfields)
growfldtab(i);
if (freeable(fldtab[i]))
xfree(fldtab[i]->sval);
fldtab[i]->tval = FLD | STR | DONTFREE;
fldtab[i]->sval = fr;
dprintf( ("refldbld: i=%d\n", i) );
if (nematch(p, rec, rec)) {
dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
strncpy(fr, rec, patbeg-rec);
fr += patbeg - rec + 1;
*(fr-1) = '\0';
rec = patbeg + patlen;
} else {
dprintf( ("no match %s\n", rec) );
strcpy(fr, rec);
break;
}
}
return i;
}
void recbld(void) /* create $0 from $1..$NF if necessary */
{
int i;
char *r, *p;
if (donerec == 1)
return;
r = record;
for (i = 1; i <= *NF; i++) {
p = getsval(fldtab[i]);
if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
FATAL("created $0 `%.30s...' too long", record);
while ((*r = *p++) != 0)
r++;
if (i < *NF) {
if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
FATAL("created $0 `%.30s...' too long", record);
for (p = *OFS; (*r = *p++) != 0; )
r++;
}
}
if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
FATAL("built giant record `%.30s...'", record);
*r = '\0';
dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
if (freeable(fldtab[0]))
xfree(fldtab[0]->sval);
fldtab[0]->tval = REC | STR | DONTFREE;
fldtab[0]->sval = record;
dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
dprintf( ("recbld = |%s|\n", record) );
donerec = 1;
}
int errorflag = 0;
void yyerror(char *s)
{
SYNTAX(s);
}
void SYNTAX(char *fmt, ...)
{
extern char *cmdname, *curfname;
static int been_here = 0;
va_list varg;
if (been_here++ > 2)
return;
fprintf(stderr, "%s: ", cmdname);
va_start(varg, fmt);
vfprintf(stderr, fmt, varg);
va_end(varg);
if(compile_time == 1 && cursource() != NULL)
fprintf(stderr, " at %s:%d", cursource(), lineno);
else
fprintf(stderr, " at line %d", lineno);
if (curfname != NULL)
fprintf(stderr, " in function %s", curfname);
fprintf(stderr, "\n");
errorflag = 2;
eprint();
}
void fpecatch(int n)
{
FATAL("floating point exception %d", n);
}
extern int bracecnt, brackcnt, parencnt;
void bracecheck(void)
{
int c;
static int beenhere = 0;
if (beenhere++)
return;
while ((c = input()) != EOF && c != '\0')
bclass(c);
bcheck2(bracecnt, '{', '}');
bcheck2(brackcnt, '[', ']');
bcheck2(parencnt, '(', ')');
}
void bcheck2(int n, int c1, int c2)
{
if (n == 1)
fprintf(stderr, "\tmissing %c\n", c2);
else if (n > 1)
fprintf(stderr, "\t%d missing %c's\n", n, c2);
else if (n == -1)
fprintf(stderr, "\textra %c\n", c2);
else if (n < -1)
fprintf(stderr, "\t%d extra %c's\n", -n, c2);
}
void FATAL(char *fmt, ...)
{
extern char *cmdname;
va_list varg;
fflush(stdout);
fprintf(stderr, "%s: ", cmdname);
va_start(varg, fmt);
vfprintf(stderr, fmt, varg);
va_end(varg);
error();
if (dbg > 1) /* core dump if serious debugging on */
abort();
exit(2);
}
void WARNING(char *fmt, ...)
{
extern char *cmdname;
va_list varg;
fflush(stdout);
fprintf(stderr, "%s: ", cmdname);
va_start(varg, fmt);
vfprintf(stderr, fmt, varg);
va_end(varg);
error();
}
void error()
{
extern Node *curnode;
int line;
fprintf(stderr, "\n");
if (compile_time != 2 && NR && *NR > 0) {
if (strcmp(*FILENAME, "-") != 0)
fprintf(stderr, " input record %s:%d", *FILENAME, (int) (*FNR));
else
fprintf(stderr, " input record number %d", (int) (*FNR));
fprintf(stderr, "\n");
}
if (compile_time != 2 && curnode)
line = curnode->lineno;
else if (compile_time != 2 && lineno)
line = lineno;
else
line = -1;
if (compile_time == 1 && cursource() != NULL){
if(line >= 0)
fprintf(stderr, " source %s:%d", cursource(), line);
else
fprintf(stderr, " source file %s", cursource());
}else if(line >= 0)
fprintf(stderr, " source line %d", line);
fprintf(stderr, "\n");
eprint();
}
void eprint(void) /* try to print context around error */
{
char *p, *q;
int c;
static int been_here = 0;
extern char ebuf[], *ep;
if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
return;
p = ep - 1;
if (p > ebuf && *p == '\n')
p--;
for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
;
while (*p == '\n')
p++;
fprintf(stderr, " context is\n\t");
for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
;
for ( ; p < q; p++)
if (*p)
putc(*p, stderr);
fprintf(stderr, " >>> ");
for ( ; p < ep; p++)
if (*p)
putc(*p, stderr);
fprintf(stderr, " <<< ");
if (*ep)
while ((c = input()) != '\n' && c != '\0' && c != EOF) {
putc(c, stderr);
bclass(c);
}
putc('\n', stderr);
ep = ebuf;
}
void bclass(int c)
{
switch (c) {
case '{': bracecnt++; break;
case '}': bracecnt--; break;
case '[': brackcnt++; break;
case ']': brackcnt--; break;
case '(': parencnt++; break;
case ')': parencnt--; break;
}
}
double errcheck(double x, char *s)
{
if (errno == EDOM) {
errno = 0;
WARNING("%s argument out of domain", s);
x = 1;
} else if (errno == ERANGE) {
errno = 0;
WARNING("%s result out of range", s);
x = 1;
}
return x;
}
int isclvar(char *s) /* is s of form var=something ? */
{
char *os = s;
if (!isalpha(*s) && *s != '_')
return 0;
for ( ; *s; s++)
if (!(isalnum(*s) || *s == '_'))
break;
return *s == '=' && s > os && *(s+1) != '=';
}
/* strtod is supposed to be a proper test of what's a valid number */
#include <math.h>
int is_number(char *s)
{
double r;
char *ep;
errno = 0;
r = strtod(s, &ep);
if (ep == s || r == HUGE_VAL || errno == ERANGE)
return 0;
while (*ep == ' ' || *ep == '\t' || *ep == '\n')
ep++;
if (*ep == '\0')
return 1;
else
return 0;
}

197
awk/main.c Normal file
View File

@@ -0,0 +1,197 @@
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name Lucent Technologies or any of
its entities not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
****************************************************************/
char *version = "version 19990602";
#define DEBUG
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "awk.h"
#include "y.tab.h"
extern char **environ;
extern int nfields;
int dbg = 0;
char *cmdname; /* gets argv[0] for error messages */
extern FILE *yyin; /* lex input file */
char *lexprog; /* points to program argument if it exists */
extern int errorflag; /* non-zero if any syntax errors; set by yyerror */
int compile_time = 2; /* for error printing: */
/* 2 = cmdline, 1 = compile, 0 = running */
char *pfile[20]; /* program filenames from -f's */
int npfile = 0; /* number of filenames */
int curpfile = 0; /* current filename */
int safe = 0; /* 1 => "safe" mode */
int main(int argc, char *argv[])
{
char *fs = NULL, *marg;
int temp;
cmdname = argv[0];
if (argc == 1) {
fprintf(stderr, "Usage: %s [-f programfile | 'program'] [-Ffieldsep] [-v var=value] [files]\n", cmdname);
exit(1);
}
signal(SIGFPE, fpecatch);
yyin = NULL;
symtab = makesymtab(NSYMTAB);
while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') {
if (strcmp(argv[1], "--") == 0) { /* explicit end of args */
argc--;
argv++;
break;
}
switch (argv[1][1]) {
case 's':
if (strcmp(argv[1], "-safe") == 0)
safe = 1;
break;
case 'f': /* next argument is program filename */
argc--;
argv++;
if (argc <= 1)
FATAL("no program filename");
pfile[npfile++] = argv[1];
break;
case 'F': /* set field separator */
if (argv[1][2] != 0) { /* arg is -Fsomething */
if (argv[1][2] == 't' && argv[1][3] == 0) /* wart: t=>\t */
fs = "\t";
else if (argv[1][2] != 0)
fs = &argv[1][2];
} else { /* arg is -F something */
argc--; argv++;
if (argc > 1 && argv[1][0] == 't' && argv[1][1] == 0) /* wart: t=>\t */
fs = "\t";
else if (argc > 1 && argv[1][0] != 0)
fs = &argv[1][0];
}
if (fs == NULL || *fs == '\0')
WARNING("field separator FS is empty");
break;
case 'v': /* -v a=1 to be done NOW. one -v for each */
if (argv[1][2] == '\0' && --argc > 1 && isclvar((++argv)[1]))
setclvar(argv[1]);
break;
case 'm': /* more memory: -mr=record, -mf=fields */
/* no longer needed */
marg = argv[1];
if (argv[1][3])
temp = atoi(&argv[1][3]);
else {
argv++; argc--;
temp = atoi(&argv[1][0]);
}
switch (marg[2]) {
case 'r': recsize = temp; break;
case 'f': nfields = temp; break;
default: FATAL("unknown option %s\n", marg);
}
break;
case 'd':
dbg = atoi(&argv[1][2]);
if (dbg == 0)
dbg = 1;
printf("awk %s\n", version);
break;
case 'V': /* added for exptools "standard" */
printf("awk %s\n", version);
exit(0);
break;
default:
WARNING("unknown option %s ignored", argv[1]);
break;
}
argc--;
argv++;
}
/* argv[1] is now the first argument */
if (npfile == 0) { /* no -f; first argument is program */
if (argc <= 1) {
if (dbg)
exit(0);
FATAL("no program given");
}
dprintf( ("program = |%s|\n", argv[1]) );
lexprog = argv[1];
argc--;
argv++;
}
recinit(recsize);
syminit();
compile_time = 1;
argv[0] = cmdname; /* put prog name at front of arglist */
dprintf( ("argc=%d, argv[0]=%s\n", argc, argv[0]) );
arginit(argc, argv);
if (!safe)
envinit(environ);
yyparse();
if (fs)
*FS = qstring(fs, '\0');
dprintf( ("errorflag=%d\n", errorflag) );
if (errorflag == 0) {
compile_time = 0;
run(winner);
} else
bracecheck();
return(errorflag);
}
int pgetc(void) /* get 1 character from awk program */
{
int c;
for (;;) {
if (yyin == NULL) {
if (curpfile >= npfile)
return EOF;
if (strcmp(pfile[curpfile], "-") == 0)
yyin = stdin;
else if ((yyin = fopen(pfile[curpfile], "r")) == NULL)
FATAL("can't open file %s", pfile[curpfile]);
lineno = 1;
}
if ((c = getc(yyin)) != EOF)
return c;
if (yyin != stdin)
fclose(yyin);
yyin = NULL;
curpfile++;
}
}
char *cursource(void) /* current source file name */
{
if (npfile > 0)
return pfile[curpfile];
else
return NULL;
}

168
awk/maketab.c Normal file
View File

@@ -0,0 +1,168 @@
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name Lucent Technologies or any of
its entities not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
****************************************************************/
/*
* this program makes the table to link function names
* and type indices that is used by execute() in run.c.
* it finds the indices in y.tab.h, produced by yacc.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "awk.h"
#include "y.tab.h"
struct xx
{ int token;
char *name;
char *pname;
} proc[] = {
{ PROGRAM, "program", NULL },
{ BOR, "boolop", " || " },
{ AND, "boolop", " && " },
{ NOT, "boolop", " !" },
{ NE, "relop", " != " },
{ EQ, "relop", " == " },
{ LE, "relop", " <= " },
{ LT, "relop", " < " },
{ GE, "relop", " >= " },
{ GT, "relop", " > " },
{ ARRAY, "array", NULL },
{ INDIRECT, "indirect", "$(" },
{ SUBSTR, "substr", "substr" },
{ SUB, "sub", "sub" },
{ GSUB, "gsub", "gsub" },
{ INDEX, "sindex", "sindex" },
{ SPRINTF, "awksprintf", "sprintf " },
{ ADD, "arith", " + " },
{ MINUS, "arith", " - " },
{ MULT, "arith", " * " },
{ DIVIDE, "arith", " / " },
{ MOD, "arith", " % " },
{ UMINUS, "arith", " -" },
{ POWER, "arith", " **" },
{ PREINCR, "incrdecr", "++" },
{ POSTINCR, "incrdecr", "++" },
{ PREDECR, "incrdecr", "--" },
{ POSTDECR, "incrdecr", "--" },
{ CAT, "cat", " " },
{ PASTAT, "pastat", NULL },
{ PASTAT2, "dopa2", NULL },
{ MATCH, "matchop", " ~ " },
{ NOTMATCH, "matchop", " !~ " },
{ MATCHFCN, "matchop", "matchop" },
{ INTEST, "intest", "intest" },
{ PRINTF, "awkprintf", "printf" },
{ PRINT, "printstat", "print" },
{ CLOSE, "closefile", "closefile" },
{ DELETE, "awkdelete", "awkdelete" },
{ SPLIT, "split", "split" },
{ ASSIGN, "assign", " = " },
{ ADDEQ, "assign", " += " },
{ SUBEQ, "assign", " -= " },
{ MULTEQ, "assign", " *= " },
{ DIVEQ, "assign", " /= " },
{ MODEQ, "assign", " %= " },
{ POWEQ, "assign", " ^= " },
{ CONDEXPR, "condexpr", " ?: " },
{ IF, "ifstat", "if(" },
{ WHILE, "whilestat", "while(" },
{ FOR, "forstat", "for(" },
{ DO, "dostat", "do" },
{ IN, "instat", "instat" },
{ NEXT, "jump", "next" },
{ NEXTFILE, "jump", "nextfile" },
{ EXIT, "jump", "exit" },
{ BREAK, "jump", "break" },
{ CONTINUE, "jump", "continue" },
{ RETURN, "jump", "ret" },
{ BLTIN, "bltin", "bltin" },
{ CALL, "call", "call" },
{ ARG, "arg", "arg" },
{ VARNF, "getnf", "NF" },
{ GETLINE, "getline", "getline" },
{ 0, "", "" },
};
#define SIZE (LASTTOKEN - FIRSTTOKEN + 1)
char *table[SIZE];
char *names[SIZE];
int main(int argc, char *argv[])
{
struct xx *p;
int i, n, tok;
char c;
FILE *fp;
char buf[200], name[200], def[200];
printf("#include <stdio.h>\n");
printf("#include \"awk.h\"\n");
printf("#include \"y.tab.h\"\n\n");
for (i = SIZE; --i >= 0; )
names[i] = "";
if ((fp = fopen("y.tab.h", "r")) == NULL) {
fprintf(stderr, "maketab can't open y.tab.h!\n");
exit(1);
}
printf("static char *printname[%d] = {\n", SIZE);
i = 0;
while (fgets(buf, sizeof buf, fp) != NULL) {
n = sscanf(buf, "%1c %s %s %d", &c, def, name, &tok);
if (c != '#' || (n != 4 && strcmp(def,"define") != 0)) /* not a valid #define */
continue;
if (tok < FIRSTTOKEN || tok > LASTTOKEN) {
fprintf(stderr, "maketab funny token %d %s\n", tok, buf);
exit(1);
}
names[tok-FIRSTTOKEN] = (char *) malloc(strlen(name)+1);
strcpy(names[tok-FIRSTTOKEN], name);
printf("\t(char *) \"%s\",\t/* %d */\n", name, tok);
i++;
}
printf("};\n\n");
for (p=proc; p->token!=0; p++)
table[p->token-FIRSTTOKEN] = p->name;
printf("\nCell *(*proctab[%d])(Node **, int) = {\n", SIZE);
for (i=0; i<SIZE; i++)
if (table[i]==0)
printf("\tnullproc,\t/* %s */\n", names[i]);
else
printf("\t%s,\t/* %s */\n", table[i], names[i]);
printf("};\n\n");
printf("char *tokname(int n)\n"); /* print a tokname() function */
printf("{\n");
printf(" static char buf[100];\n\n");
printf(" if (n < FIRSTTOKEN || n > LASTTOKEN) {\n");
printf(" sprintf(buf, \"token %%d\", n);\n");
printf(" return buf;\n");
printf(" }\n");
printf(" return printname[n-FIRSTTOKEN];\n");
printf("}\n");
return 0;
}

271
awk/parse.c Normal file
View File

@@ -0,0 +1,271 @@
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name Lucent Technologies or any of
its entities not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
****************************************************************/
#define DEBUG
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "awk.h"
#include "y.tab.h"
Node *nodealloc(int n)
{
Node *x;
x = (Node *) malloc(sizeof(Node) + (n-1)*sizeof(Node *));
if (x == NULL)
FATAL("out of space in nodealloc");
x->nnext = NULL;
x->lineno = lineno;
return(x);
}
Node *exptostat(Node *a)
{
a->ntype = NSTAT;
return(a);
}
Node *node1(int a, Node *b)
{
Node *x;
x = nodealloc(1);
x->nobj = a;
x->narg[0]=b;
return(x);
}
Node *node2(int a, Node *b, Node *c)
{
Node *x;
x = nodealloc(2);
x->nobj = a;
x->narg[0] = b;
x->narg[1] = c;
return(x);
}
Node *node3(int a, Node *b, Node *c, Node *d)
{
Node *x;
x = nodealloc(3);
x->nobj = a;
x->narg[0] = b;
x->narg[1] = c;
x->narg[2] = d;
return(x);
}
Node *node4(int a, Node *b, Node *c, Node *d, Node *e)
{
Node *x;
x = nodealloc(4);
x->nobj = a;
x->narg[0] = b;
x->narg[1] = c;
x->narg[2] = d;
x->narg[3] = e;
return(x);
}
Node *stat1(int a, Node *b)
{
Node *x;
x = node1(a,b);
x->ntype = NSTAT;
return(x);
}
Node *stat2(int a, Node *b, Node *c)
{
Node *x;
x = node2(a,b,c);
x->ntype = NSTAT;
return(x);
}
Node *stat3(int a, Node *b, Node *c, Node *d)
{
Node *x;
x = node3(a,b,c,d);
x->ntype = NSTAT;
return(x);
}
Node *stat4(int a, Node *b, Node *c, Node *d, Node *e)
{
Node *x;
x = node4(a,b,c,d,e);
x->ntype = NSTAT;
return(x);
}
Node *op1(int a, Node *b)
{
Node *x;
x = node1(a,b);
x->ntype = NEXPR;
return(x);
}
Node *op2(int a, Node *b, Node *c)
{
Node *x;
x = node2(a,b,c);
x->ntype = NEXPR;
return(x);
}
Node *op3(int a, Node *b, Node *c, Node *d)
{
Node *x;
x = node3(a,b,c,d);
x->ntype = NEXPR;
return(x);
}
Node *op4(int a, Node *b, Node *c, Node *d, Node *e)
{
Node *x;
x = node4(a,b,c,d,e);
x->ntype = NEXPR;
return(x);
}
Node *celltonode(Cell *a, int b)
{
Node *x;
a->ctype = OCELL;
a->csub = b;
x = node1(0, (Node *) a);
x->ntype = NVALUE;
return(x);
}
Node *rectonode(void) /* make $0 into a Node */
{
extern Cell *literal0;
return op1(INDIRECT, celltonode(literal0, CUNK));
}
Node *makearr(Node *p)
{
Cell *cp;
if (isvalue(p)) {
cp = (Cell *) (p->narg[0]);
if (isfcn(cp))
SYNTAX( "%s is a function, not an array", cp->nval );
else if (!isarr(cp)) {
xfree(cp->sval);
cp->sval = (char *) makesymtab(NSYMTAB);
cp->tval = ARR;
}
}
return p;
}
#define PA2NUM 50 /* max number of pat,pat patterns allowed */
int paircnt; /* number of them in use */
int pairstack[PA2NUM]; /* state of each pat,pat */
Node *pa2stat(Node *a, Node *b, Node *c) /* pat, pat {...} */
{
Node *x;
x = node4(PASTAT2, a, b, c, itonp(paircnt));
if (paircnt++ >= PA2NUM)
SYNTAX( "limited to %d pat,pat statements", PA2NUM );
x->ntype = NSTAT;
return(x);
}
Node *linkum(Node *a, Node *b)
{
Node *c;
if (errorflag) /* don't link things that are wrong */
return a;
if (a == NULL)
return(b);
else if (b == NULL)
return(a);
for (c = a; c->nnext != NULL; c = c->nnext)
;
c->nnext = b;
return(a);
}
void defn(Cell *v, Node *vl, Node *st) /* turn on FCN bit in definition, */
{ /* body of function, arglist */
Node *p;
int n;
if (isarr(v)) {
SYNTAX( "`%s' is an array name and a function name", v->nval );
return;
}
v->tval = FCN;
v->sval = (char *) st;
n = 0; /* count arguments */
for (p = vl; p; p = p->nnext)
n++;
v->fval = n;
dprintf( ("defining func %s (%d args)\n", v->nval, n) );
}
int isarg(char *s) /* is s in argument list for current function? */
{ /* return -1 if not, otherwise arg # */
extern Node *arglist;
Node *p = arglist;
int n;
for (n = 0; p != 0; p = p->nnext, n++)
if (strcmp(((Cell *)(p->narg[0]))->nval, s) == 0)
return n;
return -1;
}
int ptoi(void *p) /* convert pointer to integer */
{
return (int) (long) p; /* swearing that p fits, of course */
}
Node *itonp(int i) /* and vice versa */
{
return (Node *) (long) i;
}

206
awk/proctab.c Normal file
View File

@@ -0,0 +1,206 @@
#include <stdio.h>
#include <math.h>
#include "awk.h"
#include "y.tab.h"
static char *printname[92] = {
(char *) "FIRSTTOKEN", /* 57346 */
(char *) "PROGRAM", /* 57347 */
(char *) "PASTAT", /* 57348 */
(char *) "PASTAT2", /* 57349 */
(char *) "XBEGIN", /* 57350 */
(char *) "XEND", /* 57351 */
(char *) "NL", /* 57352 */
(char *) "ARRAY", /* 57353 */
(char *) "MATCH", /* 57354 */
(char *) "NOTMATCH", /* 57355 */
(char *) "MATCHOP", /* 57356 */
(char *) "FINAL", /* 57357 */
(char *) "DOT", /* 57358 */
(char *) "ALL", /* 57359 */
(char *) "CCL", /* 57360 */
(char *) "NCCL", /* 57361 */
(char *) "CHAR", /* 57362 */
(char *) "OR", /* 57363 */
(char *) "STAR", /* 57364 */
(char *) "QUEST", /* 57365 */
(char *) "PLUS", /* 57366 */
(char *) "AND", /* 57367 */
(char *) "BOR", /* 57368 */
(char *) "APPEND", /* 57369 */
(char *) "EQ", /* 57370 */
(char *) "GE", /* 57371 */
(char *) "GT", /* 57372 */
(char *) "LE", /* 57373 */
(char *) "LT", /* 57374 */
(char *) "NE", /* 57375 */
(char *) "IN", /* 57376 */
(char *) "ARG", /* 57377 */
(char *) "BLTIN", /* 57378 */
(char *) "BREAK", /* 57379 */
(char *) "CLOSE", /* 57380 */
(char *) "CONTINUE", /* 57381 */
(char *) "DELETE", /* 57382 */
(char *) "DO", /* 57383 */
(char *) "EXIT", /* 57384 */
(char *) "FOR", /* 57385 */
(char *) "FUNC", /* 57386 */
(char *) "SUB", /* 57387 */
(char *) "GSUB", /* 57388 */
(char *) "IF", /* 57389 */
(char *) "INDEX", /* 57390 */
(char *) "LSUBSTR", /* 57391 */
(char *) "MATCHFCN", /* 57392 */
(char *) "NEXT", /* 57393 */
(char *) "NEXTFILE", /* 57394 */
(char *) "ADD", /* 57395 */
(char *) "MINUS", /* 57396 */
(char *) "MULT", /* 57397 */
(char *) "DIVIDE", /* 57398 */
(char *) "MOD", /* 57399 */
(char *) "ASSIGN", /* 57400 */
(char *) "ASGNOP", /* 57401 */
(char *) "ADDEQ", /* 57402 */
(char *) "SUBEQ", /* 57403 */
(char *) "MULTEQ", /* 57404 */
(char *) "DIVEQ", /* 57405 */
(char *) "MODEQ", /* 57406 */
(char *) "POWEQ", /* 57407 */
(char *) "PRINT", /* 57408 */
(char *) "PRINTF", /* 57409 */
(char *) "SPRINTF", /* 57410 */
(char *) "ELSE", /* 57411 */
(char *) "INTEST", /* 57412 */
(char *) "CONDEXPR", /* 57413 */
(char *) "POSTINCR", /* 57414 */
(char *) "PREINCR", /* 57415 */
(char *) "POSTDECR", /* 57416 */
(char *) "PREDECR", /* 57417 */
(char *) "VAR", /* 57418 */
(char *) "IVAR", /* 57419 */
(char *) "VARNF", /* 57420 */
(char *) "CALL", /* 57421 */
(char *) "NUMBER", /* 57422 */
(char *) "STRING", /* 57423 */
(char *) "REGEXPR", /* 57424 */
(char *) "GETLINE", /* 57425 */
(char *) "RETURN", /* 57426 */
(char *) "SPLIT", /* 57427 */
(char *) "SUBSTR", /* 57428 */
(char *) "WHILE", /* 57429 */
(char *) "CAT", /* 57430 */
(char *) "NOT", /* 57431 */
(char *) "UMINUS", /* 57432 */
(char *) "POWER", /* 57433 */
(char *) "DECR", /* 57434 */
(char *) "INCR", /* 57435 */
(char *) "INDIRECT", /* 57436 */
(char *) "LASTTOKEN", /* 57437 */
};
Cell *(*proctab[92])(Node **, int) = {
nullproc, /* FIRSTTOKEN */
program, /* PROGRAM */
pastat, /* PASTAT */
dopa2, /* PASTAT2 */
nullproc, /* XBEGIN */
nullproc, /* XEND */
nullproc, /* NL */
array, /* ARRAY */
matchop, /* MATCH */
matchop, /* NOTMATCH */
nullproc, /* MATCHOP */
nullproc, /* FINAL */
nullproc, /* DOT */
nullproc, /* ALL */
nullproc, /* CCL */
nullproc, /* NCCL */
nullproc, /* CHAR */
nullproc, /* OR */
nullproc, /* STAR */
nullproc, /* QUEST */
nullproc, /* PLUS */
boolop, /* AND */
boolop, /* BOR */
nullproc, /* APPEND */
relop, /* EQ */
relop, /* GE */
relop, /* GT */
relop, /* LE */
relop, /* LT */
relop, /* NE */
instat, /* IN */
arg, /* ARG */
bltin, /* BLTIN */
jump, /* BREAK */
closefile, /* CLOSE */
jump, /* CONTINUE */
awkdelete, /* DELETE */
dostat, /* DO */
jump, /* EXIT */
forstat, /* FOR */
nullproc, /* FUNC */
sub, /* SUB */
gsub, /* GSUB */
ifstat, /* IF */
sindex, /* INDEX */
nullproc, /* LSUBSTR */
matchop, /* MATCHFCN */
jump, /* NEXT */
jump, /* NEXTFILE */
arith, /* ADD */
arith, /* MINUS */
arith, /* MULT */
arith, /* DIVIDE */
arith, /* MOD */
assign, /* ASSIGN */
nullproc, /* ASGNOP */
assign, /* ADDEQ */
assign, /* SUBEQ */
assign, /* MULTEQ */
assign, /* DIVEQ */
assign, /* MODEQ */
assign, /* POWEQ */
printstat, /* PRINT */
awkprintf, /* PRINTF */
awksprintf, /* SPRINTF */
nullproc, /* ELSE */
intest, /* INTEST */
condexpr, /* CONDEXPR */
incrdecr, /* POSTINCR */
incrdecr, /* PREINCR */
incrdecr, /* POSTDECR */
incrdecr, /* PREDECR */
nullproc, /* VAR */
nullproc, /* IVAR */
getnf, /* VARNF */
call, /* CALL */
nullproc, /* NUMBER */
nullproc, /* STRING */
nullproc, /* REGEXPR */
getline, /* GETLINE */
jump, /* RETURN */
split, /* SPLIT */
substr, /* SUBSTR */
whilestat, /* WHILE */
cat, /* CAT */
boolop, /* NOT */
arith, /* UMINUS */
arith, /* POWER */
nullproc, /* DECR */
nullproc, /* INCR */
indirect, /* INDIRECT */
nullproc, /* LASTTOKEN */
};
char *tokname(int n)
{
static char buf[100];
if (n < FIRSTTOKEN || n > LASTTOKEN) {
sprintf(buf, "token %d", n);
return buf;
}
return printname[n-FIRSTTOKEN];
}

177
awk/proto.h Normal file
View File

@@ -0,0 +1,177 @@
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name Lucent Technologies or any of
its entities not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
****************************************************************/
extern int yywrap(void);
extern void setfname(Cell *);
extern int constnode(Node *);
extern char *strnode(Node *);
extern Node *notnull(Node *);
extern int yyparse(void);
extern int yylex(void);
extern void startreg(void);
extern int input(void);
extern void unput(int);
extern void unputstr(char *);
extern int yylook(void);
extern int yyback(int *, int);
extern int yyinput(void);
extern void *compre(char *);
extern int hexstr(char **);
extern void quoted(char **, char **, char *);
extern int match(void *, char *, char *);
extern int pmatch(void *, char *, char *);
extern int nematch(void *, char *, char *);
extern int countposn(char *, int);
extern void overflow(void);
extern int pgetc(void);
extern char *cursource(void);
extern Node *nodealloc(int);
extern Node *exptostat(Node *);
extern Node *node1(int, Node *);
extern Node *node2(int, Node *, Node *);
extern Node *node3(int, Node *, Node *, Node *);
extern Node *node4(int, Node *, Node *, Node *, Node *);
extern Node *stat3(int, Node *, Node *, Node *);
extern Node *op2(int, Node *, Node *);
extern Node *op1(int, Node *);
extern Node *stat1(int, Node *);
extern Node *op3(int, Node *, Node *, Node *);
extern Node *op4(int, Node *, Node *, Node *, Node *);
extern Node *stat2(int, Node *, Node *);
extern Node *stat4(int, Node *, Node *, Node *, Node *);
extern Node *celltonode(Cell *, int);
extern Node *rectonode(void);
extern Node *makearr(Node *);
extern Node *pa2stat(Node *, Node *, Node *);
extern Node *linkum(Node *, Node *);
extern void defn(Cell *, Node *, Node *);
extern int isarg(char *);
extern char *tokname(int);
extern Cell *(*proctab[])(Node **, int);
extern int ptoi(void *);
extern Node *itonp(int);
extern void syminit(void);
extern void arginit(int, char **);
extern void envinit(char **);
extern Array *makesymtab(int);
extern void freesymtab(Cell *);
extern void freeelem(Cell *, char *);
extern Cell *setsymtab(char *, char *, double, unsigned int, Array *);
extern int hash(char *, int);
extern void rehash(Array *);
extern Cell *lookup(char *, Array *);
extern double setfval(Cell *, double);
extern void funnyvar(Cell *, char *);
extern char *setsval(Cell *, char *);
extern double getfval(Cell *);
extern char *getsval(Cell *);
extern char *tostring(char *);
extern char *qstring(char *, int);
extern void recinit(unsigned int);
extern void initgetrec(void);
extern void makefields(int, int);
extern void growfldtab(int n);
extern int getrec(char **, int *, int);
extern void nextfile(void);
extern int readrec(char **buf, int *bufsize, FILE *inf);
extern char *getargv(int);
extern void setclvar(char *);
extern void fldbld(void);
extern void cleanfld(int, int);
extern void newfld(int);
extern int refldbld(char *, char *);
extern void recbld(void);
extern Cell *fieldadr(int);
extern void yyerror(char *);
extern void fpecatch(int);
extern void bracecheck(void);
extern void bcheck2(int, int, int);
extern void SYNTAX(char *, ...);
extern void FATAL(char *, ...);
extern void WARNING(char *, ...);
extern void error(void);
extern void eprint(void);
extern void bclass(int);
extern double errcheck(double, char *);
extern int isclvar(char *);
extern int is_number(char *);
extern int adjbuf(char **pb, int *sz, int min, int q, char **pbp, char *what);
extern void run(Node *);
extern Cell *execute(Node *);
extern Cell *program(Node **, int);
extern Cell *call(Node **, int);
extern Cell *copycell(Cell *);
extern Cell *arg(Node **, int);
extern Cell *jump(Node **, int);
extern Cell *getline(Node **, int);
extern Cell *getnf(Node **, int);
extern Cell *array(Node **, int);
extern Cell *awkdelete(Node **, int);
extern Cell *intest(Node **, int);
extern Cell *matchop(Node **, int);
extern Cell *boolop(Node **, int);
extern Cell *relop(Node **, int);
extern void tfree(Cell *);
extern Cell *gettemp(void);
extern Cell *field(Node **, int);
extern Cell *indirect(Node **, int);
extern Cell *substr(Node **, int);
extern Cell *sindex(Node **, int);
extern int format(char **, int *, char *, Node *);
extern Cell *awksprintf(Node **, int);
extern Cell *awkprintf(Node **, int);
extern Cell *arith(Node **, int);
extern double ipow(double, int);
extern Cell *incrdecr(Node **, int);
extern Cell *assign(Node **, int);
extern Cell *cat(Node **, int);
extern Cell *pastat(Node **, int);
extern Cell *dopa2(Node **, int);
extern Cell *split(Node **, int);
extern Cell *condexpr(Node **, int);
extern Cell *ifstat(Node **, int);
extern Cell *whilestat(Node **, int);
extern Cell *dostat(Node **, int);
extern Cell *forstat(Node **, int);
extern Cell *instat(Node **, int);
extern Cell *bltin(Node **, int);
extern Cell *printstat(Node **, int);
extern Cell *nullproc(Node **, int);
extern FILE *redirect(int, Node *);
extern FILE *openfile(int, char *);
extern char *filename(FILE *);
extern Cell *closefile(Node **, int);
extern void closeall(void);
extern Cell *sub(Node **, int);
extern Cell *gsub(Node **, int);
extern FILE *popen(const char *, const char *);
extern int pclose(FILE *);

325
awk/re.c Normal file
View File

@@ -0,0 +1,325 @@
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name Lucent Technologies or any of
its entities not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
****************************************************************/
#define DEBUG
#include <stdio.h>
#include <ctype.h>
#include <setjmp.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "awk.h"
#include "y.tab.h"
#include "regexp.h"
/* This file provides the interface between the main body of
* awk and the pattern matching package. It preprocesses
* patterns prior to compilation to provide awk-like semantics
* to character sequences not supported by the pattern package.
* The following conversions are performed:
*
* "()" -> "[]"
* "[-" -> "[\-"
* "[^-" -> "[^\-"
* "-]" -> "\-]"
* "[]" -> "[]*"
* "\xdddd" -> "\z" where 'z' is the UTF sequence
* for the hex value
* "\ddd" -> "\o" where 'o' is a char octal value
* "\b" -> "\B" where 'B' is backspace
* "\t" -> "\T" where 'T' is tab
* "\f" -> "\F" where 'F' is form feed
* "\n" -> "\N" where 'N' is newline
* "\r" -> "\r" where 'C' is cr
*/
#define MAXRE 512
static char re[MAXRE]; /* copy buffer */
char *patbeg;
int patlen; /* number of chars in pattern */
#define NPATS 20 /* number of slots in pattern cache */
static struct pat_list /* dynamic pattern cache */
{
char *re;
int use;
Reprog *program;
} pattern[NPATS];
static int npats; /* cache fill level */
/* Compile a pattern */
void
*compre(char *pat)
{
int i, j, inclass;
char c, *p, *s;
Reprog *program;
if (!compile_time) { /* search cache for dynamic pattern */
for (i = 0; i < npats; i++)
if (!strcmp(pat, pattern[i].re)) {
pattern[i].use++;
return((void *) pattern[i].program);
}
}
/* Preprocess Pattern for compilation */
p = re;
s = pat;
inclass = 0;
while (c = *s++) {
if (c == '\\') {
quoted(&s, &p, re+MAXRE);
continue;
}
else if (!inclass && c == '(' && *s == ')') {
if (p < re+MAXRE-2) { /* '()' -> '[]*' */
*p++ = '[';
*p++ = ']';
c = '*';
s++;
}
else overflow();
}
else if (c == '['){ /* '[-' -> '[\-' */
inclass = 1;
if (*s == '-') {
if (p < re+MAXRE-2) {
*p++ = '[';
*p++ = '\\';
c = *s++;
}
else overflow();
} /* '[^-' -> '[^\-'*/
else if (*s == '^' && s[1] == '-'){
if (p < re+MAXRE-3) {
*p++ = '[';
*p++ = *s++;
*p++ = '\\';
c = *s++;
}
else overflow();
}
else if (*s == '['){ /* skip '[[' */
if (p < re+MAXRE-1)
*p++ = c;
else overflow();
c = *s++;
}
else if (*s == '^' && s[1] == '[') { /* skip '[^['*/
if (p < re+MAXRE-2) {
*p++ = c;
*p++ = *s++;
c = *s++;
}
else overflow();
}
else if (*s == ']') { /* '[]' -> '[]*' */
if (p < re+MAXRE-2) {
*p++ = c;
*p++ = *s++;
c = '*';
inclass = 0;
}
else overflow();
}
}
else if (c == '-' && *s == ']') { /* '-]' -> '\-]' */
if (p < re+MAXRE-1)
*p++ = '\\';
else overflow();
}
else if (c == ']')
inclass = 0;
if (p < re+MAXRE-1)
*p++ = c;
else overflow();
}
*p = 0;
program = regcomp(re); /* compile pattern */
if (!compile_time) {
if (npats < NPATS) /* Room in cache */
i = npats++;
else { /* Throw out least used */
int use = pattern[0].use;
i = 0;
for (j = 1; j < NPATS; j++) {
if (pattern[j].use < use) {
use = pattern[j].use;
i = j;
}
}
xfree(pattern[i].program);
xfree(pattern[i].re);
}
pattern[i].re = tostring(pat);
pattern[i].program = program;
pattern[i].use = 1;
}
return((void *) program);
}
/* T/F match indication - matched string not exported */
int
match(void *p, char *s, char *q)
{
return regexec((Reprog *) p, (char *) s, 0, 0);
}
/* match and delimit the matched string */
int
pmatch(void *p, char *s, char *start)
{
Resub m;
m.s.sp = start;
m.e.ep = 0;
if (regexec((Reprog *) p, (char *) s, &m, 1)) {
patbeg = m.s.sp;
patlen = m.e.ep-m.s.sp;
return 1;
}
patlen = -1;
patbeg = start;
return 0;
}
/* perform a non-empty match */
int
nematch(void *p, char *s, char *start)
{
if (pmatch(p, s, start) == 1 && patlen > 0)
return 1;
patlen = -1;
patbeg = start;
return 0;
}
/* in the parsing of regular expressions, metacharacters like . have */
/* to be seen literally; \056 is not a metacharacter. */
int
hexstr(char **pp) /* find and eval hex string at pp, return new p */
{
int c;
int n = 0;
int i;
for (i = 0, c = (*pp)[i]; i < 4 && isxdigit(c); i++, c = (*pp)[i]) {
if (isdigit(c))
n = 16 * n + c - '0';
else if ('a' <= c && c <= 'f')
n = 16 * n + c - 'a' + 10;
else if ('A' <= c && c <= 'F')
n = 16 * n + c - 'A' + 10;
}
*pp += i;
return n;
}
/* look for awk-specific escape sequences */
#define isoctdigit(c) ((c) >= '0' && (c) <= '7') /* multiple use of arg */
void
quoted(char **s, char **to, char *end) /* handle escaped sequence */
{
char *p = *s;
char *t = *to;
wchar_t c;
switch(c = *p++) {
case 't':
c = '\t';
break;
case 'n':
c = '\n';
break;
case 'f':
c = '\f';
break;
case 'r':
c = '\r';
break;
case 'b':
c = '\b';
break;
default:
if (t < end-1) /* all else must be escaped */
*t++ = '\\';
if (c == 'x') { /* hexadecimal goo follows */
c = hexstr(&p);
if (t < end-MB_CUR_MAX)
t += wctomb(t, c);
else overflow();
*to = t;
*s = p;
return;
} else if (isoctdigit(c)) { /* \d \dd \ddd */
c -= '0';
if (isoctdigit(*p)) {
c = 8 * c + *p++ - '0';
if (isoctdigit(*p))
c = 8 * c + *p++ - '0';
}
}
break;
}
if (t < end-1)
*t++ = c;
*s = p;
*to = t;
}
/* count rune positions */
int
countposn(char *s, int n)
{
int i, j;
char *end;
for (i = 0, end = s+n; *s && s < end; i++){
j = mblen(s, n);
if(j <= 0)
j = 1;
s += j;
}
return(i);
}
/* pattern package error handler */
void
regerror(char *s)
{
FATAL("%s", s);
}
void
overflow(void)
{
FATAL("%s", "regular expression too big");
}

1892
awk/run.c Normal file

File diff suppressed because it is too large Load Diff

434
awk/tran.c Normal file
View File

@@ -0,0 +1,434 @@
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name Lucent Technologies or any of
its entities not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
****************************************************************/
#define DEBUG
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "awk.h"
#include "y.tab.h"
#define FULLTAB 2 /* rehash when table gets this x full */
#define GROWTAB 4 /* grow table by this factor */
Array *symtab; /* main symbol table */
char **FS; /* initial field sep */
char **RS; /* initial record sep */
char **OFS; /* output field sep */
char **ORS; /* output record sep */
char **OFMT; /* output format for numbers */
char **CONVFMT; /* format for conversions in getsval */
Awkfloat *NF; /* number of fields in current record */
Awkfloat *NR; /* number of current record */
Awkfloat *FNR; /* number of current record in current file */
char **FILENAME; /* current filename argument */
Awkfloat *ARGC; /* number of arguments from command line */
char **SUBSEP; /* subscript separator for a[i,j,k]; default \034 */
Awkfloat *RSTART; /* start of re matched with ~; origin 1 (!) */
Awkfloat *RLENGTH; /* length of same */
Cell *nrloc; /* NR */
Cell *nfloc; /* NF */
Cell *fnrloc; /* FNR */
Array *ARGVtab; /* symbol table containing ARGV[...] */
Array *ENVtab; /* symbol table containing ENVIRON[...] */
Cell *rstartloc; /* RSTART */
Cell *rlengthloc; /* RLENGTH */
Cell *symtabloc; /* SYMTAB */
Cell *nullloc; /* a guaranteed empty cell */
Node *nullnode; /* zero&null, converted into a node for comparisons */
Cell *literal0;
extern Cell **fldtab;
void syminit(void) /* initialize symbol table with builtin vars */
{
literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
/* this is used for if(x)... tests: */
nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
nullnode = celltonode(nullloc, CCON);
FS = &setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab)->sval;
RS = &setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab)->sval;
OFS = &setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab)->sval;
ORS = &setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab)->sval;
OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
NF = &nfloc->fval;
nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
NR = &nrloc->fval;
fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
FNR = &fnrloc->fval;
SUBSEP = &setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab)->sval;
rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
RSTART = &rstartloc->fval;
rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
RLENGTH = &rlengthloc->fval;
symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
symtabloc->sval = (char *) symtab;
}
void arginit(int ac, char **av) /* set up ARGV and ARGC */
{
Cell *cp;
int i;
char temp[50];
ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
ARGVtab = makesymtab(NSYMTAB); /* could be (int) ARGC as well */
cp->sval = (char *) ARGVtab;
for (i = 0; i < ac; i++) {
sprintf(temp, "%d", i);
if (is_number(*av))
setsymtab(temp, *av, atof(*av), STR|NUM, ARGVtab);
else
setsymtab(temp, *av, 0.0, STR, ARGVtab);
av++;
}
}
void envinit(char **envp) /* set up ENVIRON variable */
{
Cell *cp;
char *p;
cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
ENVtab = makesymtab(NSYMTAB);
cp->sval = (char *) ENVtab;
for ( ; *envp; envp++) {
if ((p = strchr(*envp, '=')) == NULL)
continue;
*p++ = 0; /* split into two strings at = */
if (is_number(p))
setsymtab(*envp, p, atof(p), STR|NUM, ENVtab);
else
setsymtab(*envp, p, 0.0, STR, ENVtab);
p[-1] = '='; /* restore in case env is passed down to a shell */
}
}
Array *makesymtab(int n) /* make a new symbol table */
{
Array *ap;
Cell **tp;
ap = (Array *) malloc(sizeof(Array));
tp = (Cell **) calloc(n, sizeof(Cell *));
if (ap == NULL || tp == NULL)
FATAL("out of space in makesymtab");
ap->nelem = 0;
ap->size = n;
ap->tab = tp;
return(ap);
}
void freesymtab(Cell *ap) /* free a symbol table */
{
Cell *cp, *temp;
Array *tp;
int i;
if (!isarr(ap))
return;
tp = (Array *) ap->sval;
if (tp == NULL)
return;
for (i = 0; i < tp->size; i++) {
for (cp = tp->tab[i]; cp != NULL; cp = temp) {
xfree(cp->nval);
if (freeable(cp))
xfree(cp->sval);
temp = cp->cnext; /* avoids freeing then using */
free(cp);
}
tp->tab[i] = 0;
}
free(tp->tab);
free(tp);
}
void freeelem(Cell *ap, char *s) /* free elem s from ap (i.e., ap["s"] */
{
Array *tp;
Cell *p, *prev = NULL;
int h;
tp = (Array *) ap->sval;
h = hash(s, tp->size);
for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
if (strcmp(s, p->nval) == 0) {
if (prev == NULL) /* 1st one */
tp->tab[h] = p->cnext;
else /* middle somewhere */
prev->cnext = p->cnext;
if (freeable(p))
xfree(p->sval);
free(p->nval);
free(p);
tp->nelem--;
return;
}
}
Cell *setsymtab(char *n, char *s, Awkfloat f, unsigned t, Array *tp)
{
int h;
Cell *p;
if (n != NULL && (p = lookup(n, tp)) != NULL) {
dprintf( ("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
p, p->nval, p->sval, p->fval, p->tval) );
return(p);
}
p = (Cell *) malloc(sizeof(Cell));
if (p == NULL)
FATAL("out of space for symbol table at %s", n);
p->nval = tostring(n);
p->sval = s ? tostring(s) : tostring("");
p->fval = f;
p->tval = t;
p->csub = CUNK;
p->ctype = OCELL;
tp->nelem++;
if (tp->nelem > FULLTAB * tp->size)
rehash(tp);
h = hash(n, tp->size);
p->cnext = tp->tab[h];
tp->tab[h] = p;
dprintf( ("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
p, p->nval, p->sval, p->fval, p->tval) );
return(p);
}
int hash(char *s, int n) /* form hash value for string s */
{
unsigned hashval;
for (hashval = 0; *s != '\0'; s++)
hashval = (*s + 31 * hashval);
return hashval % n;
}
void rehash(Array *tp) /* rehash items in small table into big one */
{
int i, nh, nsz;
Cell *cp, *op, **np;
nsz = GROWTAB * tp->size;
np = (Cell **) calloc(nsz, sizeof(Cell *));
if (np == NULL) /* can't do it, but can keep running. */
return; /* someone else will run out later. */
for (i = 0; i < tp->size; i++) {
for (cp = tp->tab[i]; cp; cp = op) {
op = cp->cnext;
nh = hash(cp->nval, nsz);
cp->cnext = np[nh];
np[nh] = cp;
}
}
free(tp->tab);
tp->tab = np;
tp->size = nsz;
}
Cell *lookup(char *s, Array *tp) /* look for s in tp */
{
Cell *p;
int h;
h = hash(s, tp->size);
for (p = tp->tab[h]; p != NULL; p = p->cnext)
if (strcmp(s, p->nval) == 0)
return(p); /* found it */
return(NULL); /* not found */
}
Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */
{
int fldno;
if ((vp->tval & (NUM | STR)) == 0)
funnyvar(vp, "assign to");
if (isfld(vp)) {
donerec = 0; /* mark $0 invalid */
fldno = atoi(vp->nval);
if (fldno > *NF)
newfld(fldno);
dprintf( ("setting field %d to %g\n", fldno, f) );
} else if (isrec(vp)) {
donefld = 0; /* mark $1... invalid */
donerec = 1;
}
if (freeable(vp))
xfree(vp->sval); /* free any previous string */
vp->tval &= ~STR; /* mark string invalid */
vp->tval |= NUM; /* mark number ok */
dprintf( ("setfval %p: %s = %g, t=%o\n", vp, vp->nval, f, vp->tval) );
return vp->fval = f;
}
void funnyvar(Cell *vp, char *rw)
{
if (isarr(vp))
FATAL("can't %s %s; it's an array name.", rw, vp->nval);
if (vp->tval & FCN)
FATAL("can't %s %s; it's a function.", rw, vp->nval);
WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
vp, vp->nval, vp->sval, vp->fval, vp->tval);
}
char *setsval(Cell *vp, char *s) /* set string val of a Cell */
{
char *t;
int fldno;
dprintf( ("starting setsval %p: %s = \"%s\", t=%o\n", vp, vp->nval, s, vp->tval) );
if ((vp->tval & (NUM | STR)) == 0)
funnyvar(vp, "assign to");
if (isfld(vp)) {
donerec = 0; /* mark $0 invalid */
fldno = atoi(vp->nval);
if (fldno > *NF)
newfld(fldno);
dprintf( ("setting field %d to %s (%p)\n", fldno, s, s) );
} else if (isrec(vp)) {
donefld = 0; /* mark $1... invalid */
donerec = 1;
}
t = tostring(s); /* in case it's self-assign */
vp->tval &= ~NUM;
vp->tval |= STR;
if (freeable(vp))
xfree(vp->sval);
vp->tval &= ~DONTFREE;
dprintf( ("setsval %p: %s = \"%s (%p)\", t=%o\n", vp, vp->nval, t,t, vp->tval) );
return(vp->sval = t);
}
Awkfloat getfval(Cell *vp) /* get float val of a Cell */
{
if ((vp->tval & (NUM | STR)) == 0)
funnyvar(vp, "read value of");
if (isfld(vp) && donefld == 0)
fldbld();
else if (isrec(vp) && donerec == 0)
recbld();
if (!isnum(vp)) { /* not a number */
vp->fval = atof(vp->sval); /* best guess */
if (is_number(vp->sval) && !(vp->tval&CON))
vp->tval |= NUM; /* make NUM only sparingly */
}
dprintf( ("getfval %p: %s = %g, t=%o\n", vp, vp->nval, vp->fval, vp->tval) );
return(vp->fval);
}
char *getsval(Cell *vp) /* get string val of a Cell */
{
char s[100]; /* BUG: unchecked */
double dtemp;
if ((vp->tval & (NUM | STR)) == 0)
funnyvar(vp, "read value of");
if (isfld(vp) && donefld == 0)
fldbld();
else if (isrec(vp) && donerec == 0)
recbld();
if (isstr(vp) == 0) {
if (freeable(vp))
xfree(vp->sval);
if (modf(vp->fval, &dtemp) == 0) /* it's integral */
sprintf(s, "%.30g", vp->fval);
else
sprintf(s, *CONVFMT, vp->fval);
vp->sval = tostring(s);
vp->tval &= ~DONTFREE;
vp->tval |= STR;
}
dprintf( ("getsval %p: %s = \"%s (%p)\", t=%o\n", vp, vp->nval, vp->sval, vp->sval, vp->tval) );
return(vp->sval);
}
char *tostring(char *s) /* make a copy of string s */
{
char *p;
p = (char *) malloc(strlen(s)+1);
if (p == NULL)
FATAL("out of space in tostring on %s", s);
strcpy(p, s);
return(p);
}
char *qstring(char *s, int delim) /* collect string up to next delim */
{
char *os = s;
int c, n;
char *buf, *bp;
if ((buf = (char *) malloc(strlen(s)+3)) == NULL)
FATAL( "out of space in qstring(%s)", s);
for (bp = buf; (c = *s) != delim; s++) {
if (c == '\n')
SYNTAX( "newline in string %.20s...", os );
else if (c != '\\')
*bp++ = c;
else { /* \something */
c = *++s;
if (c == 0) { /* \ at end */
*bp++ = '\\';
break; /* for loop */
}
switch (c) {
case '\\': *bp++ = '\\'; break;
case 'n': *bp++ = '\n'; break;
case 't': *bp++ = '\t'; break;
case 'b': *bp++ = '\b'; break;
case 'f': *bp++ = '\f'; break;
case 'r': *bp++ = '\r'; break;
default:
if (!isdigit(c)) {
*bp++ = c;
break;
}
n = c - '0';
if (isdigit(s[1])) {
n = 8 * n + *++s - '0';
if (isdigit(s[1]))
n = 8 * n + *++s - '0';
}
*bp++ = n;
break;
}
}
}
*bp++ = 0;
return buf;
}

36
basename/Makefile Normal file
View File

@@ -0,0 +1,36 @@
# basename - basename unix port from plan9
# Depends on ../lib9
include ../config.mk
TARG = basename
OFILES = basename.o
MANFILES = basename.1
all: ${TARG}
@echo built ${TARG}
install: ${TARG}
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/
@chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG}
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1
@chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES}
uninstall:
rm -f ${DESTDIR}${PREFIX}/bin/${TARG}
rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES}
.c.o:
@echo CC $*.c
@${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c
clean:
rm -f ${OFILES} ${TARG}
${TARG}: ${OFILES}
@echo LD ${TARG}
@${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9

35
basename/basename.1 Normal file
View File

@@ -0,0 +1,35 @@
.TH BASENAME 1
.SH NAME
basename \- strip file name affixes
.SH SYNOPSIS
.B basename
[
.B -d
]
.I string
[
.I suffix
]
.SH DESCRIPTION
.PP
.I Basename
deletes any prefix ending in slash
.RB ( / )
and the
.IR suffix ,
if present in
.IR string ,
from
.IR string ,
and prints the result on the standard output.
.PP
The
.B -d
option instead prints the directory component,
that is,
.I string
up to but not including the final slash.
If the string contains no slash,
a period and newline are printed.
.SH SOURCE
.B \*9/src/cmd/basename.c

41
basename/basename.c Normal file
View File

@@ -0,0 +1,41 @@
#include <u.h>
#include <libc.h>
void
main(int argc, char *argv[])
{
char *pr;
int n, dflag;
dflag = 0;
if(argc>1 && strcmp(argv[1], "-d") == 0){
--argc;
++argv;
dflag = 1;
}
if(argc < 2 || argc > 3){
fprint(2, "usage: basename [-d] string [suffix]\n");
exits("usage");
}
pr = utfrrune(argv[1], '/');
if(dflag){
if(pr){
*pr = 0;
print("%s\n", argv[1]);
exits(0);
}
print(".\n");
exits(0);
}
if(pr)
pr++;
else
pr = argv[1];
if(argc==3){
n = strlen(pr)-strlen(argv[2]);
if(n >= 0 && !strcmp(pr+n, argv[2]))
pr[n] = 0;
}
print("%s\n", pr);
exits(0);
}

46
bc/Makefile Normal file
View File

@@ -0,0 +1,46 @@
# bc - bc unix port from plan9
# Depends on ../lib9
include ../config.mk
TARG = bc
OFILES = y.tab.o
YFILES = bc.y
MANFILES = bc.1
all:
@if [ ! -f y.tab.c ]; then \
${MAKE} -f Makefile depend;\
fi
@${MAKE} -f Makefile ${TARG}
@echo built ${TARG}
depend:
@echo YACC ${YFILES}
@${YACC} -d ${YFILES}
install: ${TARG}
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/
@chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG}
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1
@chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES}
uninstall:
rm -f ${DESTDIR}${PREFIX}/bin/${TARG}
rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES}
.c.o:
@echo CC $*.c
@${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c
clean:
rm -f ${OFILES} ${TARG} y.tab.c y.tab.h
${TARG}: ${OFILES}
@echo LD ${TARG}
@${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -lm -L${PREFIX}/lib -L../lib9 -l9

292
bc/bc.1 Normal file
View File

@@ -0,0 +1,292 @@
.TH BC 1
.SH NAME
bc \- arbitrary-precision arithmetic language
.SH SYNOPSIS
.B bc
[
.B -c
]
[
.B -l
]
[
.B -s
]
[
.I file ...
]
.SH DESCRIPTION
.I Bc
is an interactive processor for a language that resembles
C but provides arithmetic on numbers of arbitrary length with up
to 100 digits right of the decimal point.
It takes input from any files given, then reads
the standard input.
The
.B -l
argument stands for the name
of an arbitrary precision math library.
The
.B -s
argument suppresses the automatic display
of calculation results; all output is via the
.B print
command.
.PP
The following syntax for
.I bc
programs is like that of C;
.I L
means letter
.BR a - z ,
.I E
means expression,
.I S
means statement.
.TF length(E)
.TP
Lexical
.RS
.HP
comments are enclosed in
.B /* */
.HP
newlines end statements
.RE
.TP
Names
.IP
simple variables:
.I L
.br
array elements:
.IB L [ E ]
.br
The words
.BR ibase ,
.BR obase ,
and
.B scale
.TP
Other operands
.IP
arbitrarily long numbers with optional sign and decimal point.
.RS
.TP
.BI ( E )
.TP
.BI sqrt( E )
.TP
.BI length( E )
number of significant decimal digits
.TP
.BI scale( E )
number of digits right of decimal point
.TP
.IB L ( E , ... ,\fIE\fP)
function call
.RE
.TP
Operators
.RS
.HP
.B "+ - * / % ^\ "
.RB ( %
is remainder;
.B ^
is power)
.HP
.B "++ --\ "
.TP
.B "== <= >= != < >"
.TP
.B "= += -= *= /= %= ^="
.RE
.TP
Statements
.RS
.br
.I E
.br
.B {
.I S
.B ;
\&...
.B ;
.I S
.B }
.br
.B "print"
.I E
.br
.B "if ("
.I E
.B )
.I S
.br
.B "while ("
.I E
.B )
.I S
.br
.B "for ("
.I E
.B ;
.I E
.B ;
.I E
.B ")"
.I S
.br
null statement
.br
.B break
.br
.B quit
.br
\fL"\fRtext\fL"\fR
.RE
.TP
Function definitions
.RS
.br
.B define
.I L
.B (
.I L
.B ,
\&...
.B ,
.I L
.B ){
.PD0
.br
.B auto
.I L
.B ,
\&...
.B ,
.I L
.br
.I S
.B ;
\&...
.B ;
.I S
.br
.B return
.I E
.LP
.B }
.RE
.TP
Functions in
.B -l
math library
.RS
.TP
.BI s( x )
sine
.TP
.BI c( x )
cosine
.TP
.BI e( x )
exponential
.TP
.BI l( x )
log
.TP
.BI a( x )
arctangent
.TP
.BI j( "n, x" )
Bessel function
.RE
.PP
.DT
All function arguments are passed by value.
.PD
.PP
The value of an expression at the top level is printed
unless the main operator is an assignment or the
.B -s
command line argument is given.
Text in quotes, which may include newlines, is always printed.
Either semicolons or newlines may separate statements.
Assignment to
.B scale
influences the number of digits to be retained on arithmetic
operations in the manner of
.IR dc (1).
Assignments to
.B ibase
or
.B obase
set the input and output number radix respectively.
.PP
The same letter may be used as an array, a function,
and a simple variable simultaneously.
All variables are global to the program.
Automatic variables are pushed down during function calls.
In a declaration of an array as a function argument
or automatic variable
empty square brackets must follow the array name.
.PP
.I Bc
is actually a preprocessor for
.IR dc (1),
which it invokes automatically, unless the
.B -c
(compile only)
option is present.
In this case the
.I dc
input is sent to the standard output instead.
.SH EXAMPLE
Define a function to compute an approximate value of
the exponential.
Use it to print 10 values.
(The exponential function in the library gives better answers.)
.PP
.EX
scale = 20
define e(x) {
auto a, b, c, i, s
a = 1
b = 1
s = 1
for(i=1; 1; i++) {
a *= x
b *= i
c = a/b
if(c == 0) return s
s += c
}
}
for(i=1; i<=10; i++) print e(i)
.EE
.SH FILES
.B \*9/lib/bclib
mathematical library
.SH SOURCE
.B \*9/src/cmd/bc.y
.SH "SEE ALSO"
.IR dc (1),
.IR hoc (1)
.SH BUGS
No
.LR && ,
.LR || ,
or
.L !
operators.
.PP
A
.L for
statement must have all three
.LR E s.
.PP
A
.L quit
is interpreted when read, not when executed.

985
bc/bc.y Normal file
View File

@@ -0,0 +1,985 @@
%{
#include <u.h>
#include <libc.h>
#include <bio.h>
#define bsp_max 5000
Biobuf *in;
#define stdin bstdin
#define stdout bstdout
Biobuf stdin;
Biobuf stdout;
char cary[1000];
char* cp = { cary };
char string[1000];
char* str = { string };
int crs = 128;
int rcrs = 128; /* reset crs */
int bindx = 0;
int lev = 0;
int ln;
int* ttp;
char* ss = "";
int bstack[10] = { 0 };
char* numb[15] =
{
" 0", " 1", " 2", " 3", " 4", " 5",
" 6", " 7", " 8", " 9", " 10", " 11",
" 12", " 13", " 14"
};
int* pre;
int* post;
long peekc = -1;
int sargc;
int ifile;
char** sargv;
char *funtab[] =
{
"<1>","<2>","<3>","<4>","<5>",
"<6>","<7>","<8>","<9>","<10>",
"<11>","<12>","<13>","<14>","<15>",
"<16>","<17>","<18>","<19>","<20>",
"<21>","<22>","<23>","<24>","<25>",
"<26>"
};
char *atab[] =
{
"<221>","<222>","<223>","<224>","<225>",
"<226>","<227>","<228>","<229>","<230>",
"<231>","<232>","<233>","<234>","<235>",
"<236>","<237>","<238>","<239>","<240>",
"<241>","<242>","<243>","<244>","<245>",
"<246>"
};
char* letr[26] =
{
"a","b","c","d","e","f","g","h","i","j",
"k","l","m","n","o","p","q","r","s","t",
"u","v","w","x","y","z"
};
char* dot = { "." };
int bspace[bsp_max];
int* bsp_nxt = { bspace };
int bdebug = 0;
int lflag;
int cflag;
int sflag;
int* bundle(int, ...);
void conout(int*, char*);
int cpeek(int, int, int);
int getch(void);
int* geta(char*);
int* getf(char*);
void getout(void);
void output(int*);
void pp(char*);
void routput(int*);
void tp(char*);
void yyerror(char*, ...);
int yyparse(void);
typedef void* pointer;
/* #pragma varargck type "lx" pointer */
%}
%union
{
int* iptr;
char* cptr;
int cc;
}
%type <iptr> pstat stat stat1 def slist dlets e ase nase
%type <iptr> slist re fprefix cargs eora cons constant lora
%type <cptr> crs
%token <cptr> LETTER EQOP _AUTO DOT
%token <cc> DIGIT SQRT LENGTH _IF FFF EQ
%token <cc> _PRINT _WHILE _FOR NE LE GE INCR DECR
%token <cc> _RETURN _BREAK _DEFINE BASE OBASE SCALE
%token <cc> QSTR ERROR
%right '=' EQOP
%left '+' '-'
%left '*' '/' '%'
%right '^'
%left UMINUS
%%
start:
start stuff
| stuff
stuff:
pstat tail
{
output($1);
}
| def dargs ')' '{' dlist slist '}'
{
ttp = bundle(6, pre, $6, post , "0", numb[lev], "Q");
conout(ttp, (char*)$1);
rcrs = crs;
output((int*)""); /* this is horse puk!! */
lev = bindx = 0;
}
dlist:
tail
| dlist _AUTO dlets tail
stat:
stat1
| nase
{
if(sflag)
bundle(2, $1, "s.");
}
pstat:
stat1
{
if(sflag)
bundle(2, $1, "0");
}
| nase
{
if(!sflag)
bundle(2, $1, "ps.");
}
stat1:
{
bundle(1, "");
}
| ase
{
bundle(2, $1, "s.");
}
| SCALE '=' e
{
bundle(2, $3, "k");
}
| SCALE EQOP e
{
bundle(4, "K", $3, $2, "k");
}
| BASE '=' e
{
bundle(2, $3, "i");
}
| BASE EQOP e
{
bundle(4, "I", $3, $2, "i");
}
| OBASE '=' e
{
bundle(2, $3, "o");
}
| OBASE EQOP e
{
bundle(4, "O", $3, $2, "o");
}
| QSTR
{
bundle(3, "[", $1, "]P");
}
| _BREAK
{
bundle(2, numb[lev-bstack[bindx-1]], "Q");
}
| _PRINT e
{
bundle(2, $2, "ps.");
}
| _RETURN e
{
bundle(4, $2, post, numb[lev], "Q");
}
| _RETURN
{
bundle(4, "0", post, numb[lev], "Q");
}
| '{' slist '}'
{
$$ = $2;
}
| FFF
{
bundle(1, "fY");
}
| _IF crs BLEV '(' re ')' stat
{
conout($7, $2);
bundle(3, $5, $2, " ");
}
| _WHILE crs '(' re ')' stat BLEV
{
bundle(3, $6, $4, $2);
conout($$, $2);
bundle(3, $4, $2, " ");
}
| fprefix crs re ';' e ')' stat BLEV
{
bundle(5, $7, $5, "s.", $3, $2);
conout($$, $2);
bundle(5, $1, "s.", $3, $2, " ");
}
| '~' LETTER '=' e
{
bundle(3, $4, "S", $2);
}
fprefix:
_FOR '(' e ';'
{
$$ = $3;
}
BLEV:
=
{
--bindx;
}
slist:
stat
| slist tail stat
{
bundle(2, $1, $3);
}
tail:
'\n'
{
ln++;
}
| ';'
re:
e EQ e
{
$$ = bundle(3, $1, $3, "=");
}
| e '<' e
{
bundle(3, $1, $3, ">");
}
| e '>' e
{
bundle(3, $1, $3, "<");
}
| e NE e
{
bundle(3, $1, $3, "!=");
}
| e GE e
{
bundle(3, $1, $3, "!>");
}
| e LE e
{
bundle(3, $1, $3, "!<");
}
| e
{
bundle(2, $1, " 0!=");
}
nase:
'(' e ')'
{
$$ = $2;
}
| cons
{
bundle(3, " ", $1, " ");
}
| DOT cons
{
bundle(3, " .", $2, " ");
}
| cons DOT cons
{
bundle(5, " ", $1, ".", $3, " ");
}
| cons DOT
{
bundle(4, " ", $1, ".", " ");
}
| DOT
{
$<cptr>$ = "l.";
}
| LETTER '[' e ']'
{
bundle(3, $3, ";", geta($1));
}
| LETTER INCR
{
bundle(4, "l", $1, "d1+s", $1);
}
| INCR LETTER
{
bundle(4, "l", $2, "1+ds", $2);
}
| DECR LETTER
{
bundle(4, "l", $2, "1-ds", $2);
}
| LETTER DECR
{
bundle(4, "l", $1, "d1-s", $1);
}
| LETTER '[' e ']' INCR
{
bundle(7, $3, ";", geta($1), "d1+" ,$3, ":" ,geta($1));
}
| INCR LETTER '[' e ']'
{
bundle(7, $4, ";", geta($2), "1+d", $4, ":", geta($2));
}
| LETTER '[' e ']' DECR
{
bundle(7, $3, ";", geta($1), "d1-", $3, ":", geta($1));
}
| DECR LETTER '[' e ']'
{
bundle(7, $4, ";", geta($2), "1-d", $4, ":" ,geta($2));
}
| SCALE INCR
{
bundle(1, "Kd1+k");
}
| INCR SCALE
{
bundle(1, "K1+dk");
}
| SCALE DECR
{
bundle(1, "Kd1-k");
}
| DECR SCALE
{
bundle(1, "K1-dk");
}
| BASE INCR
{
bundle(1, "Id1+i");
}
| INCR BASE
{
bundle(1, "I1+di");
}
| BASE DECR
{
bundle(1, "Id1-i");
}
| DECR BASE
{
bundle(1, "I1-di");
}
| OBASE INCR
{
bundle(1, "Od1+o");
}
| INCR OBASE
{
bundle(1, "O1+do");
}
| OBASE DECR
{
bundle(1, "Od1-o");
}
| DECR OBASE
{
bundle(1, "O1-do");
}
| LETTER '(' cargs ')'
{
bundle(4, $3, "l", getf($1), "x");
}
| LETTER '(' ')'
{
bundle(3, "l", getf($1), "x");
}
| LETTER = {
bundle(2, "l", $1);
}
| LENGTH '(' e ')'
{
bundle(2, $3, "Z");
}
| SCALE '(' e ')'
{
bundle(2, $3, "X");
}
| '?'
{
bundle(1, "?");
}
| SQRT '(' e ')'
{
bundle(2, $3, "v");
}
| '~' LETTER
{
bundle(2, "L", $2);
}
| SCALE
{
bundle(1, "K");
}
| BASE
{
bundle(1, "I");
}
| OBASE
{
bundle(1, "O");
}
| '-' e
{
bundle(3, " 0", $2, "-");
}
| e '+' e
{
bundle(3, $1, $3, "+");
}
| e '-' e
{
bundle(3, $1, $3, "-");
}
| e '*' e
{
bundle(3, $1, $3, "*");
}
| e '/' e
{
bundle(3, $1, $3, "/");
}
| e '%' e
{
bundle(3, $1, $3, "%%");
}
| e '^' e
{
bundle(3, $1, $3, "^");
}
ase:
LETTER '=' e
{
bundle(3, $3, "ds", $1);
}
| LETTER '[' e ']' '=' e
{
bundle(5, $6, "d", $3, ":", geta($1));
}
| LETTER EQOP e
{
bundle(6, "l", $1, $3, $2, "ds", $1);
}
| LETTER '[' e ']' EQOP e
{
bundle(9, $3, ";", geta($1), $6, $5, "d", $3, ":", geta($1));
}
e:
ase
| nase
cargs:
eora
| cargs ',' eora
{
bundle(2, $1, $3);
}
eora:
e
| LETTER '[' ']'
{
bundle(2, "l", geta($1));
}
cons:
constant
{
*cp++ = 0;
}
constant:
'_'
{
$<cptr>$ = cp;
*cp++ = '_';
}
| DIGIT
{
$<cptr>$ = cp;
*cp++ = $1;
}
| constant DIGIT
{
*cp++ = $2;
}
crs:
=
{
$$ = cp;
*cp++ = '<';
*cp++ = crs/100+'0';
*cp++ = (crs%100)/10+'0';
*cp++ = crs%10+'0';
*cp++ = '>';
*cp++ = '\0';
if(crs++ >= 220) {
yyerror("program too big");
getout();
}
bstack[bindx++] = lev++;
}
def:
_DEFINE LETTER '('
{
$$ = getf($2);
pre = (int*)"";
post = (int*)"";
lev = 1;
bindx = 0;
bstack[bindx] = 0;
}
dargs:
| lora
{
pp((char*)$1);
}
| dargs ',' lora
{
pp((char*)$3);
}
dlets:
lora
{
tp((char*)$1);
}
| dlets ',' lora
{
tp((char*)$3);
}
lora:
LETTER
{
$<cptr>$=$1;
}
| LETTER '[' ']'
{
$$ = geta($1);
}
%%
int
yylex(void)
{
int c, ch;
restart:
c = getch();
peekc = -1;
while(c == ' ' || c == '\t')
c = getch();
if(c == '\\') {
getch();
goto restart;
}
if(c >= 'a' && c <= 'z') {
/* look ahead to look for reserved words */
peekc = getch();
if(peekc >= 'a' && peekc <= 'z') { /* must be reserved word */
if(c=='p' && peekc=='r') {
c = _PRINT;
goto skip;
}
if(c=='i' && peekc=='f') {
c = _IF;
goto skip;
}
if(c=='w' && peekc=='h') {
c = _WHILE;
goto skip;
}
if(c=='f' && peekc=='o') {
c = _FOR;
goto skip;
}
if(c=='s' && peekc=='q') {
c = SQRT;
goto skip;
}
if(c=='r' && peekc=='e') {
c = _RETURN;
goto skip;
}
if(c=='b' && peekc=='r') {
c = _BREAK;
goto skip;
}
if(c=='d' && peekc=='e') {
c = _DEFINE;
goto skip;
}
if(c=='s' && peekc=='c') {
c = SCALE;
goto skip;
}
if(c=='b' && peekc=='a') {
c = BASE;
goto skip;
}
if(c=='i' && peekc=='b') {
c = BASE;
goto skip;
}
if(c=='o' && peekc=='b') {
c = OBASE;
goto skip;
}
if(c=='d' && peekc=='i') {
c = FFF;
goto skip;
}
if(c=='a' && peekc=='u') {
c = _AUTO;
goto skip;
}
if(c=='l' && peekc=='e') {
c = LENGTH;
goto skip;
}
if(c=='q' && peekc=='u')
getout();
/* could not be found */
return ERROR;
skip: /* skip over rest of word */
peekc = -1;
for(;;) {
ch = getch();
if(ch < 'a' || ch > 'z')
break;
}
peekc = ch;
return c;
}
/* usual case; just one single letter */
yylval.cptr = letr[c-'a'];
return LETTER;
}
if((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) {
yylval.cc = c;
return DIGIT;
}
switch(c) {
case '.':
return DOT;
case '*':
yylval.cptr = "*";
return cpeek('=', EQOP, c);
case '%':
yylval.cptr = "%%";
return cpeek('=', EQOP, c);
case '^':
yylval.cptr = "^";
return cpeek('=', EQOP, c);
case '+':
ch = cpeek('=', EQOP, c);
if(ch == EQOP) {
yylval.cptr = "+";
return ch;
}
return cpeek('+', INCR, c);
case '-':
ch = cpeek('=', EQOP, c);
if(ch == EQOP) {
yylval.cptr = "-";
return ch;
}
return cpeek('-', DECR, c);
case '=':
return cpeek('=', EQ, '=');
case '<':
return cpeek('=', LE, '<');
case '>':
return cpeek('=', GE, '>');
case '!':
return cpeek('=', NE, '!');
case '/':
ch = cpeek('=', EQOP, c);
if(ch == EQOP) {
yylval.cptr = "/";
return ch;
}
if(peekc == '*') {
peekc = -1;
for(;;) {
ch = getch();
if(ch == '*') {
peekc = getch();
if(peekc == '/') {
peekc = -1;
goto restart;
}
}
}
}
return c;
case '"':
yylval.cptr = str;
while((c=getch()) != '"'){
*str++ = c;
if(str >= &string[999]){
yyerror("string space exceeded");
getout();
}
}
*str++ = 0;
return QSTR;
default:
return c;
}
}
int
cpeek(int c, int yes, int no)
{
peekc = getch();
if(peekc == c) {
peekc = -1;
return yes;
}
return no;
}
int
getch(void)
{
long ch;
loop:
ch = peekc;
if(ch < 0){
if(in == 0)
ch = -1;
else
ch = Bgetc(in);
}
peekc = -1;
if(ch >= 0)
return ch;
ifile++;
if(ifile > sargc) {
if(ifile >= sargc+2)
getout();
in = &stdin;
Binit(in, 0, OREAD);
ln = 0;
goto loop;
}
Bterm(in);
if((in = Bopen(sargv[ifile], OREAD)) != 0){
ln = 0;
ss = sargv[ifile];
goto loop;
}
yyerror("cannot open input file");
return 0; /* shut up ken */
}
int*
bundle(int a, ...)
{
int i, *p, *q;
p = &a;
i = *p++;
q = bsp_nxt;
if(bdebug)
fprint(2, "bundle %d elements at %lx\n", i, q);
while(i-- > 0) {
if(bsp_nxt >= &bspace[bsp_max])
yyerror("bundling space exceeded");
*bsp_nxt++ = *p++;
}
*bsp_nxt++ = 0;
yyval.iptr = q;
return q;
}
void
routput(int *p)
{
if(bdebug)
fprint(2, "routput(%lx)\n", p);
if(p >= &bspace[0] && p < &bspace[bsp_max]) {
/* part of a bundle */
while(*p != 0)
routput((int*)(*p++));
} else
Bprint(&stdout, (char*)p); /* character string */
}
void
output(int *p)
{
routput(p);
bsp_nxt = &bspace[0];
Bprint(&stdout, "\n");
Bflush(&stdout);
cp = cary;
crs = rcrs;
}
void
conout(int *p, char *s)
{
Bprint(&stdout, "[");
routput(p);
Bprint(&stdout, "]s%s\n", s);
Bflush(&stdout);
lev--;
}
void
yyerror(char *s, ...)
{
if(ifile > sargc)
ss = "teletype";
Bprint(&stdout, "c[%s on line %d, %s]pc\n", s, ln+1, ss);
Bflush(&stdout);
cp = cary;
crs = rcrs;
bindx = 0;
lev = 0;
bsp_nxt = &bspace[0];
}
void
pp(char *s)
{
/* puts the relevant stuff on pre and post for the letter s */
bundle(3, "S", s, pre);
pre = yyval.iptr;
bundle(4, post, "L", s, "s.");
post = yyval.iptr;
}
void
tp(char *s)
{
/* same as pp, but for temps */
bundle(3, "0S", s, pre);
pre = yyval.iptr;
bundle(4, post, "L", s, "s.");
post = yyval.iptr;
}
void
yyinit(int argc, char **argv)
{
Binit(&stdout, 1, OWRITE);
sargv = argv;
sargc = argc - 1;
if(sargc == 0) {
in = &stdin;
Binit(in, 0, OREAD);
} else if((in = Bopen(sargv[1], OREAD)) == 0)
yyerror("cannot open input file");
ifile = 1;
ln = 0;
ss = sargv[1];
}
void
getout(void)
{
Bprint(&stdout, "q");
Bflush(&stdout);
exits(0);
}
int*
getf(char *p)
{
return (int*)funtab[*p - 'a'];
}
int*
geta(char *p)
{
return (int*)atab[*p - 'a'];
}
void
main(int argc, char **argv)
{
int p[2];
while(argc > 1 && *argv[1] == '-') {
switch(argv[1][1]) {
case 'd':
bdebug++;
break;
case 'c':
cflag++;
break;
case 'l':
lflag++;
break;
case 's':
sflag++;
break;
default:
fprint(2, "Usage: bc [-l] [-c] [file ...]\n");
exits("usage");
}
argc--;
argv++;
}
if(lflag) {
argv--;
argc++;
argv[1] = unsharp("#9/lib/bclib");
}
if(cflag) {
yyinit(argc, argv);
for(;;)
yyparse();
/* exits(0); */
}
pipe(p);
if(fork() == 0) {
dup(p[1], 1);
close(p[0]);
close(p[1]);
yyinit(argc, argv);
for(;;)
yyparse();
}
dup(p[0], 0);
close(p[0]);
close(p[1]);
execlp("dc", "dc", (char*)0);
}

36
cat/Makefile Normal file
View File

@@ -0,0 +1,36 @@
# cat - cat unix port from plan9
# Depends on ../lib9
include ../config.mk
TARG = cat
OFILES = cat.o
MANFILES = cat.1
all: ${TARG}
@echo built ${TARG}
install: ${TARG}
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/
@chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG}
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1
@chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES}
uninstall:
rm -f ${DESTDIR}${PREFIX}/bin/${TARG}
rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES}
.c.o:
@echo CC $*.c
@${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c
clean:
rm -f ${OFILES} ${TARG}
${TARG}: ${OFILES}
@echo LD ${TARG}
@${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9

108
cat/cat.1 Normal file
View File

@@ -0,0 +1,108 @@
.TH CAT 1
.SH NAME
cat, read, nobs \- catenate files
.SH SYNOPSIS
.B cat
[
.I file ...
]
.br
.B read
[
.B -m
] [
.B -n
.I nline
] [
.I file ...
]
.br
.B nobs
[
.I file ...
]
.SH DESCRIPTION
.I Cat
reads each
.I file
in sequence and writes it on the standard output.
Thus
.IP
.L
cat file
.LP
prints a file and
.IP
.L
cat file1 file2 >file3
.LP
concatenates the first two files and places the result
on the third.
.PP
If no
.I file
is given,
.I cat
reads from the standard input.
Output is buffered in blocks matching the input.
.PP
.I Read
copies to standard output exactly one line from the named
.IR file ,
default standard input.
It is useful in interactive
.IR rc (1)
scripts.
.PP
The
.B -m
flag causes it to continue reading and writing multiple lines until end of file;
.B -n
causes it to read no more than
.I nline
lines.
.PP
.I Read
always executes a single
.B write
for each line of input, which can be helpful when
preparing input to programs that expect line-at-a-time data.
It never reads any more data from the input than it prints to the output.
.PP
.I Nobs
copies the named files to
standard output except that it removes all backspace
characters and the characters that precede them.
It is useful to use as
.B $PAGER
with the Unix version of
.IR man (1)
when run inside a
.I win
(see
.IR acme (1))
window.
.SH SOURCE
.B \*9/src/cmd/cat.c
.br
.B \*9/src/cmd/read.c
.br
.B \*9/bin/nobs
.SH SEE ALSO
.IR cp (1)
.SH DIAGNOSTICS
.I Read
exits with status
.B eof
on end of file or, in the
.B -n
case, if it doesn't read
.I nlines
lines.
.SH BUGS
Beware of
.L "cat a b >a"
and
.LR "cat a b >b" ,
which
destroy input files before reading them.

36
cat/cat.c Normal file
View File

@@ -0,0 +1,36 @@
#include <u.h>
#include <libc.h>
void
cat(int f, char *s)
{
char buf[8192];
long n;
while((n=read(f, buf, (long)sizeof buf))>0)
if(write(1, buf, n)!=n)
sysfatal("write error copying %s: %r", s);
if(n < 0)
sysfatal("error reading %s: %r", s);
}
void
main(int argc, char *argv[])
{
int f, i;
argv0 = "cat";
if(argc == 1)
cat(0, "<stdin>");
else for(i=1; i<argc; i++){
f = open(argv[i], OREAD);
if(f < 0)
sysfatal("can't open %s: %r", argv[i]);
else{
cat(f, argv[i]);
close(f);
}
}
exits(0);
}

36
cleanname/Makefile Normal file
View File

@@ -0,0 +1,36 @@
# cleanname - cleanname unix port from plan9
# Depends on ../lib9
include ../config.mk
TARG = cleanname
OFILES = cleanname.o
MANFILES = cleanname.1
all: ${TARG}
@echo built ${TARG}
install: ${TARG}
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/
@chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG}
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1
@chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES}
uninstall:
rm -f ${DESTDIR}${PREFIX}/bin/${TARG}
rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES}
.c.o:
@echo CC $*.c
@${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c
clean:
rm -f ${OFILES} ${TARG}
${TARG}: ${OFILES}
@echo LD ${TARG}
@${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9

32
cleanname/cleanname.1 Normal file
View File

@@ -0,0 +1,32 @@
.TH CLEANNAME 1
.SH NAME
cleanname \- clean a path name
.SH SYNOPSIS
.B cleanname
[
.B -d
.I pwd
]
.I names ...
.SH DESCRIPTION
For each file name argument,
.IR cleanname ,
by lexical processing only,
prints the shortest equivalent string that names the same
(possibly hypothetical) file.
It eliminates multiple and trailing slashes, and it lexically
interprets
.B .
and
.B ..
directory components in the name.
If the
.B -d
option is present,
unrooted names are prefixed with
.IB pwd /
before processing.
.SH SOURCE
.B \*9/src/cmd/cleanname.c
.SH SEE ALSO
.IR cleanname (3).

44
cleanname/cleanname.c Normal file
View File

@@ -0,0 +1,44 @@
#include <u.h>
#include <libc.h>
void
main(int argc, char **argv)
{
char *dir;
char *name;
int i;
dir = nil;
ARGBEGIN{
case 'd':
if((dir=ARGF()) == nil)
goto Usage;
break;
default:
goto Usage;
}ARGEND;
if(argc < 1) {
Usage:
fprint(2, "usage: cleanname [-d pwd] name...\n");
exits("usage");
}
for(i=0; i<argc; i++) {
if(dir == nil || argv[i][0] == '/') {
cleanname(argv[i]);
print("%s\n", argv[i]);
} else {
name = malloc(strlen(argv[i])+1+strlen(dir)+1);
if(name == nil) {
fprint(2, "cleanname: out of memory\n");
exits("out of memory");
}
sprint(name, "%s/%s", dir, argv[i]);
cleanname(name);
print("%s\n", name);
free(name);
}
}
exits(0);
}

15
config.mk Normal file
View File

@@ -0,0 +1,15 @@
# Customize to fit your system
# paths
PREFIX = /usr/local/9
MANPREFIX = ${PREFIX}/share/man
# flags
VERSION = 20051114
CFLAGS = -Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -Os -c -I. -DPREFIX="\"${PREFIX}\""
LDFLAGS = -static
# compiler
AR = ar rc
CC = cc
YACC = ../yacc/9yacc

36
date/Makefile Normal file
View File

@@ -0,0 +1,36 @@
# date - date unix port from plan9
# Depends on ../lib9
include ../config.mk
TARG = date
OFILES = date.o
MANFILES = date.1
all: ${TARG}
@echo built ${TARG}
install: ${TARG}
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/
@chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG}
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1
@chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES}
uninstall:
rm -f ${DESTDIR}${PREFIX}/bin/${TARG}
rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES}
.c.o:
@echo CC $*.c
@${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c
clean:
rm -f ${OFILES} ${TARG}
${TARG}: ${OFILES}
@echo LD ${TARG}
@${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9

58
date/date.1 Normal file
View File

@@ -0,0 +1,58 @@
.TH DATE 1
.SH NAME
date \- date and time
.SH SYNOPSIS
.B date
[
.I option
] [
.I seconds
]
.\" .br
.\" .B clock
.SH DESCRIPTION
Print the date, in the format
.PP
.B
Tue Aug 16 17:03:52 CDT 1977
.PP
The options are
.TP
.B -u
Report Greenwich Mean Time (GMT) rather than local time.
.TP
.B -n
Report the date as the number of seconds since the
epoch, 00:00:00 GMT, January 1, 1970.
.PP
The conversion from Greenwich Mean Time to local time depends on the
.B $timezone
environment variable; see
.IR ctime (3).
.PP
If the optional argument
.I seconds
is present, it is used as the time to convert rather than
the real time.
.\" .SH FILES
.\" .TF /adm/timezone/local
.\" .TP
.\" .B /env/timezone
.\" Current timezone name and adjustments.
.\" .TP
.\" .B /adm/timezone
.\" A directory containing timezone tables.
.\" .TP
.\" .B /adm/timezone/local
.\" Default timezone file, copied by
.\" .IR init (8)
.\" into
.\" .BR /env/timezone .
.\" .PD
.\" .PP
.\" .I Clock
.\" draws a simple analog clock in its window.
.SH SOURCE
.B \*9/src/cmd/date.c
.\" .br
.\" .B \*9/src/cmd/draw/clock.c

30
date/date.c Normal file
View File

@@ -0,0 +1,30 @@
#include <u.h>
#include <libc.h>
int uflg, nflg;
void
main(int argc, char *argv[])
{
ulong now;
ARGBEGIN{
case 'n': nflg = 1; break;
case 'u': uflg = 1; break;
default: fprint(2, "usage: date [-un] [seconds]\n"); exits("usage");
}ARGEND
if(argc == 1)
now = strtoul(*argv, 0, 0);
else
now = time(0);
if(nflg)
print("%ld\n", now);
else if(uflg)
print("%s", asctime(gmtime(now)));
else
print("%s", ctime(now));
exits(0);
}

36
echo/Makefile Normal file
View File

@@ -0,0 +1,36 @@
# echo - echo unix port from plan9
# Depends on ../lib9
include ../config.mk
TARG = echo
OFILES = echo.o
MANFILES = echo.1
all: ${TARG}
@echo built ${TARG}
install: ${TARG}
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/
@chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG}
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1
@chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES}
uninstall:
rm -f ${DESTDIR}${PREFIX}/bin/${TARG}
rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES}
.c.o:
@echo CC $*.c
@${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c
clean:
rm -f ${OFILES} ${TARG}
${TARG}: ${OFILES}
@echo LD ${TARG}
@${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9

26
echo/echo.1 Normal file
View File

@@ -0,0 +1,26 @@
.TH ECHO 1
.SH NAME
echo \- print arguments
.SH SYNOPSIS
.B echo
[
.B -n
]
[
.I arg ...
]
.SH DESCRIPTION
.I Echo
writes its arguments separated by blanks and terminated by
a newline on the standard output.
Option
.B -n
suppresses the newline.
.SH SOURCE
.B \*9/src/cmd/echo.c
.SH DIAGNOSTICS
If
.I echo
draws an error while writing to standard output, the exit status is
.LR "write error" .
Otherwise the exit status is empty.

38
echo/echo.c Normal file
View File

@@ -0,0 +1,38 @@
#include <u.h>
#include <libc.h>
void
main(int argc, char *argv[])
{
int nflag;
int i, len;
char *buf, *p;
nflag = 0;
if(argc > 1 && strcmp(argv[1], "-n") == 0)
nflag = 1;
len = 1;
for(i = 1+nflag; i < argc; i++)
len += strlen(argv[i])+1;
buf = malloc(len);
if(buf == 0)
exits("no memory");
p = buf;
for(i = 1+nflag; i < argc; i++){
strcpy(p, argv[i]);
p += strlen(p);
if(i < argc-1)
*p++ = ' ';
}
if(!nflag)
*p++ = '\n';
if(write(1, buf, p-buf) < 0)
fprint(2, "echo: write error: %r\n");
exits((char *)0);
}

46
grep/Makefile Normal file
View File

@@ -0,0 +1,46 @@
# grep - grep unix port from plan9
# Depends on ../lib9
include ../config.mk
TARG = grep
OFILES = y.tab.o main.o comp.o sub.o
YFILES = grep.y
MANFILES = grep.1
all:
@if [ ! -f y.tab.c ]; then \
${MAKE} -f Makefile depend;\
fi
@${MAKE} -f Makefile ${TARG}
@echo built ${TARG}
depend:
@echo YACC ${YFILES}
@${YACC} -d ${YFILES}
install: ${TARG}
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/
@chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG}
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1
@chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES}
uninstall:
rm -f ${DESTDIR}${PREFIX}/bin/${TARG}
rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES}
.c.o:
@echo CC $*.c
@${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c
clean:
rm -f ${OFILES} ${TARG} y.tab.c y.tab.h
${TARG}: ${OFILES}
@echo LD ${TARG}
@${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9

277
grep/comp.c Normal file
View File

@@ -0,0 +1,277 @@
#include "grep.h"
/*
* incremental compiler.
* add the branch c to the
* state s.
*/
void
increment(State *s, int c)
{
int i;
State *t, **tt;
Re *re1, *re2;
nfollow = 0;
gen++;
matched = 0;
for(i=0; i<s->count; i++)
fol1(s->re[i], c);
qsort(follow, nfollow, sizeof(*follow), fcmp);
for(tt=&state0; t = *tt;) {
if(t->count > nfollow) {
tt = &t->linkleft;
goto cont;
}
if(t->count < nfollow) {
tt = &t->linkright;
goto cont;
}
for(i=0; i<nfollow; i++) {
re1 = t->re[i];
re2 = follow[i];
if(re1 > re2) {
tt = &t->linkleft;
goto cont;
}
if(re1 < re2) {
tt = &t->linkright;
goto cont;
}
}
if(!!matched && !t->match) {
tt = &t->linkleft;
goto cont;
}
if(!matched && !!t->match) {
tt = &t->linkright;
goto cont;
}
s->next[c] = t;
return;
cont:;
}
t = sal(nfollow);
*tt = t;
for(i=0; i<nfollow; i++) {
re1 = follow[i];
t->re[i] = re1;
}
s->next[c] = t;
t->match = matched;
}
int
fcmp(const void *va, const void *vb)
{
Re **aa, **bb;
Re *a, *b;
aa = (Re**)va;
bb = (Re**)vb;
a = *aa;
b = *bb;
if(a > b)
return 1;
if(a < b)
return -1;
return 0;
}
void
fol1(Re *r, int c)
{
Re *r1;
loop:
if(r->gen == gen)
return;
if(nfollow >= maxfollow)
error("nfollow");
r->gen = gen;
switch(r->type) {
default:
error("fol1");
case Tcase:
if(c >= 0 && c < 256)
if(r1 = r->u.cases[c])
follow[nfollow++] = r1;
if(r = r->next)
goto loop;
break;
case Talt:
case Tor:
fol1(r->u.alt, c);
r = r->next;
goto loop;
case Tbegin:
if(c == '\n' || c == Cbegin)
follow[nfollow++] = r->next;
break;
case Tend:
if(c == '\n')
matched = 1;
break;
case Tclass:
if(c >= r->u.x.lo && c <= r->u.x.hi)
follow[nfollow++] = r->next;
break;
}
}
Rune tab1[] =
{
0x007f,
0x07ff,
};
Rune tab2[] =
{
0x003f,
0x0fff,
};
Re2
rclass(Rune p0, Rune p1)
{
char xc0[6], xc1[6];
int i, n, m;
Re2 x;
if(p0 > p1)
return re2char(0xff, 0xff); // no match
/*
* bust range into same length
* character sequences
*/
for(i=0; i<nelem(tab1); i++) {
m = tab1[i];
if(p0 <= m && p1 > m)
return re2or(rclass(p0, m), rclass(m+1, p1));
}
/*
* bust range into part of a single page
* or into full pages
*/
for(i=0; i<nelem(tab2); i++) {
m = tab2[i];
if((p0 & ~m) != (p1 & ~m)) {
if((p0 & m) != 0)
return re2or(rclass(p0, p0|m), rclass((p0|m)+1, p1));
if((p1 & m) != m)
return re2or(rclass(p0, (p1&~m)-1), rclass(p1&~m, p1));
}
}
n = runetochar(xc0, &p0);
i = runetochar(xc1, &p1);
if(i != n)
error("length");
x = re2char(xc0[0], xc1[0]);
for(i=1; i<n; i++)
x = re2cat(x, re2char(xc0[i], xc1[i]));
return x;
}
int
pcmp(const void *va, const void *vb)
{
int n;
Rune *a, *b;
a = (Rune*)va;
b = (Rune*)vb;
n = a[0] - b[0];
if(n)
return n;
return a[1] - b[1];
}
/*
* convert character chass into
* run-pair ranges of matches.
* this is 10646/utf specific and
* needs to be changed for some
* other input character set.
* this is the key to a fast
* regular search of characters
* by looking at sequential bytes.
*/
Re2
re2class(char *s)
{
Rune pairs[200], *p, *q, ov;
int nc;
Re2 x;
nc = 0;
if(*s == '^') {
nc = 1;
s++;
}
p = pairs;
s += chartorune(p, s);
for(;;) {
if(*p == '\\')
s += chartorune(p, s);
if(*p == 0)
break;
p[1] = *p;
p += 2;
s += chartorune(p, s);
if(*p != '-')
continue;
s += chartorune(p, s);
if(*p == '\\')
s += chartorune(p, s);
if(*p == 0)
break;
p[-1] = *p;
s += chartorune(p, s);
}
*p = 0;
qsort(pairs, (p-pairs)/2, 2*sizeof(*pairs), pcmp);
q = pairs;
for(p=pairs+2; *p; p+=2) {
if(p[0] > p[1])
continue;
if(p[0] > q[1] || p[1] < q[0]) {
q[2] = p[0];
q[3] = p[1];
q += 2;
continue;
}
if(p[0] < q[0])
q[0] = p[0];
if(p[1] > q[1])
q[1] = p[1];
}
q[2] = 0;
p = pairs;
if(nc) {
x = rclass(0, p[0]-1);
ov = p[1]+1;
for(p+=2; *p; p+=2) {
x = re2or(x, rclass(ov, p[0]-1));
ov = p[1]+1;
}
x = re2or(x, rclass(ov, 0xffff));
} else {
x = rclass(p[0], p[1]);
for(p+=2; *p; p+=2)
x = re2or(x, rclass(p[0], p[1]));
}
return x;
}

124
grep/grep.1 Normal file
View File

@@ -0,0 +1,124 @@
.TH GREP 1
.SH NAME
grep, g \- search a file for a pattern
.SH SYNOPSIS
.B grep
[
.I option ...
]
.I pattern
[
.I file ...
]
.PP
.B g
[
.I option ...
]
.I pattern
[
.I file ...
]
.SH DESCRIPTION
.I Grep\^
searches the input
.I files\^
(standard input default)
for lines that match the
.IR pattern ,
a regular expression as defined in
.IR regexp (7)
with the addition of a newline character as an alternative
(substitute for
.BR | )
with lowest precedence.
Normally, each line matching the pattern is `selected',
and each selected line is copied to the standard output.
The options are
.TP
.B -c
Print only a count of matching lines.
.PD 0
.TP
.B -h
Do not print file name tags (headers) with output lines.
.TP
.B -e
The following argument is taken as a
.IR pattern .
This option makes it easy to specify patterns that
might confuse argument parsing, such as
.BR -n .
.TP
.B -i
Ignore alphabetic case distinctions. The implementation
folds into lower case all letters in the pattern and input before
interpretation. Matched lines are printed in their original form.
.TP
.B -l
(ell) Print the names of files with selected lines; don't print the lines.
.TP
.B -L
Print the names of files with no selected lines;
the converse of
.BR -l .
.TP
.B -n
Mark each printed line with its line number counted in its file.
.TP
.B -s
Produce no output, but return status.
.TP
.B -v
Reverse: print lines that do not match the pattern.
.TP
.B -f
The pattern argument is the name of a file containing regular
expressions one per line.
.TP
.B -b
Don't buffer the output: write each output line as soon as it is discovered.
.PD
.PP
Output lines are tagged by file name when there is more than one
input file.
(To force this tagging, include
.B /dev/null
as a file name argument.)
.PP
Care should be taken when
using the shell metacharacters
.B $*[^|()=\e
and newline
in
.IR pattern ;
it is safest to enclose the
entire expression
in single quotes
.BR \&\|' \|.\|.\|.\| ' .
An expression starting with '*'
will treat the rest of the expression
as literal characters.
.PP
.I G
invokes grep with
.B -n
and forces tagging of output lines by file name.
If no files are listed, it searches all files matching
.IP
.EX
*.C *.b *.c *.h *.m *.cc *.java *.cgi *.pl *.py *.tex *.ms
.EE
.SH SOURCE
.B \*9/src/cmd/grep
.br
.B \*9/bin/g
.SH SEE ALSO
.IR ed (1),
.IR awk (1),
.IR sed (1),
.IR sam (1),
.IR regexp (7)
.SH DIAGNOSTICS
Exit status is null if any lines are selected,
or non-null when no lines are selected or an error occurs.

125
grep/grep.h Normal file
View File

@@ -0,0 +1,125 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#ifndef EXTERN
#define EXTERN extern
#endif
typedef struct Re Re;
typedef struct Re2 Re2;
typedef struct State State;
struct State
{
int count;
int match;
Re** re;
State* linkleft;
State* linkright;
State* next[256];
};
struct Re2
{
Re* beg;
Re* end;
};
struct Re
{
uchar type;
ushort gen;
union
{
Re* alt; /* Talt */
Re** cases; /* case */
struct /* class */
{
Rune lo;
Rune hi;
} x;
Rune val; /* char */
} u;
Re* next;
};
enum
{
Talt = 1,
Tbegin,
Tcase,
Tclass,
Tend,
Tor,
Caselim = 7,
Nhunk = 1<<16,
Cbegin = 0x10000,
Flshcnt = (1<<9)-1,
Cflag = 1<<0,
Hflag = 1<<1,
Iflag = 1<<2,
Llflag = 1<<3,
LLflag = 1<<4,
Nflag = 1<<5,
Sflag = 1<<6,
Vflag = 1<<7,
Bflag = 1<<8
};
EXTERN union
{
char string[16*1024];
struct
{
/*
* if a line requires multiple reads, we keep shifting
* buf down into pre and then do another read into
* buf. so you'll get the last 16-32k of the matching line.
* if h were smaller than buf you'd get a suffix of the
* line with a hole cut out.
*/
uchar pre[16*1024]; /* to save to previous '\n' */
uchar buf[16*1024]; /* input buffer */
} u;
} u;
EXTERN char *filename;
EXTERN Biobuf bout;
EXTERN char flags[256];
EXTERN Re** follow;
EXTERN ushort gen;
EXTERN char* input;
EXTERN long lineno;
EXTERN int literal;
EXTERN int matched;
EXTERN long maxfollow;
EXTERN long nfollow;
EXTERN int peekc;
EXTERN Biobuf* rein;
EXTERN State* state0;
EXTERN Re2 topre;
extern Re* addcase(Re*);
extern void appendnext(Re*, Re*);
extern void error(char*);
extern int fcmp(const void*, const void*); /* (Re**, Re**) */
extern void fol1(Re*, int);
extern int getrec(void);
extern void increment(State*, int);
#define initstate grepinitstate
extern State* initstate(Re*);
extern void* mal(int);
extern void patchnext(Re*, Re*);
extern Re* ral(int);
extern Re2 re2cat(Re2, Re2);
extern Re2 re2class(char*);
extern Re2 re2or(Re2, Re2);
extern Re2 re2char(int, int);
extern Re2 re2star(Re2);
extern State* sal(int);
extern int search(char*, int);
extern void str2top(char*);
extern int yyparse(void);
extern void reprint(char*, Re*);
extern void yyerror(char*, ...);

226
grep/grep.y Normal file
View File

@@ -0,0 +1,226 @@
%{
#include "grep.h"
%}
%union
{
int val;
char* str;
Re2 re;
}
%type <re> expr prog
%type <re> expr0 expr1 expr2 expr3 expr4
%token <str> LCLASS
%token <val> LCHAR
%token LLPAREN LRPAREN LALT LSTAR LPLUS LQUES
%token LBEGIN LEND LDOT LBAD LNEWLINE
%%
prog:
expr newlines
{
$$.beg = ral(Tend);
$$.end = $$.beg;
$$ = re2cat(re2star(re2or(re2char(0x00, '\n'-1), re2char('\n'+1, 0xff))), $$);
$$ = re2cat($1, $$);
$$ = re2cat(re2star(re2char(0x00, 0xff)), $$);
topre = $$;
}
expr:
expr0
| expr newlines expr0
{
$$ = re2or($1, $3);
}
expr0:
expr1
| LSTAR { literal = 1; } expr1
{
$$ = $3;
}
expr1:
expr2
| expr1 LALT expr2
{
$$ = re2or($1, $3);
}
expr2:
expr3
| expr2 expr3
{
$$ = re2cat($1, $2);
}
expr3:
expr4
| expr3 LSTAR
{
$$ = re2star($1);
}
| expr3 LPLUS
{
$$.beg = ral(Talt);
patchnext($1.end, $$.beg);
$$.beg->u.alt = $1.beg;
$$.end = $$.beg;
$$.beg = $1.beg;
}
| expr3 LQUES
{
$$.beg = ral(Talt);
$$.beg->u.alt = $1.beg;
$$.end = $1.end;
appendnext($$.end, $$.beg);
}
expr4:
LCHAR
{
$$.beg = ral(Tclass);
$$.beg->u.x.lo = $1;
$$.beg->u.x.hi = $1;
$$.end = $$.beg;
}
| LBEGIN
{
$$.beg = ral(Tbegin);
$$.end = $$.beg;
}
| LEND
{
$$.beg = ral(Tend);
$$.end = $$.beg;
}
| LDOT
{
$$ = re2class("^\n");
}
| LCLASS
{
$$ = re2class($1);
}
| LLPAREN expr1 LRPAREN
{
$$ = $2;
}
newlines:
LNEWLINE
| newlines LNEWLINE
%%
void
yyerror(char *e, ...)
{
if(filename)
fprint(2, "grep: %s:%ld: %s\n", filename, lineno, e);
else
fprint(2, "grep: %s\n", e);
exits("syntax");
}
int
yylex(void)
{
char *q, *eq;
int c, s;
if(peekc) {
s = peekc;
peekc = 0;
return s;
}
c = getrec();
if(literal) {
if(c != 0 && c != '\n') {
yylval.val = c;
return LCHAR;
}
literal = 0;
}
switch(c) {
default:
yylval.val = c;
s = LCHAR;
break;
case '\\':
c = getrec();
yylval.val = c;
s = LCHAR;
if(c == '\n')
s = LNEWLINE;
break;
case '[':
goto getclass;
case '(':
s = LLPAREN;
break;
case ')':
s = LRPAREN;
break;
case '|':
s = LALT;
break;
case '*':
s = LSTAR;
break;
case '+':
s = LPLUS;
break;
case '?':
s = LQUES;
break;
case '^':
s = LBEGIN;
break;
case '$':
s = LEND;
break;
case '.':
s = LDOT;
break;
case 0:
peekc = -1;
case '\n':
s = LNEWLINE;
break;
}
return s;
getclass:
q = u.string;
eq = q + nelem(u.string) - 5;
c = getrec();
if(c == '^') {
q[0] = '^';
q[1] = '\n';
q[2] = '-';
q[3] = '\n';
q += 4;
c = getrec();
}
for(;;) {
if(q >= eq)
error("class too long");
if(c == ']' || c == 0)
break;
if(c == '\\') {
*q++ = c;
c = getrec();
if(c == 0)
break;
}
*q++ = c;
c = getrec();
}
*q = 0;
if(c == 0)
return LBAD;
yylval.str = u.string;
return LCLASS;
}

263
grep/main.c Normal file
View File

@@ -0,0 +1,263 @@
#define EXTERN
#include "grep.h"
char *validflags = "bchiLlnsv";
void
usage(void)
{
fprint(2, "usage: grep [-%s] [-f file] [-e expr] [file ...]\n", validflags);
exits("usage");
}
void
main(int argc, char *argv[])
{
int i, status;
ARGBEGIN {
default:
if(utfrune(validflags, ARGC()) == nil)
usage();
flags[ARGC()]++;
break;
case 'E': /* ignore, turns gnu grep into egrep */
break;
case 'e':
flags['e']++;
lineno = 0;
str2top(ARGF());
break;
case 'f':
flags['f']++;
filename = ARGF();
rein = Bopen(filename, OREAD);
if(rein == 0) {
fprint(2, "grep: can't open %s: %r\n", filename);
exits("open");
}
lineno = 1;
str2top(filename);
break;
} ARGEND
if(flags['f'] == 0 && flags['e'] == 0) {
if(argc <= 0)
usage();
str2top(argv[0]);
argc--;
argv++;
}
follow = mal(maxfollow*sizeof(*follow));
state0 = initstate(topre.beg);
Binit(&bout, 1, OWRITE);
switch(argc) {
case 0:
status = search(0, 0);
break;
case 1:
status = search(argv[0], 0);
break;
default:
status = 0;
for(i=0; i<argc; i++)
status |= search(argv[i], Hflag);
break;
}
if(status)
exits(0);
exits("no matches");
}
int
search(char *file, int flag)
{
State *s, *ns;
int c, fid, eof, nl, empty;
long count, lineno, n;
uchar *elp, *lp, *bol;
if(file == 0) {
file = "stdin";
fid = 0;
flag |= Bflag;
} else
fid = open(file, OREAD);
if(fid < 0) {
fprint(2, "grep: can't open %s: %r\n", file);
return 0;
}
if(flags['b'])
flag ^= Bflag; /* dont buffer output */
if(flags['c'])
flag |= Cflag; /* count */
if(flags['h'])
flag &= ~Hflag; /* do not print file name in output */
if(flags['i'])
flag |= Iflag; /* fold upper-lower */
if(flags['l'])
flag |= Llflag; /* print only name of file if any match */
if(flags['L'])
flag |= LLflag; /* print only name of file if any non match */
if(flags['n'])
flag |= Nflag; /* count only */
if(flags['s'])
flag |= Sflag; /* status only */
if(flags['v'])
flag |= Vflag; /* inverse match */
s = state0;
lineno = 0;
count = 0;
eof = 0;
empty = 1;
nl = 0;
lp = u.u.buf;
bol = lp;
loop0:
n = lp-bol;
if(n > sizeof(u.u.pre))
n = sizeof(u.u.pre);
memmove(u.u.buf-n, bol, n);
bol = u.u.buf-n;
n = read(fid, u.u.buf, sizeof(u.u.buf));
/* if file has no final newline, simulate one to emit matches to last line */
if(n > 0) {
empty = 0;
nl = u.u.buf[n-1]=='\n';
} else {
if(n < 0){
fprint(2, "grep: read error on %s: %r\n", file);
return count != 0;
}
if(!eof && !nl && !empty) {
u.u.buf[0] = '\n';
n = 1;
eof = 1;
}
}
if(n <= 0) {
close(fid);
if(flag & Cflag) {
if(flag & Hflag)
Bprint(&bout, "%s:", file);
Bprint(&bout, "%ld\n", count);
}
if(((flag&Llflag) && count != 0) || ((flag&LLflag) && count == 0))
Bprint(&bout, "%s\n", file);
Bflush(&bout);
return count != 0;
}
lp = u.u.buf;
elp = lp+n;
if(flag & Iflag)
goto loopi;
/*
* normal character loop
*/
loop:
c = *lp;
ns = s->next[c];
if(ns == 0) {
increment(s, c);
goto loop;
}
// if(flags['2'])
// if(s->match)
// print("%d: %.2x**\n", s, c);
// else
// print("%d: %.2x\n", s, c);
lp++;
s = ns;
if(c == '\n') {
lineno++;
if(!!s->match == !(flag&Vflag)) {
count++;
if(flag & (Cflag|Sflag|Llflag|LLflag))
goto cont;
if(flag & Hflag)
Bprint(&bout, "%s:", file);
if(flag & Nflag)
Bprint(&bout, "%ld: ", lineno);
/* suppress extra newline at EOF unless we are labeling matches with file name */
Bwrite(&bout, bol, lp-bol-(eof && !(flag&Hflag)));
if(flag & Bflag)
Bflush(&bout);
}
if((lineno & Flshcnt) == 0)
Bflush(&bout);
cont:
bol = lp;
}
if(lp != elp)
goto loop;
goto loop0;
/*
* character loop for -i flag
* for speed
*/
loopi:
c = *lp;
if(c >= 'A' && c <= 'Z')
c += 'a'-'A';
ns = s->next[c];
if(ns == 0) {
increment(s, c);
goto loopi;
}
lp++;
s = ns;
if(c == '\n') {
lineno++;
if(!!s->match == !(flag&Vflag)) {
count++;
if(flag & (Cflag|Sflag|Llflag|LLflag))
goto conti;
if(flag & Hflag)
Bprint(&bout, "%s:", file);
if(flag & Nflag)
Bprint(&bout, "%ld: ", lineno);
/* suppress extra newline at EOF unless we are labeling matches with file name */
Bwrite(&bout, bol, lp-bol-(eof && !(flag&Hflag)));
if(flag & Bflag)
Bflush(&bout);
}
if((lineno & Flshcnt) == 0)
Bflush(&bout);
conti:
bol = lp;
}
if(lp != elp)
goto loopi;
goto loop0;
}
State*
initstate(Re *r)
{
State *s;
int i;
addcase(r);
if(flags['1'])
reprint("r", r);
nfollow = 0;
gen++;
fol1(r, Cbegin);
follow[nfollow++] = r;
qsort(follow, nfollow, sizeof(*follow), fcmp);
s = sal(nfollow);
for(i=0; i<nfollow; i++)
s->re[i] = follow[i];
return s;
}

317
grep/sub.c Normal file
View File

@@ -0,0 +1,317 @@
#include "grep.h"
void*
mal(int n)
{
static char *s;
static int m = 0;
void *v;
n = (n+3) & ~3;
if(m < n) {
if(n > Nhunk) {
v = sbrk(n);
memset(v, 0, n);
return v;
}
s = sbrk(Nhunk);
m = Nhunk;
}
v = s;
s += n;
m -= n;
memset(v, 0, n);
return v;
}
State*
sal(int n)
{
State *s;
s = mal(sizeof(*s));
// s->next = mal(256*sizeof(*s->next));
s->count = n;
s->re = mal(n*sizeof(*state0->re));
return s;
}
Re*
ral(int type)
{
Re *r;
r = mal(sizeof(*r));
r->type = type;
maxfollow++;
return r;
}
void
error(char *s)
{
fprint(2, "grep: internal error: %s\n", s);
exits(s);
}
int
countor(Re *r)
{
int n;
n = 0;
loop:
switch(r->type) {
case Tor:
n += countor(r->u.alt);
r = r->next;
goto loop;
case Tclass:
return n + r->u.x.hi - r->u.x.lo + 1;
}
return n;
}
Re*
oralloc(int t, Re *r, Re *b)
{
Re *a;
if(b == 0)
return r;
a = ral(t);
a->u.alt = r;
a->next = b;
return a;
}
void
case1(Re *c, Re *r)
{
int n;
loop:
switch(r->type) {
case Tor:
case1(c, r->u.alt);
r = r->next;
goto loop;
case Tclass: /* add to character */
for(n=r->u.x.lo; n<=r->u.x.hi; n++)
c->u.cases[n] = oralloc(Tor, r->next, c->u.cases[n]);
break;
default: /* add everything unknown to next */
c->next = oralloc(Talt, r, c->next);
break;
}
}
Re*
addcase(Re *r)
{
int i, n;
Re *a;
if(r->gen == gen)
return r;
r->gen = gen;
switch(r->type) {
default:
error("addcase");
case Tor:
n = countor(r);
if(n >= Caselim) {
a = ral(Tcase);
a->u.cases = mal(256*sizeof(*a->u.cases));
case1(a, r);
for(i=0; i<256; i++)
if(a->u.cases[i]) {
r = a->u.cases[i];
if(countor(r) < n)
a->u.cases[i] = addcase(r);
}
return a;
}
return r;
case Talt:
r->next = addcase(r->next);
r->u.alt = addcase(r->u.alt);
return r;
case Tbegin:
case Tend:
case Tclass:
return r;
}
}
void
str2top(char *p)
{
Re2 oldtop;
oldtop = topre;
input = p;
topre.beg = 0;
topre.end = 0;
yyparse();
gen++;
if(topre.beg == 0)
yyerror("syntax");
if(oldtop.beg)
topre = re2or(oldtop, topre);
}
void
appendnext(Re *a, Re *b)
{
Re *n;
while(n = a->next)
a = n;
a->next = b;
}
void
patchnext(Re *a, Re *b)
{
Re *n;
while(a) {
n = a->next;
a->next = b;
a = n;
}
}
int
getrec(void)
{
int c;
if(flags['f']) {
c = Bgetc(rein);
if(c <= 0)
return 0;
} else
c = *input++ & 0xff;
if(flags['i'] && c >= 'A' && c <= 'Z')
c += 'a'-'A';
if(c == '\n')
lineno++;
return c;
}
Re2
re2cat(Re2 a, Re2 b)
{
Re2 c;
c.beg = a.beg;
c.end = b.end;
patchnext(a.end, b.beg);
return c;
}
Re2
re2star(Re2 a)
{
Re2 c;
c.beg = ral(Talt);
c.beg->u.alt = a.beg;
patchnext(a.end, c.beg);
c.end = c.beg;
return c;
}
Re2
re2or(Re2 a, Re2 b)
{
Re2 c;
c.beg = ral(Tor);
c.beg->u.alt = b.beg;
c.beg->next = a.beg;
c.end = b.end;
appendnext(c.end, a.end);
return c;
}
Re2
re2char(int c0, int c1)
{
Re2 c;
c.beg = ral(Tclass);
c.beg->u.x.lo = c0 & 0xff;
c.beg->u.x.hi = c1 & 0xff;
c.end = c.beg;
return c;
}
void
reprint1(Re *a)
{
int i, j;
loop:
if(a == 0)
return;
if(a->gen == gen)
return;
a->gen = gen;
print("%p: ", a);
switch(a->type) {
default:
print("type %d\n", a->type);
error("print1 type");
case Tcase:
print("case ->%p\n", a->next);
for(i=0; i<256; i++)
if(a->u.cases[i]) {
for(j=i+1; j<256; j++)
if(a->u.cases[i] != a->u.cases[j])
break;
print(" [%.2x-%.2x] ->%p\n", i, j-1, a->u.cases[i]);
i = j-1;
}
for(i=0; i<256; i++)
reprint1(a->u.cases[i]);
break;
case Tbegin:
print("^ ->%p\n", a->next);
break;
case Tend:
print("$ ->%p\n", a->next);
break;
case Tclass:
print("[%.2x-%.2x] ->%p\n", a->u.x.lo, a->u.x.hi, a->next);
break;
case Tor:
case Talt:
print("| %p ->%p\n", a->u.alt, a->next);
reprint1(a->u.alt);
break;
}
a = a->next;
goto loop;
}
void
reprint(char *s, Re *r)
{
print("%s:\n", s);
gen++;
reprint1(r);
print("\n\n");
}

19
lib9/LICENSE Normal file
View File

@@ -0,0 +1,19 @@
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
This is a Unix port of the Plan 9 formatted I/O package.
Please send comments about the packaging
to Russ Cox <rsc@post.harvard.edu>.

208
lib9/Makefile Normal file
View File

@@ -0,0 +1,208 @@
# lib9 - unix port from plan9 lib9
# 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 ../config.mk
LIB=lib9.a
TARG=lib9
# following objects are not compiled for several reasons
# crypt.o
# netcrypt.o
OFILES=\
fmt/dofmt.o\
fmt/fltfmt.o\
fmt/fmt.o\
fmt/fmtfd.o\
fmt/fmtfdflush.o\
fmt/fmtlock.o\
fmt/fmtprint.o\
fmt/fmtquote.o\
fmt/fmtrune.o\
fmt/fmtstr.o\
fmt/fmtvprint.o\
fmt/fprint.o\
fmt/nan64.o\
fmt/print.o\
fmt/runefmtstr.o\
fmt/runeseprint.o\
fmt/runesmprint.o\
fmt/runesnprint.o\
fmt/runesprint.o\
fmt/runevseprint.o\
fmt/runevsmprint.o\
fmt/runevsnprint.o\
fmt/seprint.o\
fmt/smprint.o\
fmt/snprint.o\
fmt/sprint.o\
fmt/strtod.o\
fmt/vfprint.o\
fmt/vseprint.o\
fmt/vsmprint.o\
fmt/vsnprint.o\
fmt/charstod.o\
fmt/pow10.o\
utf/rune.o\
utf/runestrcat.o\
utf/runestrchr.o\
utf/runestrcmp.o\
utf/runestrcpy.o\
utf/runestrdup.o\
utf/runestrlen.o\
utf/runestrecpy.o\
utf/runestrncat.o\
utf/runestrncmp.o\
utf/runestrncpy.o\
utf/runestrrchr.o\
utf/runestrstr.o\
utf/runetype.o\
utf/utfecpy.o\
utf/utflen.o\
utf/utfnlen.o\
utf/utfrrune.o\
utf/utfrune.o\
utf/utfutf.o\
bio/bbuffered.o\
bio/bfildes.o\
bio/bflush.o\
bio/bgetc.o\
bio/bgetd.o\
bio/bgetrune.o\
bio/binit.o\
bio/boffset.o\
bio/bprint.o\
bio/bputc.o\
bio/bputrune.o\
bio/brdline.o\
bio/brdstr.o\
bio/bread.o\
bio/bseek.o\
bio/bvprint.o\
bio/bwrite.o\
regex/regcomp.o\
regex/regerror.o\
regex/regexec.o\
regex/regsub.o\
regex/regaux.o\
regex/rregexec.o\
regex/rregsub.o\
_exits.o\
_p9dialparse.o\
_p9dir.o\
announce.o\
argv0.o\
atexit.o\
atoi.o\
atol.o\
atoll.o\
atnotify.o\
await.o\
cistrcmp.o\
cistrncmp.o\
cistrstr.o\
cleanname.o\
convD2M.o\
convM2D.o\
convM2S.o\
convS2M.o\
create.o\
ctime.o\
date.o\
dial.o\
dirfstat.o\
dirfwstat.o\
dirmodefmt.o\
dirread.o\
dirstat.o\
dirwstat.o\
dup.o\
encodefmt.o\
errstr.o\
exec.o\
execl.o\
fcallfmt.o\
get9root.o\
getcallerpc-$(OBJTYPE).o\
getenv.o\
getfields.o\
getnetconn.o\
getns.o\
getuser.o\
getwd.o\
jmp.o\
lrand.o\
lnrand.o\
main.o\
malloc.o\
malloctag.o\
mallocz.o\
nan.o\
needsrcquote.o\
needstack.o\
netmkaddr.o\
notify.o\
nrand.o\
nulldir.o\
open.o\
opentemp.o\
pipe.o\
post9p.o\
postnote.o\
qlock.o\
quote.o\
rand.o\
read9pmsg.o\
readcons.o\
readn.o\
rfork.o\
searchpath.o\
seek.o\
sendfd.o\
sleep.o\
strdup.o\
strecpy.o\
sysfatal.o\
syslog.o\
sysname.o\
time.o\
tokenize.o\
truerand.o\
u16.o\
u32.o\
u64.o\
unsharp.o\
wait.o\
waitpid.o\
all: ${LIB}
@echo built lib9
install:
@mkdir -p ${DESTDIR}${MANPREFIX}/man7
@cp -f regexp.7 ${DESTDIR}${MANPREFIX}/man7
@chmod 444 ${DESTDIR}${MANPREFIX}/man7/regexp.7
uninstall:
rm -f ${DESTDIR}${MANPREFIX}/man7/regexp.7
${LIB}: ${OFILES}
@echo AR ${TARG}
@${AR} ${LIB} ${OFILES}
.c.o:
@echo CC $*.c
@${CC} -o $*.o ${CFLAGS} -I${PREFIX}/include $*.c
clean:
rm -f ${OFILES} ${LIB}

10
lib9/_exits.c Normal file
View File

@@ -0,0 +1,10 @@
#include <u.h>
#include <libc.h>
void
_exits(char *s)
{
if(s && *s)
_exit(1);
_exit(0);
}

185
lib9/_p9dialparse.c Normal file
View File

@@ -0,0 +1,185 @@
#include <u.h>
#define NOPLAN9DEFINES
#include <libc.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/un.h>
#include <netinet/in.h>
static char *nets[] = { "tcp", "udp", nil };
#define CLASS(p) ((*(uchar*)(p))>>6)
static struct {
char *net;
char *service;
int port;
} porttbl[] = {
"tcp", "9fs", 564,
"tcp", "whoami", 565,
"tcp", "guard", 566,
"tcp", "ticket", 567,
"tcp", "exportfs", 17007,
"tcp", "rexexec", 17009,
"tcp", "ncpu", 17010,
"tcp", "cpu", 17013,
"tcp", "venti", 17034,
"tcp", "wiki", 17035,
"tcp", "secstore", 5356,
};
static int
parseip(char *host, u32int *pip)
{
uchar addr[4];
int x, i;
char *p;
p = host;
for(i=0; i<4 && *p; i++){
x = strtoul(p, &p, 0);
if(x < 0 || x >= 256)
return -1;
if(*p != '.' && *p != 0)
return -1;
if(*p == '.')
p++;
addr[i] = x;
}
switch(CLASS(addr)){
case 0:
case 1:
if(i == 3){
addr[3] = addr[2];
addr[2] = addr[1];
addr[1] = 0;
}else if(i == 2){
addr[3] = addr[1];
addr[2] = 0;
addr[1] = 0;
}else if(i != 4)
return -1;
break;
case 2:
if(i == 3){
addr[3] = addr[2];
addr[2] = 0;
}else if(i != 4)
return -1;
break;
}
*pip = *(u32int*)addr;
return 0;
}
int
p9dialparse(char *addr, char **pnet, char **punix, u32int *phost, int *pport)
{
char *net, *host, *port, *e;
int i;
struct servent *se;
struct hostent *he;
struct sockaddr_un *sockun;
if(strncmp(addr, "/net/", 5) == 0)
addr += 5;
*punix = nil;
net = addr;
if((host = strchr(net, '!')) == nil){
werrstr("malformed address");
return -1;
}
*host++ = 0;
if((port = strchr(host, '!')) == nil){
if(strcmp(net, "unix")==0 || strcmp(net, "net")==0){
Unix:
if(strlen(host)+1 > sizeof sockun->sun_path){
werrstr("unix socket name too long");
return -1;
}
*punix = host;
*pnet = "unix";
*phost = 0;
*pport = 0;
return 0;
}
werrstr("malformed address");
return -1;
}
*port++ = 0;
if(*host == 0){
werrstr("malformed address (empty host)");
return -1;
}
if(*port == 0){
werrstr("malformed address (empty port)");
return -1;
}
if(strcmp(net, "unix") == 0)
goto Unix;
if(strcmp(net, "tcp")!=0 && strcmp(net, "udp")!=0 && strcmp(net, "net") != 0){
werrstr("bad network %s!%s!%s", net, host, port);
return -1;
}
/* translate host */
if(strcmp(host, "*") == 0)
*phost = 0;
else if(parseip(host, phost) == 0)
{}
else if((he = gethostbyname(host)) != nil)
*phost = *(u32int*)(he->h_addr);
else{
werrstr("unknown host %s", host);
return -1;
}
/* translate network and port; should return list rather than first */
if(strcmp(net, "net") == 0){
for(i=0; nets[i]; i++){
if((se = getservbyname(port, nets[i])) != nil){
*pnet = nets[i];
*pport = ntohs(se->s_port);
return 0;
}
}
}
for(i=0; i<nelem(porttbl); i++){
if(strcmp(net, "net") == 0 || strcmp(porttbl[i].net, net) == 0)
if(strcmp(porttbl[i].service, port) == 0){
*pnet = porttbl[i].net;
*pport = porttbl[i].port;
return 0;
}
}
if(strcmp(net, "net") == 0){
werrstr("unknown service net!*!%s", port);
return -1;
}
if(strcmp(net, "tcp") != 0 && strcmp(net, "udp") != 0){
werrstr("unknown network %s", net);
return -1;
}
*pnet = net;
i = strtol(port, &e, 0);
if(*e == 0){
*pport = i;
return 0;
}
if((se = getservbyname(port, net)) != nil){
*pport = ntohs(se->s_port);
return 0;
}
werrstr("unknown service %s!*!%s", net, port);
return -1;
}

231
lib9/_p9dir.c Normal file
View File

@@ -0,0 +1,231 @@
#include <u.h>
#define NOPLAN9DEFINES
#include <libc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#if defined(__FreeBSD__) || defined(__OpenBSD__)
#include <sys/disklabel.h>
#include <sys/ioctl.h>
static int diskdev[] = {
151, /* aacd */
116, /* ad */
157, /* ar */
118, /* afd */
133, /* amrd */
13, /* da */
102, /* fla */
109, /* idad */
95, /* md */
131, /* mlxd */
168, /* pst */
147, /* twed */
43, /* vn */
3, /* wd */
87, /* wfd */
};
static int
isdisk(struct stat *st)
{
int i, dev;
if(!S_ISCHR(st->st_mode))
return 0;
dev = major(st->st_rdev);
for(i=0; i<nelem(diskdev); i++)
if(diskdev[i] == dev)
return 1;
return 0;
}
#define _HAVEDISKLABEL
#endif
#if defined(__linux__)
#include <linux/hdreg.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#undef major
#define major(dev) ((int)(((dev) >> 8) & 0xff))
static vlong
disksize(int fd, int dev)
{
u64int u64;
long l;
struct hd_geometry geo;
memset(&geo, 0, sizeof geo);
l = 0;
u64 = 0;
#ifdef BLKGETSIZE64
if(ioctl(fd, BLKGETSIZE64, &u64) >= 0)
return u64;
#endif
if(ioctl(fd, BLKGETSIZE, &l) >= 0)
return (vlong)l*512;
if(ioctl(fd, HDIO_GETGEO, &geo) >= 0)
return (vlong)geo.heads*geo.sectors*geo.cylinders*512;
return 0;
}
#define _HAVEDISKSIZE
#endif
#if !defined(__linux__) && !defined(__sun__)
#define _HAVESTGEN
#endif
/*
* Caching the last group and passwd looked up is
* a significant win (stupidly enough) on most systems.
* It's not safe for threaded programs, but neither is using
* getpwnam in the first place, so I'm not too worried.
*/
int
_p9dir(struct stat *lst, struct stat *st, char *name, Dir *d, char **str, char *estr)
{
char *s;
char tmp[20];
static struct group *g;
static struct passwd *p;
static int gid, uid;
int sz, fd;
fd = -1;
USED(fd);
sz = 0;
if(d)
memset(d, 0, sizeof *d);
/* name */
s = strrchr(name, '/');
if(s)
s++;
if(!s || !*s)
s = name;
if(*s == '/')
s++;
if(*s == 0)
s = "/";
if(d){
if(*str + strlen(s)+1 > estr)
d->name = "oops";
else{
strcpy(*str, s);
d->name = *str;
*str += strlen(*str)+1;
}
}
sz += strlen(s)+1;
/* user */
if(p && st->st_uid == uid && p->pw_uid == uid)
;
else{
p = getpwuid(st->st_uid);
uid = st->st_uid;
}
if(p == nil){
snprint(tmp, sizeof tmp, "%d", (int)st->st_uid);
s = tmp;
}else
s = p->pw_name;
sz += strlen(s)+1;
if(d){
if(*str+strlen(s)+1 > estr)
d->uid = "oops";
else{
strcpy(*str, s);
d->uid = *str;
*str += strlen(*str)+1;
}
}
/* group */
if(g && st->st_gid == gid && g->gr_gid == gid)
;
else{
g = getgrgid(st->st_gid);
gid = st->st_gid;
}
if(g == nil){
snprint(tmp, sizeof tmp, "%d", (int)st->st_gid);
s = tmp;
}else
s = g->gr_name;
sz += strlen(s)+1;
if(d){
if(*str + strlen(s)+1 > estr)
d->gid = "oops";
else{
strcpy(*str, s);
d->gid = *str;
*str += strlen(*str)+1;
}
}
if(d){
d->type = 'M';
d->muid = "";
d->qid.path = ((uvlong)st->st_dev<<32) | st->st_ino;
#ifdef _HAVESTGEN
d->qid.vers = st->st_gen;
#endif
d->mode = st->st_mode&0777;
d->atime = st->st_atime;
d->mtime = st->st_mtime;
d->length = st->st_size;
if(S_ISDIR(st->st_mode)){
d->length = 0;
d->mode |= DMDIR;
d->qid.type = QTDIR;
}
if(S_ISLNK(lst->st_mode)) /* yes, lst not st */
d->mode |= DMSYMLINK;
if(S_ISFIFO(st->st_mode))
d->mode |= DMNAMEDPIPE;
if(S_ISSOCK(st->st_mode))
d->mode |= DMSOCKET;
if(S_ISBLK(st->st_mode)){
d->mode |= DMDEVICE;
d->qid.path = ('b'<<16)|st->st_rdev;
}
if(S_ISCHR(st->st_mode)){
d->mode |= DMDEVICE;
d->qid.path = ('c'<<16)|st->st_rdev;
}
/* fetch real size for disks */
#ifdef _HAVEDISKSIZE
if(S_ISBLK(st->st_mode) && (fd = open(name, O_RDONLY)) >= 0){
d->length = disksize(fd, major(st->st_dev));
close(fd);
}
#endif
#ifdef _HAVEDISKLABEL
if(isdisk(st)){
int fd, n;
struct disklabel lab;
if((fd = open(name, O_RDONLY)) < 0)
goto nosize;
if(ioctl(fd, DIOCGDINFO, &lab) < 0)
goto nosize;
n = minor(st->st_rdev)&7;
if(n >= lab.d_npartitions)
goto nosize;
d->length = (vlong)(lab.d_partitions[n].p_size) * lab.d_secsize;
nosize:
if(fd >= 0)
close(fd);
}
#endif
}
return sz;
}

46
lib9/_p9translate.c Normal file
View File

@@ -0,0 +1,46 @@
#include <u.h>
#include <libc.h>
/*
* I don't want too many of these,
* but the ones we have are just too useful.
*/
static struct {
char *old;
char *new;
} replace[] = {
"#9", nil, /* must be first */
"#d", "/dev/fd",
};
char*
plan9translate(char *old)
{
char *new;
int i, olen, nlen, len;
if(replace[0].new == nil){
replace[0].new = getenv("PLAN9");
if(replace[0].new == nil)
replace[0].new = "/usr/local/plan9";
}
for(i=0; i<nelem(replace); i++){
if(!replace[i].new)
continue;
olen = strlen(replace[i].old);
if(strncmp(old, replace[i].old, olen) != 0
|| (old[olen] != '\0' && old[olen] != '/'))
continue;
nlen = strlen(replace[i].new);
len = strlen(old)+nlen-olen;
new = malloc(len+1);
if(new == nil)
return "<out of memory>";
strcpy(new, replace[i].new);
strcpy(new+nlen, old+olen);
assert(strlen(new) == len);
return new;
}
return old;
}

152
lib9/announce.c Normal file
View File

@@ -0,0 +1,152 @@
#include <u.h>
#define NOPLAN9DEFINES
#include <libc.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/un.h>
#include <errno.h>
#undef sun
#define sun sockun
int
_p9netfd(char *dir)
{
int fd;
if(strncmp(dir, "/dev/fd/", 8) != 0)
return -1;
fd = strtol(dir+8, &dir, 0);
if(*dir != 0)
return -1;
return fd;
}
static void
putfd(char *dir, int fd)
{
snprint(dir, NETPATHLEN, "/dev/fd/%d", fd);
}
#undef unix
#define unix sockunix
int
p9announce(char *addr, char *dir)
{
int proto;
char *buf, *unix;
char *net;
u32int host;
int port, s;
int n;
socklen_t sn;
struct sockaddr_in sa;
struct sockaddr_un sun;
buf = strdup(addr);
if(buf == nil)
return -1;
if(p9dialparse(buf, &net, &unix, &host, &port) < 0){
free(buf);
return -1;
}
if(strcmp(net, "tcp") == 0)
proto = SOCK_STREAM;
else if(strcmp(net, "udp") == 0)
proto = SOCK_DGRAM;
else if(strcmp(net, "unix") == 0)
goto Unix;
else{
werrstr("can only handle tcp, udp, and unix: not %s", net);
free(buf);
return -1;
}
free(buf);
memset(&sa, 0, sizeof sa);
memmove(&sa.sin_addr, &host, 4);
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
if((s = socket(AF_INET, proto, 0)) < 0)
return -1;
sn = sizeof n;
if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0
&& n == SOCK_STREAM){
n = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n);
}
if(bind(s, (struct sockaddr*)&sa, sizeof sa) < 0){
close(s);
return -1;
}
if(proto == SOCK_STREAM){
listen(s, 8);
putfd(dir, s);
}
return s;
Unix:
memset(&sun, 0, sizeof sun);
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, unix);
if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
return -1;
sn = sizeof sun;
if(bind(s, (struct sockaddr*)&sun, sizeof sun) < 0){
if(errno == EADDRINUSE
&& connect(s, (struct sockaddr*)&sun, sizeof sun) < 0
&& errno == ECONNREFUSED){
/* dead socket, so remove it */
remove(unix);
close(s);
if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
return -1;
if(bind(s, (struct sockaddr*)&sun, sizeof sun) >= 0)
goto Success;
}
close(s);
return -1;
}
Success:
listen(s, 8);
putfd(dir, s);
return s;
}
int
p9listen(char *dir, char *newdir)
{
int fd, one;
if((fd = _p9netfd(dir)) < 0){
werrstr("bad 'directory' in listen: %s", dir);
return -1;
}
if((fd = accept(fd, nil, nil)) < 0)
return -1;
one = 1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one);
putfd(newdir, fd);
return fd;
}
int
p9accept(int cfd, char *dir)
{
int fd;
if((fd = _p9netfd(dir)) < 0){
werrstr("bad 'directory' in accept");
return -1;
}
/* need to dup because the listen fd will be closed */
return dup(fd);
}

9
lib9/argv0.c Normal file
View File

@@ -0,0 +1,9 @@
#include <lib9.h>
char *argv0;
/*
* Mac OS can't deal with files that only declare data.
* ARGBEGIN mentions this function so that this file gets pulled in.
*/
void __fixargv0(void) { }

54
lib9/atexit.c Normal file
View File

@@ -0,0 +1,54 @@
#include <u.h>
#include <libc.h>
#define NEXIT 33
static Lock onexlock;
static struct
{
void (*f)(void);
int pid;
}onex[NEXIT];
int
atexit(void (*f)(void))
{
int i;
lock(&onexlock);
for(i=0; i<NEXIT; i++)
if(onex[i].f == 0) {
onex[i].pid = getpid();
onex[i].f = f;
unlock(&onexlock);
return 1;
}
unlock(&onexlock);
return 0;
}
void
atexitdont(void (*f)(void))
{
int i, pid;
pid = getpid();
for(i=0; i<NEXIT; i++)
if(onex[i].f == f && onex[i].pid == pid)
onex[i].f = 0;
}
void
exits(char *s)
{
int i, pid;
void (*f)(void);
pid = getpid();
for(i = NEXIT-1; i >= 0; i--)
if((f = onex[i].f) && pid == onex[i].pid) {
onex[i].f = 0;
(*f)();
}
exit(s && *s ? 1 : 0);
}

58
lib9/atnotify.c Normal file
View File

@@ -0,0 +1,58 @@
#include <u.h>
#include <libc.h>
#define NFN 33
static int (*onnot[NFN])(void*, char*);
static Lock onnotlock;
static
void
notifier(void *v, char *s)
{
int i;
for(i=0; i<NFN; i++)
if(onnot[i] && ((*onnot[i])(v, s))){
noted(NCONT);
return;
}
noted(NDFLT);
}
int
atnotify(int (*f)(void*, char*), int in)
{
int i, n, ret;
static int init;
if(!init){
notify(notifier);
init = 1; /* assign = */
}
ret = 0;
lock(&onnotlock);
if(in){
for(i=0; i<NFN; i++)
if(onnot[i] == 0) {
onnot[i] = f;
ret = 1;
break;
}
}else{
n = 0;
for(i=0; i<NFN; i++)
if(onnot[i]){
if(ret==0 && onnot[i]==f){
onnot[i] = 0;
ret = 1;
}else
n++;
}
if(n == 0){
init = 0;
notify(0);
}
}
unlock(&onnotlock);
return ret;
}

9
lib9/atoi.c Normal file
View File

@@ -0,0 +1,9 @@
#include <u.h>
#include <libc.h>
int
atoi(char *s)
{
return strtol(s, 0, 0);
}

9
lib9/atol.c Normal file
View File

@@ -0,0 +1,9 @@
#include <u.h>
#include <libc.h>
long
atol(char *s)
{
return strtol(s, 0, 0);
}

9
lib9/atoll.c Normal file
View File

@@ -0,0 +1,9 @@
#include <u.h>
#include <libc.h>
vlong
atoll(char *s)
{
return strtoll(s, 0, 0);
}

137
lib9/await.c Normal file
View File

@@ -0,0 +1,137 @@
#define NOPLAN9DEFINES
#include <u.h>
#include <libc.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#ifndef WCOREDUMP /* not on Mac OS X Tiger */
#define WCOREDUMP(status) 0
#endif
static struct {
int sig;
char *str;
} tab[] = {
SIGHUP, "hangup",
SIGINT, "interrupt",
SIGQUIT, "quit",
SIGILL, "sys: illegal instruction",
SIGTRAP, "sys: breakpoint",
SIGABRT, "sys: abort",
#ifdef SIGEMT
SIGEMT, "sys: emulate instruction executed",
#endif
SIGFPE, "sys: fp: trap",
SIGKILL, "sys: kill",
SIGBUS, "sys: bus error",
SIGSEGV, "sys: segmentation violation",
SIGALRM, "alarm",
SIGTERM, "kill",
SIGURG, "sys: urgent condition on socket",
SIGSTOP, "sys: stop",
SIGTSTP, "sys: tstp",
SIGCONT, "sys: cont",
SIGCHLD, "sys: child",
SIGTTIN, "sys: ttin",
SIGTTOU, "sys: ttou",
#ifdef SIGIO /* not on Mac OS X Tiger */
SIGIO, "sys: i/o possible on fd",
#endif
SIGXCPU, "sys: cpu time limit exceeded",
SIGXFSZ, "sys: file size limit exceeded",
SIGVTALRM, "sys: virtual time alarm",
SIGPROF, "sys: profiling timer alarm",
#ifdef SIGWINCH /* not on Mac OS X Tiger */
SIGWINCH, "sys: window size change",
#endif
#ifdef SIGINFO
SIGINFO, "sys: status request",
#endif
SIGUSR1, "sys: usr1",
SIGUSR2, "sys: usr2",
SIGPIPE, "sys: write on closed pipe",
};
char*
_p9sigstr(int sig, char *tmp)
{
int i;
for(i=0; i<nelem(tab); i++)
if(tab[i].sig == sig)
return tab[i].str;
if(tmp == nil)
return nil;
sprint(tmp, "sys: signal %d", sig);
return tmp;
}
int
_p9strsig(char *s)
{
int i;
for(i=0; i<nelem(tab); i++)
if(strcmp(s, tab[i].str) == 0)
return tab[i].sig;
return 0;
}
static int
_await(int pid4, char *str, int n, int opt)
{
int pid, status, cd;
struct rusage ru;
char buf[128], tmp[64];
ulong u, s;
for(;;){
/* On Linux, pid==-1 means anyone; on SunOS, it's pid==0. */
if(pid4 == -1)
pid = wait3(&status, opt, &ru);
else
pid = wait4(pid4, &status, opt, &ru);
if(pid <= 0)
return -1;
u = ru.ru_utime.tv_sec*1000+((ru.ru_utime.tv_usec+500)/1000);
s = ru.ru_stime.tv_sec*1000+((ru.ru_stime.tv_usec+500)/1000);
if(WIFEXITED(status)){
status = WEXITSTATUS(status);
if(status)
snprint(buf, sizeof buf, "%d %lud %lud %lud %d", pid, u, s, u+s, status);
else
snprint(buf, sizeof buf, "%d %lud %lud %lud ''", pid, u, s, u+s, status);
strecpy(str, str+n, buf);
return strlen(str);
}
if(WIFSIGNALED(status)){
cd = WCOREDUMP(status);
snprint(buf, sizeof buf, "%d %lud %lud %lud 'signal: %s%s'", pid, u, s, u+s, _p9sigstr(WTERMSIG(status), tmp), cd ? " (core dumped)" : "");
strecpy(str, str+n, buf);
return strlen(str);
}
}
}
int
await(char *str, int n)
{
return _await(-1, str, n, 0);
}
int
awaitnohang(char *str, int n)
{
return _await(-1, str, n, WNOHANG);
}
int
awaitfor(int pid, char *str, int n)
{
return _await(pid, str, n, 0);
}

106
lib9/bio.h Normal file
View File

@@ -0,0 +1,106 @@
#ifndef _BIO_H_
#define _BIO_H_ 1
#if defined(__cplusplus)
extern "C" {
#endif
#ifdef AUTOLIB
AUTOLIB(bio)
#endif
#include <sys/types.h> /* for off_t */
#include <stdarg.h>
#include <fcntl.h> /* for O_RDONLY, O_WRONLY */
#define OREAD 0 /* open for read */
#define OWRITE 1 /* write */
#define ORDWR 2 /* read and write */
#define OEXEC 3 /* execute, == read but check execute permission */
#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */
#define OCEXEC 32 /* or'ed in, close on exec */
#define ORCLOSE 64 /* or'ed in, remove on close */
#define ODIRECT 128 /* or'ed in, direct access */
#define ONONBLOCK 256 /* or'ed in, non-blocking call */
#define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */
#define OLOCK 0x2000 /* or'ed in, lock after opening */
#define OAPPEND 0x4000 /* or'ed in, append only */
typedef struct Biobuf Biobuf;
enum
{
Bsize = 8*1024,
Bungetsize = 4, /* space for ungetc */
Bmagic = 0x314159,
Beof = -1,
Bbad = -2,
Binactive = 0, /* states */
Bractive,
Bwactive,
Bracteof,
Bend
};
struct Biobuf
{
int icount; /* neg num of bytes at eob */
int ocount; /* num of bytes at bob */
int rdline; /* num of bytes after rdline */
int runesize; /* num of bytes of last getrune */
int state; /* r/w/inactive */
int fid; /* open file */
int flag; /* magic if malloc'ed */
off_t offset; /* offset of buffer in file */
int bsize; /* size of buffer */
unsigned char* bbuf; /* pointer to beginning of buffer */
unsigned char* ebuf; /* pointer to end of buffer */
unsigned char* gbuf; /* pointer to good data in buf */
unsigned char b[Bungetsize+Bsize];
};
#define BGETC(bp)\
((bp)->icount?(bp)->bbuf[(bp)->bsize+(bp)->icount++]:Bgetc((bp)))
#define BPUTC(bp,c)\
((bp)->ocount?(bp)->bbuf[(bp)->bsize+(bp)->ocount++]=(c),0:Bputc((bp),(c)))
#define BOFFSET(bp)\
(((bp)->state==Bractive)?\
(bp)->offset + (bp)->icount:\
(((bp)->state==Bwactive)?\
(bp)->offset + ((bp)->bsize + (bp)->ocount):\
-1))
#define BLINELEN(bp)\
(bp)->rdline
#define BFILDES(bp)\
(bp)->fid
int Bbuffered(Biobuf*);
Biobuf* Bfdopen(int, int);
int Bfildes(Biobuf*);
int Bflush(Biobuf*);
int Bgetc(Biobuf*);
int Bgetd(Biobuf*, double*);
long Bgetrune(Biobuf*);
int Binit(Biobuf*, int, int);
int Binits(Biobuf*, int, int, unsigned char*, int);
int Blinelen(Biobuf*);
off_t Boffset(Biobuf*);
Biobuf* Bopen(char*, int);
int Bprint(Biobuf*, char*, ...);
int Bputc(Biobuf*, int);
int Bputrune(Biobuf*, long);
void* Brdline(Biobuf*, int);
char* Brdstr(Biobuf*, int, int);
long Bread(Biobuf*, void*, long);
off_t Bseek(Biobuf*, off_t, int);
int Bterm(Biobuf*);
int Bungetc(Biobuf*);
int Bungetrune(Biobuf*);
long Bwrite(Biobuf*, void*, long);
int Bvprint(Biobuf*, char*, va_list);
#if defined(__cplusplus)
}
#endif
#endif

12
lib9/bio/_lib9.h Normal file
View File

@@ -0,0 +1,12 @@
#include <fmt.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#define OREAD O_RDONLY
#define OWRITE O_WRONLY
#include <utf.h>
#define nil ((void*)0)

21
lib9/bio/bbuffered.c Normal file
View File

@@ -0,0 +1,21 @@
#include "lib9.h"
#include <bio.h>
#include <fmt.h>
int
Bbuffered(Biobuf *bp)
{
switch(bp->state) {
case Bracteof:
case Bractive:
return -bp->icount;
case Bwactive:
return bp->bsize + bp->ocount;
case Binactive:
return 0;
}
fprint(2, "Bbuffered: unknown state %d\n", bp->state);
return 0;
}

46
lib9/bio/bcat.c Normal file
View File

@@ -0,0 +1,46 @@
#include <fmt.h>
#include "bio.h"
Biobuf bout;
void
bcat(Biobuf *b, char *name)
{
char buf[1000];
int n;
while((n = Bread(b, buf, sizeof buf)) > 0){
if(Bwrite(&bout, buf, n) < 0)
fprint(2, "writing during %s: %r\n", name);
}
if(n < 0)
fprint(2, "reading %s: %r\n", name);
}
int
main(int argc, char **argv)
{
int i;
Biobuf b, *bp;
Fmt fmt;
Binit(&bout, 1, O_WRONLY);
Bfmtinit(&fmt, &bout);
fmtprint(&fmt, "hello, world\n");
Bfmtflush(&fmt);
if(argc == 1){
Binit(&b, 0, O_RDONLY);
bcat(&b, "<stdin>");
}else{
for(i=1; i<argc; i++){
if((bp = Bopen(argv[i], O_RDONLY)) == 0){
fprint(2, "Bopen %s: %r\n", argv[i]);
continue;
}
bcat(bp, argv[i]);
Bterm(bp);
}
}
exit(0);
}

9
lib9/bio/bfildes.c Normal file
View File

@@ -0,0 +1,9 @@
#include "lib9.h"
#include <bio.h>
int
Bfildes(Biobuf *bp)
{
return bp->fid;
}

34
lib9/bio/bflush.c Normal file
View File

@@ -0,0 +1,34 @@
#include "lib9.h"
#include <bio.h>
#include <unistd.h>
int
Bflush(Biobuf *bp)
{
int n, c;
switch(bp->state) {
case Bwactive:
n = bp->bsize+bp->ocount;
if(n == 0)
return 0;
c = write(bp->fid, bp->bbuf, n);
if(n == c) {
bp->offset += n;
bp->ocount = -bp->bsize;
return 0;
}
bp->state = Binactive;
bp->ocount = 0;
break;
case Bracteof:
bp->state = Bractive;
case Bractive:
bp->icount = 0;
bp->gbuf = bp->ebuf;
return 0;
}
return Beof;
}

54
lib9/bio/bgetc.c Normal file
View File

@@ -0,0 +1,54 @@
#include "lib9.h"
#include <bio.h>
#include <unistd.h>
int
Bgetc(Biobuf *bp)
{
int i;
loop:
i = bp->icount;
if(i != 0) {
bp->icount = i+1;
return bp->ebuf[i];
}
if(bp->state != Bractive) {
if(bp->state == Bracteof)
bp->state = Bractive;
return Beof;
}
/*
* get next buffer, try to keep Bungetsize
* characters pre-catenated from the previous
* buffer to allow that many ungets.
*/
memmove(bp->bbuf-Bungetsize, bp->ebuf-Bungetsize, Bungetsize);
i = read(bp->fid, bp->bbuf, bp->bsize);
bp->gbuf = bp->bbuf;
if(i <= 0) {
bp->state = Bracteof;
if(i < 0)
bp->state = Binactive;
return Beof;
}
if(i < bp->bsize) {
memmove(bp->ebuf-i-Bungetsize, bp->bbuf-Bungetsize, i+Bungetsize);
bp->gbuf = bp->ebuf-i;
}
bp->icount = -i;
bp->offset += i;
goto loop;
}
int
Bungetc(Biobuf *bp)
{
if(bp->state == Bracteof)
bp->state = Bractive;
if(bp->state != Bractive)
return Beof;
bp->icount--;
return 1;
}

37
lib9/bio/bgetd.c Normal file
View File

@@ -0,0 +1,37 @@
#include "lib9.h"
#include <bio.h>
#include <fmt.h>
struct bgetd
{
Biobuf* b;
int eof;
};
static int
Bgetdf(void *vp)
{
int c;
struct bgetd *bg = vp;
c = Bgetc(bg->b);
if(c == Beof)
bg->eof = 1;
return c;
}
int
Bgetd(Biobuf *bp, double *dp)
{
double d;
struct bgetd b;
b.b = bp;
b.eof = 0;
d = fmtcharstod(Bgetdf, &b);
if(b.eof)
return -1;
Bungetc(bp);
*dp = d;
return 1;
}

47
lib9/bio/bgetrune.c Normal file
View File

@@ -0,0 +1,47 @@
#include "lib9.h"
#include <bio.h>
#include <utf.h>
long
Bgetrune(Biobuf *bp)
{
int c, i;
Rune rune;
char str[4];
c = Bgetc(bp);
if(c < Runeself) { /* one char */
bp->runesize = 1;
return c;
}
str[0] = c;
for(i=1;;) {
c = Bgetc(bp);
if(c < 0)
return c;
str[i++] = c;
if(fullrune(str, i)) {
bp->runesize = chartorune(&rune, str);
while(i > bp->runesize) {
Bungetc(bp);
i--;
}
return rune;
}
}
}
int
Bungetrune(Biobuf *bp)
{
if(bp->state == Bracteof)
bp->state = Bractive;
if(bp->state != Bractive)
return Beof;
bp->icount -= bp->runesize;
bp->runesize = 0;
return 1;
}

156
lib9/bio/binit.c Normal file
View File

@@ -0,0 +1,156 @@
#include "lib9.h"
#include <bio.h>
#include <fmt.h>
#include <stdlib.h>
#include <unistd.h>
enum
{
MAXBUFS = 20
};
static Biobuf* wbufs[MAXBUFS];
static int atexitflag;
static
void
batexit(void)
{
Biobuf *bp;
int i;
for(i=0; i<MAXBUFS; i++) {
bp = wbufs[i];
if(bp != 0) {
wbufs[i] = 0;
Bflush(bp);
}
}
}
static
void
deinstall(Biobuf *bp)
{
int i;
for(i=0; i<MAXBUFS; i++)
if(wbufs[i] == bp)
wbufs[i] = 0;
}
static
void
install(Biobuf *bp)
{
int i;
deinstall(bp);
for(i=0; i<MAXBUFS; i++)
if(wbufs[i] == 0) {
wbufs[i] = bp;
break;
}
if(atexitflag == 0) {
atexitflag = 1;
atexit(batexit);
}
}
int
Binits(Biobuf *bp, int f, int mode, unsigned char *p, int size)
{
p += Bungetsize; /* make room for Bungets */
size -= Bungetsize;
switch(mode&~(OCEXEC|ORCLOSE|OTRUNC)) {
default:
fprint(2, "Bopen: unknown mode %d\n", mode);
return Beof;
case OREAD:
bp->state = Bractive;
bp->ocount = 0;
break;
case OWRITE:
install(bp);
bp->state = Bwactive;
bp->ocount = -size;
break;
}
bp->bbuf = p;
bp->ebuf = p+size;
bp->bsize = size;
bp->icount = 0;
bp->gbuf = bp->ebuf;
bp->fid = f;
bp->flag = 0;
bp->rdline = 0;
bp->offset = 0;
bp->runesize = 0;
return 0;
}
int
Binit(Biobuf *bp, int f, int mode)
{
return Binits(bp, f, mode, bp->b, sizeof(bp->b));
}
Biobuf*
Bfdopen(int f, int mode)
{
Biobuf *bp;
bp = malloc(sizeof(Biobuf));
if(bp == 0)
return 0;
Binits(bp, f, mode, bp->b, sizeof(bp->b));
bp->flag = Bmagic;
return bp;
}
Biobuf*
Bopen(char *name, int mode)
{
Biobuf *bp;
int f;
switch(mode&~(OCEXEC|ORCLOSE|OTRUNC)) {
default:
fprint(2, "Bopen: unknown mode %d\n", mode);
return 0;
case OREAD:
f = open(name, OREAD);
if(f < 0)
return 0;
break;
case OWRITE:
f = creat(name, 0666);
if(f < 0)
return 0;
}
bp = Bfdopen(f, mode);
if(bp == 0)
close(f);
return bp;
}
int
Bterm(Biobuf *bp)
{
deinstall(bp);
Bflush(bp);
if(bp->flag == Bmagic) {
bp->flag = 0;
close(bp->fid);
free(bp);
}
return 0;
}

26
lib9/bio/boffset.c Normal file
View File

@@ -0,0 +1,26 @@
#include "lib9.h"
#include <bio.h>
#include <fmt.h>
off_t
Boffset(Biobuf *bp)
{
off_t n;
switch(bp->state) {
default:
fprint(2, "Boffset: unknown state %d\n", bp->state);
n = Beof;
break;
case Bracteof:
case Bractive:
n = bp->offset + bp->icount;
break;
case Bwactive:
n = bp->offset + (bp->bsize + bp->ocount);
break;
}
return n;
}

14
lib9/bio/bprint.c Normal file
View File

@@ -0,0 +1,14 @@
#include "lib9.h"
#include <bio.h>
int
Bprint(Biobuf *bp, char *fmt, ...)
{
int n;
va_list arg;
va_start(arg, fmt);
n = Bvprint(bp, fmt, arg);
va_end(arg);
return n;
}

20
lib9/bio/bputc.c Normal file
View File

@@ -0,0 +1,20 @@
#include "lib9.h"
#include <bio.h>
int
Bputc(Biobuf *bp, int c)
{
int i;
for(;;) {
i = bp->ocount;
if(i) {
bp->ebuf[i++] = c;
bp->ocount = i;
return 0;
}
if(Bflush(bp) == Beof)
break;
}
return Beof;
}

23
lib9/bio/bputrune.c Normal file
View File

@@ -0,0 +1,23 @@
#include "lib9.h"
#include <bio.h>
#include <utf.h>
int
Bputrune(Biobuf *bp, long c)
{
Rune rune;
char str[4];
int n;
rune = c;
if(rune < Runeself) {
Bputc(bp, rune);
return 1;
}
n = runetochar(str, &rune);
if(n == 0)
return Bbad;
if(Bwrite(bp, str, n) != n)
return Beof;
return n;
}

95
lib9/bio/brdline.c Normal file
View File

@@ -0,0 +1,95 @@
#include "lib9.h"
#include <bio.h>
#include <unistd.h>
void*
Brdline(Biobuf *bp, int delim)
{
char *ip, *ep;
int i, j;
i = -bp->icount;
if(i == 0) {
/*
* eof or other error
*/
if(bp->state != Bractive) {
if(bp->state == Bracteof)
bp->state = Bractive;
bp->rdline = 0;
bp->gbuf = bp->ebuf;
return 0;
}
}
/*
* first try in remainder of buffer (gbuf doesn't change)
*/
ip = (char*)bp->ebuf - i;
ep = memchr(ip, delim, i);
if(ep) {
j = (ep - ip) + 1;
bp->rdline = j;
bp->icount += j;
return ip;
}
/*
* copy data to beginning of buffer
*/
if(i < bp->bsize)
memmove(bp->bbuf, ip, i);
bp->gbuf = bp->bbuf;
/*
* append to buffer looking for the delim
*/
ip = (char*)bp->bbuf + i;
while(i < bp->bsize) {
j = read(bp->fid, ip, bp->bsize-i);
if(j <= 0) {
/*
* end of file with no delim
*/
memmove(bp->ebuf-i, bp->bbuf, i);
bp->rdline = i;
bp->icount = -i;
bp->gbuf = bp->ebuf-i;
return 0;
}
bp->offset += j;
i += j;
ep = memchr(ip, delim, j);
if(ep) {
/*
* found in new piece
* copy back up and reset everything
*/
ip = (char*)bp->ebuf - i;
if(i < bp->bsize){
memmove(ip, bp->bbuf, i);
bp->gbuf = (unsigned char*)ip;
}
j = (ep - (char*)bp->bbuf) + 1;
bp->rdline = j;
bp->icount = j - i;
return ip;
}
ip += j;
}
/*
* full buffer without finding
*/
bp->rdline = bp->bsize;
bp->icount = -bp->bsize;
bp->gbuf = bp->bbuf;
return 0;
}
int
Blinelen(Biobuf *bp)
{
return bp->rdline;
}

113
lib9/bio/brdstr.c Normal file
View File

@@ -0,0 +1,113 @@
#include "lib9.h"
#include <bio.h>
#include <stdlib.h>
#include <unistd.h>
static char*
badd(char *p, int *np, char *data, int ndata, int delim, int nulldelim)
{
int n;
n = *np;
p = realloc(p, n+ndata+1);
if(p){
memmove(p+n, data, ndata);
n += ndata;
if(n>0 && nulldelim && p[n-1]==delim)
p[--n] = '\0';
else
p[n] = '\0';
*np = n;
}
return p;
}
char*
Brdstr(Biobuf *bp, int delim, int nulldelim)
{
char *ip, *ep, *p;
int i, j;
i = -bp->icount;
bp->rdline = 0;
if(i == 0) {
/*
* eof or other error
*/
if(bp->state != Bractive) {
if(bp->state == Bracteof)
bp->state = Bractive;
bp->gbuf = bp->ebuf;
return nil;
}
}
/*
* first try in remainder of buffer (gbuf doesn't change)
*/
ip = (char*)bp->ebuf - i;
ep = memchr(ip, delim, i);
if(ep) {
j = (ep - ip) + 1;
bp->icount += j;
return badd(nil, &bp->rdline, ip, j, delim, nulldelim);
}
/*
* copy data to beginning of buffer
*/
if(i < bp->bsize)
memmove(bp->bbuf, ip, i);
bp->gbuf = bp->bbuf;
/*
* append to buffer looking for the delim
*/
p = nil;
for(;;){
ip = (char*)bp->bbuf + i;
while(i < bp->bsize) {
j = read(bp->fid, ip, bp->bsize-i);
if(j <= 0 && i == 0)
return p;
if(j <= 0 && i > 0){
/*
* end of file but no delim. pretend we got a delim
* by making the delim \0 and smashing it with nulldelim.
*/
j = 1;
ep = ip;
delim = '\0';
nulldelim = 1;
*ep = delim; /* there will be room for this */
}else{
bp->offset += j;
ep = memchr(ip, delim, j);
}
i += j;
if(ep) {
/*
* found in new piece
* copy back up and reset everything
*/
ip = (char*)bp->ebuf - i;
if(i < bp->bsize){
memmove(ip, bp->bbuf, i);
bp->gbuf = (unsigned char*)ip;
}
j = (ep - (char*)bp->bbuf) + 1;
bp->icount = j - i;
return badd(p, &bp->rdline, ip, j, delim, nulldelim);
}
ip += j;
}
/*
* full buffer without finding; add to user string and continue
*/
p = badd(p, &bp->rdline, (char*)bp->bbuf, bp->bsize, 0, 0);
i = 0;
bp->icount = 0;
bp->gbuf = bp->ebuf;
}
}

46
lib9/bio/bread.c Normal file
View File

@@ -0,0 +1,46 @@
#include "lib9.h"
#include <bio.h>
#include <unistd.h>
long
Bread(Biobuf *bp, void *ap, long count)
{
long c;
unsigned char *p;
int i, n, ic;
p = ap;
c = count;
ic = bp->icount;
while(c > 0) {
n = -ic;
if(n > c)
n = c;
if(n == 0) {
if(bp->state != Bractive)
break;
i = read(bp->fid, bp->bbuf, bp->bsize);
if(i <= 0) {
bp->state = Bracteof;
if(i < 0)
bp->state = Binactive;
break;
}
bp->gbuf = bp->bbuf;
bp->offset += i;
if(i < bp->bsize) {
memmove(bp->ebuf-i, bp->bbuf, i);
bp->gbuf = bp->ebuf-i;
}
ic = -i;
continue;
}
memmove(p, bp->ebuf+ic, n);
c -= n;
ic += n;
p += n;
}
bp->icount = ic;
return count-c;
}

63
lib9/bio/bseek.c Normal file
View File

@@ -0,0 +1,63 @@
#include "lib9.h"
#include <bio.h>
#include <fmt.h>
#include <sys/types.h>
#include <unistd.h>
off_t
Bseek(Biobuf *bp, off_t offset, int base)
{
long long n, d;
int bufsz;
switch(bp->state) {
default:
fprint(2, "Bseek: unknown state %d\n", bp->state);
return Beof;
case Bracteof:
bp->state = Bractive;
bp->icount = 0;
bp->gbuf = bp->ebuf;
case Bractive:
n = offset;
if(base == 1) {
n += Boffset(bp);
base = 0;
}
/*
* try to seek within buffer
*/
if(base == 0) {
d = n - Boffset(bp);
bufsz = bp->ebuf - bp->gbuf;
if(-bufsz <= d && d <= bufsz){
bp->icount += d;
if(d >= 0) {
if(bp->icount <= 0)
return n;
} else {
if(bp->ebuf - bp->gbuf >= -bp->icount)
return n;
}
}
}
/*
* reset the buffer
*/
n = lseek(bp->fid, n, base);
bp->icount = 0;
bp->gbuf = bp->ebuf;
break;
case Bwactive:
Bflush(bp);
n = lseek(bp->fid, offset, base);
break;
}
bp->offset = n;
return n;
}

39
lib9/bio/bvprint.c Normal file
View File

@@ -0,0 +1,39 @@
#include "lib9.h"
#include <bio.h>
#include <fmt.h>
static int
fmtBflush(Fmt *f)
{
Biobuf *bp;
bp = f->farg;
bp->ocount = (char*)f->to - (char*)f->stop;
if(Bflush(bp) < 0)
return 0;
f->stop = bp->ebuf;
f->to = (char*)f->stop + bp->ocount;
f->start = f->to;
return 1;
}
int
Bvprint(Biobuf *bp, char *fmt, va_list arg)
{
int n;
Fmt f;
f.runes = 0;
f.stop = bp->ebuf;
f.start = (char*)f.stop + bp->ocount;
f.to = f.start;
f.flush = fmtBflush;
f.farg = bp;
f.nfmt = 0;
n = fmtvprint(&f, fmt, arg);
bp->ocount = (char*)f.to - (char*)f.stop;
return n;
}

39
lib9/bio/bwrite.c Normal file
View File

@@ -0,0 +1,39 @@
#include "lib9.h"
#include <bio.h>
#include <unistd.h>
long
Bwrite(Biobuf *bp, void *ap, long count)
{
long c;
unsigned char *p;
int i, n, oc;
p = ap;
c = count;
oc = bp->ocount;
while(c > 0) {
n = -oc;
if(n > c)
n = c;
if(n == 0) {
if(bp->state != Bwactive)
return Beof;
i = write(bp->fid, bp->bbuf, bp->bsize);
if(i != bp->bsize) {
bp->state = Binactive;
return Beof;
}
bp->offset += i;
oc = -bp->bsize;
continue;
}
memmove(bp->ebuf+oc, p, n);
oc += n;
c -= n;
p += n;
}
bp->ocount = oc;
return count-c;
}

20
lib9/bio/lib9.std.h Normal file
View File

@@ -0,0 +1,20 @@
#include <utf.h>
#include <fmt.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#define OREAD O_RDONLY
#define OWRITE O_WRONLY
#define OCEXEC 0
#define ORCLOSE 0
#define OTRUNC 0
#define nil ((void*)0)
typedef long long vlong;
typedef unsigned long long uvlong;

26
lib9/cistrcmp.c Normal file
View File

@@ -0,0 +1,26 @@
#include <u.h>
#include <libc.h>
int
cistrcmp(char *s1, char *s2)
{
int c1, c2;
while(*s1){
c1 = *(uchar*)s1++;
c2 = *(uchar*)s2++;
if(c1 == c2)
continue;
if(c1 >= 'A' && c1 <= 'Z')
c1 -= 'A' - 'a';
if(c2 >= 'A' && c2 <= 'Z')
c2 -= 'A' - 'a';
if(c1 != c2)
return c1 - c2;
}
return -*s2;
}

28
lib9/cistrncmp.c Normal file
View File

@@ -0,0 +1,28 @@
#include <u.h>
#include <libc.h>
int
cistrncmp(char *s1, char *s2, int n)
{
int c1, c2;
while(*s1 && n-- > 0){
c1 = *(uchar*)s1++;
c2 = *(uchar*)s2++;
if(c1 == c2)
continue;
if(c1 >= 'A' && c1 <= 'Z')
c1 -= 'A' - 'a';
if(c2 >= 'A' && c2 <= 'Z')
c2 -= 'A' - 'a';
if(c1 != c2)
return c1 - c2;
}
if(n <= 0)
return 0;
return -*s2;
}

23
lib9/cistrstr.c Normal file
View File

@@ -0,0 +1,23 @@
#include <u.h>
#include <libc.h>
char*
cistrstr(char *s, char *sub)
{
int c, csub, n;
csub = *sub;
if(csub == '\0')
return s;
if(csub >= 'A' && csub <= 'Z')
csub -= 'A' - 'a';
sub++;
n = strlen(sub);
for(; c = *s; s++){
if(c >= 'A' && c <= 'Z')
c -= 'A' - 'a';
if(c == csub && cistrncmp(s+1, sub, n) == 0)
return s;
}
return nil;
}

52
lib9/cleanname.c Normal file
View File

@@ -0,0 +1,52 @@
#include <u.h>
#include <libc.h>
/*
* In place, rewrite name to compress multiple /, eliminate ., and process ..
*/
#define SEP(x) ((x)=='/' || (x) == 0)
char*
cleanname(char *name)
{
char *p, *q, *dotdot;
int rooted;
rooted = name[0] == '/';
/*
* invariants:
* p points at beginning of path element we're considering.
* q points just past the last path element we wrote (no slash).
* dotdot points just past the point where .. cannot backtrack
* any further (no slash).
*/
p = q = dotdot = name+rooted;
while(*p) {
if(p[0] == '/') /* null element */
p++;
else if(p[0] == '.' && SEP(p[1]))
p += 1; /* don't count the separator in case it is nul */
else if(p[0] == '.' && p[1] == '.' && SEP(p[2])) {
p += 2;
if(q > dotdot) { /* can backtrack */
while(--q > dotdot && *q != '/')
;
} else if(!rooted) { /* /.. is / but ./../ is .. */
if(q != name)
*q++ = '/';
*q++ = '.';
*q++ = '.';
dotdot = q;
}
} else { /* real path element */
if(q != name+rooted)
*q++ = '/';
while((*q = *p) != '/' && *q != 0)
p++, q++;
}
}
if(q == name) /* empty string is really ``.'' */
*q++ = '.';
*q = '\0';
return name;
}

95
lib9/convD2M.c Normal file
View File

@@ -0,0 +1,95 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
uint
sizeD2M(Dir *d)
{
char *sv[4];
int i, ns;
sv[0] = d->name;
sv[1] = d->uid;
sv[2] = d->gid;
sv[3] = d->muid;
ns = 0;
for(i = 0; i < 4; i++)
if(sv[i])
ns += strlen(sv[i]);
return STATFIXLEN + ns;
}
uint
convD2M(Dir *d, uchar *buf, uint nbuf)
{
uchar *p, *ebuf;
char *sv[4];
int i, ns, nsv[4], ss;
if(nbuf < BIT16SZ)
return 0;
p = buf;
ebuf = buf + nbuf;
sv[0] = d->name;
sv[1] = d->uid;
sv[2] = d->gid;
sv[3] = d->muid;
ns = 0;
for(i = 0; i < 4; i++){
if(sv[i])
nsv[i] = strlen(sv[i]);
else
nsv[i] = 0;
ns += nsv[i];
}
ss = STATFIXLEN + ns;
/* set size befor erroring, so user can know how much is needed */
/* note that length excludes count field itself */
PBIT16(p, ss-BIT16SZ);
p += BIT16SZ;
if(ss > nbuf)
return BIT16SZ;
PBIT16(p, d->type);
p += BIT16SZ;
PBIT32(p, d->dev);
p += BIT32SZ;
PBIT8(p, d->qid.type);
p += BIT8SZ;
PBIT32(p, d->qid.vers);
p += BIT32SZ;
PBIT64(p, d->qid.path);
p += BIT64SZ;
PBIT32(p, d->mode);
p += BIT32SZ;
PBIT32(p, d->atime);
p += BIT32SZ;
PBIT32(p, d->mtime);
p += BIT32SZ;
PBIT64(p, d->length);
p += BIT64SZ;
for(i = 0; i < 4; i++){
ns = nsv[i];
if(p + ns + BIT16SZ > ebuf)
return 0;
PBIT16(p, ns);
p += BIT16SZ;
if(ns)
memmove(p, sv[i], ns);
p += ns;
}
if(ss != p - buf)
return 0;
return p - buf;
}

94
lib9/convM2D.c Normal file
View File

@@ -0,0 +1,94 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
int
statcheck(uchar *buf, uint nbuf)
{
uchar *ebuf;
int i;
ebuf = buf + nbuf;
if(nbuf < STATFIXLEN || nbuf != BIT16SZ + GBIT16(buf))
return -1;
buf += STATFIXLEN - 4 * BIT16SZ;
for(i = 0; i < 4; i++){
if(buf + BIT16SZ > ebuf)
return -1;
buf += BIT16SZ + GBIT16(buf);
}
if(buf != ebuf)
return -1;
return 0;
}
static char nullstring[] = "";
uint
convM2D(uchar *buf, uint nbuf, Dir *d, char *strs)
{
uchar *p, *ebuf;
char *sv[4];
int i, ns;
if(nbuf < STATFIXLEN)
return 0;
p = buf;
ebuf = buf + nbuf;
p += BIT16SZ; /* ignore size */
d->type = GBIT16(p);
p += BIT16SZ;
d->dev = GBIT32(p);
p += BIT32SZ;
d->qid.type = GBIT8(p);
p += BIT8SZ;
d->qid.vers = GBIT32(p);
p += BIT32SZ;
d->qid.path = GBIT64(p);
p += BIT64SZ;
d->mode = GBIT32(p);
p += BIT32SZ;
d->atime = GBIT32(p);
p += BIT32SZ;
d->mtime = GBIT32(p);
p += BIT32SZ;
d->length = GBIT64(p);
p += BIT64SZ;
for(i = 0; i < 4; i++){
if(p + BIT16SZ > ebuf)
return 0;
ns = GBIT16(p);
p += BIT16SZ;
if(p + ns > ebuf)
return 0;
if(strs){
sv[i] = strs;
memmove(strs, p, ns);
strs += ns;
*strs++ = '\0';
}
p += ns;
}
if(strs){
d->name = sv[0];
d->uid = sv[1];
d->gid = sv[2];
d->muid = sv[3];
}else{
d->name = nullstring;
d->uid = nullstring;
d->gid = nullstring;
d->muid = nullstring;
}
return p - buf;
}

323
lib9/convM2S.c Normal file
View File

@@ -0,0 +1,323 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
static
uchar*
gstring(uchar *p, uchar *ep, char **s)
{
uint n;
if(p+BIT16SZ > ep)
return nil;
n = GBIT16(p);
p += BIT16SZ - 1;
if(p+n+1 > ep)
return nil;
/* move it down, on top of count, to make room for '\0' */
memmove(p, p + 1, n);
p[n] = '\0';
*s = (char*)p;
p += n+1;
return p;
}
static
uchar*
gqid(uchar *p, uchar *ep, Qid *q)
{
if(p+QIDSZ > ep)
return nil;
q->type = GBIT8(p);
p += BIT8SZ;
q->vers = GBIT32(p);
p += BIT32SZ;
q->path = GBIT64(p);
p += BIT64SZ;
return p;
}
/*
* no syntactic checks.
* three causes for error:
* 1. message size field is incorrect
* 2. input buffer too short for its own data (counts too long, etc.)
* 3. too many names or qids
* gqid() and gstring() return nil if they would reach beyond buffer.
* main switch statement checks range and also can fall through
* to test at end of routine.
*/
uint
convM2S(uchar *ap, uint nap, Fcall *f)
{
uchar *p, *ep;
uint i, size;
p = ap;
ep = p + nap;
if(p+BIT32SZ+BIT8SZ+BIT16SZ > ep)
return 0;
size = GBIT32(p);
p += BIT32SZ;
if(size < BIT32SZ+BIT8SZ+BIT16SZ)
return 0;
f->type = GBIT8(p);
p += BIT8SZ;
f->tag = GBIT16(p);
p += BIT16SZ;
switch(f->type)
{
default:
return 0;
case Tversion:
if(p+BIT32SZ > ep)
return 0;
f->msize = GBIT32(p);
p += BIT32SZ;
p = gstring(p, ep, &f->version);
break;
case Tflush:
if(p+BIT16SZ > ep)
return 0;
f->oldtag = GBIT16(p);
p += BIT16SZ;
break;
case Tauth:
if(p+BIT32SZ > ep)
return 0;
f->afid = GBIT32(p);
p += BIT32SZ;
p = gstring(p, ep, &f->uname);
if(p == nil)
break;
p = gstring(p, ep, &f->aname);
if(p == nil)
break;
break;
case Tattach:
if(p+BIT32SZ > ep)
return 0;
f->fid = GBIT32(p);
p += BIT32SZ;
if(p+BIT32SZ > ep)
return 0;
f->afid = GBIT32(p);
p += BIT32SZ;
p = gstring(p, ep, &f->uname);
if(p == nil)
break;
p = gstring(p, ep, &f->aname);
if(p == nil)
break;
break;
case Twalk:
if(p+BIT32SZ+BIT32SZ+BIT16SZ > ep)
return 0;
f->fid = GBIT32(p);
p += BIT32SZ;
f->newfid = GBIT32(p);
p += BIT32SZ;
f->nwname = GBIT16(p);
p += BIT16SZ;
if(f->nwname > MAXWELEM)
return 0;
for(i=0; i<f->nwname; i++){
p = gstring(p, ep, &f->wname[i]);
if(p == nil)
break;
}
break;
case Topen:
case Topenfd:
if(p+BIT32SZ+BIT8SZ > ep)
return 0;
f->fid = GBIT32(p);
p += BIT32SZ;
f->mode = GBIT8(p);
p += BIT8SZ;
break;
case Tcreate:
if(p+BIT32SZ > ep)
return 0;
f->fid = GBIT32(p);
p += BIT32SZ;
p = gstring(p, ep, &f->name);
if(p == nil)
break;
if(p+BIT32SZ+BIT8SZ > ep)
return 0;
f->perm = GBIT32(p);
p += BIT32SZ;
f->mode = GBIT8(p);
p += BIT8SZ;
break;
case Tread:
if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
return 0;
f->fid = GBIT32(p);
p += BIT32SZ;
f->offset = GBIT64(p);
p += BIT64SZ;
f->count = GBIT32(p);
p += BIT32SZ;
break;
case Twrite:
if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
return 0;
f->fid = GBIT32(p);
p += BIT32SZ;
f->offset = GBIT64(p);
p += BIT64SZ;
f->count = GBIT32(p);
p += BIT32SZ;
if(p+f->count > ep)
return 0;
f->data = (char*)p;
p += f->count;
break;
case Tclunk:
case Tremove:
if(p+BIT32SZ > ep)
return 0;
f->fid = GBIT32(p);
p += BIT32SZ;
break;
case Tstat:
if(p+BIT32SZ > ep)
return 0;
f->fid = GBIT32(p);
p += BIT32SZ;
break;
case Twstat:
if(p+BIT32SZ+BIT16SZ > ep)
return 0;
f->fid = GBIT32(p);
p += BIT32SZ;
f->nstat = GBIT16(p);
p += BIT16SZ;
if(p+f->nstat > ep)
return 0;
f->stat = p;
p += f->nstat;
break;
/*
*/
case Rversion:
if(p+BIT32SZ > ep)
return 0;
f->msize = GBIT32(p);
p += BIT32SZ;
p = gstring(p, ep, &f->version);
break;
case Rerror:
p = gstring(p, ep, &f->ename);
break;
case Rflush:
break;
case Rauth:
p = gqid(p, ep, &f->aqid);
if(p == nil)
break;
break;
case Rattach:
p = gqid(p, ep, &f->qid);
if(p == nil)
break;
break;
case Rwalk:
if(p+BIT16SZ > ep)
return 0;
f->nwqid = GBIT16(p);
p += BIT16SZ;
if(f->nwqid > MAXWELEM)
return 0;
for(i=0; i<f->nwqid; i++){
p = gqid(p, ep, &f->wqid[i]);
if(p == nil)
break;
}
break;
case Ropen:
case Ropenfd:
case Rcreate:
p = gqid(p, ep, &f->qid);
if(p == nil)
break;
if(p+BIT32SZ > ep)
return 0;
f->iounit = GBIT32(p);
p += BIT32SZ;
if(f->type == Ropenfd){
if(p+BIT32SZ > ep)
return 0;
f->unixfd = GBIT32(p);
p += BIT32SZ;
}
break;
case Rread:
if(p+BIT32SZ > ep)
return 0;
f->count = GBIT32(p);
p += BIT32SZ;
if(p+f->count > ep)
return 0;
f->data = (char*)p;
p += f->count;
break;
case Rwrite:
if(p+BIT32SZ > ep)
return 0;
f->count = GBIT32(p);
p += BIT32SZ;
break;
case Rclunk:
case Rremove:
break;
case Rstat:
if(p+BIT16SZ > ep)
return 0;
f->nstat = GBIT16(p);
p += BIT16SZ;
if(p+f->nstat > ep)
return 0;
f->stat = p;
p += f->nstat;
break;
case Rwstat:
break;
}
if(p==nil || p>ep)
return 0;
if(ap+size == p)
return size;
return 0;
}

399
lib9/convS2M.c Normal file
View File

@@ -0,0 +1,399 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
static
uchar*
pstring(uchar *p, char *s)
{
uint n;
if(s == nil){
PBIT16(p, 0);
p += BIT16SZ;
return p;
}
n = strlen(s);
PBIT16(p, n);
p += BIT16SZ;
memmove(p, s, n);
p += n;
return p;
}
static
uchar*
pqid(uchar *p, Qid *q)
{
PBIT8(p, q->type);
p += BIT8SZ;
PBIT32(p, q->vers);
p += BIT32SZ;
PBIT64(p, q->path);
p += BIT64SZ;
return p;
}
static
uint
stringsz(char *s)
{
if(s == nil)
return BIT16SZ;
return BIT16SZ+strlen(s);
}
uint
sizeS2M(Fcall *f)
{
uint n;
int i;
n = 0;
n += BIT32SZ; /* size */
n += BIT8SZ; /* type */
n += BIT16SZ; /* tag */
switch(f->type)
{
default:
return 0;
case Tversion:
n += BIT32SZ;
n += stringsz(f->version);
break;
case Tflush:
n += BIT16SZ;
break;
case Tauth:
n += BIT32SZ;
n += stringsz(f->uname);
n += stringsz(f->aname);
break;
case Tattach:
n += BIT32SZ;
n += BIT32SZ;
n += stringsz(f->uname);
n += stringsz(f->aname);
break;
case Twalk:
n += BIT32SZ;
n += BIT32SZ;
n += BIT16SZ;
for(i=0; i<f->nwname; i++)
n += stringsz(f->wname[i]);
break;
case Topen:
case Topenfd:
n += BIT32SZ;
n += BIT8SZ;
break;
case Tcreate:
n += BIT32SZ;
n += stringsz(f->name);
n += BIT32SZ;
n += BIT8SZ;
break;
case Tread:
n += BIT32SZ;
n += BIT64SZ;
n += BIT32SZ;
break;
case Twrite:
n += BIT32SZ;
n += BIT64SZ;
n += BIT32SZ;
n += f->count;
break;
case Tclunk:
case Tremove:
n += BIT32SZ;
break;
case Tstat:
n += BIT32SZ;
break;
case Twstat:
n += BIT32SZ;
n += BIT16SZ;
n += f->nstat;
break;
/*
*/
case Rversion:
n += BIT32SZ;
n += stringsz(f->version);
break;
case Rerror:
n += stringsz(f->ename);
break;
case Rflush:
break;
case Rauth:
n += QIDSZ;
break;
case Rattach:
n += QIDSZ;
break;
case Rwalk:
n += BIT16SZ;
n += f->nwqid*QIDSZ;
break;
case Ropen:
case Rcreate:
n += QIDSZ;
n += BIT32SZ;
break;
case Ropenfd:
n += QIDSZ;
n += BIT32SZ;
n += BIT32SZ;
break;
case Rread:
n += BIT32SZ;
n += f->count;
break;
case Rwrite:
n += BIT32SZ;
break;
case Rclunk:
break;
case Rremove:
break;
case Rstat:
n += BIT16SZ;
n += f->nstat;
break;
case Rwstat:
break;
}
return n;
}
uint
convS2M(Fcall *f, uchar *ap, uint nap)
{
uchar *p;
uint i, size;
size = sizeS2M(f);
if(size == 0)
return 0;
if(size > nap)
return 0;
p = (uchar*)ap;
PBIT32(p, size);
p += BIT32SZ;
PBIT8(p, f->type);
p += BIT8SZ;
PBIT16(p, f->tag);
p += BIT16SZ;
switch(f->type)
{
default:
return 0;
case Tversion:
PBIT32(p, f->msize);
p += BIT32SZ;
p = pstring(p, f->version);
break;
case Tflush:
PBIT16(p, f->oldtag);
p += BIT16SZ;
break;
case Tauth:
PBIT32(p, f->afid);
p += BIT32SZ;
p = pstring(p, f->uname);
p = pstring(p, f->aname);
break;
case Tattach:
PBIT32(p, f->fid);
p += BIT32SZ;
PBIT32(p, f->afid);
p += BIT32SZ;
p = pstring(p, f->uname);
p = pstring(p, f->aname);
break;
case Twalk:
PBIT32(p, f->fid);
p += BIT32SZ;
PBIT32(p, f->newfid);
p += BIT32SZ;
PBIT16(p, f->nwname);
p += BIT16SZ;
if(f->nwname > MAXWELEM)
return 0;
for(i=0; i<f->nwname; i++)
p = pstring(p, f->wname[i]);
break;
case Topen:
case Topenfd:
PBIT32(p, f->fid);
p += BIT32SZ;
PBIT8(p, f->mode);
p += BIT8SZ;
break;
case Tcreate:
PBIT32(p, f->fid);
p += BIT32SZ;
p = pstring(p, f->name);
PBIT32(p, f->perm);
p += BIT32SZ;
PBIT8(p, f->mode);
p += BIT8SZ;
break;
case Tread:
PBIT32(p, f->fid);
p += BIT32SZ;
PBIT64(p, f->offset);
p += BIT64SZ;
PBIT32(p, f->count);
p += BIT32SZ;
break;
case Twrite:
PBIT32(p, f->fid);
p += BIT32SZ;
PBIT64(p, f->offset);
p += BIT64SZ;
PBIT32(p, f->count);
p += BIT32SZ;
memmove(p, f->data, f->count);
p += f->count;
break;
case Tclunk:
case Tremove:
PBIT32(p, f->fid);
p += BIT32SZ;
break;
case Tstat:
PBIT32(p, f->fid);
p += BIT32SZ;
break;
case Twstat:
PBIT32(p, f->fid);
p += BIT32SZ;
PBIT16(p, f->nstat);
p += BIT16SZ;
memmove(p, f->stat, f->nstat);
p += f->nstat;
break;
/*
*/
case Rversion:
PBIT32(p, f->msize);
p += BIT32SZ;
p = pstring(p, f->version);
break;
case Rerror:
p = pstring(p, f->ename);
break;
case Rflush:
break;
case Rauth:
p = pqid(p, &f->aqid);
break;
case Rattach:
p = pqid(p, &f->qid);
break;
case Rwalk:
PBIT16(p, f->nwqid);
p += BIT16SZ;
if(f->nwqid > MAXWELEM)
return 0;
for(i=0; i<f->nwqid; i++)
p = pqid(p, &f->wqid[i]);
break;
case Ropen:
case Rcreate:
case Ropenfd:
p = pqid(p, &f->qid);
PBIT32(p, f->iounit);
p += BIT32SZ;
if(f->type == Ropenfd){
PBIT32(p, f->unixfd);
p += BIT32SZ;
}
break;
case Rread:
PBIT32(p, f->count);
p += BIT32SZ;
memmove(p, f->data, f->count);
p += f->count;
break;
case Rwrite:
PBIT32(p, f->count);
p += BIT32SZ;
break;
case Rclunk:
break;
case Rremove:
break;
case Rstat:
PBIT16(p, f->nstat);
p += BIT16SZ;
memmove(p, f->stat, f->nstat);
p += f->nstat;
break;
case Rwstat:
break;
}
if(size != p-ap)
return 0;
return size;
}

75
lib9/create.c Normal file
View File

@@ -0,0 +1,75 @@
#define _GNU_SOURCE /* for Linux O_DIRECT */
#include <u.h>
#define NOPLAN9DEFINES
#include <sys/file.h>
#include <unistd.h>
#include <fcntl.h>
#include <libc.h>
#include <sys/stat.h>
#ifndef O_DIRECT
#define O_DIRECT 0
#endif
int
p9create(char *path, int mode, ulong perm)
{
int fd, cexec, umode, rclose, lock, rdwr;
struct flock fl;
rdwr = mode&3;
lock = mode&OLOCK;
cexec = mode&OCEXEC;
rclose = mode&ORCLOSE;
mode &= ~(ORCLOSE|OCEXEC|OLOCK);
/* XXX should get mode mask right? */
fd = -1;
if(perm&DMDIR){
if(mode != OREAD){
werrstr("bad mode in directory create");
goto out;
}
if(mkdir(path, perm&0777) < 0)
goto out;
fd = open(path, O_RDONLY);
}else{
umode = (mode&3)|O_CREAT|O_TRUNC;
mode &= ~(3|OTRUNC);
if(mode&ODIRECT){
umode |= O_DIRECT;
mode &= ~ODIRECT;
}
if(mode&OEXCL){
umode |= O_EXCL;
mode &= ~OEXCL;
}
if(mode&OAPPEND){
umode |= O_APPEND;
mode &= ~OAPPEND;
}
if(mode){
werrstr("unsupported mode in create");
goto out;
}
fd = open(path, umode, perm);
}
out:
if(fd >= 0){
if(lock){
fl.l_type = (rdwr==OREAD) ? F_RDLCK : F_WRLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
if(fcntl(fd, F_SETLK, &fl) < 0){
close(fd);
werrstr("lock: %r");
return -1;
}
}
if(cexec)
fcntl(fd, F_SETFL, FD_CLOEXEC);
if(rclose)
remove(path);
}
return fd;
}

58
lib9/ctime.c Normal file
View File

@@ -0,0 +1,58 @@
#include <u.h>
#include <libc.h>
static
void
ct_numb(char *cp, int n)
{
cp[0] = ' ';
if(n >= 10)
cp[0] = (n/10)%10 + '0';
cp[1] = n%10 + '0';
}
char*
asctime(Tm *t)
{
int i;
char *ncp;
static char cbuf[30];
strcpy(cbuf, "Thu Jan 01 00:00:00 GMT 1970\n");
ncp = &"SunMonTueWedThuFriSat"[t->wday*3];
cbuf[0] = *ncp++;
cbuf[1] = *ncp++;
cbuf[2] = *ncp;
ncp = &"JanFebMarAprMayJunJulAugSepOctNovDec"[t->mon*3];
cbuf[4] = *ncp++;
cbuf[5] = *ncp++;
cbuf[6] = *ncp;
ct_numb(cbuf+8, t->mday);
ct_numb(cbuf+11, t->hour+100);
ct_numb(cbuf+14, t->min+100);
ct_numb(cbuf+17, t->sec+100);
ncp = t->zone;
for(i=0; i<3; i++)
if(ncp[i] == 0)
break;
for(; i<3; i++)
ncp[i] = '?';
ncp = t->zone;
cbuf[20] = *ncp++;
cbuf[21] = *ncp++;
cbuf[22] = *ncp;
if(t->year >= 100) {
cbuf[24] = '2';
cbuf[25] = '0';
}
ct_numb(cbuf+26, t->year+100);
return cbuf;
}
char*
ctime(long t)
{
return asctime(localtime(t));
}

125
lib9/date.c Normal file
View File

@@ -0,0 +1,125 @@
#include <u.h>
#include <stdlib.h> /* setenv etc. */
#define NOPLAN9DEFINES
#include <libc.h>
#include <time.h>
#define _HAVETIMEGM 1
#define _HAVETMZONE 1
#define _HAVETMTZOFF 1
#if defined(__linux__)
# undef _HAVETMZONE
# undef _HAVETMTZOFF
#elif defined(__sun__)
# undef _HAVETIMEGM
# undef _HAVETMZONE
# undef _HAVETMTZOFF
#endif
static Tm bigtm;
static void
tm2Tm(struct tm *tm, Tm *bigtm)
{
char *s;
memset(bigtm, 0, sizeof *bigtm);
bigtm->sec = tm->tm_sec;
bigtm->min = tm->tm_min;
bigtm->hour = tm->tm_hour;
bigtm->mday = tm->tm_mday;
bigtm->mon = tm->tm_mon;
bigtm->year = tm->tm_year;
bigtm->wday = tm->tm_wday;
strftime(bigtm->zone, sizeof bigtm->zone, "%Z", tm);
#ifdef _HAVETZOFF
bigtm->tzoff = tm->tm_gmtoff;
#endif
if(bigtm->zone[0] == 0){
s = getenv("TIMEZONE");
if(s){
strecpy(bigtm->zone, bigtm->zone+4, s);
free(s);
}
}
}
static void
Tm2tm(Tm *bigtm, struct tm *tm)
{
memset(tm, 0, sizeof *tm);
tm->tm_sec = bigtm->sec;
tm->tm_min = bigtm->min;
tm->tm_hour = bigtm->hour;
tm->tm_mday = bigtm->mday;
tm->tm_mon = bigtm->mon;
tm->tm_year = bigtm->year;
tm->tm_wday = bigtm->wday;
#ifdef _HAVETMZONE
tm->tm_zone = bigtm->zone;
#endif
#ifdef _HAVETZOFF
tm->tm_gmtoff = bigtm->tzoff;
#endif
}
Tm*
p9gmtime(long x)
{
time_t t;
struct tm tm;
t = (time_t)x;
tm = *gmtime(&t);
tm2Tm(&tm, &bigtm);
return &bigtm;
}
Tm*
p9localtime(long x)
{
time_t t;
struct tm tm;
t = (time_t)x;
tm = *localtime(&t);
tm2Tm(&tm, &bigtm);
return &bigtm;
}
#if !defined(_HAVETIMEGM)
static time_t
timegm(struct tm *tm)
{
time_t ret;
char *tz;
char *s;
tz = getenv("TZ");
putenv("TZ=");
tzset();
ret = mktime(tm);
if(tz){
s = smprint("TZ=%s", tz);
if(s)
putenv(s);
}
return ret;
}
#endif
long
p9tm2sec(Tm *bigtm)
{
struct tm tm;
Tm2tm(bigtm, &tm);
if(strcmp(bigtm->zone, "GMT") == 0 || strcmp(bigtm->zone, "UCT") == 0)
return timegm(&tm);
return mktime(&tm); /* local time zone */
}

166
lib9/debugmalloc.c Normal file
View File

@@ -0,0 +1,166 @@
#include <u.h>
#define NOPLAN9DEFINES
#include <libc.h>
/*
* The Unix libc routines cannot be trusted to do their own locking.
* Sad but apparently true.
*/
static Lock malloclock;
static int mallocpid;
/*
* The Unix mallocs don't do nearly enough error checking
* for my tastes. We'll waste another 24 bytes per guy so that
* we can. This is severely antisocial, since now free and p9free
* are not interchangeable.
*/
int debugmalloc;
#define Overhead (debugmalloc ? (6*sizeof(ulong)) : 0)
#define MallocMagic 0xA110C09
#define ReallocMagic 0xB110C09
#define CallocMagic 0xC110C09
#define FreeMagic 0xF533F533
#define CheckMagic 0
#define END "\x7F\x2E\x55\x23"
static void
whoops(void *v)
{
fprint(2, "bad malloc block %p\n", v);
abort();
}
static void*
mark(void *v, ulong pc, ulong n, ulong magic)
{
ulong *u;
char *p;
if(!debugmalloc)
return v;
if(v == nil)
return nil;
if(magic == FreeMagic || magic == CheckMagic){
u = (ulong*)((char*)v-4*sizeof(ulong));
if(u[0] != MallocMagic && u[0] != ReallocMagic && u[0] != CallocMagic)
whoops(v);
n = u[1];
p = (char*)v+n;
if(memcmp(p, END, 4) != 0)
whoops(v);
if(magic != CheckMagic){
u[0] = FreeMagic;
u[1] = u[2] = u[3] = pc;
if(n > 16){
u[4] = u[5] = u[6] = u[7] = pc;
memset((char*)v+16, 0xFB, n-16);
}
}
return u;
}else{
u = v;
u[0] = magic;
u[1] = n;
u[2] = 0;
u[3] = 0;
if(magic == ReallocMagic)
u[3] = pc;
else
u[2] = pc;
p = (char*)(u+4)+n;
memmove(p, END, 4);
return u+4;
}
}
void
setmalloctag(void *v, ulong t)
{
ulong *u;
if(!debugmalloc)
return;
if(v == nil)
return;
u = mark(v, 0, 0, 0);
u[2] = t;
}
void
setrealloctag(void *v, ulong t)
{
ulong *u;
if(!debugmalloc)
return;
if(v == nil)
return;
u = mark(v, 0, 0, 0);
u[3] = t;
}
void*
p9malloc(ulong n)
{
void *v;
if(n == 0)
n++;
//fprint(2, "%s %d malloc\n", argv0, getpid());
lock(&malloclock);
mallocpid = getpid();
v = malloc(n+Overhead);
v = mark(v, getcallerpc(&n), n, MallocMagic);
unlock(&malloclock);
//fprint(2, "%s %d donemalloc\n", argv0, getpid());
return v;
}
void
p9free(void *v)
{
if(v == nil)
return;
//fprint(2, "%s %d free\n", argv0, getpid());
lock(&malloclock);
mallocpid = getpid();
v = mark(v, getcallerpc(&v), 0, FreeMagic);
free(v);
unlock(&malloclock);
//fprint(2, "%s %d donefree\n", argv0, getpid());
}
void*
p9calloc(ulong a, ulong b)
{
void *v;
//fprint(2, "%s %d calloc\n", argv0, getpid());
lock(&malloclock);
mallocpid = getpid();
v = calloc(a*b+Overhead, 1);
v = mark(v, getcallerpc(&a), a*b, CallocMagic);
unlock(&malloclock);
//fprint(2, "%s %d donecalloc\n", argv0, getpid());
return v;
}
void*
p9realloc(void *v, ulong n)
{
//fprint(2, "%s %d realloc\n", argv0, getpid());
lock(&malloclock);
mallocpid = getpid();
v = mark(v, getcallerpc(&v), 0, CheckMagic);
v = realloc(v, n+Overhead);
v = mark(v, getcallerpc(&v), n, ReallocMagic);
unlock(&malloclock);
//fprint(2, "%s %d donerealloc\n", argv0, getpid());
return v;
}

137
lib9/dial.c Normal file
View File

@@ -0,0 +1,137 @@
#include <u.h>
#include <libc.h>
#undef accept
#undef announce
#undef dial
#undef setnetmtpt
#undef hangup
#undef listen
#undef netmkaddr
#undef reject
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/un.h>
#include <netdb.h>
#undef unix
#define unix xunix
int
p9dial(char *addr, char *local, char *dummy2, int *dummy3)
{
char *buf;
char *net, *unix;
u32int host;
int port;
int proto;
socklen_t sn;
int n;
struct sockaddr_in sa, sal;
struct sockaddr_un su;
int s;
if(dummy2 || dummy3){
werrstr("cannot handle extra arguments in dial");
return -1;
}
buf = strdup(addr);
if(buf == nil)
return -1;
if(p9dialparse(buf, &net, &unix, &host, &port) < 0){
free(buf);
return -1;
}
if(strcmp(net, "tcp") == 0)
proto = SOCK_STREAM;
else if(strcmp(net, "udp") == 0)
proto = SOCK_DGRAM;
else if(strcmp(net, "unix") == 0)
goto Unix;
else{
werrstr("can only handle tcp, udp, and unix: not %s", net);
free(buf);
return -1;
}
free(buf);
memset(&sa, 0, sizeof sa);
memmove(&sa.sin_addr, &host, 4);
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
if((s = socket(AF_INET, proto, 0)) < 0)
return -1;
if(local){
buf = strdup(local);
if(buf == nil){
close(s);
return -1;
}
if(p9dialparse(buf, &net, &unix, &host, &port) < 0){
badlocal:
free(buf);
close(s);
return -1;
}
if(unix){
werrstr("bad local address %s for dial %s", local, addr);
goto badlocal;
}
memset(&sal, 0, sizeof sal);
memmove(&sal.sin_addr, &local, 4);
sal.sin_family = AF_INET;
sal.sin_port = htons(port);
sn = sizeof n;
if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0
&& n == SOCK_STREAM){
n = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n);
}
if(bind(s, (struct sockaddr*)&sal, sizeof sal) < 0)
goto badlocal;
free(buf);
}
if(connect(s, (struct sockaddr*)&sa, sizeof sa) < 0){
close(s);
return -1;
}
if(proto == SOCK_STREAM){
int one = 1;
setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one);
}
return s;
Unix:
if(local){
werrstr("local address not supported on unix network");
free(buf);
return -1;
}
memset(&su, 0, sizeof su);
su.sun_family = AF_UNIX;
if(strlen(unix)+1 > sizeof su.sun_path){
werrstr("unix socket name too long");
free(buf);
return -1;
}
strcpy(su.sun_path, unix);
free(buf);
if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){
werrstr("socket: %r");
return -1;
}
if(connect(s, (struct sockaddr*)&su, sizeof su) < 0){
werrstr("connect %s: %r", su.sun_path);
close(s);
return -1;
}
return s;
}

29
lib9/dirfstat.c Normal file
View File

@@ -0,0 +1,29 @@
#include <u.h>
#define NOPLAN9DEFINES
#include <libc.h>
#include <sys/stat.h>
extern int _p9dir(struct stat*, struct stat*, char*, Dir*, char**, char*);
Dir*
dirfstat(int fd)
{
struct stat st;
int nstr;
Dir *d;
char *str, tmp[100];
if(fstat(fd, &st) < 0)
return nil;
snprint(tmp, sizeof tmp, "/dev/fd/%d", fd);
nstr = _p9dir(&st, &st, tmp, nil, nil, nil);
d = mallocz(sizeof(Dir)+nstr, 1);
if(d == nil)
return nil;
str = (char*)&d[1];
_p9dir(&st, &st, tmp, d, &str, str+nstr);
return d;
}

53
lib9/dirfwstat.c Normal file
View File

@@ -0,0 +1,53 @@
#define NOPLAN9DEFINES
#include <u.h>
#include <libc.h>
#include <sys/time.h>
#include <sys/stat.h>
#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__)
/* do nothing -- futimes exists and is fine */
#elif defined(__SunOS5_9__)
/* use futimesat */
static int
futimes(int fd, struct timeval *tv)
{
return futimesat(fd, 0, tv);
}
#else
/* provide dummy */
/* rename just in case -- linux provides an unusable one */
#undef futimes
#define futimes myfutimes
static int
futimes(int fd, struct timeval *tv)
{
werrstr("futimes not available");
return -1;
}
#endif
int
dirfwstat(int fd, Dir *dir)
{
int ret;
struct timeval tv[2];
ret = 0;
if(~dir->mode != 0){
if(fchmod(fd, dir->mode) < 0)
ret = -1;
}
if(~dir->mtime != 0){
tv[0].tv_sec = dir->mtime;
tv[0].tv_usec = 0;
tv[1].tv_sec = dir->mtime;
tv[1].tv_usec = 0;
if(futimes(fd, tv) < 0)
ret = -1;
}
return ret;
}

62
lib9/dirmodefmt.c Normal file
View File

@@ -0,0 +1,62 @@
#include <u.h>
#include <libc.h>
static char *modes[] =
{
"---",
"--x",
"-w-",
"-wx",
"r--",
"r-x",
"rw-",
"rwx",
};
static void
rwx(long m, char *s)
{
strncpy(s, modes[m], 3);
}
int
dirmodefmt(Fmt *f)
{
static char buf[16];
ulong m;
m = va_arg(f->args, ulong);
if(m & DMDIR)
buf[0]='d';
else if(m & DMAPPEND)
buf[0]='a';
else if(m & DMAUTH)
buf[0]='A';
else if(m & DMDEVICE)
buf[0] = 'D';
else if(m & DMSOCKET)
buf[0] = 'S';
else if(m & DMNAMEDPIPE)
buf[0] = 'P';
else
buf[0]='-';
/*
* It's a little weird to have DMSYMLINK conflict with DMEXCL
* here, but since you can have symlinks to any of the above
* things, this is a better display. Especially since we don't do
* DMEXCL on any of the supported systems.
*/
if(m & DMEXCL)
buf[1]='l';
else if(m & DMSYMLINK)
buf[1] = 'L';
else
buf[1]='-';
rwx((m>>6)&7, buf+2);
rwx((m>>3)&7, buf+5);
rwx((m>>0)&7, buf+8);
buf[11] = 0;
return fmtstrcpy(f, buf);
}

188
lib9/dirread.c Normal file
View File

@@ -0,0 +1,188 @@
#include <u.h>
#define NOPLAN9DEFINES
#include <libc.h>
#include <sys/stat.h>
#include <dirent.h>
extern int _p9dir(struct stat*, struct stat*, char*, Dir*, char**, char*);
#if defined(__linux__)
static int
mygetdents(int fd, struct dirent *buf, int n)
{
off_t off;
int nn;
/* This doesn't match the man page, but it works in Debian with a 2.2 kernel */
off = p9seek(fd, 0, 1);
nn = getdirentries(fd, (void*)buf, n, &off);
return nn;
}
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
static int
mygetdents(int fd, struct dirent *buf, int n)
{
long off;
return getdirentries(fd, (void*)buf, n, &off);
}
#elif defined(__sun__)
static int
mygetdents(int fd, struct dirent *buf, int n)
{
return getdents(fd, (void*)buf, n);
}
#endif
static int
countde(char *p, int n)
{
char *e;
int m;
struct dirent *de;
e = p+n;
m = 0;
while(p < e){
de = (struct dirent*)p;
if(de->d_reclen <= 4+2+2+1 || p+de->d_reclen > e)
break;
if(de->d_name[0]=='.' && de->d_name[1]==0)
de->d_name[0] = 0;
else if(de->d_name[0]=='.' && de->d_name[1]=='.' && de->d_name[2]==0)
de->d_name[0] = 0;
m++;
p += de->d_reclen;
}
return m;
}
static int
dirpackage(int fd, char *buf, int n, Dir **dp)
{
int oldwd;
char *p, *str, *estr;
int i, nstr, m;
struct dirent *de;
struct stat st, lst;
Dir *d;
n = countde(buf, n);
if(n <= 0)
return n;
if((oldwd = open(".", O_RDONLY)) < 0)
return -1;
if(fchdir(fd) < 0)
return -1;
p = buf;
nstr = 0;
for(i=0; i<n; i++){
de = (struct dirent*)p;
memset(&lst, 0, sizeof lst);
if(de->d_name[0] == 0)
/* nothing */ {}
else if(lstat(de->d_name, &lst) < 0)
de->d_name[0] = 0;
else{
st = lst;
if(S_ISLNK(lst.st_mode))
stat(de->d_name, &st);
nstr += _p9dir(&lst, &st, de->d_name, nil, nil, nil);
}
p += de->d_reclen;
}
d = malloc(sizeof(Dir)*n+nstr);
if(d == nil){
fchdir(oldwd);
close(oldwd);
return -1;
}
str = (char*)&d[n];
estr = str+nstr;
p = buf;
m = 0;
for(i=0; i<n; i++){
de = (struct dirent*)p;
if(de->d_name[0] != 0 && lstat(de->d_name, &lst) >= 0){
st = lst;
if((lst.st_mode&S_IFMT) == S_IFLNK)
stat(de->d_name, &st);
_p9dir(&lst, &st, de->d_name, &d[m++], &str, estr);
}
p += de->d_reclen;
}
fchdir(oldwd);
close(oldwd);
*dp = d;
return m;
}
long
dirread(int fd, Dir **dp)
{
char *buf;
struct stat st;
int n;
*dp = 0;
if(fstat(fd, &st) < 0)
return -1;
if(st.st_blksize < 8192)
st.st_blksize = 8192;
buf = malloc(st.st_blksize);
if(buf == nil)
return -1;
n = mygetdents(fd, (void*)buf, st.st_blksize);
if(n < 0){
free(buf);
return -1;
}
n = dirpackage(fd, buf, n, dp);
free(buf);
return n;
}
long
dirreadall(int fd, Dir **d)
{
uchar *buf, *nbuf;
long n, ts;
struct stat st;
if(fstat(fd, &st) < 0)
return -1;
if(st.st_blksize < 8192)
st.st_blksize = 8192;
buf = nil;
ts = 0;
for(;;){
nbuf = realloc(buf, ts+st.st_blksize);
if(nbuf == nil){
free(buf);
return -1;
}
buf = nbuf;
n = mygetdents(fd, (void*)(buf+ts), st.st_blksize);
if(n <= 0)
break;
ts += n;
}
if(ts >= 0)
ts = dirpackage(fd, (char*)buf, ts, d);
free(buf);
if(ts == 0 && n < 0)
return -1;
return ts;
}

32
lib9/dirstat.c Normal file
View File

@@ -0,0 +1,32 @@
#include <u.h>
#define NOPLAN9DEFINES
#include <libc.h>
#include <sys/stat.h>
extern int _p9dir(struct stat*, struct stat*, char*, Dir*, char**, char*);
Dir*
dirstat(char *file)
{
struct stat lst;
struct stat st;
int nstr;
Dir *d;
char *str;
if(lstat(file, &lst) < 0)
return nil;
st = lst;
if((lst.st_mode&S_IFMT) == S_IFLNK)
stat(file, &st);
nstr = _p9dir(&lst, &st, file, nil, nil, nil);
d = mallocz(sizeof(Dir)+nstr, 1);
if(d == nil)
return nil;
str = (char*)&d[1];
_p9dir(&lst, &st, file, d, &str, str+nstr);
return d;
}

19
lib9/dirwstat.c Normal file
View File

@@ -0,0 +1,19 @@
#include <u.h>
#define NOPLAN9DEFINES
#include <libc.h>
#include <sys/time.h>
#include <utime.h>
int
dirwstat(char *file, Dir *dir)
{
struct utimbuf ub;
/* BUG handle more */
if(~dir->mtime == 0)
return 0;
ub.actime = dir->mtime;
ub.modtime = dir->mtime;
return utime(file, &ub);
}

12
lib9/dup.c Normal file
View File

@@ -0,0 +1,12 @@
#include <u.h>
#include <libc.h>
#undef dup
int
p9dup(int old, int new)
{
if(new == -1)
return dup(old);
return dup2(old, new);
}

83
lib9/encodefmt.c Normal file
View File

@@ -0,0 +1,83 @@
#include <lib9.h>
#include <ctype.h>
#include <stdlib.h>
#include "fmt.h"
extern int enc64(char*, int, uchar*, int);
extern int enc32(char*, int, uchar*, int);
extern int enc16(char*, int, uchar*, int);
int
encodefmt(Fmt *f)
{
char *out;
char *buf, *p;
int len;
int ilen;
int rv;
uchar *b;
char obuf[64]; // rsc optimization
b = va_arg(f->args, uchar*);
if(b == 0)
return fmtstrcpy(f, "<nil>");
ilen = f->prec;
f->prec = 0;
if(!(f->flags&FmtPrec) || ilen < 0)
goto error;
f->flags &= ~FmtPrec;
switch(f->r){
case '<':
len = (8*ilen+4)/5 + 3;
break;
case '[':
len = (8*ilen+5)/6 + 4;
break;
case 'H':
len = 2*ilen + 1;
break;
default:
goto error;
}
if(len > sizeof(obuf)){
buf = malloc(len);
if(buf == nil)
goto error;
} else
buf = obuf;
// convert
out = buf;
switch(f->r){
case '<':
rv = enc32(out, len, b, ilen);
break;
case '[':
rv = enc64(out, len, b, ilen);
break;
case 'H':
rv = enc16(out, len, b, ilen);
if(rv >= 0 && (f->flags & FmtLong))
for(p = buf; *p; p++)
*p = tolower((uchar)*p);
break;
default:
rv = -1;
break;
}
if(rv < 0)
goto error;
fmtstrcpy(f, buf);
if(buf != obuf)
free(buf);
return 0;
error:
return fmtstrcpy(f, "<encodefmt>");
}

81
lib9/errstr.c Normal file
View File

@@ -0,0 +1,81 @@
/*
* We assume there's only one error buffer for the whole system.
* If you use ffork, you need to provide a _syserrstr. Since most
* people will use libthread (which provides a _syserrstr), this is
* okay.
*/
#include <u.h>
#include <errno.h>
#include <string.h>
#include <libc.h>
enum
{
EPLAN9 = 0x19283745,
};
char *(*_syserrstr)(void);
static char xsyserr[ERRMAX];
static char*
getsyserr(void)
{
char *s;
s = nil;
if(_syserrstr)
s = (*_syserrstr)();
if(s == nil)
s = xsyserr;
return s;
}
int
errstr(char *err, uint n)
{
char tmp[ERRMAX];
char *syserr;
strecpy(tmp, tmp+ERRMAX, err);
rerrstr(err, n);
syserr = getsyserr();
strecpy(syserr, syserr+ERRMAX, tmp);
errno = EPLAN9;
return 0;
}
void
rerrstr(char *err, uint n)
{
char *syserr;
syserr = getsyserr();
if(errno == EINTR)
strcpy(syserr, "interrupted");
else if(errno != EPLAN9)
strcpy(syserr, strerror(errno));
strecpy(err, err+n, syserr);
}
/* replaces __errfmt in libfmt */
int
__errfmt(Fmt *f)
{
if(errno == EPLAN9)
return fmtstrcpy(f, getsyserr());
return fmtstrcpy(f, strerror(errno));
}
void
werrstr(char *fmt, ...)
{
va_list arg;
char buf[ERRMAX];
va_start(arg, fmt);
vseprint(buf, buf+ERRMAX, fmt, arg);
va_end(arg);
errstr(buf, ERRMAX);
}

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