commit e5f0f74b8a25039c8e02ea45d97b979a79010ee3
Author: garbeam@wmii.de <unknown>
Date: Sun, 20 Nov 2005 18:27:27 +0200
initial import
Diffstat:
323 files changed, 41871 insertions(+), 0 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -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.
+
diff --git a/Makefile b/Makefile
@@ -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
diff --git a/README b/README
@@ -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
diff --git a/awk/Makefile b/awk/Makefile
@@ -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
diff --git a/awk/awk.1 b/awk/awk.1
@@ -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.
diff --git a/awk/awk.h b/awk/awk.h
@@ -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"
diff --git a/awk/awkgram.y b/awk/awkgram.y
@@ -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;
+ }
+ }
+}
diff --git a/awk/lex.c b/awk/lex.c
@@ -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]);
+}
diff --git a/awk/lib.c b/awk/lib.c
@@ -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;
+}
diff --git a/awk/main.c b/awk/main.c
@@ -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;
+}
diff --git a/awk/maketab.c b/awk/maketab.c
@@ -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;
+}
diff --git a/awk/parse.c b/awk/parse.c
@@ -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;
+}
diff --git a/awk/proctab.c b/awk/proctab.c
@@ -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];
+}
diff --git a/awk/proto.h b/awk/proto.h
@@ -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 *);
diff --git a/awk/re.c b/awk/re.c
@@ -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");
+}
diff --git a/awk/run.c b/awk/run.c
@@ -0,0 +1,1892 @@
+/****************************************************************
+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"
+
+#define tempfree(x) if (istemp(x)) tfree(x); else
+
+/*
+#undef tempfree
+
+void tempfree(Cell *p) {
+ if (p->ctype == OCELL && (p->csub < CUNK || p->csub > CFREE)) {
+ WARNING("bad csub %d in Cell %d %s",
+ p->csub, p->ctype, p->sval);
+ }
+ if (istemp(p))
+ tfree(p);
+}
+*/
+
+#ifdef _NFILE
+#ifndef FOPEN_MAX
+#define FOPEN_MAX _NFILE
+#endif
+#endif
+
+#ifndef FOPEN_MAX
+#define FOPEN_MAX 40 /* max number of open files */
+#endif
+
+#ifndef RAND_MAX
+#define RAND_MAX 32767 /* all that ansi guarantees */
+#endif
+
+jmp_buf env;
+extern int pairstack[];
+
+Node *winner = NULL; /* root of parse tree */
+Cell *tmps; /* free temporary cells for execution */
+
+static Cell truecell ={ OBOOL, BTRUE, 0, 0, 1.0, NUM };
+Cell *True = &truecell;
+static Cell falsecell ={ OBOOL, BFALSE, 0, 0, 0.0, NUM };
+Cell *False = &falsecell;
+static Cell breakcell ={ OJUMP, JBREAK, 0, 0, 0.0, NUM };
+Cell *jbreak = &breakcell;
+static Cell contcell ={ OJUMP, JCONT, 0, 0, 0.0, NUM };
+Cell *jcont = &contcell;
+static Cell nextcell ={ OJUMP, JNEXT, 0, 0, 0.0, NUM };
+Cell *jnext = &nextcell;
+static Cell nextfilecell ={ OJUMP, JNEXTFILE, 0, 0, 0.0, NUM };
+Cell *jnextfile = &nextfilecell;
+static Cell exitcell ={ OJUMP, JEXIT, 0, 0, 0.0, NUM };
+Cell *jexit = &exitcell;
+static Cell retcell ={ OJUMP, JRET, 0, 0, 0.0, NUM };
+Cell *jret = &retcell;
+static Cell tempcell ={ OCELL, CTEMP, 0, "", 0.0, NUM|STR|DONTFREE };
+
+Node *curnode = NULL; /* the node being executed, for debugging */
+
+/* buffer memory management */
+int adjbuf(char **pbuf, int *psiz, int minlen, int quantum, char **pbptr,
+ char *whatrtn)
+/* pbuf: address of pointer to buffer being managed
+ * psiz: address of buffer size variable
+ * minlen: minimum length of buffer needed
+ * quantum: buffer size quantum
+ * pbptr: address of movable pointer into buffer, or 0 if none
+ * whatrtn: name of the calling routine if failure should cause fatal error
+ *
+ * return 0 for realloc failure, !=0 for success
+ */
+{
+ if (minlen > *psiz) {
+ char *tbuf;
+ int rminlen = quantum ? minlen % quantum : 0;
+ int boff = pbptr ? *pbptr - *pbuf : 0;
+ /* round up to next multiple of quantum */
+ if (rminlen)
+ minlen += quantum - rminlen;
+ tbuf = (char *) realloc(*pbuf, minlen);
+ if (tbuf == NULL) {
+ if (whatrtn)
+ FATAL("out of memory in %s", whatrtn);
+ return 0;
+ }
+ *pbuf = tbuf;
+ *psiz = minlen;
+ if (pbptr)
+ *pbptr = tbuf + boff;
+ }
+ return 1;
+}
+
+void run(Node *a) /* execution of parse tree starts here */
+{
+ extern void stdinit(void);
+
+ stdinit();
+ execute(a);
+ closeall();
+}
+
+Cell *execute(Node *u) /* execute a node of the parse tree */
+{
+ Cell *(*proc)(Node **, int);
+ Cell *x;
+ Node *a;
+
+ if (u == NULL)
+ return(True);
+ for (a = u; ; a = a->nnext) {
+ curnode = a;
+ if (isvalue(a)) {
+ x = (Cell *) (a->narg[0]);
+ if (isfld(x) && !donefld)
+ fldbld();
+ else if (isrec(x) && !donerec)
+ recbld();
+ return(x);
+ }
+ if (notlegal(a->nobj)) /* probably a Cell* but too risky to print */
+ FATAL("illegal statement");
+ proc = proctab[a->nobj-FIRSTTOKEN];
+ x = (*proc)(a->narg, a->nobj);
+ if (isfld(x) && !donefld)
+ fldbld();
+ else if (isrec(x) && !donerec)
+ recbld();
+ if (isexpr(a))
+ return(x);
+ if (isjump(x))
+ return(x);
+ if (a->nnext == NULL)
+ return(x);
+ tempfree(x);
+ }
+}
+
+
+Cell *program(Node **a, int n) /* execute an awk program */
+{ /* a[0] = BEGIN, a[1] = body, a[2] = END */
+ Cell *x;
+
+ if (setjmp(env) != 0)
+ goto ex;
+ if (a[0]) { /* BEGIN */
+ x = execute(a[0]);
+ if (isexit(x))
+ return(True);
+ if (isjump(x))
+ FATAL("illegal break, continue, next or nextfile from BEGIN");
+ tempfree(x);
+ }
+ if (a[1] || a[2])
+ while (getrec(&record, &recsize, 1) > 0) {
+ x = execute(a[1]);
+ if (isexit(x))
+ break;
+ tempfree(x);
+ }
+ ex:
+ if (setjmp(env) != 0) /* handles exit within END */
+ goto ex1;
+ if (a[2]) { /* END */
+ x = execute(a[2]);
+ if (isbreak(x) || isnext(x) || iscont(x))
+ FATAL("illegal break, continue, next or nextfile from END");
+ tempfree(x);
+ }
+ ex1:
+ return(True);
+}
+
+struct Frame { /* stack frame for awk function calls */
+ int nargs; /* number of arguments in this call */
+ Cell *fcncell; /* pointer to Cell for function */
+ Cell **args; /* pointer to array of arguments after execute */
+ Cell *retval; /* return value */
+};
+
+#define NARGS 50 /* max args in a call */
+
+struct Frame *frame = NULL; /* base of stack frames; dynamically allocated */
+int nframe = 0; /* number of frames allocated */
+struct Frame *fp = NULL; /* frame pointer. bottom level unused */
+
+Cell *call(Node **a, int n) /* function call. very kludgy and fragile */
+{
+ static Cell newcopycell = { OCELL, CCOPY, 0, "", 0.0, NUM|STR|DONTFREE };
+ int i, ncall, ndef;
+ Node *x;
+ Cell *args[NARGS], *oargs[NARGS]; /* BUG: fixed size arrays */
+ Cell *y, *z, *fcn;
+ char *s;
+
+ fcn = execute(a[0]); /* the function itself */
+ s = fcn->nval;
+ if (!isfcn(fcn))
+ FATAL("calling undefined function %s", s);
+ if (frame == NULL) {
+ fp = frame = (struct Frame *) calloc(nframe += 100, sizeof(struct Frame));
+ if (frame == NULL)
+ FATAL("out of space for stack frames calling %s", s);
+ }
+ for (ncall = 0, x = a[1]; x != NULL; x = x->nnext) /* args in call */
+ ncall++;
+ ndef = (int) fcn->fval; /* args in defn */
+ dprintf( ("calling %s, %d args (%d in defn), fp=%d\n", s, ncall, ndef, (int) (fp-frame)) );
+ if (ncall > ndef)
+ WARNING("function %s called with %d args, uses only %d",
+ s, ncall, ndef);
+ if (ncall + ndef > NARGS)
+ FATAL("function %s has %d arguments, limit %d", s, ncall+ndef, NARGS);
+ for (i = 0, x = a[1]; x != NULL; i++, x = x->nnext) { /* get call args */
+ dprintf( ("evaluate args[%d], fp=%d:\n", i, (int) (fp-frame)) );
+ y = execute(x);
+ oargs[i] = y;
+ dprintf( ("args[%d]: %s %f <%s>, t=%o\n",
+ i, y->nval, y->fval, isarr(y) ? "(array)" : y->sval, y->tval) );
+ if (isfcn(y))
+ FATAL("can't use function %s as argument in %s", y->nval, s);
+ if (isarr(y))
+ args[i] = y; /* arrays by ref */
+ else
+ args[i] = copycell(y);
+ tempfree(y);
+ }
+ for ( ; i < ndef; i++) { /* add null args for ones not provided */
+ args[i] = gettemp();
+ *args[i] = newcopycell;
+ }
+ fp++; /* now ok to up frame */
+ if (fp >= frame + nframe) {
+ int dfp = fp - frame; /* old index */
+ frame = (struct Frame *)
+ realloc((char *) frame, (nframe += 100) * sizeof(struct Frame));
+ if (frame == NULL)
+ FATAL("out of space for stack frames in %s", s);
+ fp = frame + dfp;
+ }
+ fp->fcncell = fcn;
+ fp->args = args;
+ fp->nargs = ndef; /* number defined with (excess are locals) */
+ fp->retval = gettemp();
+
+ dprintf( ("start exec of %s, fp=%d\n", s, (int) (fp-frame)) );
+ y = execute((Node *)(fcn->sval)); /* execute body */
+ dprintf( ("finished exec of %s, fp=%d\n", s, (int) (fp-frame)) );
+
+ for (i = 0; i < ndef; i++) {
+ Cell *t = fp->args[i];
+ if (isarr(t)) {
+ if (t->csub == CCOPY) {
+ if (i >= ncall) {
+ freesymtab(t);
+ t->csub = CTEMP;
+ tempfree(t);
+ } else {
+ oargs[i]->tval = t->tval;
+ oargs[i]->tval &= ~(STR|NUM|DONTFREE);
+ oargs[i]->sval = t->sval;
+ tempfree(t);
+ }
+ }
+ } else if (t != y) { /* kludge to prevent freeing twice */
+ t->csub = CTEMP;
+ tempfree(t);
+ }
+ }
+ tempfree(fcn);
+ if (isexit(y) || isnext(y) || isnextfile(y))
+ return y;
+ tempfree(y); /* this can free twice! */
+ z = fp->retval; /* return value */
+ dprintf( ("%s returns %g |%s| %o\n", s, getfval(z), getsval(z), z->tval) );
+ fp--;
+ return(z);
+}
+
+Cell *copycell(Cell *x) /* make a copy of a cell in a temp */
+{
+ Cell *y;
+
+ y = gettemp();
+ y->csub = CCOPY; /* prevents freeing until call is over */
+ y->nval = x->nval; /* BUG? */
+ y->sval = x->sval ? tostring(x->sval) : NULL;
+ y->fval = x->fval;
+ y->tval = x->tval & ~(CON|FLD|REC|DONTFREE); /* copy is not constant or field */
+ /* is DONTFREE right? */
+ return y;
+}
+
+Cell *arg(Node **a, int n) /* nth argument of a function */
+{
+
+ n = ptoi(a[0]); /* argument number, counting from 0 */
+ dprintf( ("arg(%d), fp->nargs=%d\n", n, fp->nargs) );
+ if (n+1 > fp->nargs)
+ FATAL("argument #%d of function %s was not supplied",
+ n+1, fp->fcncell->nval);
+ return fp->args[n];
+}
+
+Cell *jump(Node **a, int n) /* break, continue, next, nextfile, return */
+{
+ Cell *y;
+
+ switch (n) {
+ case EXIT:
+ if (a[0] != NULL) {
+ y = execute(a[0]);
+ errorflag = (int) getfval(y);
+ tempfree(y);
+ }
+ longjmp(env, 1);
+ case RETURN:
+ if (a[0] != NULL) {
+ y = execute(a[0]);
+ if ((y->tval & (STR|NUM)) == (STR|NUM)) {
+ setsval(fp->retval, getsval(y));
+ fp->retval->fval = getfval(y);
+ fp->retval->tval |= NUM;
+ }
+ else if (y->tval & STR)
+ setsval(fp->retval, getsval(y));
+ else if (y->tval & NUM)
+ setfval(fp->retval, getfval(y));
+ else /* can't happen */
+ FATAL("bad type variable %d", y->tval);
+ tempfree(y);
+ }
+ return(jret);
+ case NEXT:
+ return(jnext);
+ case NEXTFILE:
+ nextfile();
+ return(jnextfile);
+ case BREAK:
+ return(jbreak);
+ case CONTINUE:
+ return(jcont);
+ default: /* can't happen */
+ FATAL("illegal jump type %d", n);
+ }
+ return 0; /* not reached */
+}
+
+Cell *getline(Node **a, int n) /* get next line from specific input */
+{ /* a[0] is variable, a[1] is operator, a[2] is filename */
+ Cell *r, *x;
+ extern Cell **fldtab;
+ FILE *fp;
+ char *buf;
+ int bufsize = recsize;
+ int mode;
+
+ if ((buf = (char *) malloc(bufsize)) == NULL)
+ FATAL("out of memory in getline");
+
+ fflush(stdout); /* in case someone is waiting for a prompt */
+ r = gettemp();
+ if (a[1] != NULL) { /* getline < file */
+ x = execute(a[2]); /* filename */
+ mode = ptoi(a[1]);
+ if (mode == '|') /* input pipe */
+ mode = LE; /* arbitrary flag */
+ fp = openfile(mode, getsval(x));
+ tempfree(x);
+ if (fp == NULL)
+ n = -1;
+ else
+ n = readrec(&buf, &bufsize, fp);
+ if (n <= 0) {
+ ;
+ } else if (a[0] != NULL) { /* getline var <file */
+ x = execute(a[0]);
+ setsval(x, buf);
+ tempfree(x);
+ } else { /* getline <file */
+ setsval(fldtab[0], buf);
+ if (is_number(fldtab[0]->sval)) {
+ fldtab[0]->fval = atof(fldtab[0]->sval);
+ fldtab[0]->tval |= NUM;
+ }
+ }
+ } else { /* bare getline; use current input */
+ if (a[0] == NULL) /* getline */
+ n = getrec(&record, &recsize, 1);
+ else { /* getline var */
+ n = getrec(&buf, &bufsize, 0);
+ x = execute(a[0]);
+ setsval(x, buf);
+ tempfree(x);
+ }
+ }
+ setfval(r, (Awkfloat) n);
+ free(buf);
+ return r;
+}
+
+Cell *getnf(Node **a, int n) /* get NF */
+{
+ if (donefld == 0)
+ fldbld();
+ return (Cell *) a[0];
+}
+
+Cell *array(Node **a, int n) /* a[0] is symtab, a[1] is list of subscripts */
+{
+ Cell *x, *y, *z;
+ char *s;
+ Node *np;
+ char *buf;
+ int bufsz = recsize;
+ int nsub = strlen(*SUBSEP);
+
+ if ((buf = (char *) malloc(bufsz)) == NULL)
+ FATAL("out of memory in array");
+
+ x = execute(a[0]); /* Cell* for symbol table */
+ buf[0] = 0;
+ for (np = a[1]; np; np = np->nnext) {
+ y = execute(np); /* subscript */
+ s = getsval(y);
+ if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, 0))
+ FATAL("out of memory for %s[%s...]", x->nval, buf);
+ strcat(buf, s);
+ if (np->nnext)
+ strcat(buf, *SUBSEP);
+ tempfree(y);
+ }
+ if (!isarr(x)) {
+ dprintf( ("making %s into an array\n", x->nval) );
+ if (freeable(x))
+ xfree(x->sval);
+ x->tval &= ~(STR|NUM|DONTFREE);
+ x->tval |= ARR;
+ x->sval = (char *) makesymtab(NSYMTAB);
+ }
+ z = setsymtab(buf, "", 0.0, STR|NUM, (Array *) x->sval);
+ z->ctype = OCELL;
+ z->csub = CVAR;
+ tempfree(x);
+ free(buf);
+ return(z);
+}
+
+Cell *awkdelete(Node **a, int n) /* a[0] is symtab, a[1] is list of subscripts */
+{
+ Cell *x, *y;
+ Node *np;
+ char *s;
+ int nsub = strlen(*SUBSEP);
+
+ x = execute(a[0]); /* Cell* for symbol table */
+ if (!isarr(x))
+ return True;
+ if (a[1] == 0) { /* delete the elements, not the table */
+ freesymtab(x);
+ x->tval &= ~STR;
+ x->tval |= ARR;
+ x->sval = (char *) makesymtab(NSYMTAB);
+ } else {
+ int bufsz = recsize;
+ char *buf;
+ if ((buf = (char *) malloc(bufsz)) == NULL)
+ FATAL("out of memory in adelete");
+ buf[0] = 0;
+ for (np = a[1]; np; np = np->nnext) {
+ y = execute(np); /* subscript */
+ s = getsval(y);
+ if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, 0))
+ FATAL("out of memory deleting %s[%s...]", x->nval, buf);
+ strcat(buf, s);
+ if (np->nnext)
+ strcat(buf, *SUBSEP);
+ tempfree(y);
+ }
+ freeelem(x, buf);
+ free(buf);
+ }
+ tempfree(x);
+ return True;
+}
+
+Cell *intest(Node **a, int n) /* a[0] is index (list), a[1] is symtab */
+{
+ Cell *x, *ap, *k;
+ Node *p;
+ char *buf;
+ char *s;
+ int bufsz = recsize;
+ int nsub = strlen(*SUBSEP);
+
+ ap = execute(a[1]); /* array name */
+ if (!isarr(ap)) {
+ dprintf( ("making %s into an array\n", ap->nval) );
+ if (freeable(ap))
+ xfree(ap->sval);
+ ap->tval &= ~(STR|NUM|DONTFREE);
+ ap->tval |= ARR;
+ ap->sval = (char *) makesymtab(NSYMTAB);
+ }
+ if ((buf = (char *) malloc(bufsz)) == NULL) {
+ FATAL("out of memory in intest");
+ }
+ buf[0] = 0;
+ for (p = a[0]; p; p = p->nnext) {
+ x = execute(p); /* expr */
+ s = getsval(x);
+ if (!adjbuf(&buf, &bufsz, strlen(buf)+strlen(s)+nsub+1, recsize, 0, 0))
+ FATAL("out of memory deleting %s[%s...]", x->nval, buf);
+ strcat(buf, s);
+ tempfree(x);
+ if (p->nnext)
+ strcat(buf, *SUBSEP);
+ }
+ k = lookup(buf, (Array *) ap->sval);
+ tempfree(ap);
+ free(buf);
+ if (k == NULL)
+ return(False);
+ else
+ return(True);
+}
+
+
+Cell *matchop(Node **a, int n) /* ~ and match() */
+{
+ Cell *x, *y;
+ char *s, *t;
+ int i;
+ void *p;
+
+ x = execute(a[1]); /* a[1] = target text */
+ s = getsval(x);
+ if (a[0] == 0) /* a[1] == 0: already-compiled reg expr */
+ p = (void *) a[2];
+ else {
+ y = execute(a[2]); /* a[2] = regular expr */
+ t = getsval(y);
+ p = compre(t);
+ tempfree(y);
+ }
+ if (n == MATCHFCN)
+ i = pmatch(p, s, s);
+ else
+ i = match(p, s, s);
+ tempfree(x);
+ if (n == MATCHFCN) {
+ int start = countposn(s, patbeg-s)+1;
+ if (patlen < 0)
+ start = 0;
+ setfval(rstartloc, (Awkfloat) start);
+ setfval(rlengthloc, (Awkfloat) countposn(patbeg, patlen));
+ x = gettemp();
+ x->tval = NUM;
+ x->fval = start;
+ return x;
+ } else if ((n == MATCH && i == 1) || (n == NOTMATCH && i == 0))
+ return(True);
+ else
+ return(False);
+}
+
+
+Cell *boolop(Node **a, int n) /* a[0] || a[1], a[0] && a[1], !a[0] */
+{
+ Cell *x, *y;
+ int i;
+
+ x = execute(a[0]);
+ i = istrue(x);
+ tempfree(x);
+ switch (n) {
+ case BOR:
+ if (i) return(True);
+ y = execute(a[1]);
+ i = istrue(y);
+ tempfree(y);
+ if (i) return(True);
+ else return(False);
+ case AND:
+ if ( !i ) return(False);
+ y = execute(a[1]);
+ i = istrue(y);
+ tempfree(y);
+ if (i) return(True);
+ else return(False);
+ case NOT:
+ if (i) return(False);
+ else return(True);
+ default: /* can't happen */
+ FATAL("unknown boolean operator %d", n);
+ }
+ return 0; /*NOTREACHED*/
+}
+
+Cell *relop(Node **a, int n) /* a[0 < a[1], etc. */
+{
+ int i;
+ Cell *x, *y;
+ Awkfloat j;
+
+ x = execute(a[0]);
+ y = execute(a[1]);
+ if (x->tval&NUM && y->tval&NUM) {
+ j = x->fval - y->fval;
+ i = j<0? -1: (j>0? 1: 0);
+ } else {
+ i = strcmp(getsval(x), getsval(y));
+ }
+ tempfree(x);
+ tempfree(y);
+ switch (n) {
+ case LT: if (i<0) return(True);
+ else return(False);
+ case LE: if (i<=0) return(True);
+ else return(False);
+ case NE: if (i!=0) return(True);
+ else return(False);
+ case EQ: if (i == 0) return(True);
+ else return(False);
+ case GE: if (i>=0) return(True);
+ else return(False);
+ case GT: if (i>0) return(True);
+ else return(False);
+ default: /* can't happen */
+ FATAL("unknown relational operator %d", n);
+ }
+ return 0; /*NOTREACHED*/
+}
+
+void tfree(Cell *a) /* free a tempcell */
+{
+ if (freeable(a)) {
+ dprintf( ("freeing %s %s %o\n", a->nval, a->sval, a->tval) );
+ xfree(a->sval);
+ }
+ if (a == tmps)
+ FATAL("tempcell list is curdled");
+ a->cnext = tmps;
+ tmps = a;
+}
+
+Cell *gettemp(void) /* get a tempcell */
+{ int i;
+ Cell *x;
+
+ if (!tmps) {
+ tmps = (Cell *) calloc(100, sizeof(Cell));
+ if (!tmps)
+ FATAL("out of space for temporaries");
+ for(i = 1; i < 100; i++)
+ tmps[i-1].cnext = &tmps[i];
+ tmps[i-1].cnext = 0;
+ }
+ x = tmps;
+ tmps = x->cnext;
+ *x = tempcell;
+ return(x);
+}
+
+Cell *indirect(Node **a, int n) /* $( a[0] ) */
+{
+ Cell *x;
+ int m;
+ char *s;
+
+ x = execute(a[0]);
+ m = (int) getfval(x);
+ if (m == 0 && !is_number(s = getsval(x))) /* suspicion! */
+ FATAL("illegal field $(%s), name \"%s\"", s, x->nval);
+ /* BUG: can x->nval ever be null??? */
+ tempfree(x);
+ x = fieldadr(m);
+ x->ctype = OCELL; /* BUG? why are these needed? */
+ x->csub = CFLD;
+ return(x);
+}
+
+Cell *substr(Node **a, int nnn) /* substr(a[0], a[1], a[2]) */
+{
+ int k, m, n;
+ char *s, *p;
+ int temp;
+ Cell *x, *y, *z = 0;
+
+ x = execute(a[0]);
+ y = execute(a[1]);
+ if (a[2] != 0)
+ z = execute(a[2]);
+ s = getsval(x);
+ k = countposn(s, strlen(s)) + 1;
+ if (k <= 1) {
+ tempfree(x);
+ tempfree(y);
+ if (a[2] != 0)
+ tempfree(z);
+ x = gettemp();
+ setsval(x, "");
+ return(x);
+ }
+ m = (int) getfval(y);
+ if (m <= 0)
+ m = 1;
+ else if (m > k)
+ m = k;
+ tempfree(y);
+ if (a[2] != 0) {
+ n = (int) getfval(z);
+ tempfree(z);
+ } else
+ n = k - 1;
+ if (n < 0)
+ n = 0;
+ else if (n > k - m)
+ n = k - m;
+ dprintf( ("substr: m=%d, n=%d, s=%s\n", m, n, s) );
+ y = gettemp();
+ while (*s && --m)
+ s += mblen(s, k);
+ for (p = s; *p && n--; p += mblen(p, k))
+ ;
+ temp = *p; /* with thanks to John Linderman */
+ *p = '\0';
+ setsval(y, s);
+ *p = temp;
+ tempfree(x);
+ return(y);
+}
+
+Cell *sindex(Node **a, int nnn) /* index(a[0], a[1]) */
+{
+ Cell *x, *y, *z;
+ char *s1, *s2, *p1, *p2, *q;
+ Awkfloat v = 0.0;
+
+ x = execute(a[0]);
+ s1 = getsval(x);
+ y = execute(a[1]);
+ s2 = getsval(y);
+
+ z = gettemp();
+ for (p1 = s1; *p1 != '\0'; p1++) {
+ for (q=p1, p2=s2; *p2 != '\0' && *q == *p2; q++, p2++)
+ ;
+ if (*p2 == '\0') {
+ v = (Awkfloat) countposn(s1, p1-s1) + 1; /* origin 1 */
+ break;
+ }
+ }
+ tempfree(x);
+ tempfree(y);
+ setfval(z, v);
+ return(z);
+}
+
+#define MAXNUMSIZE 50
+
+int format(char **pbuf, int *pbufsize, char *s, Node *a) /* printf-like conversions */
+{
+ char *fmt;
+ char *p, *t, *os;
+ Cell *x;
+ int flag = 0, n;
+ int fmtwd; /* format width */
+ int fmtsz = recsize;
+ char *buf = *pbuf;
+ int bufsize = *pbufsize;
+
+ os = s;
+ p = buf;
+ if ((fmt = (char *) malloc(fmtsz)) == NULL)
+ FATAL("out of memory in format()");
+ while (*s) {
+ adjbuf(&buf, &bufsize, MAXNUMSIZE+1+p-buf, recsize, &p, "format");
+ if (*s != '%') {
+ *p++ = *s++;
+ continue;
+ }
+ if (*(s+1) == '%') {
+ *p++ = '%';
+ s += 2;
+ continue;
+ }
+ /* have to be real careful in case this is a huge number, eg, %100000d */
+ fmtwd = atoi(s+1);
+ if (fmtwd < 0)
+ fmtwd = -fmtwd;
+ adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format");
+ for (t = fmt; (*t++ = *s) != '\0'; s++) {
+ if (!adjbuf(&fmt, &fmtsz, MAXNUMSIZE+1+t-fmt, recsize, &t, 0))
+ FATAL("format item %.30s... ran format() out of memory", os);
+ if (isalpha(*s) && *s != 'l' && *s != 'h' && *s != 'L')
+ break; /* the ansi panoply */
+ if (*s == '*') {
+ x = execute(a);
+ a = a->nnext;
+ sprintf(t-1, "%d", fmtwd=(int) getfval(x));
+ if (fmtwd < 0)
+ fmtwd = -fmtwd;
+ adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format");
+ t = fmt + strlen(fmt);
+ tempfree(x);
+ }
+ }
+ *t = '\0';
+ if (fmtwd < 0)
+ fmtwd = -fmtwd;
+ adjbuf(&buf, &bufsize, fmtwd+1+p-buf, recsize, &p, "format");
+
+ switch (*s) {
+ case 'f': case 'e': case 'g': case 'E': case 'G':
+ flag = 1;
+ break;
+ case 'd': case 'i':
+ flag = 2;
+ if(*(s-1) == 'l') break;
+ *(t-1) = 'l';
+ *t = 'd';
+ *++t = '\0';
+ break;
+ case 'o': case 'x': case 'X': case 'u':
+ flag = *(s-1) == 'l' ? 2 : 3;
+ break;
+ case 's':
+ flag = 4;
+ break;
+ case 'c':
+ flag = 5;
+ break;
+ default:
+ WARNING("weird printf conversion %s", fmt);
+ flag = 0;
+ break;
+ }
+ if (a == NULL)
+ FATAL("not enough args in printf(%s)", os);
+ x = execute(a);
+ a = a->nnext;
+ n = MAXNUMSIZE;
+ if (fmtwd > n)
+ n = fmtwd;
+ adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, "format");
+ switch (flag) {
+ case 0: sprintf(p, "%s", fmt); /* unknown, so dump it too */
+ t = getsval(x);
+ n = strlen(t);
+ if (fmtwd > n)
+ n = fmtwd;
+ adjbuf(&buf, &bufsize, 1+strlen(p)+n+p-buf, recsize, &p, "format");
+ p += strlen(p);
+ sprintf(p, "%s", t);
+ break;
+ case 1: sprintf(p, fmt, getfval(x)); break;
+ case 2: sprintf(p, fmt, (long) getfval(x)); break;
+ case 3: sprintf(p, fmt, (int) getfval(x)); break;
+ case 4:
+ t = getsval(x);
+ n = strlen(t);
+ if (fmtwd > n)
+ n = fmtwd;
+ if (!adjbuf(&buf, &bufsize, 1+n+p-buf, recsize, &p, 0))
+ FATAL("huge string/format (%d chars) in printf %.30s... ran format() out of memory", n, t);
+ sprintf(p, fmt, t);
+ break;
+ case 5:
+ if (isnum(x)) {
+ if (getfval(x))
+ sprintf(p, fmt, (int) getfval(x));
+ else
+ *p++ = '\0';
+ } else
+ sprintf(p, fmt, getsval(x)[0]);
+ break;
+ }
+ tempfree(x);
+ p += strlen(p);
+ s++;
+ }
+ *p = '\0';
+ free(fmt);
+ for ( ; a; a = a->nnext) /* evaluate any remaining args */
+ execute(a);
+ *pbuf = buf;
+ *pbufsize = bufsize;
+ return p - buf;
+}
+
+Cell *awksprintf(Node **a, int n) /* sprintf(a[0]) */
+{
+ Cell *x;
+ Node *y;
+ char *buf;
+ int bufsz=3*recsize;
+
+ if ((buf = (char *) malloc(bufsz)) == NULL)
+ FATAL("out of memory in awksprintf");
+ y = a[0]->nnext;
+ x = execute(a[0]);
+ if (format(&buf, &bufsz, getsval(x), y) == -1)
+ FATAL("sprintf string %.30s... too long. can't happen.", buf);
+ tempfree(x);
+ x = gettemp();
+ x->sval = buf;
+ x->tval = STR;
+ return(x);
+}
+
+Cell *awkprintf(Node **a, int n) /* printf */
+{ /* a[0] is list of args, starting with format string */
+ /* a[1] is redirection operator, a[2] is redirection file */
+ FILE *fp;
+ Cell *x;
+ Node *y;
+ char *buf;
+ int len;
+ int bufsz=3*recsize;
+
+ if ((buf = (char *) malloc(bufsz)) == NULL)
+ FATAL("out of memory in awkprintf");
+ y = a[0]->nnext;
+ x = execute(a[0]);
+ if ((len = format(&buf, &bufsz, getsval(x), y)) == -1)
+ FATAL("printf string %.30s... too long. can't happen.", buf);
+ tempfree(x);
+ if (a[1] == NULL) {
+ /* fputs(buf, stdout); */
+ fwrite(buf, len, 1, stdout);
+ if (ferror(stdout))
+ FATAL("write error on stdout");
+ } else {
+ fp = redirect(ptoi(a[1]), a[2]);
+ /* fputs(buf, fp); */
+ fwrite(buf, len, 1, fp);
+ fflush(fp);
+ if (ferror(fp))
+ FATAL("write error on %s", filename(fp));
+ }
+ free(buf);
+ return(True);
+}
+
+Cell *arith(Node **a, int n) /* a[0] + a[1], etc. also -a[0] */
+{
+ Awkfloat i, j = 0;
+ double v;
+ Cell *x, *y, *z;
+
+ x = execute(a[0]);
+ i = getfval(x);
+ tempfree(x);
+ if (n != UMINUS) {
+ y = execute(a[1]);
+ j = getfval(y);
+ tempfree(y);
+ }
+ z = gettemp();
+ switch (n) {
+ case ADD:
+ i += j;
+ break;
+ case MINUS:
+ i -= j;
+ break;
+ case MULT:
+ i *= j;
+ break;
+ case DIVIDE:
+ if (j == 0)
+ FATAL("division by zero");
+ i /= j;
+ break;
+ case MOD:
+ if (j == 0)
+ FATAL("division by zero in mod");
+ modf(i/j, &v);
+ i = i - j * v;
+ break;
+ case UMINUS:
+ i = -i;
+ break;
+ case POWER:
+ if (j >= 0 && modf(j, &v) == 0.0) /* pos integer exponent */
+ i = ipow(i, (int) j);
+ else
+ i = errcheck(pow(i, j), "pow");
+ break;
+ default: /* can't happen */
+ FATAL("illegal arithmetic operator %d", n);
+ }
+ setfval(z, i);
+ return(z);
+}
+
+double ipow(double x, int n) /* x**n. ought to be done by pow, but isn't always */
+{
+ double v;
+
+ if (n <= 0)
+ return 1;
+ v = ipow(x, n/2);
+ if (n % 2 == 0)
+ return v * v;
+ else
+ return x * v * v;
+}
+
+Cell *incrdecr(Node **a, int n) /* a[0]++, etc. */
+{
+ Cell *x, *z;
+ int k;
+ Awkfloat xf;
+
+ x = execute(a[0]);
+ xf = getfval(x);
+ k = (n == PREINCR || n == POSTINCR) ? 1 : -1;
+ if (n == PREINCR || n == PREDECR) {
+ setfval(x, xf + k);
+ return(x);
+ }
+ z = gettemp();
+ setfval(z, xf);
+ setfval(x, xf + k);
+ tempfree(x);
+ return(z);
+}
+
+Cell *assign(Node **a, int n) /* a[0] = a[1], a[0] += a[1], etc. */
+{ /* this is subtle; don't muck with it. */
+ Cell *x, *y;
+ Awkfloat xf, yf;
+ double v;
+
+ y = execute(a[1]);
+ x = execute(a[0]);
+ if (n == ASSIGN) { /* ordinary assignment */
+ if (x == y && !(x->tval & (FLD|REC))) /* self-assignment: */
+ ; /* leave alone unless it's a field */
+ else if ((y->tval & (STR|NUM)) == (STR|NUM)) {
+ setsval(x, getsval(y));
+ x->fval = getfval(y);
+ x->tval |= NUM;
+ }
+ else if (isstr(y))
+ setsval(x, getsval(y));
+ else if (isnum(y))
+ setfval(x, getfval(y));
+ else
+ funnyvar(y, "read value of");
+ tempfree(y);
+ return(x);
+ }
+ xf = getfval(x);
+ yf = getfval(y);
+ switch (n) {
+ case ADDEQ:
+ xf += yf;
+ break;
+ case SUBEQ:
+ xf -= yf;
+ break;
+ case MULTEQ:
+ xf *= yf;
+ break;
+ case DIVEQ:
+ if (yf == 0)
+ FATAL("division by zero in /=");
+ xf /= yf;
+ break;
+ case MODEQ:
+ if (yf == 0)
+ FATAL("division by zero in %%=");
+ modf(xf/yf, &v);
+ xf = xf - yf * v;
+ break;
+ case POWEQ:
+ if (yf >= 0 && modf(yf, &v) == 0.0) /* pos integer exponent */
+ xf = ipow(xf, (int) yf);
+ else
+ xf = errcheck(pow(xf, yf), "pow");
+ break;
+ default:
+ FATAL("illegal assignment operator %d", n);
+ break;
+ }
+ tempfree(y);
+ setfval(x, xf);
+ return(x);
+}
+
+Cell *cat(Node **a, int q) /* a[0] cat a[1] */
+{
+ Cell *x, *y, *z;
+ int n1, n2;
+ char *s;
+
+ x = execute(a[0]);
+ y = execute(a[1]);
+ getsval(x);
+ getsval(y);
+ n1 = strlen(x->sval);
+ n2 = strlen(y->sval);
+ s = (char *) malloc(n1 + n2 + 1);
+ if (s == NULL)
+ FATAL("out of space concatenating %.15s... and %.15s...",
+ x->sval, y->sval);
+ strcpy(s, x->sval);
+ strcpy(s+n1, y->sval);
+ tempfree(y);
+ z = gettemp();
+ z->sval = s;
+ z->tval = STR;
+ tempfree(x);
+ return(z);
+}
+
+Cell *pastat(Node **a, int n) /* a[0] { a[1] } */
+{
+ Cell *x;
+
+ if (a[0] == 0)
+ x = execute(a[1]);
+ else {
+ x = execute(a[0]);
+ if (istrue(x)) {
+ tempfree(x);
+ x = execute(a[1]);
+ }
+ }
+ return x;
+}
+
+Cell *dopa2(Node **a, int n) /* a[0], a[1] { a[2] } */
+{
+ Cell *x;
+ int pair;
+
+ pair = ptoi(a[3]);
+ if (pairstack[pair] == 0) {
+ x = execute(a[0]);
+ if (istrue(x))
+ pairstack[pair] = 1;
+ tempfree(x);
+ }
+ if (pairstack[pair] == 1) {
+ x = execute(a[1]);
+ if (istrue(x))
+ pairstack[pair] = 0;
+ tempfree(x);
+ x = execute(a[2]);
+ return(x);
+ }
+ return(False);
+}
+
+Cell *split(Node **a, int nnn) /* split(a[0], a[1], a[2]); a[3] is type */
+{
+ Cell *x = 0, *y, *ap;
+ char *s;
+ int sep;
+ char *t, temp, num[50], *fs = 0;
+ int n, arg3type;
+
+ y = execute(a[0]); /* source string */
+ s = getsval(y);
+ arg3type = ptoi(a[3]);
+ if (a[2] == 0) /* fs string */
+ fs = *FS;
+ else if (arg3type == STRING) { /* split(str,arr,"string") */
+ x = execute(a[2]);
+ fs = getsval(x);
+ } else if (arg3type == REGEXPR)
+ fs = "(regexpr)"; /* split(str,arr,/regexpr/) */
+ else
+ FATAL("illegal type of split");
+ sep = *fs;
+ ap = execute(a[1]); /* array name */
+ freesymtab(ap);
+ dprintf( ("split: s=|%s|, a=%s, sep=|%s|\n", s, ap->nval, fs) );
+ ap->tval &= ~STR;
+ ap->tval |= ARR;
+ ap->sval = (char *) makesymtab(NSYMTAB);
+
+ n = 0;
+ if ((*s != '\0' && strlen(fs) > 1) || arg3type == REGEXPR) { /* reg expr */
+ void *p;
+ if (arg3type == REGEXPR) { /* it's ready already */
+ p = (void *) a[2];
+ } else {
+ p = compre(fs);
+ }
+ t = s;
+ if (nematch(p,s,t)) {
+ do {
+ n++;
+ sprintf(num, "%d", n);
+ temp = *patbeg;
+ *patbeg = '\0';
+ if (is_number(t))
+ setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval);
+ else
+ setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
+ *patbeg = temp;
+ t = patbeg + patlen;
+ if (t[-1] == 0 || *t == 0) {
+ n++;
+ sprintf(num, "%d", n);
+ setsymtab(num, "", 0.0, STR, (Array *) ap->sval);
+ goto spdone;
+ }
+ } while (nematch(p,s,t));
+ }
+ n++;
+ sprintf(num, "%d", n);
+ if (is_number(t))
+ setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval);
+ else
+ setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
+ spdone:
+ p = NULL;
+ } else if (sep == ' ') {
+ for (n = 0; ; ) {
+ while (*s == ' ' || *s == '\t' || *s == '\n')
+ s++;
+ if (*s == 0)
+ break;
+ n++;
+ t = s;
+ do
+ s++;
+ while (*s!=' ' && *s!='\t' && *s!='\n' && *s!='\0');
+ temp = *s;
+ *s = '\0';
+ sprintf(num, "%d", n);
+ if (is_number(t))
+ setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval);
+ else
+ setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
+ *s = temp;
+ if (*s != 0)
+ s++;
+ }
+ } else if (sep == 0) { /* new: split(s, a, "") => 1 char/elem */
+ for (n = 0; *s != 0; s++) {
+ char buf[2];
+ n++;
+ sprintf(num, "%d", n);
+ buf[0] = *s;
+ buf[1] = 0;
+ if (isdigit(buf[0]))
+ setsymtab(num, buf, atof(buf), STR|NUM, (Array *) ap->sval);
+ else
+ setsymtab(num, buf, 0.0, STR, (Array *) ap->sval);
+ }
+ } else if (*s != 0) {
+ for (;;) {
+ n++;
+ t = s;
+ while (*s != sep && *s != '\n' && *s != '\0')
+ s++;
+ temp = *s;
+ *s = '\0';
+ sprintf(num, "%d", n);
+ if (is_number(t))
+ setsymtab(num, t, atof(t), STR|NUM, (Array *) ap->sval);
+ else
+ setsymtab(num, t, 0.0, STR, (Array *) ap->sval);
+ *s = temp;
+ if (*s++ == 0)
+ break;
+ }
+ }
+ tempfree(ap);
+ tempfree(y);
+ if (a[2] != 0 && arg3type == STRING)
+ tempfree(x);
+ x = gettemp();
+ x->tval = NUM;
+ x->fval = n;
+ return(x);
+}
+
+Cell *condexpr(Node **a, int n) /* a[0] ? a[1] : a[2] */
+{
+ Cell *x;
+
+ x = execute(a[0]);
+ if (istrue(x)) {
+ tempfree(x);
+ x = execute(a[1]);
+ } else {
+ tempfree(x);
+ x = execute(a[2]);
+ }
+ return(x);
+}
+
+Cell *ifstat(Node **a, int n) /* if (a[0]) a[1]; else a[2] */
+{
+ Cell *x;
+
+ x = execute(a[0]);
+ if (istrue(x)) {
+ tempfree(x);
+ x = execute(a[1]);
+ } else if (a[2] != 0) {
+ tempfree(x);
+ x = execute(a[2]);
+ }
+ return(x);
+}
+
+Cell *whilestat(Node **a, int n) /* while (a[0]) a[1] */
+{
+ Cell *x;
+
+ for (;;) {
+ x = execute(a[0]);
+ if (!istrue(x))
+ return(x);
+ tempfree(x);
+ x = execute(a[1]);
+ if (isbreak(x)) {
+ x = True;
+ return(x);
+ }
+ if (isnext(x) || isexit(x) || isret(x))
+ return(x);
+ tempfree(x);
+ }
+}
+
+Cell *dostat(Node **a, int n) /* do a[0]; while(a[1]) */
+{
+ Cell *x;
+
+ for (;;) {
+ x = execute(a[0]);
+ if (isbreak(x))
+ return True;
+ if (isnext(x) || isnextfile(x) || isexit(x) || isret(x))
+ return(x);
+ tempfree(x);
+ x = execute(a[1]);
+ if (!istrue(x))
+ return(x);
+ tempfree(x);
+ }
+}
+
+Cell *forstat(Node **a, int n) /* for (a[0]; a[1]; a[2]) a[3] */
+{
+ Cell *x;
+
+ x = execute(a[0]);
+ tempfree(x);
+ for (;;) {
+ if (a[1]!=0) {
+ x = execute(a[1]);
+ if (!istrue(x)) return(x);
+ else tempfree(x);
+ }
+ x = execute(a[3]);
+ if (isbreak(x)) /* turn off break */
+ return True;
+ if (isnext(x) || isexit(x) || isret(x))
+ return(x);
+ tempfree(x);
+ x = execute(a[2]);
+ tempfree(x);
+ }
+}
+
+Cell *instat(Node **a, int n) /* for (a[0] in a[1]) a[2] */
+{
+ Cell *x, *vp, *arrayp, *cp, *ncp;
+ Array *tp;
+ int i;
+
+ vp = execute(a[0]);
+ arrayp = execute(a[1]);
+ if (!isarr(arrayp)) {
+ return True;
+ }
+ tp = (Array *) arrayp->sval;
+ tempfree(arrayp);
+ for (i = 0; i < tp->size; i++) { /* this routine knows too much */
+ for (cp = tp->tab[i]; cp != NULL; cp = ncp) {
+ setsval(vp, cp->nval);
+ ncp = cp->cnext;
+ x = execute(a[2]);
+ if (isbreak(x)) {
+ tempfree(vp);
+ return True;
+ }
+ if (isnext(x) || isexit(x) || isret(x)) {
+ tempfree(vp);
+ return(x);
+ }
+ tempfree(x);
+ }
+ }
+ return True;
+}
+
+Cell *bltin(Node **a, int n) /* builtin functions. a[0] is type, a[1] is arg list */
+{
+ Cell *x, *y;
+ Awkfloat u;
+ int t;
+ wchar_t wc;
+ char *p, *buf;
+ char mbc[50];
+ Node *nextarg;
+ FILE *fp;
+
+ t = ptoi(a[0]);
+ x = execute(a[1]);
+ nextarg = a[1]->nnext;
+ switch (t) {
+ case FLENGTH:
+ p = getsval(x);
+ u = (Awkfloat) countposn(p, strlen(p)); break;
+ case FLOG:
+ u = errcheck(log(getfval(x)), "log"); break;
+ case FINT:
+ modf(getfval(x), &u); break;
+ case FEXP:
+ u = errcheck(exp(getfval(x)), "exp"); break;
+ case FSQRT:
+ u = errcheck(sqrt(getfval(x)), "sqrt"); break;
+ case FSIN:
+ u = sin(getfval(x)); break;
+ case FCOS:
+ u = cos(getfval(x)); break;
+ case FATAN:
+ if (nextarg == 0) {
+ WARNING("atan2 requires two arguments; returning 1.0");
+ u = 1.0;
+ } else {
+ y = execute(a[1]->nnext);
+ u = atan2(getfval(x), getfval(y));
+ tempfree(y);
+ nextarg = nextarg->nnext;
+ }
+ break;
+ case FSYSTEM:
+ fflush(stdout); /* in case something is buffered already */
+ u = (Awkfloat) system(getsval(x)) / 256; /* 256 is unix-dep */
+ break;
+ case FRAND:
+ /* in principle, rand() returns something in 0..RAND_MAX */
+ u = (Awkfloat) (rand() % RAND_MAX) / RAND_MAX;
+ break;
+ case FSRAND:
+ if (isrec(x)) /* no argument provided */
+ u = time((time_t *)0);
+ else
+ u = getfval(x);
+ srand((unsigned int) u);
+ break;
+ case FTOUPPER:
+ case FTOLOWER:
+ buf = tostring(getsval(x));
+ if (t == FTOUPPER) {
+ for (p = buf; *p; p++)
+ if (islower(*p))
+ *p = toupper(*p);
+ } else {
+ for (p = buf; *p; p++)
+ if (isupper(*p))
+ *p = tolower(*p);
+ }
+ tempfree(x);
+ x = gettemp();
+ setsval(x, buf);
+ free(buf);
+ return x;
+ case FFLUSH:
+ if ((fp = openfile(FFLUSH, getsval(x))) == NULL)
+ u = EOF;
+ else
+ u = fflush(fp);
+ break;
+ case FUTF:
+ wc = (int)getfval(x);
+ mbc[wctomb(mbc, wc)] = 0;
+ tempfree(x);
+ x = gettemp();
+ setsval(x, mbc);
+ return x;
+ default: /* can't happen */
+ FATAL("illegal function type %d", t);
+ break;
+ }
+ tempfree(x);
+ x = gettemp();
+ setfval(x, u);
+ if (nextarg != 0) {
+ WARNING("warning: function has too many arguments");
+ for ( ; nextarg; nextarg = nextarg->nnext)
+ execute(nextarg);
+ }
+ return(x);
+}
+
+Cell *printstat(Node **a, int n) /* print a[0] */
+{
+ Node *x;
+ Cell *y;
+ FILE *fp;
+
+ if (a[1] == 0) /* a[1] is redirection operator, a[2] is file */
+ fp = stdout;
+ else
+ fp = redirect(ptoi(a[1]), a[2]);
+ for (x = a[0]; x != NULL; x = x->nnext) {
+ y = execute(x);
+ fputs(getsval(y), fp);
+ tempfree(y);
+ if (x->nnext == NULL)
+ fputs(*ORS, fp);
+ else
+ fputs(*OFS, fp);
+ }
+ if (a[1] != 0)
+ fflush(fp);
+ if (ferror(fp))
+ FATAL("write error on %s", filename(fp));
+ return(True);
+}
+
+Cell *nullproc(Node **a, int n)
+{
+ n = n;
+ a = a;
+ return 0;
+}
+
+
+FILE *redirect(int a, Node *b) /* set up all i/o redirections */
+{
+ FILE *fp;
+ Cell *x;
+ char *fname;
+
+ x = execute(b);
+ fname = getsval(x);
+ fp = openfile(a, fname);
+ if (fp == NULL)
+ FATAL("can't open file %s", fname);
+ tempfree(x);
+ return fp;
+}
+
+struct files {
+ FILE *fp;
+ char *fname;
+ int mode; /* '|', 'a', 'w' => LE/LT, GT */
+} files[FOPEN_MAX] ={
+ { NULL, "/dev/stdin", LT }, /* watch out: don't free this! */
+ { NULL, "/dev/stdout", GT },
+ { NULL, "/dev/stderr", GT }
+};
+
+void stdinit(void) /* in case stdin, etc., are not constants */
+{
+ files[0].fp = stdin;
+ files[1].fp = stdout;
+ files[2].fp = stderr;
+}
+
+FILE *openfile(int a, char *us)
+{
+ char *s = us;
+ int i, m;
+ FILE *fp = 0;
+
+ if (*s == '\0')
+ FATAL("null file name in print or getline");
+ for (i=0; i < FOPEN_MAX; i++)
+ if (files[i].fname && strcmp(s, files[i].fname) == 0) {
+ if (a == files[i].mode || (a==APPEND && files[i].mode==GT))
+ return files[i].fp;
+ if (a == FFLUSH)
+ return files[i].fp;
+ }
+ if (a == FFLUSH) /* didn't find it, so don't create it! */
+ return NULL;
+
+ for (i=0; i < FOPEN_MAX; i++)
+ if (files[i].fp == 0)
+ break;
+ if (i >= FOPEN_MAX)
+ FATAL("%s makes too many open files", s);
+ fflush(stdout); /* force a semblance of order */
+ m = a;
+ if (a == GT) {
+ fp = fopen(s, "w");
+ } else if (a == APPEND) {
+ fp = fopen(s, "a");
+ m = GT; /* so can mix > and >> */
+ } else if (a == '|') { /* output pipe */
+ fp = popen(s, "w");
+ } else if (a == LE) { /* input pipe */
+ fp = popen(s, "r");
+ } else if (a == LT) { /* getline <file */
+ fp = strcmp(s, "-") == 0 ? stdin : fopen(s, "r"); /* "-" is stdin */
+ } else /* can't happen */
+ FATAL("illegal redirection %d", a);
+ if (fp != NULL) {
+ files[i].fname = tostring(s);
+ files[i].fp = fp;
+ files[i].mode = m;
+ }
+ return fp;
+}
+
+char *filename(FILE *fp)
+{
+ int i;
+
+ for (i = 0; i < FOPEN_MAX; i++)
+ if (fp == files[i].fp)
+ return files[i].fname;
+ return "???";
+}
+
+Cell *closefile(Node **a, int n)
+{
+ Cell *x;
+ int i, stat;
+
+ n = n;
+ x = execute(a[0]);
+ getsval(x);
+ for (i = 0; i < FOPEN_MAX; i++)
+ if (files[i].fname && strcmp(x->sval, files[i].fname) == 0) {
+ if (ferror(files[i].fp))
+ WARNING( "i/o error occurred on %s", files[i].fname );
+ if (files[i].mode == '|' || files[i].mode == LE)
+ stat = pclose(files[i].fp);
+ else
+ stat = fclose(files[i].fp);
+ if (stat == EOF)
+ WARNING( "i/o error occurred closing %s", files[i].fname );
+ if (i > 2) /* don't do /dev/std... */
+ xfree(files[i].fname);
+ files[i].fname = NULL; /* watch out for ref thru this */
+ files[i].fp = NULL;
+ }
+ tempfree(x);
+ return(True);
+}
+
+void closeall(void)
+{
+ int i, stat;
+
+ for (i = 0; i < FOPEN_MAX; i++)
+ if (files[i].fp) {
+ if (ferror(files[i].fp))
+ WARNING( "i/o error occurred on %s", files[i].fname );
+ if (files[i].mode == '|' || files[i].mode == LE)
+ stat = pclose(files[i].fp);
+ else
+ stat = fclose(files[i].fp);
+ if (stat == EOF)
+ WARNING( "i/o error occurred while closing %s", files[i].fname );
+ }
+}
+
+void backsub(char **pb_ptr, char **sptr_ptr);
+
+Cell *sub(Node **a, int nnn) /* substitute command */
+{
+ char *sptr, *pb, *q;
+ Cell *x, *y, *result;
+ char *t, *buf;
+ void *p;
+ int bufsz = recsize;
+
+ if ((buf = (char *) malloc(bufsz)) == NULL)
+ FATAL("out of memory in sub");
+ x = execute(a[3]); /* target string */
+ t = getsval(x);
+ if (a[0] == 0) /* 0 => a[1] is already-compiled regexpr */
+ p = (void *) a[1]; /* regular expression */
+ else {
+ y = execute(a[1]);
+ p = compre(getsval(y));
+ tempfree(y);
+ }
+ y = execute(a[2]); /* replacement string */
+ result = False;
+ if (pmatch(p, t, t)) {
+ sptr = t;
+ adjbuf(&buf, &bufsz, 1+patbeg-sptr, recsize, 0, "sub");
+ pb = buf;
+ while (sptr < patbeg)
+ *pb++ = *sptr++;
+ sptr = getsval(y);
+ while (*sptr != 0) {
+ adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "sub");
+ if (*sptr == '\\') {
+ backsub(&pb, &sptr);
+ } else if (*sptr == '&') {
+ sptr++;
+ adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "sub");
+ for (q = patbeg; q < patbeg+patlen; )
+ *pb++ = *q++;
+ } else
+ *pb++ = *sptr++;
+ }
+ *pb = '\0';
+ if (pb > buf + bufsz)
+ FATAL("sub result1 %.30s too big; can't happen", buf);
+ sptr = patbeg + patlen;
+ if ((patlen == 0 && *patbeg) || (patlen && *(sptr-1))) {
+ adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "sub");
+ while ((*pb++ = *sptr++) != 0)
+ ;
+ }
+ if (pb > buf + bufsz)
+ FATAL("sub result2 %.30s too big; can't happen", buf);
+ setsval(x, buf); /* BUG: should be able to avoid copy */
+ result = True;;
+ }
+ tempfree(x);
+ tempfree(y);
+ free(buf);
+ return result;
+}
+
+Cell *gsub(Node **a, int nnn) /* global substitute */
+{
+ Cell *x, *y;
+ char *rptr, *sptr, *t, *pb, *c;
+ char *buf;
+ void *p;
+ int mflag, num;
+ int bufsz = recsize;
+
+ if ((buf = (char *)malloc(bufsz)) == NULL)
+ FATAL("out of memory in gsub");
+ mflag = 0; /* if mflag == 0, can replace empty string */
+ num = 0;
+ x = execute(a[3]); /* target string */
+ c = t = getsval(x);
+ if (a[0] == 0) /* 0 => a[1] is already-compiled regexpr */
+ p = (void *) a[1]; /* regular expression */
+ else {
+ y = execute(a[1]);
+ p = compre(getsval(y));
+ tempfree(y);
+ }
+ y = execute(a[2]); /* replacement string */
+ if (pmatch(p, t, c)) {
+ pb = buf;
+ rptr = getsval(y);
+ do {
+ if (patlen == 0 && *patbeg != 0) { /* matched empty string */
+ if (mflag == 0) { /* can replace empty */
+ num++;
+ sptr = rptr;
+ while (*sptr != 0) {
+ adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gsub");
+ if (*sptr == '\\') {
+ backsub(&pb, &sptr);
+ } else if (*sptr == '&') {
+ char *q;
+ sptr++;
+ adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gsub");
+ for (q = patbeg; q < patbeg+patlen; )
+ *pb++ = *q++;
+ } else
+ *pb++ = *sptr++;
+ }
+ }
+ if (*c == 0) /* at end */
+ goto done;
+ adjbuf(&buf, &bufsz, 2+pb-buf, recsize, &pb, "gsub");
+ *pb++ = *c++;
+ if (pb > buf + bufsz) /* BUG: not sure of this test */
+ FATAL("gsub result0 %.30s too big; can't happen", buf);
+ mflag = 0;
+ }
+ else { /* matched nonempty string */
+ num++;
+ sptr = c;
+ adjbuf(&buf, &bufsz, 1+(patbeg-sptr)+pb-buf, recsize, &pb, "gsub");
+ while (sptr < patbeg)
+ *pb++ = *sptr++;
+ sptr = rptr;
+ while (*sptr != 0) {
+ adjbuf(&buf, &bufsz, 5+pb-buf, recsize, &pb, "gsub");
+ if (*sptr == '\\') {
+ backsub(&pb, &sptr);
+ } else if (*sptr == '&') {
+ char *q;
+ sptr++;
+ adjbuf(&buf, &bufsz, 1+patlen+pb-buf, recsize, &pb, "gsub");
+ for (q = patbeg; q < patbeg+patlen; )
+ *pb++ = *q++;
+ } else
+ *pb++ = *sptr++;
+ }
+ c = patbeg + patlen;
+ if ((c[-1] == 0) || (*c == 0))
+ goto done;
+ if (pb > buf + bufsz)
+ FATAL("gsub result1 %.30s too big; can't happen", buf);
+ mflag = 1;
+ }
+ } while (pmatch(p, t, c));
+ sptr = c;
+ adjbuf(&buf, &bufsz, 1+strlen(sptr)+pb-buf, 0, &pb, "gsub");
+ while ((*pb++ = *sptr++) != 0)
+ ;
+ done: if (pb > buf + bufsz)
+ FATAL("gsub result2 %.30s too big; can't happen", buf);
+ *pb = '\0';
+ setsval(x, buf); /* BUG: should be able to avoid copy + free */
+ }
+ tempfree(x);
+ tempfree(y);
+ x = gettemp();
+ x->tval = NUM;
+ x->fval = num;
+ free(buf);
+ return(x);
+}
+
+void backsub(char **pb_ptr, char **sptr_ptr) /* handle \\& variations */
+{ /* sptr[0] == '\\' */
+ char *pb = *pb_ptr, *sptr = *sptr_ptr;
+
+ if (sptr[1] == '\\') {
+ if (sptr[2] == '\\' && sptr[3] == '&') { /* \\\& -> \& */
+ *pb++ = '\\';
+ *pb++ = '&';
+ sptr += 4;
+ } else if (sptr[2] == '&') { /* \\& -> \ + matched */
+ *pb++ = '\\';
+ sptr += 2;
+ } else { /* \\x -> \\x */
+ *pb++ = *sptr++;
+ *pb++ = *sptr++;
+ }
+ } else if (sptr[1] == '&') { /* literal & */
+ sptr++;
+ *pb++ = *sptr++;
+ } else /* literal \ */
+ *pb++ = *sptr++;
+
+ *pb_ptr = pb;
+ *sptr_ptr = sptr;
+}
diff --git a/awk/tran.c b/awk/tran.c
@@ -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;
+}
diff --git a/basename/Makefile b/basename/Makefile
@@ -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
diff --git a/basename/basename.1 b/basename/basename.1
@@ -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
diff --git a/basename/basename.c b/basename/basename.c
@@ -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);
+}
diff --git a/bc/Makefile b/bc/Makefile
@@ -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
diff --git a/bc/bc.1 b/bc/bc.1
@@ -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.
diff --git a/bc/bc.y b/bc/bc.y
@@ -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);
+}
diff --git a/cat/Makefile b/cat/Makefile
@@ -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
diff --git a/cat/cat.1 b/cat/cat.1
@@ -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.
diff --git a/cat/cat.c b/cat/cat.c
@@ -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);
+}
+
diff --git a/cleanname/Makefile b/cleanname/Makefile
@@ -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
diff --git a/cleanname/cleanname.1 b/cleanname/cleanname.1
@@ -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).
diff --git a/cleanname/cleanname.c b/cleanname/cleanname.c
@@ -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);
+}
diff --git a/config.mk b/config.mk
@@ -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
diff --git a/date/Makefile b/date/Makefile
@@ -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
diff --git a/date/date.1 b/date/date.1
@@ -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
diff --git a/date/date.c b/date/date.c
@@ -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);
+}
diff --git a/echo/Makefile b/echo/Makefile
@@ -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
diff --git a/echo/echo.1 b/echo/echo.1
@@ -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.
diff --git a/echo/echo.c b/echo/echo.c
@@ -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);
+}
diff --git a/grep/Makefile b/grep/Makefile
@@ -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
diff --git a/grep/comp.c b/grep/comp.c
@@ -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;
+}
diff --git a/grep/grep.1 b/grep/grep.1
@@ -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.
diff --git a/grep/grep.h b/grep/grep.h
@@ -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*, ...);
diff --git a/grep/grep.y b/grep/grep.y
@@ -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;
+}
diff --git a/grep/main.c b/grep/main.c
@@ -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;
+}
diff --git a/grep/sub.c b/grep/sub.c
@@ -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");
+}
diff --git a/lib9/LICENSE b/lib9/LICENSE
@@ -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>.
+
diff --git a/lib9/Makefile b/lib9/Makefile
@@ -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}
diff --git a/lib9/_exits.c b/lib9/_exits.c
@@ -0,0 +1,10 @@
+#include <u.h>
+#include <libc.h>
+
+void
+_exits(char *s)
+{
+ if(s && *s)
+ _exit(1);
+ _exit(0);
+}
diff --git a/lib9/_p9dialparse.c b/lib9/_p9dialparse.c
@@ -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;
+}
diff --git a/lib9/_p9dir.c b/lib9/_p9dir.c
@@ -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;
+}
+
diff --git a/lib9/_p9translate.c b/lib9/_p9translate.c
@@ -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;
+}
diff --git a/lib9/announce.c b/lib9/announce.c
@@ -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);
+}
+
diff --git a/lib9/argv0.c b/lib9/argv0.c
@@ -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) { }
diff --git a/lib9/atexit.c b/lib9/atexit.c
@@ -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);
+}
diff --git a/lib9/atnotify.c b/lib9/atnotify.c
@@ -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;
+}
diff --git a/lib9/atoi.c b/lib9/atoi.c
@@ -0,0 +1,9 @@
+#include <u.h>
+#include <libc.h>
+
+int
+atoi(char *s)
+{
+ return strtol(s, 0, 0);
+}
+
diff --git a/lib9/atol.c b/lib9/atol.c
@@ -0,0 +1,9 @@
+#include <u.h>
+#include <libc.h>
+
+long
+atol(char *s)
+{
+ return strtol(s, 0, 0);
+}
+
diff --git a/lib9/atoll.c b/lib9/atoll.c
@@ -0,0 +1,9 @@
+#include <u.h>
+#include <libc.h>
+
+vlong
+atoll(char *s)
+{
+ return strtoll(s, 0, 0);
+}
+
diff --git a/lib9/await.c b/lib9/await.c
@@ -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);
+}
+
diff --git a/lib9/bio.h b/lib9/bio.h
@@ -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
diff --git a/lib9/bio/_lib9.h b/lib9/bio/_lib9.h
@@ -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)
diff --git a/lib9/bio/bbuffered.c b/lib9/bio/bbuffered.c
@@ -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;
+}
diff --git a/lib9/bio/bcat.c b/lib9/bio/bcat.c
@@ -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);
+}
diff --git a/lib9/bio/bfildes.c b/lib9/bio/bfildes.c
@@ -0,0 +1,9 @@
+#include "lib9.h"
+#include <bio.h>
+
+int
+Bfildes(Biobuf *bp)
+{
+
+ return bp->fid;
+}
diff --git a/lib9/bio/bflush.c b/lib9/bio/bflush.c
@@ -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;
+}
diff --git a/lib9/bio/bgetc.c b/lib9/bio/bgetc.c
@@ -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;
+}
diff --git a/lib9/bio/bgetd.c b/lib9/bio/bgetd.c
@@ -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;
+}
diff --git a/lib9/bio/bgetrune.c b/lib9/bio/bgetrune.c
@@ -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;
+}
diff --git a/lib9/bio/binit.c b/lib9/bio/binit.c
@@ -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;
+}
diff --git a/lib9/bio/boffset.c b/lib9/bio/boffset.c
@@ -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;
+}
diff --git a/lib9/bio/bprint.c b/lib9/bio/bprint.c
@@ -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;
+}
diff --git a/lib9/bio/bputc.c b/lib9/bio/bputc.c
@@ -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;
+}
diff --git a/lib9/bio/bputrune.c b/lib9/bio/bputrune.c
@@ -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;
+}
diff --git a/lib9/bio/brdline.c b/lib9/bio/brdline.c
@@ -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;
+}
diff --git a/lib9/bio/brdstr.c b/lib9/bio/brdstr.c
@@ -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;
+ }
+}
diff --git a/lib9/bio/bread.c b/lib9/bio/bread.c
@@ -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;
+}
diff --git a/lib9/bio/bseek.c b/lib9/bio/bseek.c
@@ -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;
+}
diff --git a/lib9/bio/bvprint.c b/lib9/bio/bvprint.c
@@ -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;
+}
+
+
+
diff --git a/lib9/bio/bwrite.c b/lib9/bio/bwrite.c
@@ -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;
+}
diff --git a/lib9/bio/lib9.std.h b/lib9/bio/lib9.std.h
@@ -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;
diff --git a/lib9/cistrcmp.c b/lib9/cistrcmp.c
@@ -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;
+}
diff --git a/lib9/cistrncmp.c b/lib9/cistrncmp.c
@@ -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;
+}
diff --git a/lib9/cistrstr.c b/lib9/cistrstr.c
@@ -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;
+}
diff --git a/lib9/cleanname.c b/lib9/cleanname.c
@@ -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;
+}
diff --git a/lib9/convD2M.c b/lib9/convD2M.c
@@ -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;
+}
diff --git a/lib9/convM2D.c b/lib9/convM2D.c
@@ -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;
+}
diff --git a/lib9/convM2S.c b/lib9/convM2S.c
@@ -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;
+}
diff --git a/lib9/convS2M.c b/lib9/convS2M.c
@@ -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;
+}
diff --git a/lib9/create.c b/lib9/create.c
@@ -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;
+}
diff --git a/lib9/ctime.c b/lib9/ctime.c
@@ -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));
+}
+
diff --git a/lib9/date.c b/lib9/date.c
@@ -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 */
+}
+
diff --git a/lib9/debugmalloc.c b/lib9/debugmalloc.c
@@ -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;
+}
diff --git a/lib9/dial.c b/lib9/dial.c
@@ -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;
+}
+
diff --git a/lib9/dirfstat.c b/lib9/dirfstat.c
@@ -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;
+}
+
diff --git a/lib9/dirfwstat.c b/lib9/dirfwstat.c
@@ -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;
+}
+
diff --git a/lib9/dirmodefmt.c b/lib9/dirmodefmt.c
@@ -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);
+}
diff --git a/lib9/dirread.c b/lib9/dirread.c
@@ -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;
+}
diff --git a/lib9/dirstat.c b/lib9/dirstat.c
@@ -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;
+}
+
diff --git a/lib9/dirwstat.c b/lib9/dirwstat.c
@@ -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);
+}
diff --git a/lib9/dup.c b/lib9/dup.c
@@ -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);
+}
diff --git a/lib9/encodefmt.c b/lib9/encodefmt.c
@@ -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>");
+}
diff --git a/lib9/errstr.c b/lib9/errstr.c
@@ -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);
+}
+
diff --git a/lib9/exec.c b/lib9/exec.c
@@ -0,0 +1,9 @@
+#include <u.h>
+#include <libc.h>
+
+int
+exec(char *prog, char *argv[])
+{
+ /* to mimic plan 9 should be just exec, but execvp is a better fit for unix */
+ return execvp(prog, argv);
+}
diff --git a/lib9/execl.c b/lib9/execl.c
@@ -0,0 +1,29 @@
+#include <u.h>
+#include <libc.h>
+
+int
+execl(char *prog, ...)
+{
+ int i;
+ va_list arg;
+ char **argv;
+
+ va_start(arg, prog);
+ for(i=0; va_arg(arg, char*) != nil; i++)
+ ;
+ va_end(arg);
+
+ argv = malloc((i+1)*sizeof(char*));
+ if(argv == nil)
+ return -1;
+
+ va_start(arg, prog);
+ for(i=0; (argv[i] = va_arg(arg, char*)) != nil; i++)
+ ;
+ va_end(arg);
+
+ exec(prog, argv);
+ free(argv);
+ return -1;
+}
+
diff --git a/lib9/fcall.h b/lib9/fcall.h
@@ -0,0 +1,133 @@
+#ifndef _FCALL_H_
+#define _FCALL_H_ 1
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+#pragma src "/sys/src/libc/9sys"
+#pragma lib "libc.a"
+*/
+
+#define VERSION9P "9P2000"
+#define MAXWELEM 16
+
+typedef
+struct Fcall
+{
+ uchar type;
+ u32int fid;
+ ushort tag;
+ u32int msize; /* Tversion, Rversion */
+ char *version; /* Tversion, Rversion */
+ ushort oldtag; /* Tflush */
+ char *ename; /* Rerror */
+ Qid qid; /* Rattach, Ropen, Rcreate */
+ u32int iounit; /* Ropen, Rcreate */
+ Qid aqid; /* Rauth */
+ u32int afid; /* Tauth, Tattach */
+ char *uname; /* Tauth, Tattach */
+ char *aname; /* Tauth, Tattach */
+ u32int perm; /* Tcreate */
+ char *name; /* Tcreate */
+ uchar mode; /* Tcreate, Topen */
+ u32int newfid; /* Twalk */
+ ushort nwname; /* Twalk */
+ char *wname[MAXWELEM]; /* Twalk */
+ ushort nwqid; /* Rwalk */
+ Qid wqid[MAXWELEM]; /* Rwalk */
+ vlong offset; /* Tread, Twrite */
+ u32int count; /* Tread, Twrite, Rread */
+ char *data; /* Twrite, Rread */
+ ushort nstat; /* Twstat, Rstat */
+ uchar *stat; /* Twstat, Rstat */
+ int unixfd; /* Ropenfd */
+} Fcall;
+
+
+#define GBIT8(p) ((p)[0])
+#define GBIT16(p) ((p)[0]|((p)[1]<<8))
+#define GBIT32(p) ((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))
+#define GBIT64(p) ((vlong)((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24)) |\
+ ((vlong)((p)[4]|((p)[5]<<8)|((p)[6]<<16)|((p)[7]<<24)) << 32))
+
+#define PBIT8(p,v) (p)[0]=(v)
+#define PBIT16(p,v) (p)[0]=(v);(p)[1]=(v)>>8
+#define PBIT32(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24
+#define PBIT64(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24;\
+ (p)[4]=(v)>>32;(p)[5]=(v)>>40;(p)[6]=(v)>>48;(p)[7]=(v)>>56
+
+#define BIT8SZ 1
+#define BIT16SZ 2
+#define BIT32SZ 4
+#define BIT64SZ 8
+#define QIDSZ (BIT8SZ+BIT32SZ+BIT64SZ)
+
+/* STATFIXLEN includes leading 16-bit count */
+/* The count, however, excludes itself; total size is BIT16SZ+count */
+#define STATFIXLEN (BIT16SZ+QIDSZ+5*BIT16SZ+4*BIT32SZ+1*BIT64SZ) /* amount of fixed length data in a stat buffer */
+
+#define NOTAG (ushort)~0U /* Dummy tag */
+#define NOFID (u32int)~0U /* Dummy fid */
+#define IOHDRSZ 24 /* ample room for Twrite/Rread header (iounit) */
+
+enum
+{
+ Tversion = 100,
+ Rversion,
+ Tauth = 102,
+ Rauth,
+ Tattach = 104,
+ Rattach,
+ Terror = 106, /* illegal */
+ Rerror,
+ Tflush = 108,
+ Rflush,
+ Twalk = 110,
+ Rwalk,
+ Topen = 112,
+ Ropen,
+ Tcreate = 114,
+ Rcreate,
+ Tread = 116,
+ Rread,
+ Twrite = 118,
+ Rwrite,
+ Tclunk = 120,
+ Rclunk,
+ Tremove = 122,
+ Rremove,
+ Tstat = 124,
+ Rstat,
+ Twstat = 126,
+ Rwstat,
+ Tmax,
+
+ Topenfd = 98,
+ Ropenfd,
+};
+
+uint convM2S(uchar*, uint, Fcall*);
+uint convS2M(Fcall*, uchar*, uint);
+uint sizeS2M(Fcall*);
+
+int statcheck(uchar *abuf, uint nbuf);
+uint convM2D(uchar*, uint, Dir*, char*);
+uint convD2M(Dir*, uchar*, uint);
+uint sizeD2M(Dir*);
+
+int fcallfmt(Fmt*);
+int dirfmt(Fmt*);
+int dirmodefmt(Fmt*);
+
+int read9pmsg(int, void*, uint);
+
+/*
+#pragma varargck type "F" Fcall*
+#pragma varargck type "M" ulong
+#pragma varargck type "D" Dir*
+*/
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib9/fcallfmt.c b/lib9/fcallfmt.c
@@ -0,0 +1,253 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+static uint dumpsome(char*, char*, char*, long);
+static void fdirconv(char*, char*, Dir*);
+static char *qidtype(char*, uchar);
+
+#define QIDFMT "(%.16llux %lud %s)"
+
+int
+fcallfmt(Fmt *fmt)
+{
+ Fcall *f;
+ int fid, type, tag, i;
+ char buf[512], tmp[200];
+ char *p, *e;
+ Dir *d;
+ Qid *q;
+
+ e = buf+sizeof(buf);
+ f = va_arg(fmt->args, Fcall*);
+ type = f->type;
+ fid = f->fid;
+ tag = f->tag;
+ switch(type){
+ case Tversion: /* 100 */
+ seprint(buf, e, "Tversion tag %ud msize %ud version '%s'", tag, f->msize, f->version);
+ break;
+ case Rversion:
+ seprint(buf, e, "Rversion tag %ud msize %ud version '%s'", tag, f->msize, f->version);
+ break;
+ case Tauth: /* 102 */
+ seprint(buf, e, "Tauth tag %ud afid %d uname %s aname %s", tag,
+ f->afid, f->uname, f->aname);
+ break;
+ case Rauth:
+ seprint(buf, e, "Rauth tag %ud qid " QIDFMT, tag,
+ f->aqid.path, f->aqid.vers, qidtype(tmp, f->aqid.type));
+ break;
+ case Tattach: /* 104 */
+ seprint(buf, e, "Tattach tag %ud fid %d afid %d uname %s aname %s", tag,
+ fid, f->afid, f->uname, f->aname);
+ break;
+ case Rattach:
+ seprint(buf, e, "Rattach tag %ud qid " QIDFMT, tag,
+ f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type));
+ break;
+ case Rerror: /* 107; 106 (Terror) illegal */
+ seprint(buf, e, "Rerror tag %ud ename %s", tag, f->ename);
+ break;
+ case Tflush: /* 108 */
+ seprint(buf, e, "Tflush tag %ud oldtag %ud", tag, f->oldtag);
+ break;
+ case Rflush:
+ seprint(buf, e, "Rflush tag %ud", tag);
+ break;
+ case Twalk: /* 110 */
+ p = seprint(buf, e, "Twalk tag %ud fid %d newfid %d nwname %d ", tag, fid, f->newfid, f->nwname);
+ if(f->nwname <= MAXWELEM)
+ for(i=0; i<f->nwname; i++)
+ p = seprint(p, e, "%d:%s ", i, f->wname[i]);
+ break;
+ case Rwalk:
+ p = seprint(buf, e, "Rwalk tag %ud nwqid %ud ", tag, f->nwqid);
+ if(f->nwqid <= MAXWELEM)
+ for(i=0; i<f->nwqid; i++){
+ q = &f->wqid[i];
+ p = seprint(p, e, "%d:" QIDFMT " ", i,
+ q->path, q->vers, qidtype(tmp, q->type));
+ }
+ break;
+ case Topen: /* 112 */
+ seprint(buf, e, "Topen tag %ud fid %ud mode %d", tag, fid, f->mode);
+ break;
+ case Ropen:
+ seprint(buf, e, "Ropen tag %ud qid " QIDFMT " iounit %ud", tag,
+ f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit);
+ break;
+ case Topenfd: /* 98 */
+ seprint(buf, e, "Topenfd tag %ud fid %ud mode %d", tag, fid, f->mode);
+ break;
+ case Ropenfd:
+ seprint(buf, e, "Ropenfd tag %ud qid " QIDFMT " iounit %ud unixfd %d", tag,
+ f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit, f->unixfd);
+ break;
+ case Tcreate: /* 114 */
+ seprint(buf, e, "Tcreate tag %ud fid %ud name %s perm %M mode %d", tag, fid, f->name, (ulong)f->perm, f->mode);
+ break;
+ case Rcreate:
+ seprint(buf, e, "Rcreate tag %ud qid " QIDFMT " iounit %ud ", tag,
+ f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit);
+ break;
+ case Tread: /* 116 */
+ seprint(buf, e, "Tread tag %ud fid %d offset %lld count %ud",
+ tag, fid, f->offset, f->count);
+ break;
+ case Rread:
+ p = seprint(buf, e, "Rread tag %ud count %ud ", tag, f->count);
+ dumpsome(p, e, f->data, f->count);
+ break;
+ case Twrite: /* 118 */
+ p = seprint(buf, e, "Twrite tag %ud fid %d offset %lld count %ud ",
+ tag, fid, f->offset, f->count);
+ dumpsome(p, e, f->data, f->count);
+ break;
+ case Rwrite:
+ seprint(buf, e, "Rwrite tag %ud count %ud", tag, f->count);
+ break;
+ case Tclunk: /* 120 */
+ seprint(buf, e, "Tclunk tag %ud fid %ud", tag, fid);
+ break;
+ case Rclunk:
+ seprint(buf, e, "Rclunk tag %ud", tag);
+ break;
+ case Tremove: /* 122 */
+ seprint(buf, e, "Tremove tag %ud fid %ud", tag, fid);
+ break;
+ case Rremove:
+ seprint(buf, e, "Rremove tag %ud", tag);
+ break;
+ case Tstat: /* 124 */
+ seprint(buf, e, "Tstat tag %ud fid %ud", tag, fid);
+ break;
+ case Rstat:
+ p = seprint(buf, e, "Rstat tag %ud ", tag);
+ if(f->nstat > sizeof tmp)
+ seprint(p, e, " stat(%d bytes)", f->nstat);
+ else{
+ d = (Dir*)tmp;
+ convM2D(f->stat, f->nstat, d, (char*)(d+1));
+ seprint(p, e, " stat ");
+ fdirconv(p+6, e, d);
+ }
+ break;
+ case Twstat: /* 126 */
+ p = seprint(buf, e, "Twstat tag %ud fid %ud", tag, fid);
+ if(f->nstat > sizeof tmp)
+ seprint(p, e, " stat(%d bytes)", f->nstat);
+ else{
+ d = (Dir*)tmp;
+ convM2D(f->stat, f->nstat, d, (char*)(d+1));
+ seprint(p, e, " stat ");
+ fdirconv(p+6, e, d);
+ }
+ break;
+ case Rwstat:
+ seprint(buf, e, "Rwstat tag %ud", tag);
+ break;
+ default:
+ seprint(buf, e, "unknown type %d", type);
+ }
+ return fmtstrcpy(fmt, buf);
+}
+
+static char*
+qidtype(char *s, uchar t)
+{
+ char *p;
+
+ p = s;
+ if(t & QTDIR)
+ *p++ = 'd';
+ if(t & QTAPPEND)
+ *p++ = 'a';
+ if(t & QTEXCL)
+ *p++ = 'l';
+ if(t & QTAUTH)
+ *p++ = 'A';
+ *p = '\0';
+ return s;
+}
+
+int
+dirfmt(Fmt *fmt)
+{
+ char buf[160];
+
+ fdirconv(buf, buf+sizeof buf, va_arg(fmt->args, Dir*));
+ return fmtstrcpy(fmt, buf);
+}
+
+static void
+fdirconv(char *buf, char *e, Dir *d)
+{
+ char tmp[16];
+
+ seprint(buf, e, "'%s' '%s' '%s' '%s' "
+ "q " QIDFMT " m %#luo "
+ "at %ld mt %ld l %lld "
+ "t %d d %d",
+ d->name, d->uid, d->gid, d->muid,
+ d->qid.path, d->qid.vers, qidtype(tmp, d->qid.type), d->mode,
+ d->atime, d->mtime, d->length,
+ d->type, d->dev);
+}
+
+/*
+ * dump out count (or DUMPL, if count is bigger) bytes from
+ * buf to ans, as a string if they are all printable,
+ * else as a series of hex bytes
+ */
+#define DUMPL 64
+
+static uint
+dumpsome(char *ans, char *e, char *buf, long count)
+{
+ int i, printable;
+ char *p;
+
+ if(buf == nil){
+ seprint(ans, e, "<no data>");
+ return strlen(ans);
+ }
+ printable = 1;
+ if(count > DUMPL)
+ count = DUMPL;
+ for(i=0; i<count && printable; i++)
+ if((buf[i]!=0 && buf[i]<32) || (uchar)buf[i]>127)
+ printable = 0;
+ p = ans;
+ *p++ = '\'';
+ if(printable){
+ if(2*count > e-p-2)
+ count = (e-p-2)/2;
+ for(i=0; i<count; i++){
+ if(buf[i] == 0){
+ *p++ = '\\';
+ *p++ = '0';
+ }else if(buf[i] == '\t'){
+ *p++ = '\\';
+ *p++ = 't';
+ }else if(buf[i] == '\n'){
+ *p++ = '\\';
+ *p++ = 'n';
+ }else
+ *p++ = buf[i];
+ }
+ }else{
+ if(2*count > e-p-2)
+ count = (e-p-2)/2;
+ for(i=0; i<count; i++){
+ if(i>0 && i%4==0)
+ *p++ = ' ';
+ sprint(p, "%2.2ux", (uchar)buf[i]);
+ p += 2;
+ }
+ }
+ *p++ = '\'';
+ *p = 0;
+ assert(p < e);
+ return p - ans;
+}
diff --git a/lib9/fmt.h b/lib9/fmt.h
@@ -0,0 +1,101 @@
+#ifndef _FMT_H_
+#define _FMT_H_ 1
+#if defined(__cplusplus)
+extern "C" {
+#endif
+/*
+ * 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.
+ */
+
+#include <stdarg.h>
+#include <utf.h>
+
+typedef struct Fmt Fmt;
+struct Fmt{
+ unsigned char runes; /* output buffer is runes or chars? */
+ void *start; /* of buffer */
+ void *to; /* current place in the buffer */
+ void *stop; /* end of the buffer; overwritten if flush fails */
+ int (*flush)(Fmt *); /* called when to == stop */
+ void *farg; /* to make flush a closure */
+ int nfmt; /* num chars formatted so far */
+ va_list args; /* args passed to dofmt */
+ int r; /* % format Rune */
+ int width;
+ int prec;
+ unsigned long flags;
+};
+
+enum{
+ FmtWidth = 1,
+ FmtLeft = FmtWidth << 1,
+ FmtPrec = FmtLeft << 1,
+ FmtSharp = FmtPrec << 1,
+ FmtSpace = FmtSharp << 1,
+ FmtSign = FmtSpace << 1,
+ FmtZero = FmtSign << 1,
+ FmtUnsigned = FmtZero << 1,
+ FmtShort = FmtUnsigned << 1,
+ FmtLong = FmtShort << 1,
+ FmtVLong = FmtLong << 1,
+ FmtComma = FmtVLong << 1,
+ FmtByte = FmtComma << 1,
+ FmtLDouble = FmtByte << 1,
+
+ FmtFlag = FmtLDouble << 1
+};
+
+extern int (*fmtdoquote)(int);
+
+/* Edit .+1,/^$/ | cfn $PLAN9/src/lib9/fmt/?*.c | grep -v static |grep -v __ */
+int dofmt(Fmt *f, char *fmt);
+int dorfmt(Fmt *f, const Rune *fmt);
+double fmtcharstod(int(*f)(void*), void *vp);
+int fmtfdflush(Fmt *f);
+int fmtfdinit(Fmt *f, int fd, char *buf, int size);
+int fmtinstall(int c, int (*f)(Fmt*));
+int fmtprint(Fmt *f, char *fmt, ...);
+int fmtrune(Fmt *f, int r);
+int fmtrunestrcpy(Fmt *f, Rune *s);
+int fmtstrcpy(Fmt *f, char *s);
+char* fmtstrflush(Fmt *f);
+int fmtstrinit(Fmt *f);
+double fmtstrtod(const char *as, char **aas);
+int fmtvprint(Fmt *f, char *fmt, va_list args);
+int fprint(int fd, char *fmt, ...);
+int print(char *fmt, ...);
+void quotefmtinstall(void);
+int quoterunestrfmt(Fmt *f);
+int quotestrfmt(Fmt *f);
+Rune* runefmtstrflush(Fmt *f);
+int runefmtstrinit(Fmt *f);
+Rune* runeseprint(Rune *buf, Rune *e, char *fmt, ...);
+Rune* runesmprint(char *fmt, ...);
+int runesnprint(Rune *buf, int len, char *fmt, ...);
+int runesprint(Rune *buf, char *fmt, ...);
+Rune* runevseprint(Rune *buf, Rune *e, char *fmt, va_list args);
+Rune* runevsmprint(char *fmt, va_list args);
+int runevsnprint(Rune *buf, int len, char *fmt, va_list args);
+char* seprint(char *buf, char *e, char *fmt, ...);
+char* smprint(char *fmt, ...);
+int snprint(char *buf, int len, char *fmt, ...);
+int sprint(char *buf, char *fmt, ...);
+int vfprint(int fd, char *fmt, va_list args);
+char* vseprint(char *buf, char *e, char *fmt, va_list args);
+char* vsmprint(char *fmt, va_list args);
+int vsnprint(char *buf, int len, char *fmt, va_list args);
+
+#if defined(__cplusplus)
+}
+#endif
+#endif
diff --git a/lib9/fmt/LICENSE b/lib9/fmt/LICENSE
@@ -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>.
+
diff --git a/lib9/fmt/charstod.c b/lib9/fmt/charstod.c
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+/*
+ * Reads a floating-point number by interpreting successive characters
+ * returned by (*f)(vp). The last call it makes to f terminates the
+ * scan, so is not a character in the number. It may therefore be
+ * necessary to back up the input stream up one byte after calling charstod.
+ */
+
+double
+fmtcharstod(int(*f)(void*), void *vp)
+{
+ double num, dem;
+ int neg, eneg, dig, exp, c;
+
+ num = 0;
+ neg = 0;
+ dig = 0;
+ exp = 0;
+ eneg = 0;
+
+ c = (*f)(vp);
+ while(c == ' ' || c == '\t')
+ c = (*f)(vp);
+ if(c == '-' || c == '+'){
+ if(c == '-')
+ neg = 1;
+ c = (*f)(vp);
+ }
+ while(c >= '0' && c <= '9'){
+ num = num*10 + c-'0';
+ c = (*f)(vp);
+ }
+ if(c == '.')
+ c = (*f)(vp);
+ while(c >= '0' && c <= '9'){
+ num = num*10 + c-'0';
+ dig++;
+ c = (*f)(vp);
+ }
+ if(c == 'e' || c == 'E'){
+ c = (*f)(vp);
+ if(c == '-' || c == '+'){
+ if(c == '-'){
+ dig = -dig;
+ eneg = 1;
+ }
+ c = (*f)(vp);
+ }
+ while(c >= '0' && c <= '9'){
+ exp = exp*10 + c-'0';
+ c = (*f)(vp);
+ }
+ }
+ exp -= dig;
+ if(exp < 0){
+ exp = -exp;
+ eneg = !eneg;
+ }
+ dem = __fmtpow10(exp);
+ if(eneg)
+ num /= dem;
+ else
+ num *= dem;
+ if(neg)
+ return -num;
+ return num;
+}
diff --git a/lib9/fmt/dofmt.c b/lib9/fmt/dofmt.c
@@ -0,0 +1,552 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+/* format the output into f->to and return the number of characters fmted */
+int
+dofmt(Fmt *f, char *fmt)
+{
+ Rune rune, *rt, *rs;
+ int r;
+ char *t, *s;
+ int n, nfmt;
+
+ nfmt = f->nfmt;
+ for(;;){
+ if(f->runes){
+ rt = (Rune*)f->to;
+ rs = (Rune*)f->stop;
+ while((r = *(uchar*)fmt) && r != '%'){
+ if(r < Runeself)
+ fmt++;
+ else{
+ fmt += chartorune(&rune, fmt);
+ r = rune;
+ }
+ FMTRCHAR(f, rt, rs, r);
+ }
+ fmt++;
+ f->nfmt += rt - (Rune *)f->to;
+ f->to = rt;
+ if(!r)
+ return f->nfmt - nfmt;
+ f->stop = rs;
+ }else{
+ t = (char*)f->to;
+ s = (char*)f->stop;
+ while((r = *(uchar*)fmt) && r != '%'){
+ if(r < Runeself){
+ FMTCHAR(f, t, s, r);
+ fmt++;
+ }else{
+ n = chartorune(&rune, fmt);
+ if(t + n > s){
+ t = (char*)__fmtflush(f, t, n);
+ if(t != nil)
+ s = (char*)f->stop;
+ else
+ return -1;
+ }
+ while(n--)
+ *t++ = *fmt++;
+ }
+ }
+ fmt++;
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ if(!r)
+ return f->nfmt - nfmt;
+ f->stop = s;
+ }
+
+ fmt = (char*)__fmtdispatch(f, fmt, 0);
+ if(fmt == nil)
+ return -1;
+ }
+}
+
+void *
+__fmtflush(Fmt *f, void *t, int len)
+{
+ if(f->runes)
+ f->nfmt += (Rune*)t - (Rune*)f->to;
+ else
+ f->nfmt += (char*)t - (char *)f->to;
+ f->to = t;
+ if(f->flush == 0 || (*f->flush)(f) == 0 || (char*)f->to + len > (char*)f->stop){
+ f->stop = f->to;
+ return nil;
+ }
+ return f->to;
+}
+
+/*
+ * put a formatted block of memory sz bytes long of n runes into the output buffer,
+ * left/right justified in a field of at least f->width charactes
+ */
+int
+__fmtpad(Fmt *f, int n)
+{
+ char *t, *s;
+ int i;
+
+ t = (char*)f->to;
+ s = (char*)f->stop;
+ for(i = 0; i < n; i++)
+ FMTCHAR(f, t, s, ' ');
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ return 0;
+}
+
+int
+__rfmtpad(Fmt *f, int n)
+{
+ Rune *t, *s;
+ int i;
+
+ t = (Rune*)f->to;
+ s = (Rune*)f->stop;
+ for(i = 0; i < n; i++)
+ FMTRCHAR(f, t, s, ' ');
+ f->nfmt += t - (Rune *)f->to;
+ f->to = t;
+ return 0;
+}
+
+int
+__fmtcpy(Fmt *f, const void *vm, int n, int sz)
+{
+ Rune *rt, *rs, r;
+ char *t, *s, *m, *me;
+ ulong fl;
+ int nc, w;
+
+ m = (char*)vm;
+ me = m + sz;
+ w = f->width;
+ fl = f->flags;
+ if((fl & FmtPrec) && n > f->prec)
+ n = f->prec;
+ if(f->runes){
+ if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0)
+ return -1;
+ rt = (Rune*)f->to;
+ rs = (Rune*)f->stop;
+ for(nc = n; nc > 0; nc--){
+ r = *(uchar*)m;
+ if(r < Runeself)
+ m++;
+ else if((me - m) >= UTFmax || fullrune(m, me-m))
+ m += chartorune(&r, m);
+ else
+ break;
+ FMTRCHAR(f, rt, rs, r);
+ }
+ f->nfmt += rt - (Rune *)f->to;
+ f->to = rt;
+ if(fl & FmtLeft && __rfmtpad(f, w - n) < 0)
+ return -1;
+ }else{
+ if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0)
+ return -1;
+ t = (char*)f->to;
+ s = (char*)f->stop;
+ for(nc = n; nc > 0; nc--){
+ r = *(uchar*)m;
+ if(r < Runeself)
+ m++;
+ else if((me - m) >= UTFmax || fullrune(m, me-m))
+ m += chartorune(&r, m);
+ else
+ break;
+ FMTRUNE(f, t, s, r);
+ }
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ if(fl & FmtLeft && __fmtpad(f, w - n) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+int
+__fmtrcpy(Fmt *f, const void *vm, int n)
+{
+ Rune r, *m, *me, *rt, *rs;
+ char *t, *s;
+ ulong fl;
+ int w;
+
+ m = (Rune*)vm;
+ w = f->width;
+ fl = f->flags;
+ if((fl & FmtPrec) && n > f->prec)
+ n = f->prec;
+ if(f->runes){
+ if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0)
+ return -1;
+ rt = (Rune*)f->to;
+ rs = (Rune*)f->stop;
+ for(me = m + n; m < me; m++)
+ FMTRCHAR(f, rt, rs, *m);
+ f->nfmt += rt - (Rune *)f->to;
+ f->to = rt;
+ if(fl & FmtLeft && __rfmtpad(f, w - n) < 0)
+ return -1;
+ }else{
+ if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0)
+ return -1;
+ t = (char*)f->to;
+ s = (char*)f->stop;
+ for(me = m + n; m < me; m++){
+ r = *m;
+ FMTRUNE(f, t, s, r);
+ }
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ if(fl & FmtLeft && __fmtpad(f, w - n) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+/* fmt out one character */
+int
+__charfmt(Fmt *f)
+{
+ char x[1];
+
+ x[0] = va_arg(f->args, int);
+ f->prec = 1;
+ return __fmtcpy(f, (const char*)x, 1, 1);
+}
+
+/* fmt out one rune */
+int
+__runefmt(Fmt *f)
+{
+ Rune x[1];
+
+ x[0] = va_arg(f->args, int);
+ return __fmtrcpy(f, (const void*)x, 1);
+}
+
+/* public helper routine: fmt out a null terminated string already in hand */
+int
+fmtstrcpy(Fmt *f, char *s)
+{
+ int i, j;
+ Rune r;
+
+ if(!s)
+ return __fmtcpy(f, "<nil>", 5, 5);
+ /* if precision is specified, make sure we don't wander off the end */
+ if(f->flags & FmtPrec){
+ i = 0;
+ for(j=0; j<f->prec && s[i]; j++)
+ i += chartorune(&r, s+i);
+ return __fmtcpy(f, s, j, i);
+ }
+ return __fmtcpy(f, s, utflen(s), strlen(s));
+}
+
+/* fmt out a null terminated utf string */
+int
+__strfmt(Fmt *f)
+{
+ char *s;
+
+ s = va_arg(f->args, char *);
+ return fmtstrcpy(f, s);
+}
+
+/* public helper routine: fmt out a null terminated rune string already in hand */
+int
+fmtrunestrcpy(Fmt *f, Rune *s)
+{
+ Rune *e;
+ int n, p;
+
+ if(!s)
+ return __fmtcpy(f, "<nil>", 5, 5);
+ /* if precision is specified, make sure we don't wander off the end */
+ if(f->flags & FmtPrec){
+ p = f->prec;
+ for(n = 0; n < p; n++)
+ if(s[n] == 0)
+ break;
+ }else{
+ for(e = s; *e; e++)
+ ;
+ n = e - s;
+ }
+ return __fmtrcpy(f, s, n);
+}
+
+/* fmt out a null terminated rune string */
+int
+__runesfmt(Fmt *f)
+{
+ Rune *s;
+
+ s = va_arg(f->args, Rune *);
+ return fmtrunestrcpy(f, s);
+}
+
+/* fmt a % */
+int
+__percentfmt(Fmt *f)
+{
+ Rune x[1];
+
+ x[0] = f->r;
+ f->prec = 1;
+ return __fmtrcpy(f, (const void*)x, 1);
+}
+
+/* fmt an integer */
+int
+__ifmt(Fmt *f)
+{
+ char buf[70], *p, *conv;
+ uvlong vu;
+ ulong u;
+ int neg, base, i, n, fl, w, isv;
+
+ neg = 0;
+ fl = f->flags;
+ isv = 0;
+ vu = 0;
+ u = 0;
+#ifndef PLAN9PORT
+ /*
+ * Unsigned verbs for ANSI C
+ */
+ switch(f->r){
+ case 'x':
+ case 'X':
+ case 'o':
+ case 'u':
+ case 'p':
+ fl |= FmtUnsigned;
+ fl &= ~(FmtSign|FmtSpace);
+ break;
+ }
+#endif
+ if(f->r == 'p'){
+ u = (ulong)va_arg(f->args, void*);
+ f->r = 'x';
+ fl |= FmtUnsigned;
+ }else if(fl & FmtVLong){
+ isv = 1;
+ if(fl & FmtUnsigned)
+ vu = va_arg(f->args, uvlong);
+ else
+ vu = va_arg(f->args, vlong);
+ }else if(fl & FmtLong){
+ if(fl & FmtUnsigned)
+ u = va_arg(f->args, ulong);
+ else
+ u = va_arg(f->args, long);
+ }else if(fl & FmtByte){
+ if(fl & FmtUnsigned)
+ u = (uchar)va_arg(f->args, int);
+ else
+ u = (char)va_arg(f->args, int);
+ }else if(fl & FmtShort){
+ if(fl & FmtUnsigned)
+ u = (ushort)va_arg(f->args, int);
+ else
+ u = (short)va_arg(f->args, int);
+ }else{
+ if(fl & FmtUnsigned)
+ u = va_arg(f->args, uint);
+ else
+ u = va_arg(f->args, int);
+ }
+ conv = "0123456789abcdef";
+ switch(f->r){
+ case 'd':
+ case 'i':
+ case 'u':
+ base = 10;
+ break;
+ case 'x':
+ base = 16;
+ break;
+ case 'X':
+ base = 16;
+ conv = "0123456789ABCDEF";
+ break;
+ case 'b':
+ base = 2;
+ break;
+ case 'o':
+ base = 8;
+ break;
+ default:
+ return -1;
+ }
+ if(!(fl & FmtUnsigned)){
+ if(isv && (vlong)vu < 0){
+ vu = -(vlong)vu;
+ neg = 1;
+ }else if(!isv && (long)u < 0){
+ u = -(long)u;
+ neg = 1;
+ }
+ }
+ p = buf + sizeof buf - 1;
+ n = 0;
+ if(isv){
+ while(vu){
+ i = vu % base;
+ vu /= base;
+ if((fl & FmtComma) && n % 4 == 3){
+ *p-- = ',';
+ n++;
+ }
+ *p-- = conv[i];
+ n++;
+ }
+ }else{
+ while(u){
+ i = u % base;
+ u /= base;
+ if((fl & FmtComma) && n % 4 == 3){
+ *p-- = ',';
+ n++;
+ }
+ *p-- = conv[i];
+ n++;
+ }
+ }
+ if(n == 0){
+ *p-- = '0';
+ n = 1;
+ }
+ for(w = f->prec; n < w && p > buf+3; n++)
+ *p-- = '0';
+ if(neg || (fl & (FmtSign|FmtSpace)))
+ n++;
+ if(fl & FmtSharp){
+ if(base == 16)
+ n += 2;
+ else if(base == 8){
+ if(p[1] == '0')
+ fl &= ~FmtSharp;
+ else
+ n++;
+ }
+ }
+ if((fl & FmtZero) && !(fl & (FmtLeft|FmtPrec))){
+ for(w = f->width; n < w && p > buf+3; n++)
+ *p-- = '0';
+ f->width = 0;
+ }
+ if(fl & FmtSharp){
+ if(base == 16)
+ *p-- = f->r;
+ if(base == 16 || base == 8)
+ *p-- = '0';
+ }
+ if(neg)
+ *p-- = '-';
+ else if(fl & FmtSign)
+ *p-- = '+';
+ else if(fl & FmtSpace)
+ *p-- = ' ';
+ f->flags &= ~FmtPrec;
+ return __fmtcpy(f, p + 1, n, n);
+}
+
+int
+__countfmt(Fmt *f)
+{
+ void *p;
+ ulong fl;
+
+ fl = f->flags;
+ p = va_arg(f->args, void*);
+ if(fl & FmtVLong){
+ *(vlong*)p = f->nfmt;
+ }else if(fl & FmtLong){
+ *(long*)p = f->nfmt;
+ }else if(fl & FmtByte){
+ *(char*)p = f->nfmt;
+ }else if(fl & FmtShort){
+ *(short*)p = f->nfmt;
+ }else{
+ *(int*)p = f->nfmt;
+ }
+ return 0;
+}
+
+int
+__flagfmt(Fmt *f)
+{
+ switch(f->r){
+ case ',':
+ f->flags |= FmtComma;
+ break;
+ case '-':
+ f->flags |= FmtLeft;
+ break;
+ case '+':
+ f->flags |= FmtSign;
+ break;
+ case '#':
+ f->flags |= FmtSharp;
+ break;
+ case ' ':
+ f->flags |= FmtSpace;
+ break;
+ case 'u':
+ f->flags |= FmtUnsigned;
+ break;
+ case 'h':
+ if(f->flags & FmtShort)
+ f->flags |= FmtByte;
+ f->flags |= FmtShort;
+ break;
+ case 'L':
+ f->flags |= FmtLDouble;
+ break;
+ case 'l':
+ if(f->flags & FmtLong)
+ f->flags |= FmtVLong;
+ f->flags |= FmtLong;
+ break;
+ }
+ return 1;
+}
+
+/* default error format */
+int
+__badfmt(Fmt *f)
+{
+ char x[3];
+
+ x[0] = '%';
+ x[1] = f->r;
+ x[2] = '%';
+ f->prec = 3;
+ __fmtcpy(f, (const void*)x, 3, 3);
+ return 0;
+}
diff --git a/lib9/fmt/dorfmt.c b/lib9/fmt/dorfmt.c
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+/* format the output into f->to and return the number of characters fmted */
+
+int
+dorfmt(Fmt *f, const Rune *fmt)
+{
+ Rune *rt, *rs;
+ int r;
+ char *t, *s;
+ int nfmt;
+
+ nfmt = f->nfmt;
+ for(;;){
+ if(f->runes){
+ rt = f->to;
+ rs = f->stop;
+ while((r = *fmt++) && r != '%'){
+ FMTRCHAR(f, rt, rs, r);
+ }
+ f->nfmt += rt - (Rune *)f->to;
+ f->to = rt;
+ if(!r)
+ return f->nfmt - nfmt;
+ f->stop = rs;
+ }else{
+ t = f->to;
+ s = f->stop;
+ while((r = *fmt++) && r != '%'){
+ FMTRUNE(f, t, f->stop, r);
+ }
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ if(!r)
+ return f->nfmt - nfmt;
+ f->stop = s;
+ }
+
+ fmt = __fmtdispatch(f, (Rune*)fmt, 1);
+ if(fmt == nil)
+ return -1;
+ }
+ return 0; /* not reached */
+}
diff --git a/lib9/fmt/errfmt.c b/lib9/fmt/errfmt.c
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+__errfmt(Fmt *f)
+{
+ char *s;
+
+ s = strerror(errno);
+ return fmtstrcpy(f, s);
+}
diff --git a/lib9/fmt/fltfmt.c b/lib9/fmt/fltfmt.c
@@ -0,0 +1,394 @@
+/*
+ * 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.
+ */
+#include <stdio.h>
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <fmt.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+enum
+{
+ FDIGIT = 30,
+ FDEFLT = 6,
+ NSIGNIF = 17
+};
+
+/*
+ * first few powers of 10, enough for about 1/2 of the
+ * total space for doubles.
+ */
+static double pows10[] =
+{
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+ 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
+ 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39,
+ 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49,
+ 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59,
+ 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69,
+ 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79,
+ 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89,
+ 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99,
+ 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109,
+ 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119,
+ 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129,
+ 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139,
+ 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149,
+ 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159,
+};
+
+#define pow10(x) fmtpow10(x)
+
+static double
+pow10(int n)
+{
+ double d;
+ int neg;
+
+ neg = 0;
+ if(n < 0){
+ if(n < DBL_MIN_10_EXP){
+ return 0.;
+ }
+ neg = 1;
+ n = -n;
+ }else if(n > DBL_MAX_10_EXP){
+ return HUGE_VAL;
+ }
+ if(n < (int)(sizeof(pows10)/sizeof(pows10[0])))
+ d = pows10[n];
+ else{
+ d = pows10[sizeof(pows10)/sizeof(pows10[0]) - 1];
+ for(;;){
+ n -= sizeof(pows10)/sizeof(pows10[0]) - 1;
+ if(n < (int)(sizeof(pows10)/sizeof(pows10[0]))){
+ d *= pows10[n];
+ break;
+ }
+ d *= pows10[sizeof(pows10)/sizeof(pows10[0]) - 1];
+ }
+ }
+ if(neg){
+ return 1./d;
+ }
+ return d;
+}
+
+static int
+xadd(char *a, int n, int v)
+{
+ char *b;
+ int c;
+
+ if(n < 0 || n >= NSIGNIF)
+ return 0;
+ for(b = a+n; b >= a; b--) {
+ c = *b + v;
+ if(c <= '9') {
+ *b = c;
+ return 0;
+ }
+ *b = '0';
+ v = 1;
+ }
+ *a = '1'; /* overflow adding */
+ return 1;
+}
+
+static int
+xsub(char *a, int n, int v)
+{
+ char *b;
+ int c;
+
+ for(b = a+n; b >= a; b--) {
+ c = *b - v;
+ if(c >= '0') {
+ *b = c;
+ return 0;
+ }
+ *b = '9';
+ v = 1;
+ }
+ *a = '9'; /* underflow subtracting */
+ return 1;
+}
+
+static void
+xdtoa(Fmt *fmt, char *s2, double f)
+{
+ char s1[NSIGNIF+10];
+ double g, h;
+ int e, d, i, n;
+ int c1, c2, c3, c4, ucase, sign, chr, prec;
+
+ prec = FDEFLT;
+ if(fmt->flags & FmtPrec)
+ prec = fmt->prec;
+ if(prec > FDIGIT)
+ prec = FDIGIT;
+ if(__isNaN(f)) {
+ strcpy(s2, "NaN");
+ return;
+ }
+ if(__isInf(f, 1)) {
+ strcpy(s2, "+Inf");
+ return;
+ }
+ if(__isInf(f, -1)) {
+ strcpy(s2, "-Inf");
+ return;
+ }
+ sign = 0;
+ if(f < 0) {
+ f = -f;
+ sign++;
+ }
+ ucase = 0;
+ chr = fmt->r;
+ if(isupper(chr)) {
+ ucase = 1;
+ chr = tolower(chr);
+ }
+
+ e = 0;
+ g = f;
+ if(g != 0) {
+ frexp(f, &e);
+ e = e * .301029995664;
+ if(e >= -150 && e <= +150) {
+ d = 0;
+ h = f;
+ } else {
+ d = e/2;
+ h = f * pow10(-d);
+ }
+ g = h * pow10(d-e);
+ while(g < 1) {
+ e--;
+ g = h * pow10(d-e);
+ }
+ while(g >= 10) {
+ e++;
+ g = h * pow10(d-e);
+ }
+ }
+
+ /*
+ * convert NSIGNIF digits and convert
+ * back to get accuracy.
+ */
+ for(i=0; i<NSIGNIF; i++) {
+ d = g;
+ s1[i] = d + '0';
+ g = (g - d) * 10;
+ }
+ s1[i] = 0;
+
+ /*
+ * try decimal rounding to eliminate 9s
+ */
+ c2 = prec + 1;
+ if(chr == 'f')
+ c2 += e;
+ if(c2 >= NSIGNIF-2) {
+ strcpy(s2, s1);
+ d = e;
+ s1[NSIGNIF-2] = '0';
+ s1[NSIGNIF-1] = '0';
+ sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
+ g = strtod(s1, nil);
+ if(g == f)
+ goto found;
+ if(xadd(s1, NSIGNIF-3, 1)) {
+ e++;
+ sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
+ }
+ g = strtod(s1, nil);
+ if(g == f)
+ goto found;
+ strcpy(s1, s2);
+ e = d;
+ }
+
+ /*
+ * convert back so s1 gets exact answer
+ */
+ for(;;) {
+ sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
+ g = strtod(s1, nil);
+ if(f > g) {
+ if(xadd(s1, NSIGNIF-1, 1))
+ e--;
+ continue;
+ }
+ if(f < g) {
+ if(xsub(s1, NSIGNIF-1, 1))
+ e++;
+ continue;
+ }
+ break;
+ }
+
+found:
+ /*
+ * sign
+ */
+ d = 0;
+ i = 0;
+ if(sign)
+ s2[d++] = '-';
+ else if(fmt->flags & FmtSign)
+ s2[d++] = '+';
+ else if(fmt->flags & FmtSpace)
+ s2[d++] = ' ';
+
+ /*
+ * copy into final place
+ * c1 digits of leading '0'
+ * c2 digits from conversion
+ * c3 digits of trailing '0'
+ * c4 digits after '.'
+ */
+ c1 = 0;
+ c2 = prec + 1;
+ c3 = 0;
+ c4 = prec;
+ switch(chr) {
+ default:
+ if(xadd(s1, c2, 5))
+ e++;
+ break;
+ case 'g':
+ /*
+ * decide on 'e' of 'f' style convers
+ */
+ if(xadd(s1, c2, 5))
+ e++;
+ if(e >= -5 && e <= prec) {
+ c1 = -e - 1;
+ c4 = prec - e;
+ chr = 'h'; // flag for 'f' style
+ }
+ break;
+ case 'f':
+ if(xadd(s1, c2+e, 5))
+ e++;
+ c1 = -e;
+ if(c1 > prec)
+ c1 = c2;
+ c2 += e;
+ break;
+ }
+
+ /*
+ * clean up c1 c2 and c3
+ */
+ if(c1 < 0)
+ c1 = 0;
+ if(c2 < 0)
+ c2 = 0;
+ if(c2 > NSIGNIF) {
+ c3 = c2-NSIGNIF;
+ c2 = NSIGNIF;
+ }
+
+ /*
+ * copy digits
+ */
+ while(c1 > 0) {
+ if(c1+c2+c3 == c4)
+ s2[d++] = '.';
+ s2[d++] = '0';
+ c1--;
+ }
+ while(c2 > 0) {
+ if(c2+c3 == c4)
+ s2[d++] = '.';
+ s2[d++] = s1[i++];
+ c2--;
+ }
+ while(c3 > 0) {
+ if(c3 == c4)
+ s2[d++] = '.';
+ s2[d++] = '0';
+ c3--;
+ }
+
+ /*
+ * strip trailing '0' on g conv
+ */
+ if(fmt->flags & FmtSharp) {
+ if(0 == c4)
+ s2[d++] = '.';
+ } else
+ if(chr == 'g' || chr == 'h') {
+ for(n=d-1; n>=0; n--)
+ if(s2[n] != '0')
+ break;
+ for(i=n; i>=0; i--)
+ if(s2[i] == '.') {
+ d = n;
+ if(i != n)
+ d++;
+ break;
+ }
+ }
+ if(chr == 'e' || chr == 'g') {
+ if(ucase)
+ s2[d++] = 'E';
+ else
+ s2[d++] = 'e';
+ c1 = e;
+ if(c1 < 0) {
+ s2[d++] = '-';
+ c1 = -c1;
+ } else
+ s2[d++] = '+';
+ if(c1 >= 100) {
+ s2[d++] = c1/100 + '0';
+ c1 = c1%100;
+ }
+ s2[d++] = c1/10 + '0';
+ s2[d++] = c1%10 + '0';
+ }
+ s2[d] = 0;
+}
+
+static int
+floatfmt(Fmt *fmt, double f)
+{
+ char s[FDIGIT+10];
+
+ xdtoa(fmt, s, f);
+ fmt->flags &= FmtWidth|FmtLeft;
+ __fmtcpy(fmt, s, strlen(s), strlen(s));
+ return 0;
+}
+
+int
+__efgfmt(Fmt *f)
+{
+ double d;
+
+ d = va_arg(f->args, double);
+ return floatfmt(f, d);
+}
diff --git a/lib9/fmt/fmt.c b/lib9/fmt/fmt.c
@@ -0,0 +1,231 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+enum
+{
+ Maxfmt = 64
+};
+
+typedef struct Convfmt Convfmt;
+struct Convfmt
+{
+ int c;
+ volatile Fmts fmt; /* for spin lock in fmtfmt; avoids race due to write order */
+};
+
+struct
+{
+ /* lock by calling __fmtlock, __fmtunlock */
+ int nfmt;
+ Convfmt fmt[Maxfmt];
+} fmtalloc;
+
+static Convfmt knownfmt[] = {
+ ' ', __flagfmt,
+ '#', __flagfmt,
+ '%', __percentfmt,
+ '+', __flagfmt,
+ ',', __flagfmt,
+ '-', __flagfmt,
+ 'C', __runefmt, /* Plan 9 addition */
+ 'E', __efgfmt,
+#ifndef PLAN9PORT
+ 'F', __efgfmt, /* ANSI only */
+#endif
+ 'G', __efgfmt,
+#ifndef PLAN9PORT
+ 'L', __flagfmt, /* ANSI only */
+#endif
+ 'S', __runesfmt, /* Plan 9 addition */
+ 'X', __ifmt,
+ 'b', __ifmt, /* Plan 9 addition */
+ 'c', __charfmt,
+ 'd', __ifmt,
+ 'e', __efgfmt,
+ 'f', __efgfmt,
+ 'g', __efgfmt,
+ 'h', __flagfmt,
+#ifndef PLAN9PORT
+ 'i', __ifmt, /* ANSI only */
+#endif
+ 'l', __flagfmt,
+ 'n', __countfmt,
+ 'o', __ifmt,
+ 'p', __ifmt,
+ 'r', __errfmt,
+ 's', __strfmt,
+#ifdef PLAN9PORT
+ 'u', __flagfmt,
+#else
+ 'u', __ifmt,
+#endif
+ 'x', __ifmt,
+ 0, nil,
+};
+
+
+int (*fmtdoquote)(int);
+
+/*
+ * __fmtlock() must be set
+ */
+static int
+__fmtinstall(int c, Fmts f)
+{
+ Convfmt *p, *ep;
+
+ if(c<=0 || c>=65536)
+ return -1;
+ if(!f)
+ f = __badfmt;
+
+ ep = &fmtalloc.fmt[fmtalloc.nfmt];
+ for(p=fmtalloc.fmt; p<ep; p++)
+ if(p->c == c)
+ break;
+
+ if(p == &fmtalloc.fmt[Maxfmt])
+ return -1;
+
+ p->fmt = f;
+ if(p == ep){ /* installing a new format character */
+ fmtalloc.nfmt++;
+ p->c = c;
+ }
+
+ return 0;
+}
+
+int
+fmtinstall(int c, int (*f)(Fmt*))
+{
+ int ret;
+
+ __fmtlock();
+ ret = __fmtinstall(c, f);
+ __fmtunlock();
+ return ret;
+}
+
+static Fmts
+fmtfmt(int c)
+{
+ Convfmt *p, *ep;
+
+ ep = &fmtalloc.fmt[fmtalloc.nfmt];
+ for(p=fmtalloc.fmt; p<ep; p++)
+ if(p->c == c){
+ while(p->fmt == nil) /* loop until value is updated */
+ ;
+ return p->fmt;
+ }
+
+ /* is this a predefined format char? */
+ __fmtlock();
+ for(p=knownfmt; p->c; p++)
+ if(p->c == c){
+ __fmtinstall(p->c, p->fmt);
+ __fmtunlock();
+ return p->fmt;
+ }
+ __fmtunlock();
+
+ return __badfmt;
+}
+
+void*
+__fmtdispatch(Fmt *f, void *fmt, int isrunes)
+{
+ Rune rune, r;
+ int i, n;
+
+ f->flags = 0;
+ f->width = f->prec = 0;
+
+ for(;;){
+ if(isrunes){
+ r = *(Rune*)fmt;
+ fmt = (Rune*)fmt + 1;
+ }else{
+ fmt = (char*)fmt + chartorune(&rune, (char*)fmt);
+ r = rune;
+ }
+ f->r = r;
+ switch(r){
+ case '\0':
+ return nil;
+ case '.':
+ f->flags |= FmtWidth|FmtPrec;
+ continue;
+ case '0':
+ if(!(f->flags & FmtWidth)){
+ f->flags |= FmtZero;
+ continue;
+ }
+ /* fall through */
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ i = 0;
+ while(r >= '0' && r <= '9'){
+ i = i * 10 + r - '0';
+ if(isrunes){
+ r = *(Rune*)fmt;
+ fmt = (Rune*)fmt + 1;
+ }else{
+ r = *(char*)fmt;
+ fmt = (char*)fmt + 1;
+ }
+ }
+ if(isrunes)
+ fmt = (Rune*)fmt - 1;
+ else
+ fmt = (char*)fmt - 1;
+ numflag:
+ if(f->flags & FmtWidth){
+ f->flags |= FmtPrec;
+ f->prec = i;
+ }else{
+ f->flags |= FmtWidth;
+ f->width = i;
+ }
+ continue;
+ case '*':
+ i = va_arg(f->args, int);
+ if(i < 0){
+ /*
+ * negative precision =>
+ * ignore the precision.
+ */
+ if(f->flags & FmtPrec){
+ f->flags &= ~FmtPrec;
+ f->prec = 0;
+ continue;
+ }
+ i = -i;
+ f->flags |= FmtLeft;
+ }
+ goto numflag;
+ }
+ n = (*fmtfmt(r))(f);
+ if(n < 0)
+ return nil;
+ if(n == 0)
+ return fmt;
+ }
+}
diff --git a/lib9/fmt/fmtdef.h b/lib9/fmt/fmtdef.h
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+/*
+ * dofmt -- format to a buffer
+ * the number of characters formatted is returned,
+ * or -1 if there was an error.
+ * if the buffer is ever filled, flush is called.
+ * it should reset the buffer and return whether formatting should continue.
+ */
+
+typedef int (*Fmts)(Fmt*);
+
+typedef struct Quoteinfo Quoteinfo;
+struct Quoteinfo
+{
+ int quoted; /* if set, string must be quoted */
+ int nrunesin; /* number of input runes that can be accepted */
+ int nbytesin; /* number of input bytes that can be accepted */
+ int nrunesout; /* number of runes that will be generated */
+ int nbytesout; /* number of bytes that will be generated */
+};
+
+/* Edit .+1,/^$/ |cfn |grep -v static | grep __ */
+double __Inf(int sign);
+double __NaN(void);
+int __badfmt(Fmt *f);
+int __charfmt(Fmt *f);
+int __countfmt(Fmt *f);
+int __efgfmt(Fmt *fmt);
+int __errfmt(Fmt *f);
+int __flagfmt(Fmt *f);
+int __fmtFdFlush(Fmt *f);
+int __fmtcpy(Fmt *f, const void *vm, int n, int sz);
+void* __fmtdispatch(Fmt *f, void *fmt, int isrunes);
+void * __fmtflush(Fmt *f, void *t, int len);
+void __fmtlock(void);
+int __fmtpad(Fmt *f, int n);
+double __fmtpow10(int n);
+int __fmtrcpy(Fmt *f, const void *vm, int n);
+void __fmtunlock(void);
+int __ifmt(Fmt *f);
+int __isInf(double d, int sign);
+int __isNaN(double d);
+int __needsquotes(char *s, int *quotelenp);
+int __percentfmt(Fmt *f);
+void __quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q, int sharp, int runesout);
+int __quotestrfmt(int runesin, Fmt *f);
+int __rfmtpad(Fmt *f, int n);
+int __runefmt(Fmt *f);
+int __runeneedsquotes(Rune *r, int *quotelenp);
+int __runesfmt(Fmt *f);
+int __strfmt(Fmt *f);
+
+#define FMTCHAR(f, t, s, c)\
+ do{\
+ if(t + 1 > (char*)s){\
+ t = __fmtflush(f, t, 1);\
+ if(t != nil)\
+ s = f->stop;\
+ else\
+ return -1;\
+ }\
+ *t++ = c;\
+ }while(0)
+
+#define FMTRCHAR(f, t, s, c)\
+ do{\
+ if(t + 1 > (Rune*)s){\
+ t = __fmtflush(f, t, sizeof(Rune));\
+ if(t != nil)\
+ s = f->stop;\
+ else\
+ return -1;\
+ }\
+ *t++ = c;\
+ }while(0)
+
+#define FMTRUNE(f, t, s, r)\
+ do{\
+ Rune _rune;\
+ int _runelen;\
+ if(t + UTFmax > (char*)s && t + (_runelen = runelen(r)) > (char*)s){\
+ t = __fmtflush(f, t, _runelen);\
+ if(t != nil)\
+ s = f->stop;\
+ else\
+ return -1;\
+ }\
+ if(r < Runeself)\
+ *t++ = r;\
+ else{\
+ _rune = r;\
+ t += runetochar(t, &_rune);\
+ }\
+ }while(0)
+
+#ifdef va_copy
+# define VA_COPY(a,b) va_copy(a,b)
+# define VA_END(a) va_end(a)
+#else
+# define VA_COPY(a,b) (a) = (b)
+# define VA_END(a)
+#endif
+
diff --git a/lib9/fmt/fmtfd.c b/lib9/fmt/fmtfd.c
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+/*
+ * public routine for final flush of a formatting buffer
+ * to a file descriptor; returns total char count.
+ */
+int
+fmtfdflush(Fmt *f)
+{
+ if(__fmtFdFlush(f) <= 0)
+ return -1;
+ return f->nfmt;
+}
+
+/*
+ * initialize an output buffer for buffered printing
+ */
+int
+fmtfdinit(Fmt *f, int fd, char *buf, int size)
+{
+ f->runes = 0;
+ f->start = buf;
+ f->to = buf;
+ f->stop = buf + size;
+ f->flush = __fmtFdFlush;
+ f->farg = (void*)fd;
+ f->nfmt = 0;
+ return 0;
+}
diff --git a/lib9/fmt/fmtfdflush.c b/lib9/fmt/fmtfdflush.c
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <unistd.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+/*
+ * generic routine for flushing a formatting buffer
+ * to a file descriptor
+ */
+int
+__fmtFdFlush(Fmt *f)
+{
+ int n;
+
+ n = (char*)f->to - (char*)f->start;
+ if(n && write((int)f->farg, f->start, n) != n)
+ return 0;
+ f->to = f->start;
+ return 1;
+}
diff --git a/lib9/fmt/fmtlock.c b/lib9/fmt/fmtlock.c
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+void
+__fmtlock(void)
+{
+}
+
+void
+__fmtunlock(void)
+{
+}
diff --git a/lib9/fmt/fmtprint.c b/lib9/fmt/fmtprint.c
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+/*
+ * format a string into the output buffer
+ * designed for formats which themselves call fmt,
+ * but ignore any width flags
+ */
+int
+fmtprint(Fmt *f, char *fmt, ...)
+{
+ va_list va;
+ int n;
+
+ f->flags = 0;
+ f->width = 0;
+ f->prec = 0;
+ VA_COPY(va, f->args);
+ VA_END(f->args);
+ va_start(f->args, fmt);
+ n = dofmt(f, fmt);
+ va_end(f->args);
+ f->flags = 0;
+ f->width = 0;
+ f->prec = 0;
+ VA_COPY(f->args,va);
+ VA_END(va);
+ if(n >= 0)
+ return 0;
+ return n;
+}
+
diff --git a/lib9/fmt/fmtquote.c b/lib9/fmt/fmtquote.c
@@ -0,0 +1,264 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+/*
+ * How many bytes of output UTF will be produced by quoting (if necessary) this string?
+ * How many runes? How much of the input will be consumed?
+ * The parameter q is filled in by __quotesetup.
+ * The string may be UTF or Runes (s or r).
+ * Return count does not include NUL.
+ * Terminate the scan at the first of:
+ * NUL in input
+ * count exceeded in input
+ * count exceeded on output
+ * *ninp is set to number of input bytes accepted.
+ * nin may be <0 initially, to avoid checking input by count.
+ */
+void
+__quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q, int sharp, int runesout)
+{
+ int w;
+ Rune c;
+
+ q->quoted = 0;
+ q->nbytesout = 0;
+ q->nrunesout = 0;
+ q->nbytesin = 0;
+ q->nrunesin = 0;
+ if(sharp || nin==0 || (s && *s=='\0') || (r && *r=='\0')){
+ if(nout < 2)
+ return;
+ q->quoted = 1;
+ q->nbytesout = 2;
+ q->nrunesout = 2;
+ }
+ for(; nin!=0; nin--){
+ if(s)
+ w = chartorune(&c, s);
+ else{
+ c = *r;
+ w = runelen(c);
+ }
+
+ if(c == '\0')
+ break;
+ if(runesout){
+ if(q->nrunesout+1 > nout)
+ break;
+ }else{
+ if(q->nbytesout+w > nout)
+ break;
+ }
+
+ if((c <= L' ') || (c == L'\'') || (fmtdoquote!=nil && fmtdoquote(c))){
+ if(!q->quoted){
+ if(runesout){
+ if(1+q->nrunesout+1+1 > nout) /* no room for quotes */
+ break;
+ }else{
+ if(1+q->nbytesout+w+1 > nout) /* no room for quotes */
+ break;
+ }
+ q->nrunesout += 2; /* include quotes */
+ q->nbytesout += 2; /* include quotes */
+ q->quoted = 1;
+ }
+ if(c == '\'') {
+ if(runesout){
+ if(1+q->nrunesout+1 > nout) /* no room for quotes */
+ break;
+ }else{
+ if(1+q->nbytesout+w > nout) /* no room for quotes */
+ break;
+ }
+ q->nbytesout++;
+ q->nrunesout++; /* quotes reproduce as two characters */
+ }
+ }
+
+ /* advance input */
+ if(s)
+ s += w;
+ else
+ r++;
+ q->nbytesin += w;
+ q->nrunesin++;
+
+ /* advance output */
+ q->nbytesout += w;
+ q->nrunesout++;
+ }
+}
+
+static int
+qstrfmt(char *sin, Rune *rin, Quoteinfo *q, Fmt *f)
+{
+ Rune r, *rm, *rme;
+ char *t, *s, *m, *me;
+ Rune *rt, *rs;
+ ulong fl;
+ int nc, w;
+
+ m = sin;
+ me = m + q->nbytesin;
+ rm = rin;
+ rme = rm + q->nrunesin;
+
+ w = f->width;
+ fl = f->flags;
+ if(f->runes){
+ if(!(fl & FmtLeft) && __rfmtpad(f, w - q->nrunesout) < 0)
+ return -1;
+ }else{
+ if(!(fl & FmtLeft) && __fmtpad(f, w - q->nbytesout) < 0)
+ return -1;
+ }
+ t = (char*)f->to;
+ s = (char*)f->stop;
+ rt = (Rune*)f->to;
+ rs = (Rune*)f->stop;
+ if(f->runes)
+ FMTRCHAR(f, rt, rs, '\'');
+ else
+ FMTRUNE(f, t, s, '\'');
+ for(nc = q->nrunesin; nc > 0; nc--){
+ if(sin){
+ r = *(uchar*)m;
+ if(r < Runeself)
+ m++;
+ else if((me - m) >= UTFmax || fullrune(m, me-m))
+ m += chartorune(&r, m);
+ else
+ break;
+ }else{
+ if(rm >= rme)
+ break;
+ r = *(uchar*)rm++;
+ }
+ if(f->runes){
+ FMTRCHAR(f, rt, rs, r);
+ if(r == '\'')
+ FMTRCHAR(f, rt, rs, r);
+ }else{
+ FMTRUNE(f, t, s, r);
+ if(r == '\'')
+ FMTRUNE(f, t, s, r);
+ }
+ }
+
+ if(f->runes){
+ FMTRCHAR(f, rt, rs, '\'');
+ USED(rs);
+ f->nfmt += rt - (Rune *)f->to;
+ f->to = rt;
+ if(fl & FmtLeft && __rfmtpad(f, w - q->nrunesout) < 0)
+ return -1;
+ }else{
+ FMTRUNE(f, t, s, '\'');
+ USED(s);
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ if(fl & FmtLeft && __fmtpad(f, w - q->nbytesout) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+int
+__quotestrfmt(int runesin, Fmt *f)
+{
+ int nin, outlen;
+ Rune *r;
+ char *s;
+ Quoteinfo q;
+
+ nin = -1;
+ if(f->flags&FmtPrec)
+ nin = f->prec;
+ if(runesin){
+ r = va_arg(f->args, Rune *);
+ s = nil;
+ }else{
+ s = va_arg(f->args, char *);
+ r = nil;
+ }
+ if(!s && !r)
+ return __fmtcpy(f, (void*)"<nil>", 5, 5);
+
+ if(f->flush)
+ outlen = 0x7FFFFFFF; /* if we can flush, no output limit */
+ else if(f->runes)
+ outlen = (Rune*)f->stop - (Rune*)f->to;
+ else
+ outlen = (char*)f->stop - (char*)f->to;
+
+ __quotesetup(s, r, nin, outlen, &q, f->flags&FmtSharp, f->runes);
+//print("bytes in %d bytes out %d runes in %d runesout %d\n", q.nbytesin, q.nbytesout, q.nrunesin, q.nrunesout);
+
+ if(runesin){
+ if(!q.quoted)
+ return __fmtrcpy(f, r, q.nrunesin);
+ return qstrfmt(nil, r, &q, f);
+ }
+
+ if(!q.quoted)
+ return __fmtcpy(f, s, q.nrunesin, q.nbytesin);
+ return qstrfmt(s, nil, &q, f);
+}
+
+int
+quotestrfmt(Fmt *f)
+{
+ return __quotestrfmt(0, f);
+}
+
+int
+quoterunestrfmt(Fmt *f)
+{
+ return __quotestrfmt(1, f);
+}
+
+void
+quotefmtinstall(void)
+{
+ fmtinstall('q', quotestrfmt);
+ fmtinstall('Q', quoterunestrfmt);
+}
+
+int
+__needsquotes(char *s, int *quotelenp)
+{
+ Quoteinfo q;
+
+ __quotesetup(s, nil, -1, 0x7FFFFFFF, &q, 0, 0);
+ *quotelenp = q.nbytesout;
+
+ return q.quoted;
+}
+
+int
+__runeneedsquotes(Rune *r, int *quotelenp)
+{
+ Quoteinfo q;
+
+ __quotesetup(nil, r, -1, 0x7FFFFFFF, &q, 0, 0);
+ *quotelenp = q.nrunesout;
+
+ return q.quoted;
+}
diff --git a/lib9/fmt/fmtrune.c b/lib9/fmt/fmtrune.c
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+fmtrune(Fmt *f, int r)
+{
+ Rune *rt;
+ char *t;
+ int n;
+
+ if(f->runes){
+ rt = (Rune*)f->to;
+ FMTRCHAR(f, rt, f->stop, r);
+ f->to = rt;
+ n = 1;
+ }else{
+ t = (char*)f->to;
+ FMTRUNE(f, t, f->stop, r);
+ n = t - (char*)f->to;
+ f->to = t;
+ }
+ f->nfmt += n;
+ return 0;
+}
diff --git a/lib9/fmt/fmtstr.c b/lib9/fmt/fmtstr.c
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+#include <stdlib.h>
+#include <stdarg.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+char*
+fmtstrflush(Fmt *f)
+{
+ if(f->start == nil)
+ return nil;
+ *(char*)f->to = '\0';
+ return (char*)f->start;
+}
diff --git a/lib9/fmt/fmtvprint.c b/lib9/fmt/fmtvprint.c
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+
+/*
+ * format a string into the output buffer
+ * designed for formats which themselves call fmt,
+ * but ignore any width flags
+ */
+int
+fmtvprint(Fmt *f, char *fmt, va_list args)
+{
+ va_list va;
+ int n;
+
+ f->flags = 0;
+ f->width = 0;
+ f->prec = 0;
+ VA_COPY(va,f->args);
+ VA_END(f->args);
+ VA_COPY(f->args,args);
+ n = dofmt(f, fmt);
+ f->flags = 0;
+ f->width = 0;
+ f->prec = 0;
+ VA_END(f->args);
+ VA_COPY(f->args,va);
+ VA_END(va);
+ if(n >= 0)
+ return 0;
+ return n;
+}
+
diff --git a/lib9/fmt/fprint.c b/lib9/fmt/fprint.c
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+fprint(int fd, char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = vfprint(fd, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/lib9/fmt/nan64.c b/lib9/fmt/nan64.c
@@ -0,0 +1,67 @@
+/*
+ * 64-bit IEEE not-a-number routines.
+ * This is big/little-endian portable assuming that
+ * the 64-bit doubles and 64-bit integers have the
+ * same byte ordering.
+ */
+
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+#if defined (__APPLE__) || (__powerpc__)
+#define _NEEDLL
+#endif
+
+static uvlong uvnan = ((uvlong)0x7FF00000<<32)|0x00000001;
+static uvlong uvinf = ((uvlong)0x7FF00000<<32)|0x00000000;
+static uvlong uvneginf = ((uvlong)0xFFF00000<<32)|0x00000000;
+
+double
+__NaN(void)
+{
+ uvlong *p;
+
+ /* gcc complains about "return *(double*)&uvnan;" */
+ p = &uvnan;
+ return *(double*)p;
+}
+
+int
+__isNaN(double d)
+{
+ uvlong x;
+ double *p;
+
+ p = &d;
+ x = *(uvlong*)p;
+ return (ulong)(x>>32)==0x7FF00000 && !__isInf(d, 0);
+}
+
+double
+__Inf(int sign)
+{
+ uvlong *p;
+
+ if(sign < 0)
+ p = &uvinf;
+ else
+ p = &uvneginf;
+ return *(double*)p;
+}
+
+int
+__isInf(double d, int sign)
+{
+ uvlong x;
+ double *p;
+
+ p = &d;
+ x = *(uvlong*)p;
+ if(sign == 0)
+ return x==uvinf || x==uvneginf;
+ else if(sign > 0)
+ return x==uvinf;
+ else
+ return x==uvneginf;
+}
diff --git a/lib9/fmt/plan9.h b/lib9/fmt/plan9.h
@@ -0,0 +1,33 @@
+/*
+ * compiler directive on Plan 9
+ */
+#ifndef USED
+#define USED(x) if(x);else
+#endif
+
+/*
+ * easiest way to make sure these are defined
+ */
+#define uchar _fmtuchar
+#define ushort _fmtushort
+#define uint _fmtuint
+#define ulong _fmtulong
+#define vlong _fmtvlong
+#define uvlong _fmtuvlong
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+typedef unsigned long long uvlong;
+typedef long long vlong;
+
+/*
+ * nil cannot be ((void*)0) on ANSI C,
+ * because it is used for function pointers
+ */
+#undef nil
+#define nil 0
+
+#undef nelem
+#define nelem(x) (sizeof (x)/sizeof (x)[0])
+
diff --git a/lib9/fmt/portdate b/lib9/fmt/portdate
@@ -0,0 +1,30 @@
+dofmt.c 2004/1225
+dorfmt.c 2004/1225
+errfmt.c 2004/1225
+fltfmt.c 2004/1225
+fmt.c 2004/1225
+fmtfd.c 2004/1225
+fmtlock.c 2004/1225
+fmtprint.c 2004/1225
+fmtquote.c 2004/1225
+fmtrune.c 2004/1225
+fmtstr.c 2004/1225
+fmtvprint.c 2004/1225
+fprint.c 2004/1225
+print.c 2004/1225
+runefmtstr.c 2004/1225
+runeseprint.c 2004/1225
+runesmprint.c 2004/1225
+runesnprint.c 2004/1225
+runesprint.c 2004/1225
+runevseprint.c 2004/1225
+runevsmprint.c 2004/1225
+runevsnprint.c 2004/1225
+seprint.c 2004/1225
+smprint.c 2004/1225
+snprint.c 2004/1225
+sprint.c 2004/1225
+vfprint.c 2004/1225
+vseprint.c 2004/1225
+vsmprint.c 2004/1225
+vsnprint.c 2004/1225
diff --git a/lib9/fmt/pow10.c b/lib9/fmt/pow10.c
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+/*
+ * this table might overflow 127-bit exponent representations.
+ * in that case, truncate it after 1.0e38.
+ * it is important to get all one can from this
+ * routine since it is used in atof to scale numbers.
+ * the presumption is that C converts fp numbers better
+ * than multipication of lower powers of 10.
+ */
+
+static
+double tab[] =
+{
+ 1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9,
+ 1.0e10,1.0e11,1.0e12,1.0e13,1.0e14,1.0e15,1.0e16,1.0e17,1.0e18,1.0e19,
+ 1.0e20,1.0e21,1.0e22,1.0e23,1.0e24,1.0e25,1.0e26,1.0e27,1.0e28,1.0e29,
+ 1.0e30,1.0e31,1.0e32,1.0e33,1.0e34,1.0e35,1.0e36,1.0e37,1.0e38,1.0e39,
+ 1.0e40,1.0e41,1.0e42,1.0e43,1.0e44,1.0e45,1.0e46,1.0e47,1.0e48,1.0e49,
+ 1.0e50,1.0e51,1.0e52,1.0e53,1.0e54,1.0e55,1.0e56,1.0e57,1.0e58,1.0e59,
+ 1.0e60,1.0e61,1.0e62,1.0e63,1.0e64,1.0e65,1.0e66,1.0e67,1.0e68,1.0e69,
+};
+
+double
+__fmtpow10(int n)
+{
+ int m;
+
+ if(n < 0) {
+ n = -n;
+ if(n < (int)(sizeof(tab)/sizeof(tab[0])))
+ return 1/tab[n];
+ m = n/2;
+ return __fmtpow10(-m) * __fmtpow10(m-n);
+ }
+ if(n < (int)(sizeof(tab)/sizeof(tab[0])))
+ return tab[n];
+ m = n/2;
+ return __fmtpow10(m) * __fmtpow10(n-m);
+}
diff --git a/lib9/fmt/print.c b/lib9/fmt/print.c
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+print(char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = vfprint(1, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/lib9/fmt/runefmtstr.c b/lib9/fmt/runefmtstr.c
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+Rune*
+runefmtstrflush(Fmt *f)
+{
+ if(f->start == nil)
+ return nil;
+ *(Rune*)f->to = '\0';
+ return f->start;
+}
diff --git a/lib9/fmt/runeseprint.c b/lib9/fmt/runeseprint.c
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+Rune*
+runeseprint(Rune *buf, Rune *e, char *fmt, ...)
+{
+ Rune *p;
+ va_list args;
+
+ va_start(args, fmt);
+ p = runevseprint(buf, e, fmt, args);
+ va_end(args);
+ return p;
+}
diff --git a/lib9/fmt/runesmprint.c b/lib9/fmt/runesmprint.c
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+Rune*
+runesmprint(char *fmt, ...)
+{
+ va_list args;
+ Rune *p;
+
+ va_start(args, fmt);
+ p = runevsmprint(fmt, args);
+ va_end(args);
+ return p;
+}
diff --git a/lib9/fmt/runesnprint.c b/lib9/fmt/runesnprint.c
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+runesnprint(Rune *buf, int len, char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = runevsnprint(buf, len, fmt, args);
+ va_end(args);
+ return n;
+}
+
diff --git a/lib9/fmt/runesprint.c b/lib9/fmt/runesprint.c
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+runesprint(Rune *buf, char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = runevsnprint(buf, 256, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/lib9/fmt/runevseprint.c b/lib9/fmt/runevseprint.c
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+Rune*
+runevseprint(Rune *buf, Rune *e, char *fmt, va_list args)
+{
+ Fmt f;
+
+ if(e <= buf)
+ return nil;
+ f.runes = 1;
+ f.start = buf;
+ f.to = buf;
+ f.stop = e - 1;
+ f.flush = nil;
+ f.farg = nil;
+ f.nfmt = 0;
+ VA_COPY(f.args,args);
+ dofmt(&f, fmt);
+ VA_END(f.args);
+ *(Rune*)f.to = '\0';
+ return (Rune*)f.to;
+}
+
diff --git a/lib9/fmt/runevsmprint.c b/lib9/fmt/runevsmprint.c
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+/*
+ * Plan 9 port version must include libc.h in order to
+ * get Plan 9 debugging malloc, which sometimes returns
+ * different pointers than the standard malloc.
+ */
+#ifdef PLAN9PORT
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+#else
+#include <stdlib.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+#endif
+
+static int
+runeFmtStrFlush(Fmt *f)
+{
+ Rune *s;
+ int n;
+
+ if(f->start == nil)
+ return 0;
+ n = (int)f->farg;
+ n *= 2;
+ s = (Rune*)f->start;
+ f->start = realloc(s, sizeof(Rune)*n);
+ if(f->start == nil){
+ f->farg = nil;
+ f->to = nil;
+ f->stop = nil;
+ free(s);
+ return 0;
+ }
+ f->farg = (void*)n;
+ f->to = (Rune*)f->start + ((Rune*)f->to - s);
+ f->stop = (Rune*)f->start + n - 1;
+ return 1;
+}
+
+int
+runefmtstrinit(Fmt *f)
+{
+ int n;
+
+ memset(f, 0, sizeof *f);
+ f->runes = 1;
+ n = 32;
+ f->start = malloc(sizeof(Rune)*n);
+ if(f->start == nil)
+ return -1;
+ f->to = f->start;
+ f->stop = (Rune*)f->start + n - 1;
+ f->flush = runeFmtStrFlush;
+ f->farg = (void*)n;
+ f->nfmt = 0;
+ return 0;
+}
+
+/*
+ * print into an allocated string buffer
+ */
+Rune*
+runevsmprint(char *fmt, va_list args)
+{
+ Fmt f;
+ int n;
+
+ if(runefmtstrinit(&f) < 0)
+ return nil;
+ VA_COPY(f.args,args);
+ n = dofmt(&f, fmt);
+ VA_END(f.args);
+ if(f.start == nil)
+ return nil;
+ if(n < 0){
+ free(f.start);
+ return nil;
+ }
+ *(Rune*)f.to = '\0';
+ return (Rune*)f.start;
+}
diff --git a/lib9/fmt/runevsnprint.c b/lib9/fmt/runevsnprint.c
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+runevsnprint(Rune *buf, int len, char *fmt, va_list args)
+{
+ Fmt f;
+
+ if(len <= 0)
+ return -1;
+ f.runes = 1;
+ f.start = buf;
+ f.to = buf;
+ f.stop = buf + len - 1;
+ f.flush = nil;
+ f.farg = nil;
+ f.nfmt = 0;
+ VA_COPY(f.args,args);
+ dofmt(&f, fmt);
+ VA_END(f.args);
+ *(Rune*)f.to = '\0';
+ return (Rune*)f.to - buf;
+}
diff --git a/lib9/fmt/seprint.c b/lib9/fmt/seprint.c
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+char*
+seprint(char *buf, char *e, char *fmt, ...)
+{
+ char *p;
+ va_list args;
+
+ va_start(args, fmt);
+ p = vseprint(buf, e, fmt, args);
+ va_end(args);
+ return p;
+}
diff --git a/lib9/fmt/smprint.c b/lib9/fmt/smprint.c
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+char*
+smprint(char *fmt, ...)
+{
+ va_list args;
+ char *p;
+
+ va_start(args, fmt);
+ p = vsmprint(fmt, args);
+ va_end(args);
+ return p;
+}
diff --git a/lib9/fmt/snprint.c b/lib9/fmt/snprint.c
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+snprint(char *buf, int len, char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = vsnprint(buf, len, fmt, args);
+ va_end(args);
+ return n;
+}
+
diff --git a/lib9/fmt/sprint.c b/lib9/fmt/sprint.c
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <fmt.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+sprint(char *buf, char *fmt, ...)
+{
+ int n;
+ uint len;
+ va_list args;
+
+ len = 1<<30; /* big number, but sprint is deprecated anyway */
+ /*
+ * on PowerPC, the stack is near the top of memory, so
+ * we must be sure not to overflow a 32-bit pointer.
+ */
+ if(buf+len < buf)
+ len = -(uint)buf-1;
+
+ va_start(args, fmt);
+ n = vsnprint(buf, len, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/lib9/fmt/strtod.c b/lib9/fmt/strtod.c
@@ -0,0 +1,532 @@
+/*
+ * 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.
+ */
+#include <stdlib.h>
+#include <math.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+static ulong
+umuldiv(ulong a, ulong b, ulong c)
+{
+ double d;
+
+ d = ((double)a * (double)b) / (double)c;
+ if(d >= 4294967295.)
+ d = 4294967295.;
+ return (ulong)d;
+}
+
+/*
+ * This routine will convert to arbitrary precision
+ * floating point entirely in multi-precision fixed.
+ * The answer is the closest floating point number to
+ * the given decimal number. Exactly half way are
+ * rounded ala ieee rules.
+ * Method is to scale input decimal between .500 and .999...
+ * with external power of 2, then binary search for the
+ * closest mantissa to this decimal number.
+ * Nmant is is the required precision. (53 for ieee dp)
+ * Nbits is the max number of bits/word. (must be <= 28)
+ * Prec is calculated - the number of words of fixed mantissa.
+ */
+enum
+{
+ Nbits = 28, /* bits safely represented in a ulong */
+ Nmant = 53, /* bits of precision required */
+ Prec = (Nmant+Nbits+1)/Nbits, /* words of Nbits each to represent mantissa */
+ Sigbit = 1<<(Prec*Nbits-Nmant), /* first significant bit of Prec-th word */
+ Ndig = 1500,
+ One = (ulong)(1<<Nbits),
+ Half = (ulong)(One>>1),
+ Maxe = 310,
+
+ Fsign = 1<<0, /* found - */
+ Fesign = 1<<1, /* found e- */
+ Fdpoint = 1<<2, /* found . */
+
+ S0 = 0, /* _ _S0 +S1 #S2 .S3 */
+ S1, /* _+ #S2 .S3 */
+ S2, /* _+# #S2 .S4 eS5 */
+ S3, /* _+. #S4 */
+ S4, /* _+#.# #S4 eS5 */
+ S5, /* _+#.#e +S6 #S7 */
+ S6, /* _+#.#e+ #S7 */
+ S7, /* _+#.#e+# #S7 */
+};
+
+static int xcmp(char*, char*);
+static int fpcmp(char*, ulong*);
+static void frnorm(ulong*);
+static void divascii(char*, int*, int*, int*);
+static void mulascii(char*, int*, int*, int*);
+
+typedef struct Tab Tab;
+struct Tab
+{
+ int bp;
+ int siz;
+ char* cmp;
+};
+
+double
+fmtstrtod(const char *as, char **aas)
+{
+ int na, ex, dp, bp, c, i, flag, state;
+ ulong low[Prec], hig[Prec], mid[Prec];
+ double d;
+ char *s, a[Ndig];
+
+ flag = 0; /* Fsign, Fesign, Fdpoint */
+ na = 0; /* number of digits of a[] */
+ dp = 0; /* na of decimal point */
+ ex = 0; /* exonent */
+
+ state = S0;
+ for(s=(char*)as;; s++) {
+ c = *s;
+ if(c >= '0' && c <= '9') {
+ switch(state) {
+ case S0:
+ case S1:
+ case S2:
+ state = S2;
+ break;
+ case S3:
+ case S4:
+ state = S4;
+ break;
+
+ case S5:
+ case S6:
+ case S7:
+ state = S7;
+ ex = ex*10 + (c-'0');
+ continue;
+ }
+ if(na == 0 && c == '0') {
+ dp--;
+ continue;
+ }
+ if(na < Ndig-50)
+ a[na++] = c;
+ continue;
+ }
+ switch(c) {
+ case '\t':
+ case '\n':
+ case '\v':
+ case '\f':
+ case '\r':
+ case ' ':
+ if(state == S0)
+ continue;
+ break;
+ case '-':
+ if(state == S0)
+ flag |= Fsign;
+ else
+ flag |= Fesign;
+ case '+':
+ if(state == S0)
+ state = S1;
+ else
+ if(state == S5)
+ state = S6;
+ else
+ break; /* syntax */
+ continue;
+ case '.':
+ flag |= Fdpoint;
+ dp = na;
+ if(state == S0 || state == S1) {
+ state = S3;
+ continue;
+ }
+ if(state == S2) {
+ state = S4;
+ continue;
+ }
+ break;
+ case 'e':
+ case 'E':
+ if(state == S2 || state == S4) {
+ state = S5;
+ continue;
+ }
+ break;
+ }
+ break;
+ }
+
+ /*
+ * clean up return char-pointer
+ */
+ switch(state) {
+ case S0:
+ if(xcmp(s, "nan") == 0) {
+ if(aas != nil)
+ *aas = s+3;
+ goto retnan;
+ }
+ case S1:
+ if(xcmp(s, "infinity") == 0) {
+ if(aas != nil)
+ *aas = s+8;
+ goto retinf;
+ }
+ if(xcmp(s, "inf") == 0) {
+ if(aas != nil)
+ *aas = s+3;
+ goto retinf;
+ }
+ case S3:
+ if(aas != nil)
+ *aas = (char*)as;
+ goto ret0; /* no digits found */
+ case S6:
+ s--; /* back over +- */
+ case S5:
+ s--; /* back over e */
+ break;
+ }
+ if(aas != nil)
+ *aas = s;
+
+ if(flag & Fdpoint)
+ while(na > 0 && a[na-1] == '0')
+ na--;
+ if(na == 0)
+ goto ret0; /* zero */
+ a[na] = 0;
+ if(!(flag & Fdpoint))
+ dp = na;
+ if(flag & Fesign)
+ ex = -ex;
+ dp += ex;
+ if(dp < -Maxe){
+ errno = ERANGE;
+ goto ret0; /* underflow by exp */
+ } else
+ if(dp > +Maxe)
+ goto retinf; /* overflow by exp */
+
+ /*
+ * normalize the decimal ascii number
+ * to range .[5-9][0-9]* e0
+ */
+ bp = 0; /* binary exponent */
+ while(dp > 0)
+ divascii(a, &na, &dp, &bp);
+ while(dp < 0 || a[0] < '5')
+ mulascii(a, &na, &dp, &bp);
+
+ /* close approx by naive conversion */
+ mid[0] = 0;
+ mid[1] = 1;
+ for(i=0; c=a[i]; i++) {
+ mid[0] = mid[0]*10 + (c-'0');
+ mid[1] = mid[1]*10;
+ if(i >= 8)
+ break;
+ }
+ low[0] = umuldiv(mid[0], One, mid[1]);
+ hig[0] = umuldiv(mid[0]+1, One, mid[1]);
+ for(i=1; i<Prec; i++) {
+ low[i] = 0;
+ hig[i] = One-1;
+ }
+
+ /* binary search for closest mantissa */
+ for(;;) {
+ /* mid = (hig + low) / 2 */
+ c = 0;
+ for(i=0; i<Prec; i++) {
+ mid[i] = hig[i] + low[i];
+ if(c)
+ mid[i] += One;
+ c = mid[i] & 1;
+ mid[i] >>= 1;
+ }
+ frnorm(mid);
+
+ /* compare */
+ c = fpcmp(a, mid);
+ if(c > 0) {
+ c = 1;
+ for(i=0; i<Prec; i++)
+ if(low[i] != mid[i]) {
+ c = 0;
+ low[i] = mid[i];
+ }
+ if(c)
+ break; /* between mid and hig */
+ continue;
+ }
+ if(c < 0) {
+ for(i=0; i<Prec; i++)
+ hig[i] = mid[i];
+ continue;
+ }
+
+ /* only hard part is if even/odd roundings wants to go up */
+ c = mid[Prec-1] & (Sigbit-1);
+ if(c == Sigbit/2 && (mid[Prec-1]&Sigbit) == 0)
+ mid[Prec-1] -= c;
+ break; /* exactly mid */
+ }
+
+ /* normal rounding applies */
+ c = mid[Prec-1] & (Sigbit-1);
+ mid[Prec-1] -= c;
+ if(c >= Sigbit/2) {
+ mid[Prec-1] += Sigbit;
+ frnorm(mid);
+ }
+ goto out;
+
+ret0:
+ return 0;
+
+retnan:
+ return __NaN();
+
+retinf:
+ /*
+ * Unix strtod requires these. Plan 9 would return Inf(0) or Inf(-1). */
+ errno = ERANGE;
+ if(flag & Fsign)
+ return -HUGE_VAL;
+ return HUGE_VAL;
+
+out:
+ d = 0;
+ for(i=0; i<Prec; i++)
+ d = d*One + mid[i];
+ if(flag & Fsign)
+ d = -d;
+ d = ldexp(d, bp - Prec*Nbits);
+ if(d == 0){ /* underflow */
+ errno = ERANGE;
+ }
+ return d;
+}
+
+static void
+frnorm(ulong *f)
+{
+ int i, c;
+
+ c = 0;
+ for(i=Prec-1; i>0; i--) {
+ f[i] += c;
+ c = f[i] >> Nbits;
+ f[i] &= One-1;
+ }
+ f[0] += c;
+}
+
+static int
+fpcmp(char *a, ulong* f)
+{
+ ulong tf[Prec];
+ int i, d, c;
+
+ for(i=0; i<Prec; i++)
+ tf[i] = f[i];
+
+ for(;;) {
+ /* tf *= 10 */
+ for(i=0; i<Prec; i++)
+ tf[i] = tf[i]*10;
+ frnorm(tf);
+ d = (tf[0] >> Nbits) + '0';
+ tf[0] &= One-1;
+
+ /* compare next digit */
+ c = *a;
+ if(c == 0) {
+ if('0' < d)
+ return -1;
+ if(tf[0] != 0)
+ goto cont;
+ for(i=1; i<Prec; i++)
+ if(tf[i] != 0)
+ goto cont;
+ return 0;
+ }
+ if(c > d)
+ return +1;
+ if(c < d)
+ return -1;
+ a++;
+ cont:;
+ }
+}
+
+static void
+divby(char *a, int *na, int b)
+{
+ int n, c;
+ char *p;
+
+ p = a;
+ n = 0;
+ while(n>>b == 0) {
+ c = *a++;
+ if(c == 0) {
+ while(n) {
+ c = n*10;
+ if(c>>b)
+ break;
+ n = c;
+ }
+ goto xx;
+ }
+ n = n*10 + c-'0';
+ (*na)--;
+ }
+ for(;;) {
+ c = n>>b;
+ n -= c<<b;
+ *p++ = c + '0';
+ c = *a++;
+ if(c == 0)
+ break;
+ n = n*10 + c-'0';
+ }
+ (*na)++;
+xx:
+ while(n) {
+ n = n*10;
+ c = n>>b;
+ n -= c<<b;
+ *p++ = c + '0';
+ (*na)++;
+ }
+ *p = 0;
+}
+
+static Tab tab1[] =
+{
+ 1, 0, "",
+ 3, 1, "7",
+ 6, 2, "63",
+ 9, 3, "511",
+ 13, 4, "8191",
+ 16, 5, "65535",
+ 19, 6, "524287",
+ 23, 7, "8388607",
+ 26, 8, "67108863",
+ 27, 9, "134217727",
+};
+
+static void
+divascii(char *a, int *na, int *dp, int *bp)
+{
+ int b, d;
+ Tab *t;
+
+ d = *dp;
+ if(d >= (int)(nelem(tab1)))
+ d = (int)(nelem(tab1))-1;
+ t = tab1 + d;
+ b = t->bp;
+ if(memcmp(a, t->cmp, t->siz) > 0)
+ d--;
+ *dp -= d;
+ *bp += b;
+ divby(a, na, b);
+}
+
+static void
+mulby(char *a, char *p, char *q, int b)
+{
+ int n, c;
+
+ n = 0;
+ *p = 0;
+ for(;;) {
+ q--;
+ if(q < a)
+ break;
+ c = *q - '0';
+ c = (c<<b) + n;
+ n = c/10;
+ c -= n*10;
+ p--;
+ *p = c + '0';
+ }
+ while(n) {
+ c = n;
+ n = c/10;
+ c -= n*10;
+ p--;
+ *p = c + '0';
+ }
+}
+
+static Tab tab2[] =
+{
+ 1, 1, "", /* dp = 0-0 */
+ 3, 3, "125",
+ 6, 5, "15625",
+ 9, 7, "1953125",
+ 13, 10, "1220703125",
+ 16, 12, "152587890625",
+ 19, 14, "19073486328125",
+ 23, 17, "11920928955078125",
+ 26, 19, "1490116119384765625",
+ 27, 19, "7450580596923828125", /* dp 8-9 */
+};
+
+static void
+mulascii(char *a, int *na, int *dp, int *bp)
+{
+ char *p;
+ int d, b;
+ Tab *t;
+
+ d = -*dp;
+ if(d >= (int)(nelem(tab2)))
+ d = (int)(nelem(tab2))-1;
+ t = tab2 + d;
+ b = t->bp;
+ if(memcmp(a, t->cmp, t->siz) < 0)
+ d--;
+ p = a + *na;
+ *bp -= b;
+ *dp += d;
+ *na += d;
+ mulby(a, p+d, p, b);
+}
+
+static int
+xcmp(char *a, char *b)
+{
+ int c1, c2;
+
+ while(c1 = *b++) {
+ c2 = *a++;
+ if(isupper(c2))
+ c2 = tolower(c2);
+ if(c1 != c2)
+ return 1;
+ }
+ return 0;
+}
diff --git a/lib9/fmt/strtod.h b/lib9/fmt/strtod.h
@@ -0,0 +1,4 @@
+extern double __NaN(void);
+extern double __Inf(int);
+extern double __isNaN(double);
+extern double __isInf(double, int);
diff --git a/lib9/fmt/test.c b/lib9/fmt/test.c
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+#include <stdio.h>
+#include <stdarg.h>
+#include <utf.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+main(int argc, char *argv[])
+{
+ quotefmtinstall();
+ print("hello world\n");
+ print("x: %x\n", 0x87654321);
+ print("u: %u\n", 0x87654321);
+ print("d: %d\n", 0x87654321);
+ print("s: %s\n", "hi there");
+ print("q: %q\n", "hi i'm here");
+ print("c: %c\n", '!');
+ print("g: %g %g %g\n", 3.14159, 3.14159e10, 3.14159e-10);
+ print("e: %e %e %e\n", 3.14159, 3.14159e10, 3.14159e-10);
+ print("f: %f %f %f\n", 3.14159, 3.14159e10, 3.14159e-10);
+ print("smiley: %C\n", (Rune)0x263a);
+ print("%g %.18g\n", 2e25, 2e25);
+ print("%2.18g\n", 1.0);
+ print("%2.18f\n", 1.0);
+ print("%f\n", 3.1415927/4);
+ print("%d\n", 23);
+ print("%i\n", 23);
+ print("%0.10d\n", 12345);
+ return 0;
+}
diff --git a/lib9/fmt/test2.c b/lib9/fmt/test2.c
@@ -0,0 +1,9 @@
+#include <stdarg.h>
+#include <utf.h>
+#include <fmt.h>
+
+int
+main(int argc, char **argv)
+{
+ print("%020.10d\n", 100);
+}
diff --git a/lib9/fmt/test3.c b/lib9/fmt/test3.c
@@ -0,0 +1,52 @@
+#include <u.h>
+#include <libc.h>
+#include <stdio.h>
+
+void
+test(char *fmt, ...)
+{
+ va_list arg;
+ char fmtbuf[100], stdbuf[100];
+
+ va_start(arg, fmt);
+ vsnprint(fmtbuf, sizeof fmtbuf, fmt, arg);
+ va_end(arg);
+
+ va_start(arg, fmt);
+ vsnprint(stdbuf, sizeof stdbuf, fmt, arg);
+ va_end(arg);
+
+ if(strcmp(fmtbuf, stdbuf) != 0)
+ print("fmt %s: fmt=\"%s\" std=\"%s\"\n", fmt, fmtbuf, stdbuf);
+
+ print("fmt %s: %s\n", fmt, fmtbuf);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ test("%f", 3.14159);
+ test("%f", 3.14159e10);
+ test("%f", 3.14159e-10);
+
+ test("%e", 3.14159);
+ test("%e", 3.14159e10);
+ test("%e", 3.14159e-10);
+
+ test("%g", 3.14159);
+ test("%g", 3.14159e10);
+ test("%g", 3.14159e-10);
+
+ test("%g", 2e25);
+ test("%.18g", 2e25);
+
+ test("%2.18g", 1.0);
+ test("%2.18f", 1.0);
+ test("%f", 3.1415927/4);
+
+ test("%20.10d", 12345);
+ test("%0.10d", 12345);
+
+ return 0;
+}
diff --git a/lib9/fmt/vfprint.c b/lib9/fmt/vfprint.c
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+vfprint(int fd, char *fmt, va_list args)
+{
+ Fmt f;
+ char buf[256];
+ int n;
+
+ fmtfdinit(&f, fd, buf, sizeof(buf));
+ VA_COPY(f.args,args);
+ n = dofmt(&f, fmt);
+ VA_END(f.args);
+ if(n > 0 && __fmtFdFlush(&f) == 0)
+ return -1;
+ return n;
+}
diff --git a/lib9/fmt/vseprint.c b/lib9/fmt/vseprint.c
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+char*
+vseprint(char *buf, char *e, char *fmt, va_list args)
+{
+ Fmt f;
+
+ if(e <= buf)
+ return nil;
+ f.runes = 0;
+ f.start = buf;
+ f.to = buf;
+ f.stop = e - 1;
+ f.flush = 0;
+ f.farg = nil;
+ f.nfmt = 0;
+ VA_COPY(f.args,args);
+ dofmt(&f, fmt);
+ VA_END(f.args);
+ *(char*)f.to = '\0';
+ return (char*)f.to;
+}
+
diff --git a/lib9/fmt/vsmprint.c b/lib9/fmt/vsmprint.c
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+/*
+ * Plan 9 port version must include libc.h in order to
+ * get Plan 9 debugging malloc, which sometimes returns
+ * different pointers than the standard malloc.
+ */
+#ifdef PLAN9PORT
+#include <u.h>
+#include <libc.h>
+#include "fmtdef.h"
+#else
+#include <stdlib.h>
+#include <string.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+#endif
+
+static int
+fmtStrFlush(Fmt *f)
+{
+ char *s;
+ int n;
+
+ if(f->start == nil)
+ return 0;
+ n = (int)f->farg;
+ n *= 2;
+ s = (char*)f->start;
+ f->start = realloc(s, n);
+ if(f->start == nil){
+ f->farg = nil;
+ f->to = nil;
+ f->stop = nil;
+ free(s);
+ return 0;
+ }
+ f->farg = (void*)n;
+ f->to = (char*)f->start + ((char*)f->to - s);
+ f->stop = (char*)f->start + n - 1;
+ return 1;
+}
+
+int
+fmtstrinit(Fmt *f)
+{
+ int n;
+
+ memset(f, 0, sizeof *f);
+ f->runes = 0;
+ n = 32;
+ f->start = malloc(n);
+ if(f->start == nil)
+ return -1;
+ f->to = f->start;
+ f->stop = (char*)f->start + n - 1;
+ f->flush = fmtStrFlush;
+ f->farg = (void*)n;
+ f->nfmt = 0;
+ return 0;
+}
+
+/*
+ * print into an allocated string buffer
+ */
+char*
+vsmprint(char *fmt, va_list args)
+{
+ Fmt f;
+ int n;
+
+ if(fmtstrinit(&f) < 0)
+ return nil;
+ VA_COPY(f.args,args);
+ n = dofmt(&f, fmt);
+ VA_END(f.args);
+ if(n < 0){
+ free(f.start);
+ return nil;
+ }
+ return fmtstrflush(&f);
+}
diff --git a/lib9/fmt/vsnprint.c b/lib9/fmt/vsnprint.c
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+#include <stdlib.h>
+#include <stdarg.h>
+#include "plan9.h"
+#include "fmt.h"
+#include "fmtdef.h"
+
+int
+vsnprint(char *buf, int len, char *fmt, va_list args)
+{
+ Fmt f;
+
+ if(len <= 0)
+ return -1;
+ f.runes = 0;
+ f.start = buf;
+ f.to = buf;
+ f.stop = buf + len - 1;
+ f.flush = 0;
+ f.farg = nil;
+ f.nfmt = 0;
+ VA_COPY(f.args,args);
+ dofmt(&f, fmt);
+ VA_END(f.args);
+ *(char*)f.to = '\0';
+ return (char*)f.to - buf;
+}
diff --git a/lib9/fmtlock2.c b/lib9/fmtlock2.c
@@ -0,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+
+static Lock fmtlock;
+
+void
+__fmtlock(void)
+{
+ lock(&fmtlock);
+}
+
+void
+__fmtunlock(void)
+{
+ unlock(&fmtlock);
+}
diff --git a/lib9/fork.c b/lib9/fork.c
@@ -0,0 +1,22 @@
+#include <u.h>
+#include <signal.h>
+#include <libc.h>
+#include "9proc.h"
+#undef fork
+
+int
+p9fork(void)
+{
+ int pid;
+ sigset_t all, old;
+
+ sigfillset(&all);
+ sigprocmask(SIG_SETMASK, &all, &old);
+ pid = fork();
+ if(pid == 0){
+ _clearuproc();
+ _p9uproc(0);
+ }
+ sigprocmask(SIG_SETMASK, &old, nil);
+ return pid;
+}
diff --git a/lib9/get9root.c b/lib9/get9root.c
@@ -0,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+get9root(void)
+{
+ static char *s;
+
+ if(s)
+ return s;
+
+ if((s = getenv("PLAN9")) != 0)
+ return s;
+ /* could do better - search $PATH */
+ s = "/usr/local/plan9";
+ return s;
+}
+
diff --git a/lib9/getcallerpc-386.c b/lib9/getcallerpc-386.c
@@ -0,0 +1,7 @@
+#include <lib9.h>
+
+ulong
+getcallerpc(void *x)
+{
+ return (((ulong*)(x))[-1]);
+}
diff --git a/lib9/getcallerpc-PowerMacintosh.c b/lib9/getcallerpc-PowerMacintosh.c
@@ -0,0 +1,7 @@
+#include <lib9.h>
+
+ulong
+getcallerpc(void *x)
+{
+ return (((ulong*)(x))[-4]);
+}
diff --git a/lib9/getcallerpc-power.c b/lib9/getcallerpc-power.c
@@ -0,0 +1,11 @@
+#include <lib9.h>
+
+ulong
+getcallerpc(void *x)
+{
+ ulong *lp;
+
+ lp = x;
+
+ return lp[-1];
+}
diff --git a/lib9/getcallerpc-ppc.c b/lib9/getcallerpc-ppc.c
@@ -0,0 +1,7 @@
+#include <lib9.h>
+
+ulong
+getcallerpc(void *x)
+{
+ return (((ulong*)(x))[-4]);
+}
diff --git a/lib9/getcallerpc-sun4u.s b/lib9/getcallerpc-sun4u.s
@@ -0,0 +1,5 @@
+.text
+.globl getcallerpc
+getcallerpc:
+ retl
+ or %o7, %r0, %o0
diff --git a/lib9/getcallerpc-x86_64.c b/lib9/getcallerpc-x86_64.c
@@ -0,0 +1,7 @@
+#include <lib9.h>
+
+ulong
+getcallerpc(void *x)
+{
+ return (((ulong*)(x))[-1]);
+}
diff --git a/lib9/getenv.c b/lib9/getenv.c
@@ -0,0 +1,26 @@
+#include <u.h>
+#define NOPLAN9DEFINES
+#include <libc.h>
+
+char*
+p9getenv(char *s)
+{
+ char *t;
+
+ t = getenv(s);
+ if(t == 0)
+ return 0;
+ return strdup(t);
+}
+
+int
+p9putenv(char *s, char *v)
+{
+ char *t;
+
+ t = smprint("%s=%s", s, v);
+ if(t == nil)
+ return -1;
+ putenv(t);
+ return 0;
+}
diff --git a/lib9/getfields.c b/lib9/getfields.c
@@ -0,0 +1,36 @@
+#include <lib9.h>
+
+int
+getfields(char *str, char **args, int max, int mflag, char *set)
+{
+ Rune r;
+ int nr, intok, narg;
+
+ if(max <= 0)
+ return 0;
+
+ narg = 0;
+ args[narg] = str;
+ if(!mflag)
+ narg++;
+ intok = 0;
+ for(;; str += nr) {
+ nr = chartorune(&r, str);
+ if(r == 0)
+ break;
+ if(utfrune(set, r)) {
+ if(narg >= max)
+ break;
+ *str = 0;
+ intok = 0;
+ args[narg] = str + nr;
+ if(!mflag)
+ narg++;
+ } else {
+ if(!intok && mflag)
+ narg++;
+ intok = 1;
+ }
+ }
+ return narg;
+}
diff --git a/lib9/getnetconn.c b/lib9/getnetconn.c
@@ -0,0 +1,134 @@
+#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
+
+extern int _p9netfd(char*);
+
+static char *unknown = "unknown";
+
+static int
+convert(int s, struct sockaddr *sa, char **lsys, char **lserv, char **laddr)
+{
+ struct sockaddr_un *sun;
+ struct sockaddr_in *sin;
+ uchar *ip;
+ u32int ipl;
+ socklen_t sn;
+ int n;
+ char *net;
+
+ switch(sa->sa_family){
+ case AF_INET:
+ sin = (void*)sa;
+ ip = (uchar*)&sin->sin_addr;
+ ipl = *(u32int*)ip;
+ if(ipl == 0)
+ *lsys = strdup("*");
+ else
+ *lsys = smprint("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+ *lserv = smprint("%d", ntohs(sin->sin_port));
+ sn = sizeof n;
+ if(getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) < 0)
+ return -1;
+ if(n == SOCK_STREAM)
+ net = "tcp";
+ else if(n == SOCK_DGRAM)
+ net = "udp";
+ else{
+ werrstr("unknown network type");
+ return -1;
+ }
+ *laddr = smprint("%s!%s!%s", net, *lsys, *lserv);
+ if(*lsys == nil || *lserv == nil || *laddr == nil)
+ return -1;
+ return 0;
+ case AF_UNIX:
+ sun = (void*)sa;
+ *lsys = unknown;
+ *lserv = unknown;
+ *laddr = smprint("unix!%s", sun->sun_path);
+ if(*laddr == nil)
+ return -1;
+ return 0;
+ default:
+ werrstr("unknown socket family");
+ return -1;
+ }
+}
+
+NetConnInfo*
+getnetconninfo(char *dir, int fd)
+{
+ socklen_t sn;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_un sun;
+ } u;
+ NetConnInfo *nci;
+
+ if(dir){
+ if((fd = _p9netfd(dir)) < 0){
+ werrstr("no such network connection %s", dir);
+ return nil;
+ }
+ }
+
+ nci = mallocz(sizeof *nci, 1);
+ if(nci == nil)
+ goto err;
+ nci->dir = smprint("/dev/fd/%d", fd);
+ nci->root = strdup("/net");
+ nci->spec = unknown;
+ if(nci->dir == nil || nci->root == nil)
+ goto err;
+ sn = sizeof sn;
+ if(getsockname(fd, &u.sa, &sn) < 0)
+ goto err;
+ if(convert(fd, &u.sa, &nci->lsys, &nci->lserv, &nci->laddr) < 0)
+ goto err;
+ sn = sizeof sn;
+ if(getpeername(fd, &u.sa, &sn) < 0)
+ goto err;
+ if(convert(fd, &u.sa, &nci->rsys, &nci->rserv, &nci->raddr) < 0)
+ goto err;
+ return nci;
+
+err:
+ freenetconninfo(nci);
+ return nil;
+}
+
+static void
+xfree(void *v)
+{
+ if(v != nil && v != unknown)
+ free(v);
+}
+
+void
+freenetconninfo(NetConnInfo *nci)
+{
+ if(nci == nil)
+ return;
+ xfree(nci->dir);
+ xfree(nci->root);
+ xfree(nci->spec);
+ xfree(nci->lsys);
+ xfree(nci->lserv);
+ xfree(nci->rsys);
+ xfree(nci->rserv);
+ xfree(nci->laddr);
+ xfree(nci->raddr);
+ free(nci);
+}
+
diff --git a/lib9/getns.c b/lib9/getns.c
@@ -0,0 +1,74 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+
+/*
+ * Absent other hints, it works reasonably well to use
+ * the X11 display name as the name space identifier.
+ * This is how sam's B has worked since the early days.
+ * Since most programs using name spaces are also using X,
+ * this still seems reasonable. Terminal-only sessions
+ * can set $NAMESPACE.
+ */
+static char*
+nsfromdisplay(void)
+{
+ int fd;
+ Dir *d;
+ char *disp, *p;
+
+ if((disp = getenv("DISPLAY")) == nil){
+ werrstr("$DISPLAY not set");
+ return nil;
+ }
+
+ /* canonicalize: xxx:0.0 => xxx:0 */
+ p = strrchr(disp, ':');
+ if(p){
+ p++;
+ while(isdigit((uchar)*p))
+ p++;
+ if(strcmp(p, ".0") == 0)
+ *p = 0;
+ }
+
+ p = smprint("/tmp/ns.%s.%s", getuser(), disp);
+ free(disp);
+ if(p == nil){
+ werrstr("out of memory");
+ return p;
+ }
+ if((fd=create(p, OREAD, DMDIR|0700)) >= 0){
+ close(fd);
+ return p;
+ }
+ if((d = dirstat(p)) == nil){
+ free(d);
+ werrstr("stat %s: %r", p);
+ free(p);
+ return nil;
+ }
+ if((d->mode&0777) != 0700 || strcmp(d->uid, getuser()) != 0){
+ werrstr("bad name space dir %s", p);
+ free(p);
+ free(d);
+ return nil;
+ }
+ free(d);
+ return p;
+}
+
+char*
+getns(void)
+{
+ char *ns;
+
+ ns = getenv("NAMESPACE");
+ if(ns == nil)
+ ns = nsfromdisplay();
+ if(ns == nil){
+ werrstr("$NAMESPACE not set, %r");
+ return nil;
+ }
+ return ns;
+}
diff --git a/lib9/getuser.c b/lib9/getuser.c
@@ -0,0 +1,16 @@
+#include <u.h>
+#include <pwd.h>
+#include <libc.h>
+
+char*
+getuser(void)
+{
+ static char user[64];
+ struct passwd *pw;
+
+ pw = getpwuid(getuid());
+ if(pw == nil)
+ return "none";
+ strecpy(user, user+sizeof user, pw->pw_name);
+ return user;
+}
diff --git a/lib9/getwd.c b/lib9/getwd.c
@@ -0,0 +1,10 @@
+#include <u.h>
+#include <libc.h>
+
+#undef getwd
+
+char*
+p9getwd(char *s, int ns)
+{
+ return getcwd(s, ns);
+}
diff --git a/lib9/jmp-FreeBSD.s b/lib9/jmp-FreeBSD.s
@@ -0,0 +1,3 @@
+.globl sigsetjmp, p9setjmp
+p9setjmp:
+ jmp sigsetjmp
diff --git a/lib9/jmp.c b/lib9/jmp.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#define NOPLAN9DEFINES
+#include <libc.h>
+
+void
+p9longjmp(p9jmp_buf buf, int val)
+{
+ siglongjmp((void*)buf, val);
+}
+
+void
+p9notejmp(void *x, p9jmp_buf buf, int val)
+{
+ USED(x);
+ siglongjmp((void*)buf, val);
+}
+
diff --git a/lib9/lib9.h b/lib9/lib9.h
@@ -0,0 +1,17 @@
+#include <string.h>
+#include "utf.h"
+
+#define nil ((void*)0)
+
+#define uchar _fmtuchar
+#define ushort _fmtushort
+#define uint _fmtuint
+#define ulong _fmtulong
+#define vlong _fmtvlong
+#define uvlong _fmtuvlong
+
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+
diff --git a/lib9/libc.h b/lib9/libc.h
@@ -0,0 +1,871 @@
+/*
+ * Lib9 is miscellany from the Plan 9 C library that doesn't
+ * fit into libutf or into libfmt, but is still missing from traditional
+ * Unix C libraries.
+ */
+#ifndef _LIBC_H_
+#define _LIBC_H_ 1
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * Begin usual libc.h
+ */
+
+#ifndef nil
+#define nil ((void*)0)
+#endif
+#define nelem(x) (sizeof(x)/sizeof((x)[0]))
+
+#ifndef offsetof
+#define offsetof(s, m) (ulong)(&(((s*)0)->m))
+#endif
+
+/*
+ * mem routines (provided by system <string.h>)
+ *
+extern void* memccpy(void*, void*, int, ulong);
+extern void* memset(void*, int, ulong);
+extern int memcmp(void*, void*, ulong);
+extern void* memcpy(void*, void*, ulong);
+extern void* memmove(void*, void*, ulong);
+extern void* memchr(void*, int, ulong);
+ */
+
+/*
+ * string routines (provided by system <string.h>)
+ *
+extern char* strcat(char*, char*);
+extern char* strchr(char*, int);
+extern int strcmp(char*, char*);
+extern char* strcpy(char*, char*);
+ */
+extern char* strecpy(char*, char*, char*);
+extern char* p9strdup(char*);
+/*
+extern char* strncat(char*, char*, long);
+extern char* strncpy(char*, char*, long);
+extern int strncmp(char*, char*, long);
+extern char* strpbrk(char*, char*);
+extern char* strrchr(char*, int);
+extern char* strtok(char*, char*);
+extern long strlen(char*);
+extern long strspn(char*, char*);
+extern long strcspn(char*, char*);
+extern char* strstr(char*, char*);
+ */
+extern int cistrncmp(char*, char*, int);
+extern int cistrcmp(char*, char*);
+extern char* cistrstr(char*, char*);
+extern int tokenize(char*, char**, int);
+
+/*
+enum
+{
+ UTFmax = 3,
+ Runesync = 0x80,
+ Runeself = 0x80,
+ Runeerror = 0x80,
+};
+*/
+
+/*
+ * rune routines (provided by <utf.h>
+ *
+extern int runetochar(char*, Rune*);
+extern int chartorune(Rune*, char*);
+extern int runelen(long);
+extern int runenlen(Rune*, int);
+extern int fullrune(char*, int);
+extern int utflen(char*);
+extern int utfnlen(char*, long);
+extern char* utfrune(char*, long);
+extern char* utfrrune(char*, long);
+extern char* utfutf(char*, char*);
+extern char* utfecpy(char*, char*, char*);
+
+extern Rune* runestrcat(Rune*, Rune*);
+extern Rune* runestrchr(Rune*, Rune);
+extern int runestrcmp(Rune*, Rune*);
+extern Rune* runestrcpy(Rune*, Rune*);
+extern Rune* runestrncpy(Rune*, Rune*, long);
+extern Rune* runestrecpy(Rune*, Rune*, Rune*);
+extern Rune* runestrdup(Rune*);
+extern Rune* runestrncat(Rune*, Rune*, long);
+extern int runestrncmp(Rune*, Rune*, long);
+extern Rune* runestrrchr(Rune*, Rune);
+extern long runestrlen(Rune*);
+extern Rune* runestrstr(Rune*, Rune*);
+
+extern Rune tolowerrune(Rune);
+extern Rune totitlerune(Rune);
+extern Rune toupperrune(Rune);
+extern int isalpharune(Rune);
+extern int islowerrune(Rune);
+extern int isspacerune(Rune);
+extern int istitlerune(Rune);
+extern int isupperrune(Rune);
+ */
+
+/*
+ * malloc (provied by system <stdlib.h>)
+ *
+extern void* malloc(ulong);
+ */
+extern void* p9malloc(ulong);
+extern void* mallocz(ulong, int);
+extern void p9free(void*);
+extern void* p9calloc(ulong, ulong);
+extern void* p9realloc(void*, ulong);
+extern void setmalloctag(void*, ulong);
+extern void setrealloctag(void*, ulong);
+extern ulong getmalloctag(void*);
+extern ulong getrealloctag(void*);
+/*
+extern void* malloctopoolblock(void*);
+*/
+#ifndef NOPLAN9DEFINES
+#define malloc p9malloc
+#define realloc p9realloc
+#define calloc p9calloc
+#define free p9free
+#undef strdup
+#define strdup p9strdup
+#endif
+
+/*
+ * print routines (provided by <fmt.h>)
+ *
+typedef struct Fmt Fmt;
+struct Fmt{
+ uchar runes;
+ void *start;
+ void *to;
+ void *stop;
+ int (*flush)(Fmt *);
+ void *farg;
+ int nfmt;
+ va_list args;
+ int r;
+ int width;
+ int prec;
+ ulong flags;
+};
+
+enum{
+ FmtWidth = 1,
+ FmtLeft = FmtWidth << 1,
+ FmtPrec = FmtLeft << 1,
+ FmtSharp = FmtPrec << 1,
+ FmtSpace = FmtSharp << 1,
+ FmtSign = FmtSpace << 1,
+ FmtZero = FmtSign << 1,
+ FmtUnsigned = FmtZero << 1,
+ FmtShort = FmtUnsigned << 1,
+ FmtLong = FmtShort << 1,
+ FmtVLong = FmtLong << 1,
+ FmtComma = FmtVLong << 1,
+ FmtByte = FmtComma << 1,
+
+ FmtFlag = FmtByte << 1
+};
+
+extern int print(char*, ...);
+extern char* seprint(char*, char*, char*, ...);
+extern char* vseprint(char*, char*, char*, va_list);
+extern int snprint(char*, int, char*, ...);
+extern int vsnprint(char*, int, char*, va_list);
+extern char* smprint(char*, ...);
+extern char* vsmprint(char*, va_list);
+extern int sprint(char*, char*, ...);
+extern int fprint(int, char*, ...);
+extern int vfprint(int, char*, va_list);
+
+extern int runesprint(Rune*, char*, ...);
+extern int runesnprint(Rune*, int, char*, ...);
+extern int runevsnprint(Rune*, int, char*, va_list);
+extern Rune* runeseprint(Rune*, Rune*, char*, ...);
+extern Rune* runevseprint(Rune*, Rune*, char*, va_list);
+extern Rune* runesmprint(char*, ...);
+extern Rune* runevsmprint(char*, va_list);
+
+extern int fmtfdinit(Fmt*, int, char*, int);
+extern int fmtfdflush(Fmt*);
+extern int fmtstrinit(Fmt*);
+extern char* fmtstrflush(Fmt*);
+extern int runefmtstrinit(Fmt*);
+extern Rune* runefmtstrflush(Fmt*);
+
+extern int fmtinstall(int, int (*)(Fmt*));
+extern int dofmt(Fmt*, char*);
+extern int dorfmt(Fmt*, Rune*);
+extern int fmtprint(Fmt*, char*, ...);
+extern int fmtvprint(Fmt*, char*, va_list);
+extern int fmtrune(Fmt*, int);
+extern int fmtstrcpy(Fmt*, char*);
+extern int fmtrunestrcpy(Fmt*, Rune*);
+ */
+
+/*
+ * error string for %r
+ * supplied on per os basis, not part of fmt library
+ *
+ * (provided by lib9, but declared in fmt.h)
+ *
+extern int errfmt(Fmt *f);
+ */
+
+/*
+ * quoted strings
+ */
+extern char *unquotestrdup(char*);
+extern Rune *unquoterunestrdup(Rune*);
+extern char *quotestrdup(char*);
+extern Rune *quoterunestrdup(Rune*);
+/*
+ * in fmt.h
+ *
+extern void quotefmtinstall(void);
+extern int quotestrfmt(Fmt*);
+extern int quoterunestrfmt(Fmt*);
+ */
+#ifndef NOPLAN9DEFINES
+#define doquote fmtdoquote
+#endif
+extern int needsrcquote(int);
+
+/*
+ * random number
+ */
+extern void p9srand(long);
+extern int p9rand(void);
+
+extern int p9nrand(int);
+extern long p9lrand(void);
+extern long p9lnrand(long);
+extern double p9frand(void);
+extern ulong truerand(void); /* uses /dev/random */
+extern ulong ntruerand(ulong); /* uses /dev/random */
+
+#ifndef NOPLAN9DEFINES
+#define srand p9srand
+#define rand p9rand
+#define nrand p9nrand
+#define lrand p9lrand
+#define lnrand p9lnrand
+#define frand p9frand
+#endif
+
+/*
+ * math
+ */
+extern ulong getfcr(void);
+extern void setfsr(ulong);
+extern ulong getfsr(void);
+extern void setfcr(ulong);
+extern double NaN(void);
+extern double Inf(int);
+extern int isNaN(double);
+extern int isInf(double, int);
+extern ulong umuldiv(ulong, ulong, ulong);
+extern long muldiv(long, long, long);
+
+/*
+ * provided by math.h
+ *
+extern double pow(double, double);
+extern double atan2(double, double);
+extern double fabs(double);
+extern double atan(double);
+extern double log(double);
+extern double log10(double);
+extern double exp(double);
+extern double floor(double);
+extern double ceil(double);
+extern double hypot(double, double);
+extern double sin(double);
+extern double cos(double);
+extern double tan(double);
+extern double asin(double);
+extern double acos(double);
+extern double sinh(double);
+extern double cosh(double);
+extern double tanh(double);
+extern double sqrt(double);
+extern double fmod(double, double);
+#define HUGE 3.4028234e38
+#define PIO2 1.570796326794896619231e0
+#define PI (PIO2+PIO2)
+ */
+#define PI M_PI
+#define PIO2 M_PI_2
+
+/*
+ * Time-of-day
+ */
+
+typedef
+struct Tm
+{
+ int sec;
+ int min;
+ int hour;
+ int mday;
+ int mon;
+ int year;
+ int wday;
+ int yday;
+ char zone[4];
+ int tzoff;
+} Tm;
+
+extern Tm* p9gmtime(long);
+extern Tm* p9localtime(long);
+extern char* p9asctime(Tm*);
+extern char* p9ctime(long);
+extern double p9cputime(void);
+extern long p9times(long*);
+extern long p9tm2sec(Tm*);
+extern vlong p9nsec(void);
+
+#ifndef NOPLAN9DEFINES
+#define gmtime p9gmtime
+#define localtime p9localtime
+#define asctime p9asctime
+#define ctime p9ctime
+#define cputime p9cputime
+#define times p9times
+#define tm2sec p9tm2sec
+#define nsec p9nsec
+#endif
+
+/*
+ * one-of-a-kind
+ */
+enum
+{
+ PNPROC = 1,
+ PNGROUP = 2,
+};
+
+/* extern int abs(int); <stdlib.h> */
+extern int p9atexit(void(*)(void));
+extern void p9atexitdont(void(*)(void));
+extern int atnotify(int(*)(void*, char*), int);
+/*
+ * <stdlib.h>
+extern double atof(char*); <stdlib.h>
+ */
+extern int p9atoi(char*);
+extern long p9atol(char*);
+extern vlong p9atoll(char*);
+extern double fmtcharstod(int(*)(void*), void*);
+extern char* cleanname(char*);
+extern int p9decrypt(void*, void*, int);
+extern int p9encrypt(void*, void*, int);
+extern int netcrypt(void*, void*);
+extern int dec64(uchar*, int, char*, int);
+extern int enc64(char*, int, uchar*, int);
+extern int dec32(uchar*, int, char*, int);
+extern int enc32(char*, int, uchar*, int);
+extern int dec16(uchar*, int, char*, int);
+extern int enc16(char*, int, uchar*, int);
+extern int encodefmt(Fmt*);
+extern int dirmodefmt(Fmt*);
+extern void exits(char*);
+extern double frexp(double, int*);
+extern ulong getcallerpc(void*);
+extern char* p9getenv(char*);
+extern int p9putenv(char*, char*);
+extern int getfields(char*, char**, int, int, char*);
+extern int gettokens(char *, char **, int, char *);
+extern char* getuser(void);
+extern char* p9getwd(char*, int);
+extern int iounit(int);
+/* extern long labs(long); <math.h> */
+/* extern double ldexp(double, int); <math.h> */
+extern void p9longjmp(p9jmp_buf, int);
+extern char* mktemp(char*);
+extern int opentemp(char*);
+/* extern double modf(double, double*); <math.h> */
+extern void p9notejmp(void*, p9jmp_buf, int);
+extern void perror(const char*);
+extern int postnote(int, int, char *);
+extern double p9pow10(int);
+/* extern int putenv(char*, char*); <stdlib.h. */
+/* extern void qsort(void*, long, long, int (*)(void*, void*)); <stdlib.h> */
+extern char* searchpath(char*);
+/* extern int p9setjmp(p9jmp_buf); */
+#define p9setjmp(b) sigsetjmp((void*)(b), 1)
+/*
+ * <stdlib.h>
+extern long strtol(char*, char**, int);
+extern ulong strtoul(char*, char**, int);
+extern vlong strtoll(char*, char**, int);
+extern uvlong strtoull(char*, char**, int);
+ */
+extern void sysfatal(char*, ...);
+extern void p9syslog(int, char*, char*, ...);
+extern long p9time(long*);
+/* extern int tolower(int); <ctype.h> */
+/* extern int toupper(int); <ctype.h> */
+extern void needstack(int);
+extern char* readcons(char*, char*, int);
+
+#ifndef NOPLAN9DEFINES
+#define atexit p9atexit
+#define atexitdont p9atexitdont
+#define atoi p9atoi
+#define atol p9atol
+#define atoll p9atoll
+#define encrypt p9encrypt
+#define decrypt p9decrypt
+#define getenv p9getenv
+#define getwd p9getwd
+#define longjmp p9longjmp
+#undef setjmp
+#define setjmp p9setjmp
+#define putenv p9putenv
+#define notejmp p9notejmp
+#define jmp_buf p9jmp_buf
+#define time p9time
+#define pow10 p9pow10
+#define strtod fmtstrtod
+#define charstod fmtcharstod
+#define syslog p9syslog
+#endif
+
+/*
+ * just enough information so that libc can be
+ * properly locked without dragging in all of libthread
+ */
+typedef struct _Thread _Thread;
+typedef struct _Threadlist _Threadlist;
+struct _Threadlist
+{
+ _Thread *head;
+ _Thread *tail;
+};
+
+extern _Thread *(*threadnow)(void);
+
+/*
+ * synchronization
+ */
+typedef struct Lock Lock;
+struct Lock
+{
+#ifdef PLAN9PORT_USING_PTHREADS
+ int init;
+ pthread_mutex_t mutex;
+#endif
+ int held;
+};
+
+extern void lock(Lock*);
+extern void unlock(Lock*);
+extern int canlock(Lock*);
+extern int (*_lock)(Lock*, int, ulong);
+extern void (*_unlock)(Lock*, ulong);
+
+typedef struct QLock QLock;
+struct QLock
+{
+ Lock l;
+ _Thread *owner;
+ _Threadlist waiting;
+};
+
+extern void qlock(QLock*);
+extern void qunlock(QLock*);
+extern int canqlock(QLock*);
+extern int (*_qlock)(QLock*, int, ulong); /* do not use */
+extern void (*_qunlock)(QLock*, ulong);
+
+typedef struct Rendez Rendez;
+struct Rendez
+{
+ QLock *l;
+ _Threadlist waiting;
+};
+
+extern void rsleep(Rendez*); /* unlocks r->l, sleeps, locks r->l again */
+extern int rwakeup(Rendez*);
+extern int rwakeupall(Rendez*);
+extern void (*_rsleep)(Rendez*, ulong); /* do not use */
+extern int (*_rwakeup)(Rendez*, int, ulong);
+
+typedef struct RWLock RWLock;
+struct RWLock
+{
+ Lock l;
+ int readers;
+ _Thread *writer;
+ _Threadlist rwaiting;
+ _Threadlist wwaiting;
+};
+
+extern void rlock(RWLock*);
+extern void runlock(RWLock*);
+extern int canrlock(RWLock*);
+extern void wlock(RWLock*);
+extern void wunlock(RWLock*);
+extern int canwlock(RWLock*);
+extern int (*_rlock)(RWLock*, int, ulong); /* do not use */
+extern int (*_wlock)(RWLock*, int, ulong);
+extern void (*_runlock)(RWLock*, ulong);
+extern void (*_wunlock)(RWLock*, ulong);
+
+/*
+ * per-process private data
+ */
+extern void** privalloc(void);
+extern void privfree(void**);
+
+/*
+ * network dialing
+ */
+#define NETPATHLEN 40
+extern int p9accept(int, char*);
+extern int p9announce(char*, char*);
+extern int p9dial(char*, char*, char*, int*);
+extern int p9dialparse(char *ds, char **net, char **unixa, u32int *ip, int *port);
+extern void p9setnetmtpt(char*, int, char*);
+extern int p9listen(char*, char*);
+extern char* p9netmkaddr(char*, char*, char*);
+extern int p9reject(int, char*, char*);
+
+#ifndef NOPLAN9DEFINES
+#define accept p9accept
+#define announce p9announce
+#define dial p9dial
+#define setnetmtpt p9setnetmtpt
+#define listen p9listen
+#define netmkaddr p9netmkaddr
+#define reject p9reject
+#endif
+
+/*
+ * encryption
+ */
+extern int pushssl(int, char*, char*, char*, int*);
+extern int pushtls(int, char*, char*, int, char*, char*);
+
+/*
+ * network services
+ */
+typedef struct NetConnInfo NetConnInfo;
+struct NetConnInfo
+{
+ char *dir; /* connection directory */
+ char *root; /* network root */
+ char *spec; /* binding spec */
+ char *lsys; /* local system */
+ char *lserv; /* local service */
+ char *rsys; /* remote system */
+ char *rserv; /* remote service */
+ char *laddr;
+ char *raddr;
+};
+extern NetConnInfo* getnetconninfo(char*, int);
+extern void freenetconninfo(NetConnInfo*);
+
+/*
+ * system calls
+ *
+ */
+#define STATMAX 65535U /* max length of machine-independent stat structure */
+#define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */
+#define ERRMAX 128 /* max length of error string */
+
+#define MORDER 0x0003 /* mask for bits defining order of mounting */
+#define MREPL 0x0000 /* mount replaces object */
+#define MBEFORE 0x0001 /* mount goes before others in union directory */
+#define MAFTER 0x0002 /* mount goes after others in union directory */
+#define MCREATE 0x0004 /* permit creation in mounted directory */
+#define MCACHE 0x0010 /* cache some data */
+#define MMASK 0x0017 /* all bits on */
+
+#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 */
+
+#define AEXIST 0 /* accessible: exists */
+#define AEXEC 1 /* execute access */
+#define AWRITE 2 /* write access */
+#define AREAD 4 /* read access */
+
+/* Segattch */
+#define SG_RONLY 0040 /* read only */
+#define SG_CEXEC 0100 /* detach on exec */
+
+#define NCONT 0 /* continue after note */
+#define NDFLT 1 /* terminate after note */
+#define NSAVE 2 /* clear note but hold state */
+#define NRSTR 3 /* restore saved state */
+
+/* bits in Qid.type */
+#define QTDIR 0x80 /* type bit for directories */
+#define QTAPPEND 0x40 /* type bit for append only files */
+#define QTEXCL 0x20 /* type bit for exclusive use files */
+#define QTMOUNT 0x10 /* type bit for mounted channel */
+#define QTAUTH 0x08 /* type bit for authentication file */
+#define QTLINK 0x04 /* symbolic link */
+#define QTFILE 0x00 /* plain file */
+
+/* bits in Dir.mode */
+#define DMDIR 0x80000000 /* mode bit for directories */
+#define DMAPPEND 0x40000000 /* mode bit for append only files */
+#define DMEXCL 0x20000000 /* mode bit for exclusive use files */
+#define DMMOUNT 0x10000000 /* mode bit for mounted channel */
+#define DMAUTH 0x08000000 /* mode bit for authentication file */
+#define DMDEVICE 0x00800000 /* mode bit for device files (Unix) */
+#define DMSYMLINK 0x00400000 /* mode bit for symbolic links (Unix) */
+#define DMNAMEDPIPE 0x00200000 /* mode bit for named pipes (Unix) */
+#define DMSOCKET 0x00100000 /* mode bit for sockets (Unix) */
+
+#define DMREAD 0x4 /* mode bit for read permission */
+#define DMWRITE 0x2 /* mode bit for write permission */
+#define DMEXEC 0x1 /* mode bit for execute permission */
+
+#ifdef RFMEM /* FreeBSD, OpenBSD */
+#undef RFFDG
+#undef RFNOTEG
+#undef RFPROC
+#undef RFMEM
+#undef RFNOWAIT
+#undef RFCFDG
+#undef RFNAMEG
+#undef RFENVG
+#undef RFCENVG
+#undef RFCFDG
+#undef RFCNAMEG
+#endif
+
+enum
+{
+ RFNAMEG = (1<<0),
+ RFENVG = (1<<1),
+ RFFDG = (1<<2),
+ RFNOTEG = (1<<3),
+ RFPROC = (1<<4),
+ RFMEM = (1<<5),
+ RFNOWAIT = (1<<6),
+ RFCNAMEG = (1<<10),
+ RFCENVG = (1<<11),
+ RFCFDG = (1<<12),
+/* RFREND = (1<<13), */
+/* RFNOMNT = (1<<14) */
+};
+
+typedef
+struct Qid
+{
+ uvlong path;
+ ulong vers;
+ uchar type;
+} Qid;
+
+typedef
+struct Dir {
+ /* system-modified data */
+ ushort type; /* server type */
+ uint dev; /* server subtype */
+ /* file data */
+ Qid qid; /* unique id from server */
+ ulong mode; /* permissions */
+ ulong atime; /* last read time */
+ ulong mtime; /* last write time */
+ vlong length; /* file length */
+ char *name; /* last element of path */
+ char *uid; /* owner name */
+ char *gid; /* group name */
+ char *muid; /* last modifier name */
+} Dir;
+
+/* keep /sys/src/ape/lib/ap/plan9/sys9.h in sync with this -rsc */
+typedef
+struct Waitmsg
+{
+ int pid; /* of loved one */
+ ulong time[3]; /* of loved one & descendants */
+ char *msg;
+} Waitmsg;
+
+typedef
+struct IOchunk
+{
+ void *addr;
+ ulong len;
+} IOchunk;
+
+extern void _exits(char*);
+
+extern void abort(void);
+/* extern int access(char*, int); */
+extern long p9alarm(ulong);
+extern int await(char*, int);
+extern int awaitfor(int, char*, int);
+extern int awaitnohang(char*, int);
+/* extern int bind(char*, char*, int); give up */
+/* extern int brk(void*); <unistd.h> */
+extern int p9chdir(char*);
+extern int close(int);
+extern int p9create(char*, int, ulong);
+extern int p9dup(int, int);
+extern int errstr(char*, uint);
+extern int p9exec(char*, char*[]);
+extern int p9execl(char*, ...);
+/* extern int p9fork(void); */
+extern int p9rfork(int);
+/* not implemented
+extern int fauth(int, char*);
+extern int fstat(int, uchar*, int);
+extern int fwstat(int, uchar*, int);
+extern int fversion(int, int, char*, int);
+extern int mount(int, int, char*, int, char*);
+extern int unmount(char*, char*);
+*/
+extern int noted(int);
+extern int notify(void(*)(void*, char*));
+extern int noteenable(char*);
+extern int notedisable(char*);
+extern int notifyon(char*);
+extern int notifyoff(char*);
+extern int p9open(char*, int);
+extern int fd2path(int, char*, int);
+extern int p9pipe(int*);
+/*
+ * use defs from <unistd.h>
+extern long pread(int, void*, long, vlong);
+extern long preadv(int, IOchunk*, int, vlong);
+extern long pwrite(int, void*, long, vlong);
+extern long pwritev(int, IOchunk*, int, vlong);
+extern long read(int, void*, long);
+ */
+extern long readn(int, void*, long);
+/* extern long readv(int, IOchunk*, int); <unistd.h> */
+extern int remove(const char*);
+/* extern void* sbrk(ulong); <unistd.h> */
+/* extern long oseek(int, long, int); */
+extern vlong p9seek(int, vlong, int);
+/* give up
+extern long segattach(int, char*, void*, ulong);
+extern int segbrk(void*, void*);
+extern int segdetach(void*);
+extern int segflush(void*, ulong);
+extern int segfree(void*, ulong);
+*/
+extern int p9sleep(long);
+/* extern int stat(char*, uchar*, int); give up */
+extern Waitmsg* p9wait(void);
+extern Waitmsg* p9waitfor(int);
+extern Waitmsg* waitnohang(void);
+extern int p9waitpid(void);
+/* <unistd.h>
+extern long write(int, void*, long);
+extern long writev(int, IOchunk*, int);
+*/
+/* extern int wstat(char*, uchar*, int); give up */
+extern ulong rendezvous(ulong, ulong);
+
+#ifndef NOPLAN9DEFINES
+#define alarm p9alarm
+#define dup p9dup
+#define exec p9exec
+#define execl p9execl
+#define seek p9seek
+#define sleep p9sleep
+#define wait p9wait
+#define waitpid p9waitpid
+/* #define fork p9fork */
+#define rfork p9rfork
+/* #define access p9access */
+#define create p9create
+#undef open
+#define open p9open
+#define pipe p9pipe
+#define waitfor p9waitfor
+#endif
+
+extern Dir* dirstat(char*);
+extern Dir* dirfstat(int);
+extern int dirwstat(char*, Dir*);
+extern int dirfwstat(int, Dir*);
+extern long dirread(int, Dir**);
+extern void nulldir(Dir*);
+extern long dirreadall(int, Dir**);
+/* extern int getpid(void); <unistd.h> */
+/* extern int getppid(void); */
+extern void rerrstr(char*, uint);
+extern char* sysname(void);
+extern void werrstr(char*, ...);
+extern char* getns(void);
+extern char* get9root(void);
+extern char* unsharp(char*);
+extern int sendfd(int, int);
+extern int recvfd(int);
+extern int post9pservice(int, char*);
+
+/* external names that we don't want to step on */
+#ifndef NOPLAN9DEFINES
+#define main p9main
+#endif
+
+/* compiler directives on plan 9 */
+#define SET(x) ((x)=0)
+#define USED(x) if(x){}else{}
+#ifdef __GNUC__
+# if __GNUC__ >= 3
+# undef USED
+# define USED(x) { ulong __y __attribute__ ((unused)); __y = (ulong)(x); }
+# endif
+#endif
+
+#if defined(__OpenBSD__) || (defined(__NetBSD__) && !defined(sched_yield))
+#define sched_yield() \
+ do { \
+ struct timespec ts; \
+ ts.tv_sec = 0; \
+ ts.tv_nsec = 10; \
+ nanosleep(&ts, NULL); \
+ } while(0)
+#endif
+
+/* command line */
+extern char *argv0;
+extern void __fixargv0(void);
+#define ARGBEGIN for((argv0?0:(argv0=(__fixargv0(),*argv))),argv++,argc--;\
+ argv[0] && argv[0][0]=='-' && argv[0][1];\
+ argc--, argv++) {\
+ char *_args, *_argt;\
+ Rune _argc;\
+ _args = &argv[0][1];\
+ if(_args[0]=='-' && _args[1]==0){\
+ argc--; argv++; break;\
+ }\
+ _argc = 0;\
+ while(*_args && (_args += chartorune(&_argc, _args)))\
+ switch(_argc)
+#define ARGEND SET(_argt);USED(_argt);USED(_argc);USED(_args);}USED(argv);USED(argc);
+#define ARGF() (_argt=_args, _args="",\
+ (*_argt? _argt: argv[1]? (argc--, *++argv): 0))
+#define EARGF(x) (_argt=_args, _args="",\
+ (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0)))
+
+#define ARGC() _argc
+
+#if defined(__cplusplus)
+}
+#endif
+#endif /* _LIB9_H_ */
diff --git a/lib9/lnrand.c b/lib9/lnrand.c
@@ -0,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+
+#define MASK 0x7fffffffL
+
+long
+lnrand(long n)
+{
+ long slop, v;
+
+ if(n < 0)
+ return n;
+ slop = MASK % n;
+ do
+ v = lrand();
+ while(v <= slop);
+ return v % n;
+}
diff --git a/lib9/lock.c b/lib9/lock.c
@@ -0,0 +1,55 @@
+#include <u.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sched.h>
+#include <errno.h>
+#include <libc.h>
+
+static pthread_mutex_t initmutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void
+lockinit(Lock *lk)
+{
+ pthread_mutexattr_t attr;
+
+ pthread_mutex_lock(&initmutex);
+ if(lk->init == 0){
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
+ pthread_mutex_init(&lk->mutex, &attr);
+ pthread_mutexattr_destroy(&attr);
+ lk->init = 1;
+ }
+ pthread_mutex_unlock(&initmutex);
+}
+
+void
+lock(Lock *lk)
+{
+ if(!lk->init)
+ lockinit(lk);
+ if(pthread_mutex_lock(&lk->mutex) != 0)
+ abort();
+}
+
+int
+canlock(Lock *lk)
+{
+ int r;
+
+ if(!lk->init)
+ lockinit(lk);
+ r = pthread_mutex_trylock(&lk->mutex);
+ if(r == 0)
+ return 1;
+ if(r == EBUSY)
+ return 0;
+ abort();
+}
+
+void
+unlock(Lock *lk)
+{
+ if(pthread_mutex_unlock(&lk->mutex) != 0)
+ abort();
+}
diff --git a/lib9/lrand.c b/lib9/lrand.c
@@ -0,0 +1,83 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * algorithm by
+ * D. P. Mitchell & J. A. Reeds
+ */
+
+#define LEN 607
+#define TAP 273
+#define MASK 0x7fffffffL
+#define A 48271
+#define M 2147483647
+#define Q 44488
+#define R 3399
+#define NORM (1.0/(1.0+MASK))
+
+static ulong rng_vec[LEN];
+static ulong* rng_tap = rng_vec;
+static ulong* rng_feed = 0;
+static Lock lk;
+
+static void
+isrand(long seed)
+{
+ long lo, hi, x;
+ int i;
+
+ rng_tap = rng_vec;
+ rng_feed = rng_vec+LEN-TAP;
+ seed = seed%M;
+ if(seed < 0)
+ seed += M;
+ if(seed == 0)
+ seed = 89482311;
+ x = seed;
+ /*
+ * Initialize by x[n+1] = 48271 * x[n] mod (2**31 - 1)
+ */
+ for(i = -20; i < LEN; i++) {
+ hi = x / Q;
+ lo = x % Q;
+ x = A*lo - R*hi;
+ if(x < 0)
+ x += M;
+ if(i >= 0)
+ rng_vec[i] = x;
+ }
+}
+
+void
+p9srand(long seed)
+{
+ lock(&lk);
+ isrand(seed);
+ unlock(&lk);
+}
+
+long
+p9lrand(void)
+{
+ ulong x;
+
+ lock(&lk);
+
+ rng_tap--;
+ if(rng_tap < rng_vec) {
+ if(rng_feed == 0) {
+ isrand(1);
+ rng_tap--;
+ }
+ rng_tap += LEN;
+ }
+ rng_feed--;
+ if(rng_feed < rng_vec)
+ rng_feed += LEN;
+ x = (*rng_feed + *rng_tap) & MASK;
+ *rng_feed = x;
+
+ unlock(&lk);
+
+ return x;
+}
diff --git a/lib9/main.c b/lib9/main.c
@@ -0,0 +1,13 @@
+#include <u.h>
+#define NOPLAN9DEFINES
+#include <libc.h>
+
+extern void p9main(int, char**);
+
+int
+main(int argc, char **argv)
+{
+ p9main(argc, argv);
+ exits("main");
+ return 99;
+}
diff --git a/lib9/malloc.c b/lib9/malloc.c
@@ -0,0 +1,56 @@
+/*
+ * These are here mainly so that I can link against
+ * debugmalloc.c instead and not recompile the world.
+ */
+
+#include <u.h>
+#define NOPLAN9DEFINES
+#include <libc.h>
+
+static Lock malloclock;
+
+void*
+p9malloc(ulong n)
+{
+ void *v;
+
+ if(n == 0)
+ n++;
+ lock(&malloclock);
+ v = malloc(n);
+ unlock(&malloclock);
+ return v;
+}
+
+void
+p9free(void *v)
+{
+ if(v == nil)
+ return;
+ lock(&malloclock);
+ free(v);
+ unlock(&malloclock);
+}
+
+void*
+p9calloc(ulong a, ulong b)
+{
+ void *v;
+
+ if(a*b == 0)
+ a = b = 1;
+
+ lock(&malloclock);
+ v = calloc(a*b, 1);
+ unlock(&malloclock);
+ return v;
+}
+
+void*
+p9realloc(void *v, ulong n)
+{
+ lock(&malloclock);
+ v = realloc(v, n);
+ unlock(&malloclock);
+ return v;
+}
diff --git a/lib9/malloctag.c b/lib9/malloctag.c
@@ -0,0 +1,19 @@
+#include <lib9.h>
+
+extern long p9lrand(void);
+#define USED(x) if(x){}else{}
+#define lrand p9lrand
+
+void
+setmalloctag(void *v, ulong t)
+{
+ USED(v);
+ USED(t);
+}
+
+void
+setrealloctag(void *v, ulong t)
+{
+ USED(v);
+ USED(t);
+}
diff --git a/lib9/mallocz.c b/lib9/mallocz.c
@@ -0,0 +1,15 @@
+#include <u.h>
+#include <unistd.h>
+#include <string.h>
+#include <libc.h>
+
+void*
+mallocz(unsigned long n, int clr)
+{
+ void *v;
+
+ v = malloc(n);
+ if(clr && v)
+ memset(v, 0, n);
+ return v;
+}
diff --git a/lib9/nan.c b/lib9/nan.c
@@ -0,0 +1,27 @@
+#include <u.h>
+#include <libc.h>
+#include "nan.h"
+
+double
+NaN(void)
+{
+ return __NaN();
+}
+
+double
+Inf(int sign)
+{
+ return __Inf(sign);
+}
+
+int
+isNaN(double x)
+{
+ return __isNaN(x);
+}
+
+int
+isInf(double x, int sign)
+{
+ return __isInf(x, sign);
+}
diff --git a/lib9/nan.h b/lib9/nan.h
@@ -0,0 +1,4 @@
+extern double __NaN(void);
+extern double __Inf(int);
+extern int __isNaN(double);
+extern int __isInf(double, int);
diff --git a/lib9/needsrcquote.c b/lib9/needsrcquote.c
@@ -0,0 +1,12 @@
+#include <u.h>
+#include <libc.h>
+
+int
+needsrcquote(int c)
+{
+ if(c <= ' ')
+ return 1;
+ if(strchr("`^#*[]=|\\?${}()'<>&;", c))
+ return 1;
+ return 0;
+}
diff --git a/lib9/needstack.c b/lib9/needstack.c
@@ -0,0 +1,8 @@
+#include <u.h>
+#include <libc.h>
+
+void
+needstack(int howmuch)
+{
+ USED(howmuch);
+}
diff --git a/lib9/netmkaddr.c b/lib9/netmkaddr.c
@@ -0,0 +1,52 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+
+/*
+ * make an address, add the defaults
+ */
+char *
+netmkaddr(char *linear, char *defnet, char *defsrv)
+{
+ static char addr[256];
+ char *cp;
+
+ /*
+ * dump network name
+ */
+ cp = strchr(linear, '!');
+ if(cp == 0){
+ if(defnet==0){
+ if(defsrv)
+ snprint(addr, sizeof(addr), "net!%s!%s",
+ linear, defsrv);
+ else
+ snprint(addr, sizeof(addr), "net!%s", linear);
+ }
+ else {
+ if(defsrv)
+ snprint(addr, sizeof(addr), "%s!%s!%s", defnet,
+ linear, defsrv);
+ else
+ snprint(addr, sizeof(addr), "%s!%s", defnet,
+ linear);
+ }
+ return addr;
+ }
+
+ /*
+ * if there is already a service, use it
+ */
+ cp = strchr(cp+1, '!');
+ if(cp)
+ return linear;
+
+ /*
+ * add default service
+ */
+ if(defsrv == 0)
+ return linear;
+ snprint(addr, sizeof(addr), "%s!%s", linear, defsrv);
+
+ return addr;
+}
diff --git a/lib9/notify.c b/lib9/notify.c
@@ -0,0 +1,272 @@
+/*
+ * Signal handling for Plan 9 programs.
+ * We stubbornly use the strings from Plan 9 instead
+ * of the enumerated Unix constants.
+ * There are some weird translations. In particular,
+ * a "kill" note is the same as SIGTERM in Unix.
+ * There is no equivalent note to Unix's SIGKILL, since
+ * it's not a deliverable signal anyway.
+ *
+ * We do not handle SIGABRT or SIGSEGV, mainly because
+ * the thread library queues its notes for later, and we want
+ * to dump core with the state at time of delivery.
+ *
+ * We have to add some extra entry points to provide the
+ * ability to tweak which signals are deliverable and which
+ * are acted upon. Notifydisable and notifyenable play with
+ * the process signal mask. Notifyignore enables the signal
+ * but will not call notifyf when it comes in. This is occasionally
+ * useful.
+ */
+
+#include <u.h>
+#include <signal.h>
+#define NOPLAN9DEFINES
+#include <libc.h>
+
+extern char *_p9sigstr(int, char*);
+extern int _p9strsig(char*);
+
+typedef struct Sig Sig;
+struct Sig
+{
+ int sig; /* signal number */
+ int flags;
+};
+
+enum
+{
+ Restart = 1<<0,
+ Ignore = 1<<1,
+};
+
+static Sig sigs[] = {
+ SIGHUP, 0,
+ SIGINT, 0,
+ SIGQUIT, 0,
+ SIGILL, 0,
+ SIGTRAP, 0,
+/* SIGABRT, 0, */
+#ifdef SIGEMT
+ SIGEMT, 0,
+#endif
+ SIGFPE, 0,
+ SIGBUS, 0,
+/* SIGSEGV, 0, */
+ SIGCHLD, Restart|Ignore,
+ SIGSYS, 0,
+ SIGPIPE, Ignore,
+ SIGALRM, 0,
+ SIGTERM, 0,
+ SIGTSTP, Restart|Ignore,
+/* SIGTTIN, Restart|Ignore, */
+/* SIGTTOU, Restart|Ignore, */
+ SIGXCPU, 0,
+ SIGXFSZ, 0,
+ SIGVTALRM, 0,
+ SIGUSR1, 0,
+ SIGUSR2, 0,
+#ifdef SIGWINCH
+ SIGWINCH, Restart|Ignore,
+#endif
+#ifdef SIGINFO
+ SIGINFO, Restart|Ignore,
+#endif
+};
+
+static Sig*
+findsig(int s)
+{
+ int i;
+
+ for(i=0; i<nelem(sigs); i++)
+ if(sigs[i].sig == s)
+ return &sigs[i];
+ return nil;
+}
+
+/*
+ * The thread library initializes _notejmpbuf to its own
+ * routine which provides a per-pthread jump buffer.
+ * If we're not using the thread library, we assume we are
+ * single-threaded.
+ */
+typedef struct Jmp Jmp;
+struct Jmp
+{
+ p9jmp_buf b;
+};
+
+static Jmp onejmp;
+
+static Jmp*
+getonejmp(void)
+{
+ return &onejmp;
+}
+
+Jmp *(*_notejmpbuf)(void) = getonejmp;
+static void noteinit(void);
+
+/*
+ * Actual signal handler.
+ */
+
+static void (*notifyf)(void*, char*); /* Plan 9 handler */
+
+static void
+signotify(int sig)
+{
+ char tmp[64];
+ Jmp *j;
+ Sig *s;
+
+ j = (*_notejmpbuf)();
+ switch(p9setjmp(j->b)){
+ case 0:
+ if(notifyf)
+ (*notifyf)(nil, _p9sigstr(sig, tmp));
+ /* fall through */
+ case 1: /* noted(NDFLT) */
+ if(0)print("DEFAULT %d\n", sig);
+ s = findsig(sig);
+ if(s && (s->flags&Ignore))
+ return;
+ signal(sig, SIG_DFL);
+ raise(sig);
+ _exit(1);
+ case 2: /* noted(NCONT) */
+ if(0)print("HANDLED %d\n", sig);
+ return;
+ }
+}
+
+static void
+signonotify(int sig)
+{
+ USED(sig);
+}
+
+int
+noted(int v)
+{
+ p9longjmp((*_notejmpbuf)()->b, v==NCONT ? 2 : 1);
+ abort();
+ return 0;
+}
+
+int
+notify(void (*f)(void*, char*))
+{
+ static int init;
+
+ notifyf = f;
+ if(!init){
+ init = 1;
+ noteinit();
+ }
+ return 0;
+}
+
+/*
+ * Nonsense about enabling and disabling signals.
+ */
+typedef void Sighandler(int);
+static Sighandler*
+handler(int s)
+{
+ struct sigaction sa;
+
+ sigaction(s, nil, &sa);
+ return sa.sa_handler;
+}
+
+static int
+notesetenable(int sig, int enabled)
+{
+ sigset_t mask, omask;
+
+ if(sig == 0)
+ return -1;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, sig);
+ sigprocmask(enabled ? SIG_UNBLOCK : SIG_BLOCK, &mask, &omask);
+ return !sigismember(&omask, sig);
+}
+
+int
+noteenable(char *msg)
+{
+ return notesetenable(_p9strsig(msg), 1);
+}
+
+int
+notedisable(char *msg)
+{
+ return notesetenable(_p9strsig(msg), 0);
+}
+
+static int
+notifyseton(int s, int on)
+{
+ Sig *sig;
+ struct sigaction sa, osa;
+
+ sig = findsig(s);
+ if(sig == nil)
+ return -1;
+ memset(&sa, 0, sizeof sa);
+ sa.sa_handler = on ? signotify : signonotify;
+ if(sig->flags&Restart)
+ sa.sa_flags |= SA_RESTART;
+
+ /*
+ * We can't allow signals within signals because there's
+ * only one jump buffer.
+ */
+ sigfillset(&sa.sa_mask);
+
+ /*
+ * Install handler.
+ */
+ sigaction(sig->sig, &sa, &osa);
+ return osa.sa_handler == signotify;
+}
+
+int
+notifyon(char *msg)
+{
+ return notifyseton(_p9strsig(msg), 1);
+}
+
+int
+notifyoff(char *msg)
+{
+ return notifyseton(_p9strsig(msg), 0);
+}
+
+/*
+ * Initialization follows sigs table.
+ */
+static void
+noteinit(void)
+{
+ int i;
+ Sig *sig;
+
+ for(i=0; i<nelem(sigs); i++){
+ sig = &sigs[i];
+ /*
+ * If someone has already installed a handler,
+ * It's probably some ld preload nonsense,
+ * like pct (a SIGVTALRM-based profiler).
+ * Or maybe someone has already called notifyon/notifyoff.
+ * Leave it alone.
+ */
+ if(handler(sig->sig) != SIG_DFL)
+ continue;
+ notifyseton(sig->sig, 1);
+ }
+}
+
diff --git a/lib9/nrand.c b/lib9/nrand.c
@@ -0,0 +1,19 @@
+#include <lib9.h>
+
+extern long p9lrand(void);
+#define lrand p9lrand
+#define MASK 0x7fffffffL
+
+int
+nrand(int n)
+{
+ long slop, v;
+
+ if(n < 0)
+ return n;
+ slop = MASK % n;
+ do
+ v = lrand();
+ while(v <= slop);
+ return v % n;
+}
diff --git a/lib9/nulldir.c b/lib9/nulldir.c
@@ -0,0 +1,9 @@
+#include <u.h>
+#include <libc.h>
+
+void
+nulldir(Dir *d)
+{
+ memset(d, ~0, sizeof(Dir));
+ d->name = d->uid = d->gid = d->muid = "";
+}
diff --git a/lib9/open.c b/lib9/open.c
@@ -0,0 +1,62 @@
+#define _GNU_SOURCE /* for Linux O_DIRECT */
+#include <u.h>
+#define NOPLAN9DEFINES
+#include <sys/file.h>
+#include <libc.h>
+#ifndef O_DIRECT
+#define O_DIRECT 0
+#endif
+
+int
+p9open(char *name, int mode)
+{
+ int cexec, rclose;
+ int fd, umode, lock, rdwr;
+ struct flock fl;
+
+ rdwr = mode&3;
+ umode = rdwr;
+ cexec = mode&OCEXEC;
+ rclose = mode&ORCLOSE;
+ lock = mode&OLOCK;
+ mode &= ~(3|OCEXEC|ORCLOSE|OLOCK);
+ if(mode&OTRUNC){
+ umode |= O_TRUNC;
+ mode ^= OTRUNC;
+ }
+ if(mode&ODIRECT){
+ umode |= O_DIRECT;
+ mode ^= ODIRECT;
+ }
+ if(mode&ONONBLOCK){
+ umode |= O_NONBLOCK;
+ mode ^= ONONBLOCK;
+ }
+ if(mode&OAPPEND){
+ umode |= O_APPEND;
+ mode ^= OAPPEND;
+ }
+ if(mode){
+ werrstr("mode 0x%x not supported", mode);
+ return -1;
+ }
+ fd = open(name, umode);
+ 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(name);
+ }
+ return fd;
+}
diff --git a/lib9/opentemp.c b/lib9/opentemp.c
@@ -0,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+
+int
+opentemp(char *template)
+{
+ int fd;
+
+ fd = mkstemp(template);
+ if(fd < 0)
+ return -1;
+ remove(template);
+ return fd;
+}
+
diff --git a/lib9/pipe.c b/lib9/pipe.c
@@ -0,0 +1,15 @@
+#include <u.h>
+#define NOPLAN9DEFINES
+#include <libc.h>
+#include <sys/socket.h>
+
+/*
+ * We use socketpair to get a two-way pipe.
+ * The pipe still doesn't preserve message boundaries.
+ * Worse, it cannot be reopened via /dev/fd/NNN on Linux.
+ */
+int
+p9pipe(int fd[2])
+{
+ return socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+}
diff --git a/lib9/portdate b/lib9/portdate
@@ -0,0 +1,30 @@
+dofmt.c 2004/1225
+dorfmt.c 2004/1225
+errfmt.c 2004/1225
+fltfmt.c 2004/1225
+fmt.c 2004/1225
+fmtfd.c 2004/1225
+fmtlock.c 2004/1225
+fmtprint.c 2004/1225
+fmtquote.c 2004/1225
+fmtrune.c 2004/1225
+fmtstr.c 2004/1225
+fmtvprint.c 2004/1225
+fprint.c 2004/1225
+print.c 2004/1225
+runefmtstr.c 2004/1225
+runeseprint.c 2004/1225
+runesmprint.c 2004/1225
+runesnprint.c 2004/1225
+runesprint.c 2004/1225
+runevseprint.c 2004/1225
+runevsmprint.c 2004/1225
+runevsnprint.c 2004/1225
+seprint.c 2004/1225
+smprint.c 2004/1225
+snprint.c 2004/1225
+sprint.c 2004/1225
+vfprint.c 2004/1225
+vseprint.c 2004/1225
+vsmprint.c 2004/1225
+vsnprint.c 2004/1225
diff --git a/lib9/post9p.c b/lib9/post9p.c
@@ -0,0 +1,46 @@
+#include <u.h>
+#include <libc.h>
+
+int
+post9pservice(int fd, char *name)
+{
+ int i;
+ char *ns, *s;
+ Waitmsg *w;
+
+ if(strchr(name, '!')) /* assume is already network address */
+ s = strdup(name);
+ else{
+ if((ns = getns()) == nil)
+ return -1;
+ s = smprint("unix!%s/%s", ns, name);
+ free(ns);
+ }
+ if(s == nil)
+ return -1;
+ switch(fork()){
+ case -1:
+ return -1;
+ case 0:
+ dup(fd, 0);
+ dup(fd, 1);
+ for(i=3; i<20; i++)
+ close(i);
+ execlp("9pserve", "9pserve", "-u", s, (char*)0);
+ fprint(2, "exec 9pserve: %r\n");
+ _exits("exec");
+ default:
+ w = wait();
+ if(w == nil)
+ return -1;
+ close(fd);
+ free(s);
+ if(w->msg && w->msg[0]){
+ free(w);
+ werrstr("9pserve failed");
+ return -1;
+ }
+ free(w);
+ return 0;
+ }
+}
diff --git a/lib9/postnote.c b/lib9/postnote.c
@@ -0,0 +1,34 @@
+#include <u.h>
+#define NOPLAN9DEFINES
+#include <libc.h>
+
+#include <signal.h>
+
+
+extern int _p9strsig(char*);
+
+int
+postnote(int who, int pid, char *msg)
+{
+ int sig;
+
+ sig = _p9strsig(msg);
+ if(sig == 0){
+ werrstr("unknown note");
+ return -1;
+ }
+
+ switch(who){
+ default:
+ werrstr("bad who in postnote");
+ return -1;
+ case PNPROC:
+ return kill(pid, sig);
+ case PNGROUP:
+ if((pid = getpgid(pid)) < 0)
+ return -1;
+ return killpg(pid, sig);
+ }
+}
+
+
diff --git a/lib9/priv.c b/lib9/priv.c
@@ -0,0 +1,32 @@
+#include <u.h>
+#include <libc.h>
+#include "9proc.h"
+
+static Lock privlock;
+static ulong privmap;
+
+int
+privalloc(void)
+{
+ int i;
+
+ lock(&privlock);
+ for(i=0; i<NPRIV; i++)
+ if((privmap&(1<<i)) == 0){
+ privmap |= (1<<i);
+ unlock(&privlock);
+ return i;
+ }
+ unlock(&privlock);
+ return -1;
+}
+
+void**
+privmem(int i)
+{
+ Uproc *up;
+
+ up = _p9uproc(0);
+ return &up->priv[i];
+}
+
diff --git a/lib9/qlock.c b/lib9/qlock.c
@@ -0,0 +1,166 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * The function pointers are supplied by the thread
+ * library during its initialization. If there is no thread
+ * library, there is no multithreading.
+ */
+
+int (*_lock)(Lock*, int, ulong);
+void (*_unlock)(Lock*, ulong);
+int (*_qlock)(QLock*, int, ulong); /* do not use */
+void (*_qunlock)(QLock*, ulong);
+void (*_rsleep)(Rendez*, ulong); /* do not use */
+int (*_rwakeup)(Rendez*, int, ulong);
+int (*_rlock)(RWLock*, int, ulong); /* do not use */
+int (*_wlock)(RWLock*, int, ulong);
+void (*_runlock)(RWLock*, ulong);
+void (*_wunlock)(RWLock*, ulong);
+
+void
+lock(Lock *l)
+{
+ if(_lock)
+ (*_lock)(l, 1, getcallerpc(&l));
+ else
+ l->held = 1;
+}
+
+int
+canlock(Lock *l)
+{
+ if(_lock)
+ return (*_lock)(l, 0, getcallerpc(&l));
+ else{
+ if(l->held)
+ return 0;
+ l->held = 1;
+ return 1;
+ }
+}
+
+void
+unlock(Lock *l)
+{
+ if(_unlock)
+ (*_unlock)(l, getcallerpc(&l));
+ else
+ l->held = 0;
+}
+
+void
+qlock(QLock *l)
+{
+ if(_qlock)
+ (*_qlock)(l, 1, getcallerpc(&l));
+ else
+ l->l.held = 1;
+}
+
+int
+canqlock(QLock *l)
+{
+ if(_qlock)
+ return (*_qlock)(l, 0, getcallerpc(&l));
+ else{
+ if(l->l.held)
+ return 0;
+ l->l.held = 1;
+ return 1;
+ }
+}
+
+void
+qunlock(QLock *l)
+{
+ if(_qunlock)
+ (*_qunlock)(l, getcallerpc(&l));
+ else
+ l->l.held = 0;
+}
+
+void
+rlock(RWLock *l)
+{
+ if(_rlock)
+ (*_rlock)(l, 1, getcallerpc(&l));
+ else
+ l->readers++;
+}
+
+int
+canrlock(RWLock *l)
+{
+ if(_rlock)
+ return (*_rlock)(l, 0, getcallerpc(&l));
+ else{
+ if(l->writer)
+ return 0;
+ l->readers++;
+ return 1;
+ }
+}
+
+void
+runlock(RWLock *l)
+{
+ if(_runlock)
+ (*_runlock)(l, getcallerpc(&l));
+ else
+ l->readers--;
+}
+
+void
+wlock(RWLock *l)
+{
+ if(_wlock)
+ (*_wlock)(l, 1, getcallerpc(&l));
+ else
+ l->writer = (void*)1;
+}
+
+int
+canwlock(RWLock *l)
+{
+ if(_wlock)
+ return (*_wlock)(l, 0, getcallerpc(&l));
+ else{
+ if(l->writer || l->readers)
+ return 0;
+ l->writer = (void*)1;
+ return 1;
+ }
+}
+
+void
+wunlock(RWLock *l)
+{
+ if(_wunlock)
+ (*_wunlock)(l, getcallerpc(&l));
+ else
+ l->writer = nil;
+}
+
+void
+rsleep(Rendez *r)
+{
+ if(_rsleep)
+ (*_rsleep)(r, getcallerpc(&r));
+}
+
+int
+rwakeup(Rendez *r)
+{
+ if(_rwakeup)
+ return (*_rwakeup)(r, 0, getcallerpc(&r));
+ return 0;
+}
+
+int
+rwakeupall(Rendez *r)
+{
+ if(_rwakeup)
+ return (*_rwakeup)(r, 1, getcallerpc(&r));
+ return 0;
+}
diff --git a/lib9/quote.c b/lib9/quote.c
@@ -0,0 +1,136 @@
+#include <u.h>
+#include <libc.h>
+
+
+/* in libfmt */
+extern int (*doquote)(int);
+extern int __needsquotes(char*, int*);
+extern int __runeneedsquotes(Rune*, int*);
+
+char*
+unquotestrdup(char *s)
+{
+ char *t, *ret;
+ int quoting;
+
+ ret = s = strdup(s); /* return unquoted copy */
+ if(ret == nil)
+ return ret;
+ quoting = 0;
+ t = s; /* s is output string, t is input string */
+ while(*t!='\0' && (quoting || (*t!=' ' && *t!='\t'))){
+ if(*t != '\''){
+ *s++ = *t++;
+ continue;
+ }
+ /* *t is a quote */
+ if(!quoting){
+ quoting = 1;
+ t++;
+ continue;
+ }
+ /* quoting and we're on a quote */
+ if(t[1] != '\''){
+ /* end of quoted section; absorb closing quote */
+ t++;
+ quoting = 0;
+ continue;
+ }
+ /* doubled quote; fold one quote into two */
+ t++;
+ *s++ = *t++;
+ }
+ if(t != s)
+ memmove(s, t, strlen(t)+1);
+ return ret;
+}
+
+Rune*
+unquoterunestrdup(Rune *s)
+{
+ Rune *t, *ret;
+ int quoting;
+
+ ret = s = runestrdup(s); /* return unquoted copy */
+ if(ret == nil)
+ return ret;
+ quoting = 0;
+ t = s; /* s is output string, t is input string */
+ while(*t!='\0' && (quoting || (*t!=' ' && *t!='\t'))){
+ if(*t != '\''){
+ *s++ = *t++;
+ continue;
+ }
+ /* *t is a quote */
+ if(!quoting){
+ quoting = 1;
+ t++;
+ continue;
+ }
+ /* quoting and we're on a quote */
+ if(t[1] != '\''){
+ /* end of quoted section; absorb closing quote */
+ t++;
+ quoting = 0;
+ continue;
+ }
+ /* doubled quote; fold one quote into two */
+ t++;
+ *s++ = *t++;
+ }
+ if(t != s)
+ memmove(s, t, (runestrlen(t)+1)*sizeof(Rune));
+ return ret;
+}
+
+char*
+quotestrdup(char *s)
+{
+ char *t, *u, *ret;
+ int quotelen;
+ Rune r;
+
+ if(__needsquotes(s, "elen) == 0)
+ return strdup(s);
+
+ ret = malloc(quotelen+1);
+ if(ret == nil)
+ return nil;
+ u = ret;
+ *u++ = '\'';
+ for(t=s; *t; t++){
+ r = *t;
+ if(r == '\'')
+ *u++ = r; /* double the quote */
+ *u++ = r;
+ }
+ *u++ = '\'';
+ *u = '\0';
+ return ret;
+}
+
+Rune*
+quoterunestrdup(Rune *s)
+{
+ Rune *t, *u, *ret;
+ int quotelen;
+ Rune r;
+
+ if(__runeneedsquotes(s, "elen) == 0)
+ return runestrdup(s);
+
+ ret = malloc((quotelen+1)*sizeof(Rune));
+ if(ret == nil)
+ return nil;
+ u = ret;
+ *u++ = '\'';
+ for(t=s; *t; t++){
+ r = *t;
+ if(r == '\'')
+ *u++ = r; /* double the quote */
+ *u++ = r;
+ }
+ *u++ = '\'';
+ *u = '\0';
+ return ret;
+}
diff --git a/lib9/rand.c b/lib9/rand.c
@@ -0,0 +1,10 @@
+#include <lib9.h>
+
+extern long p9lrand(void);
+#define lrand p9lrand
+
+int
+p9rand(void)
+{
+ return lrand() & 0x7fff;
+}
diff --git a/lib9/read9pmsg.c b/lib9/read9pmsg.c
@@ -0,0 +1,31 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+int
+read9pmsg(int fd, void *abuf, uint n)
+{
+ int m, len;
+ uchar *buf;
+
+ buf = abuf;
+
+ /* read count */
+ m = readn(fd, buf, BIT32SZ);
+ if(m != BIT32SZ){
+ if(m < 0)
+ return -1;
+ return 0;
+ }
+
+ len = GBIT32(buf);
+ if(len <= BIT32SZ || len > n){
+ werrstr("bad length in 9P2000 message header");
+ return -1;
+ }
+ len -= BIT32SZ;
+ m = readn(fd, buf+BIT32SZ, len);
+ if(m < len)
+ return 0;
+ return BIT32SZ+m;
+}
diff --git a/lib9/readcons.c b/lib9/readcons.c
@@ -0,0 +1,102 @@
+#include <u.h>
+#define NOPLAN9DEFINES
+#include <libc.h>
+#include <termios.h>
+#include <sys/termios.h>
+
+static int
+rawx(int fd, int echoing)
+{
+ int was;
+ static struct termios ttmode;
+
+ if(echoing == -1)
+ return -1;
+
+ if(tcgetattr(fd, &ttmode) < 0)
+ return -1;
+ was = (ttmode.c_lflag&(ECHO|ICANON));
+ ttmode.c_lflag &= ~(ECHO|ICANON);
+ ttmode.c_lflag |= echoing;
+ if(tcsetattr(fd, TCSANOW, &ttmode) < 0)
+ return -1;
+ return was;
+}
+
+char*
+readcons(char *prompt, char *def, int secret)
+{
+ int fd, n, raw;
+ char line[10];
+ char *s, *t;
+ int l;
+
+ if((fd = open("/dev/tty", ORDWR)) < 0)
+ return nil;
+
+ raw = -1;
+ if(secret){
+ raw = rawx(fd, 0);
+ if(raw == -1)
+ return nil;
+ }
+
+ if(def)
+ fprint(fd, "%s[%s]: ", prompt, def);
+ else
+ fprint(fd, "%s: ", prompt);
+
+ s = strdup("");
+ if(s == nil)
+ return nil;
+
+ for(;;){
+ n = read(fd, line, 1);
+ if(n < 0){
+ Error:
+ if(secret){
+ rawx(fd, raw);
+ write(fd, "\n", 1);
+ }
+ close(fd);
+ free(s);
+ return nil;
+ }
+ if(n > 0 && line[0] == 0x7F)
+ goto Error;
+ if(n == 0 || line[0] == 0x04 || line[0] == '\n' || line[0] == '\r'){
+ if(secret){
+ rawx(fd, raw);
+ write(fd, "\n", 1);
+ }
+ close(fd);
+ if(*s == 0 && def){
+ free(s);
+ s = strdup(def);
+ }
+ return s;
+ }
+ if(line[0] == '\b'){
+ if(strlen(s) > 0)
+ s[strlen(s)-1] = 0;
+ }else if(line[0] == 0x15){ /* ^U: line kill */
+ if(def != nil)
+ fprint(fd, "\n%s[%s]: ", prompt, def);
+ else
+ fprint(fd, "\n%s: ", prompt);
+ s[0] = 0;
+ }else{
+ l = strlen(s);
+ t = malloc(l+2);
+ if(t)
+ memmove(t, s, l);
+ memset(s, 'X', l);
+ free(s);
+ if(t == nil)
+ return nil;
+ t[l] = line[0];
+ t[l+1] = 0;
+ s = t;
+ }
+ }
+}
diff --git a/lib9/readn.c b/lib9/readn.c
@@ -0,0 +1,22 @@
+#include <lib9.h>
+#include <unistd.h>
+
+long
+readn(int f, void *av, long n)
+{
+ char *a;
+ long m, t;
+
+ a = av;
+ t = 0;
+ while(t < n){
+ m = read(f, a+t, n-t);
+ if(m <= 0){
+ if(t == 0)
+ return m;
+ break;
+ }
+ t += m;
+ }
+ return t;
+}
diff --git a/lib9/regex/README b/lib9/regex/README
@@ -0,0 +1,7 @@
+This is a Unix port of the Plan 9 regular expression library,
+originally done for the Inferno operating system.
+
+Russ Cox repackaged this to build as a standalone
+Unix library. Send comments about packaging to
+Russ Cox <rsc@post.harvard.edu>
+
diff --git a/lib9/regex/cvt b/lib9/regex/cvt
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+/usr/bin/sed -E '
+ s/\.(sp|rsp)/.s.\1/g
+ s/\.(ep|rep)/.e.\1/g
+ s/(\.|->)(cp|r|subid|right)([^a-zA-Z0-9_])/\1u1.\2\3/g
+ s/(\.|->)(left|next)([^a-z])/\1u2.\2\3/g
+ /#include <u.h>/d
+ s/<libc.h>/"lib9.h"/g
+ s/"regexp.h"/"regexp9.h"/g
+' $*
diff --git a/lib9/regex/lib9.h b/lib9/regex/lib9.h
@@ -0,0 +1,2 @@
+#include <u.h>
+#include <libc.h>
diff --git a/lib9/regex/lib9.std.h b/lib9/regex/lib9.std.h
@@ -0,0 +1,10 @@
+#include <fmt.h>
+#include <setjmp.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define exits(x) exit(x && *x ? 1 : 0)
+
+#define nil 0
+
diff --git a/lib9/regex/mkfile b/lib9/regex/mkfile
@@ -0,0 +1,25 @@
+<$PLAN9/src/mkhdr
+
+LIB=libregexp9.a
+
+OFILES=\
+ regcomp.$O\
+ regerror.$O\
+ regexec.$O\
+ regsub.$O\
+ regaux.$O\
+ rregexec.$O\
+ rregsub.$O\
+
+HFILES=\
+ $PLAN9/include/regexp9.h\
+ regcomp.h\
+
+<$PLAN9/src/mksyslib
+
+test: test.$O $LIB
+ $CC -o test test.$O $LIB -L/usr/local/lib -lfmt -lutf
+
+test2: test2.$O $LIB
+ $CC -o test2 test2.$O $LIB -L/usr/local/lib -lfmt -lutf
+
diff --git a/lib9/regex/regaux.c b/lib9/regex/regaux.c
@@ -0,0 +1,112 @@
+#include "lib9.h"
+#include "regexp9.h"
+#include "regcomp.h"
+
+
+/*
+ * save a new match in mp
+ */
+extern void
+_renewmatch(Resub *mp, int ms, Resublist *sp)
+{
+ int i;
+
+ if(mp==0 || ms<=0)
+ return;
+ if(mp[0].s.sp==0 || sp->m[0].s.sp<mp[0].s.sp ||
+ (sp->m[0].s.sp==mp[0].s.sp && sp->m[0].e.ep>mp[0].e.ep)){
+ for(i=0; i<ms && i<NSUBEXP; i++)
+ mp[i] = sp->m[i];
+ for(; i<ms; i++)
+ mp[i].s.sp = mp[i].e.ep = 0;
+ }
+}
+
+/*
+ * Note optimization in _renewthread:
+ * *lp must be pending when _renewthread called; if *l has been looked
+ * at already, the optimization is a bug.
+ */
+extern Relist*
+_renewthread(Relist *lp, /* _relist to add to */
+ Reinst *ip, /* instruction to add */
+ int ms,
+ Resublist *sep) /* pointers to subexpressions */
+{
+ Relist *p;
+
+ for(p=lp; p->inst; p++){
+ if(p->inst == ip){
+ if(sep->m[0].s.sp < p->se.m[0].s.sp){
+ if(ms > 1)
+ p->se = *sep;
+ else
+ p->se.m[0] = sep->m[0];
+ }
+ return 0;
+ }
+ }
+ p->inst = ip;
+ if(ms > 1)
+ p->se = *sep;
+ else
+ p->se.m[0] = sep->m[0];
+ (++p)->inst = 0;
+ return p;
+}
+
+/*
+ * same as renewthread, but called with
+ * initial empty start pointer.
+ */
+extern Relist*
+_renewemptythread(Relist *lp, /* _relist to add to */
+ Reinst *ip, /* instruction to add */
+ int ms,
+ char *sp) /* pointers to subexpressions */
+{
+ Relist *p;
+
+ for(p=lp; p->inst; p++){
+ if(p->inst == ip){
+ if(sp < p->se.m[0].s.sp) {
+ if(ms > 1)
+ memset(&p->se, 0, sizeof(p->se));
+ p->se.m[0].s.sp = sp;
+ }
+ return 0;
+ }
+ }
+ p->inst = ip;
+ if(ms > 1)
+ memset(&p->se, 0, sizeof(p->se));
+ p->se.m[0].s.sp = sp;
+ (++p)->inst = 0;
+ return p;
+}
+
+extern Relist*
+_rrenewemptythread(Relist *lp, /* _relist to add to */
+ Reinst *ip, /* instruction to add */
+ int ms,
+ Rune *rsp) /* pointers to subexpressions */
+{
+ Relist *p;
+
+ for(p=lp; p->inst; p++){
+ if(p->inst == ip){
+ if(rsp < p->se.m[0].s.rsp) {
+ if(ms > 1)
+ memset(&p->se, 0, sizeof(p->se));
+ p->se.m[0].s.rsp = rsp;
+ }
+ return 0;
+ }
+ }
+ p->inst = ip;
+ if(ms > 1)
+ memset(&p->se, 0, sizeof(p->se));
+ p->se.m[0].s.rsp = rsp;
+ (++p)->inst = 0;
+ return p;
+}
diff --git a/lib9/regex/regcomp.c b/lib9/regex/regcomp.c
@@ -0,0 +1,555 @@
+#include "lib9.h"
+#include "regexp9.h"
+#include "regcomp.h"
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Parser Information
+ */
+typedef
+struct Node
+{
+ Reinst* first;
+ Reinst* last;
+}Node;
+
+#define NSTACK 20
+static Node andstack[NSTACK];
+static Node *andp;
+static int atorstack[NSTACK];
+static int* atorp;
+static int cursubid; /* id of current subexpression */
+static int subidstack[NSTACK]; /* parallel to atorstack */
+static int* subidp;
+static int lastwasand; /* Last token was operand */
+static int nbra;
+static char* exprp; /* pointer to next character in source expression */
+static int lexdone;
+static int nclass;
+static Reclass*classp;
+static Reinst* freep;
+static int errors;
+static Rune yyrune; /* last lex'd rune */
+static Reclass*yyclassp; /* last lex'd class */
+
+/* predeclared crap */
+static void operator(int);
+static void pushand(Reinst*, Reinst*);
+static void pushator(int);
+static void evaluntil(int);
+static int bldcclass(void);
+
+static jmp_buf regkaboom;
+
+static void
+rcerror(char *s)
+{
+ errors++;
+ regerror(s);
+ longjmp(regkaboom, 1);
+}
+
+static Reinst*
+newinst(int t)
+{
+ freep->type = t;
+ freep->u2.left = 0;
+ freep->u1.right = 0;
+ return freep++;
+}
+
+static void
+operand(int t)
+{
+ Reinst *i;
+
+ if(lastwasand)
+ operator(CAT); /* catenate is implicit */
+ i = newinst(t);
+
+ if(t == CCLASS || t == NCCLASS)
+ i->u1.cp = yyclassp;
+ if(t == RUNE)
+ i->u1.r = yyrune;
+
+ pushand(i, i);
+ lastwasand = TRUE;
+}
+
+static void
+operator(int t)
+{
+ if(t==RBRA && --nbra<0)
+ rcerror("unmatched right paren");
+ if(t==LBRA){
+ if(++cursubid >= NSUBEXP)
+ rcerror ("too many subexpressions");
+ nbra++;
+ if(lastwasand)
+ operator(CAT);
+ } else
+ evaluntil(t);
+ if(t != RBRA)
+ pushator(t);
+ lastwasand = FALSE;
+ if(t==STAR || t==QUEST || t==PLUS || t==RBRA)
+ lastwasand = TRUE; /* these look like operands */
+}
+
+static void
+regerr2(char *s, int c)
+{
+ char buf[100];
+ char *cp = buf;
+ while(*s)
+ *cp++ = *s++;
+ *cp++ = c;
+ *cp = '\0';
+ rcerror(buf);
+}
+
+static void
+cant(char *s)
+{
+ char buf[100];
+ strcpy(buf, "can't happen: ");
+ strcat(buf, s);
+ rcerror(buf);
+}
+
+static void
+pushand(Reinst *f, Reinst *l)
+{
+ if(andp >= &andstack[NSTACK])
+ cant("operand stack overflow");
+ andp->first = f;
+ andp->last = l;
+ andp++;
+}
+
+static void
+pushator(int t)
+{
+ if(atorp >= &atorstack[NSTACK])
+ cant("operator stack overflow");
+ *atorp++ = t;
+ *subidp++ = cursubid;
+}
+
+static Node*
+popand(int op)
+{
+ Reinst *inst;
+
+ if(andp <= &andstack[0]){
+ regerr2("missing operand for ", op);
+ inst = newinst(NOP);
+ pushand(inst,inst);
+ }
+ return --andp;
+}
+
+static int
+popator(void)
+{
+ if(atorp <= &atorstack[0])
+ cant("operator stack underflow");
+ --subidp;
+ return *--atorp;
+}
+
+static void
+evaluntil(int pri)
+{
+ Node *op1, *op2;
+ Reinst *inst1, *inst2;
+
+ while(pri==RBRA || atorp[-1]>=pri){
+ switch(popator()){
+ default:
+ rcerror("unknown operator in evaluntil");
+ break;
+ case LBRA: /* must have been RBRA */
+ op1 = popand('(');
+ inst2 = newinst(RBRA);
+ inst2->u1.subid = *subidp;
+ op1->last->u2.next = inst2;
+ inst1 = newinst(LBRA);
+ inst1->u1.subid = *subidp;
+ inst1->u2.next = op1->first;
+ pushand(inst1, inst2);
+ return;
+ case OR:
+ op2 = popand('|');
+ op1 = popand('|');
+ inst2 = newinst(NOP);
+ op2->last->u2.next = inst2;
+ op1->last->u2.next = inst2;
+ inst1 = newinst(OR);
+ inst1->u1.right = op1->first;
+ inst1->u2.left = op2->first;
+ pushand(inst1, inst2);
+ break;
+ case CAT:
+ op2 = popand(0);
+ op1 = popand(0);
+ op1->last->u2.next = op2->first;
+ pushand(op1->first, op2->last);
+ break;
+ case STAR:
+ op2 = popand('*');
+ inst1 = newinst(OR);
+ op2->last->u2.next = inst1;
+ inst1->u1.right = op2->first;
+ pushand(inst1, inst1);
+ break;
+ case PLUS:
+ op2 = popand('+');
+ inst1 = newinst(OR);
+ op2->last->u2.next = inst1;
+ inst1->u1.right = op2->first;
+ pushand(op2->first, inst1);
+ break;
+ case QUEST:
+ op2 = popand('?');
+ inst1 = newinst(OR);
+ inst2 = newinst(NOP);
+ inst1->u2.left = inst2;
+ inst1->u1.right = op2->first;
+ op2->last->u2.next = inst2;
+ pushand(inst1, inst2);
+ break;
+ }
+ }
+}
+
+static Reprog*
+optimize(Reprog *pp)
+{
+ Reinst *inst, *target;
+ int size;
+ Reprog *npp;
+ Reclass *cl;
+ int diff;
+
+ /*
+ * get rid of NOOP chains
+ */
+ for(inst=pp->firstinst; inst->type!=END; inst++){
+ target = inst->u2.next;
+ while(target->type == NOP)
+ target = target->u2.next;
+ inst->u2.next = target;
+ }
+
+ /*
+ * The original allocation is for an area larger than
+ * necessary. Reallocate to the actual space used
+ * and then relocate the code.
+ */
+ size = sizeof(Reprog) + (freep - pp->firstinst)*sizeof(Reinst);
+ npp = realloc(pp, size);
+ if(npp==0 || npp==pp)
+ return pp;
+ diff = (char *)npp - (char *)pp;
+ freep = (Reinst *)((char *)freep + diff);
+ for(inst=npp->firstinst; inst<freep; inst++){
+ switch(inst->type){
+ case OR:
+ case STAR:
+ case PLUS:
+ case QUEST:
+ *(char **)&inst->u1.right += diff;
+ break;
+ case CCLASS:
+ case NCCLASS:
+ *(char **)&inst->u1.right += diff;
+ cl = inst->u1.cp;
+ *(char **)&cl->end += diff;
+ break;
+ }
+ *(char **)&inst->u2.left += diff;
+ }
+ *(char **)&npp->startinst += diff;
+ return npp;
+}
+
+#ifdef DEBUG
+static void
+dumpstack(void){
+ Node *stk;
+ int *ip;
+
+ print("operators\n");
+ for(ip=atorstack; ip<atorp; ip++)
+ print("0%o\n", *ip);
+ print("operands\n");
+ for(stk=andstack; stk<andp; stk++)
+ print("0%o\t0%o\n", stk->first->type, stk->last->type);
+}
+
+static void
+dump(Reprog *pp)
+{
+ Reinst *l;
+ Rune *p;
+
+ l = pp->firstinst;
+ do{
+ print("%d:\t0%o\t%d\t%d", l-pp->firstinst, l->type,
+ l->u2.left-pp->firstinst, l->u1.right-pp->firstinst);
+ if(l->type == RUNE)
+ print("\t%C\n", l->u1.r);
+ else if(l->type == CCLASS || l->type == NCCLASS){
+ print("\t[");
+ if(l->type == NCCLASS)
+ print("^");
+ for(p = l->u1.cp->spans; p < l->u1.cp->end; p += 2)
+ if(p[0] == p[1])
+ print("%C", p[0]);
+ else
+ print("%C-%C", p[0], p[1]);
+ print("]\n");
+ } else
+ print("\n");
+ }while(l++->type);
+}
+#endif
+
+static Reclass*
+newclass(void)
+{
+ if(nclass >= NCLASS)
+ regerr2("too many character classes; limit", NCLASS+'0');
+ return &(classp[nclass++]);
+}
+
+static int
+nextc(Rune *rp)
+{
+ if(lexdone){
+ *rp = 0;
+ return 1;
+ }
+ exprp += chartorune(rp, exprp);
+ if(*rp == '\\'){
+ exprp += chartorune(rp, exprp);
+ return 1;
+ }
+ if(*rp == 0)
+ lexdone = 1;
+ return 0;
+}
+
+static int
+lex(int literal, int dot_type)
+{
+ int quoted;
+
+ quoted = nextc(&yyrune);
+ if(literal || quoted){
+ if(yyrune == 0)
+ return END;
+ return RUNE;
+ }
+
+ switch(yyrune){
+ case 0:
+ return END;
+ case '*':
+ return STAR;
+ case '?':
+ return QUEST;
+ case '+':
+ return PLUS;
+ case '|':
+ return OR;
+ case '.':
+ return dot_type;
+ case '(':
+ return LBRA;
+ case ')':
+ return RBRA;
+ case '^':
+ return BOL;
+ case '$':
+ return EOL;
+ case '[':
+ return bldcclass();
+ }
+ return RUNE;
+}
+
+static int
+bldcclass(void)
+{
+ int type;
+ Rune r[NCCRUNE];
+ Rune *p, *ep, *np;
+ Rune rune;
+ int quoted;
+
+ /* we have already seen the '[' */
+ type = CCLASS;
+ yyclassp = newclass();
+
+ /* look ahead for negation */
+ /* SPECIAL CASE!!! negated classes don't match \n */
+ ep = r;
+ quoted = nextc(&rune);
+ if(!quoted && rune == '^'){
+ type = NCCLASS;
+ quoted = nextc(&rune);
+ *ep++ = '\n';
+ *ep++ = '\n';
+ }
+
+ /* parse class into a set of spans */
+ for(; ep<&r[NCCRUNE];){
+ if(rune == 0){
+ rcerror("malformed '[]'");
+ return 0;
+ }
+ if(!quoted && rune == ']')
+ break;
+ if(!quoted && rune == '-'){
+ if(ep == r){
+ rcerror("malformed '[]'");
+ return 0;
+ }
+ quoted = nextc(&rune);
+ if((!quoted && rune == ']') || rune == 0){
+ rcerror("malformed '[]'");
+ return 0;
+ }
+ *(ep-1) = rune;
+ } else {
+ *ep++ = rune;
+ *ep++ = rune;
+ }
+ quoted = nextc(&rune);
+ }
+
+ /* sort on span start */
+ for(p = r; p < ep; p += 2){
+ for(np = p; np < ep; np += 2)
+ if(*np < *p){
+ rune = np[0];
+ np[0] = p[0];
+ p[0] = rune;
+ rune = np[1];
+ np[1] = p[1];
+ p[1] = rune;
+ }
+ }
+
+ /* merge spans */
+ np = yyclassp->spans;
+ p = r;
+ if(r == ep)
+ yyclassp->end = np;
+ else {
+ np[0] = *p++;
+ np[1] = *p++;
+ for(; p < ep; p += 2)
+ if(p[0] <= np[1]){
+ if(p[1] > np[1])
+ np[1] = p[1];
+ } else {
+ np += 2;
+ np[0] = p[0];
+ np[1] = p[1];
+ }
+ yyclassp->end = np+2;
+ }
+
+ return type;
+}
+
+static Reprog*
+regcomp1(char *s, int literal, int dot_type)
+{
+ int token;
+ Reprog *volatile pp;
+
+ /* get memory for the program */
+ pp = malloc(sizeof(Reprog) + 6*sizeof(Reinst)*strlen(s));
+ if(pp == 0){
+ regerror("out of memory");
+ return 0;
+ }
+ freep = pp->firstinst;
+ classp = pp->class;
+ errors = 0;
+
+ if(setjmp(regkaboom))
+ goto out;
+
+ /* go compile the sucker */
+ lexdone = 0;
+ exprp = s;
+ nclass = 0;
+ nbra = 0;
+ atorp = atorstack;
+ andp = andstack;
+ subidp = subidstack;
+ lastwasand = FALSE;
+ cursubid = 0;
+
+ /* Start with a low priority operator to prime parser */
+ pushator(START-1);
+ while((token = lex(literal, dot_type)) != END){
+ if((token&0300) == OPERATOR)
+ operator(token);
+ else
+ operand(token);
+ }
+
+ /* Close with a low priority operator */
+ evaluntil(START);
+
+ /* Force END */
+ operand(END);
+ evaluntil(START);
+#ifdef DEBUG
+ dumpstack();
+#endif
+ if(nbra)
+ rcerror("unmatched left paren");
+ --andp; /* points to first and only operand */
+ pp->startinst = andp->first;
+#ifdef DEBUG
+ dump(pp);
+#endif
+ pp = optimize(pp);
+#ifdef DEBUG
+ print("start: %d\n", andp->first-pp->firstinst);
+ dump(pp);
+#endif
+out:
+ if(errors){
+ free(pp);
+ pp = 0;
+ }
+ return pp;
+}
+
+extern Reprog*
+regcomp(char *s)
+{
+ return regcomp1(s, 0, ANY);
+}
+
+extern Reprog*
+regcomplit(char *s)
+{
+ return regcomp1(s, 1, ANY);
+}
+
+extern Reprog*
+regcompnl(char *s)
+{
+ return regcomp1(s, 0, ANYNL);
+}
diff --git a/lib9/regex/regcomp.h b/lib9/regex/regcomp.h
@@ -0,0 +1,74 @@
+/*
+ * substitution list
+ */
+#define uchar __reuchar
+typedef unsigned char uchar;
+#define nelem(x) (sizeof(x)/sizeof((x)[0]))
+
+#define NSUBEXP 32
+typedef struct Resublist Resublist;
+struct Resublist
+{
+ Resub m[NSUBEXP];
+};
+
+/* max character classes per program */
+extern Reprog RePrOg;
+#define NCLASS (sizeof(RePrOg.class)/sizeof(Reclass))
+
+/* max rune ranges per character class */
+#define NCCRUNE (sizeof(Reclass)/sizeof(Rune))
+
+/*
+ * Actions and Tokens (Reinst types)
+ *
+ * 02xx are operators, value == precedence
+ * 03xx are tokens, i.e. operands for operators
+ */
+#define RUNE 0177
+#define OPERATOR 0200 /* Bitmask of all operators */
+#define START 0200 /* Start, used for marker on stack */
+#define RBRA 0201 /* Right bracket, ) */
+#define LBRA 0202 /* Left bracket, ( */
+#define OR 0203 /* Alternation, | */
+#define CAT 0204 /* Concatentation, implicit operator */
+#define STAR 0205 /* Closure, * */
+#define PLUS 0206 /* a+ == aa* */
+#define QUEST 0207 /* a? == a|nothing, i.e. 0 or 1 a's */
+#define ANY 0300 /* Any character except newline, . */
+#define ANYNL 0301 /* Any character including newline, . */
+#define NOP 0302 /* No operation, internal use only */
+#define BOL 0303 /* Beginning of line, ^ */
+#define EOL 0304 /* End of line, $ */
+#define CCLASS 0305 /* Character class, [] */
+#define NCCLASS 0306 /* Negated character class, [] */
+#define END 0377 /* Terminate: match found */
+
+/*
+ * regexec execution lists
+ */
+#define LISTSIZE 10
+#define BIGLISTSIZE (10*LISTSIZE)
+typedef struct Relist Relist;
+struct Relist
+{
+ Reinst* inst; /* Reinstruction of the thread */
+ Resublist se; /* matched subexpressions in this thread */
+};
+typedef struct Reljunk Reljunk;
+struct Reljunk
+{
+ Relist* relist[2];
+ Relist* reliste[2];
+ int starttype;
+ Rune startchar;
+ char* starts;
+ char* eol;
+ Rune* rstarts;
+ Rune* reol;
+};
+
+extern Relist* _renewthread(Relist*, Reinst*, int, Resublist*);
+extern void _renewmatch(Resub*, int, Resublist*);
+extern Relist* _renewemptythread(Relist*, Reinst*, int, char*);
+extern Relist* _rrenewemptythread(Relist*, Reinst*, int, Rune*);
diff --git a/lib9/regex/regerror.c b/lib9/regex/regerror.c
@@ -0,0 +1,14 @@
+#include "lib9.h"
+#include "regexp9.h"
+
+void
+regerror(char *s)
+{
+ char buf[132];
+
+ strcpy(buf, "regerror: ");
+ strcat(buf, s);
+ strcat(buf, "\n");
+ write(2, buf, strlen(buf));
+ exits("regerr");
+}
diff --git a/lib9/regex/regexec.c b/lib9/regex/regexec.c
@@ -0,0 +1,231 @@
+#include "lib9.h"
+#include "regexp9.h"
+#include "regcomp.h"
+
+
+/*
+ * return 0 if no match
+ * >0 if a match
+ * <0 if we ran out of _relist space
+ */
+static int
+regexec1(Reprog *progp, /* program to run */
+ char *bol, /* string to run machine on */
+ Resub *mp, /* subexpression elements */
+ int ms, /* number of elements at mp */
+ Reljunk *j
+)
+{
+ int flag=0;
+ Reinst *inst;
+ Relist *tlp;
+ char *s;
+ int i, checkstart;
+ Rune r, *rp, *ep;
+ int n;
+ Relist* tl; /* This list, next list */
+ Relist* nl;
+ Relist* tle; /* ends of this and next list */
+ Relist* nle;
+ int match;
+ char *p;
+
+ match = 0;
+ checkstart = j->starttype;
+ if(mp)
+ for(i=0; i<ms; i++) {
+ mp[i].s.sp = 0;
+ mp[i].e.ep = 0;
+ }
+ j->relist[0][0].inst = 0;
+ j->relist[1][0].inst = 0;
+
+ /* Execute machine once for each character, including terminal NUL */
+ s = j->starts;
+ do{
+ /* fast check for first char */
+ if(checkstart) {
+ switch(j->starttype) {
+ case RUNE:
+ p = utfrune(s, j->startchar);
+ if(p == 0 || s == j->eol)
+ return match;
+ s = p;
+ break;
+ case BOL:
+ if(s == bol)
+ break;
+ p = utfrune(s, '\n');
+ if(p == 0 || s == j->eol)
+ return match;
+ s = p;
+ break;
+ }
+ }
+ r = *(uchar*)s;
+ if(r < Runeself)
+ n = 1;
+ else
+ n = chartorune(&r, s);
+
+ /* switch run lists */
+ tl = j->relist[flag];
+ tle = j->reliste[flag];
+ nl = j->relist[flag^=1];
+ nle = j->reliste[flag];
+ nl->inst = 0;
+
+ /* Add first instruction to current list */
+ if(match == 0)
+ _renewemptythread(tl, progp->startinst, ms, s);
+
+ /* Execute machine until current list is empty */
+ for(tlp=tl; tlp->inst; tlp++){ /* assignment = */
+ for(inst = tlp->inst; ; inst = inst->u2.next){
+ switch(inst->type){
+ case RUNE: /* regular character */
+ if(inst->u1.r == r){
+ if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
+ return -1;
+ }
+ break;
+ case LBRA:
+ tlp->se.m[inst->u1.subid].s.sp = s;
+ continue;
+ case RBRA:
+ tlp->se.m[inst->u1.subid].e.ep = s;
+ continue;
+ case ANY:
+ if(r != '\n')
+ if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
+ return -1;
+ break;
+ case ANYNL:
+ if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
+ return -1;
+ break;
+ case BOL:
+ if(s == bol || *(s-1) == '\n')
+ continue;
+ break;
+ case EOL:
+ if(s == j->eol || r == 0 || r == '\n')
+ continue;
+ break;
+ case CCLASS:
+ ep = inst->u1.cp->end;
+ for(rp = inst->u1.cp->spans; rp < ep; rp += 2)
+ if(r >= rp[0] && r <= rp[1]){
+ if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
+ return -1;
+ break;
+ }
+ break;
+ case NCCLASS:
+ ep = inst->u1.cp->end;
+ for(rp = inst->u1.cp->spans; rp < ep; rp += 2)
+ if(r >= rp[0] && r <= rp[1])
+ break;
+ if(rp == ep)
+ if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
+ return -1;
+ break;
+ case OR:
+ /* evaluate right choice later */
+ if(_renewthread(tlp, inst->u1.right, ms, &tlp->se) == tle)
+ return -1;
+ /* efficiency: advance and re-evaluate */
+ continue;
+ case END: /* Match! */
+ match = 1;
+ tlp->se.m[0].e.ep = s;
+ if(mp != 0)
+ _renewmatch(mp, ms, &tlp->se);
+ break;
+ }
+ break;
+ }
+ }
+ if(s == j->eol)
+ break;
+ checkstart = j->starttype && nl->inst==0;
+ s += n;
+ }while(r);
+ return match;
+}
+
+static int
+regexec2(Reprog *progp, /* program to run */
+ char *bol, /* string to run machine on */
+ Resub *mp, /* subexpression elements */
+ int ms, /* number of elements at mp */
+ Reljunk *j
+)
+{
+ int rv;
+ Relist *relist0, *relist1;
+
+ /* mark space */
+ relist0 = malloc(BIGLISTSIZE*sizeof(Relist));
+ if(relist0 == nil)
+ return -1;
+ relist1 = malloc(BIGLISTSIZE*sizeof(Relist));
+ if(relist1 == nil){
+ free(relist1);
+ return -1;
+ }
+ j->relist[0] = relist0;
+ j->relist[1] = relist1;
+ j->reliste[0] = relist0 + BIGLISTSIZE - 2;
+ j->reliste[1] = relist1 + BIGLISTSIZE - 2;
+
+ rv = regexec1(progp, bol, mp, ms, j);
+ free(relist0);
+ free(relist1);
+ return rv;
+}
+
+extern int
+regexec(Reprog *progp, /* program to run */
+ char *bol, /* string to run machine on */
+ Resub *mp, /* subexpression elements */
+ int ms) /* number of elements at mp */
+{
+ Reljunk j;
+ Relist relist0[LISTSIZE], relist1[LISTSIZE];
+ int rv;
+
+ /*
+ * use user-specified starting/ending location if specified
+ */
+ j.starts = bol;
+ j.eol = 0;
+ if(mp && ms>0){
+ if(mp->s.sp)
+ j.starts = mp->s.sp;
+ if(mp->e.ep)
+ j.eol = mp->e.ep;
+ }
+ j.starttype = 0;
+ j.startchar = 0;
+ if(progp->startinst->type == RUNE && progp->startinst->u1.r < Runeself) {
+ j.starttype = RUNE;
+ j.startchar = progp->startinst->u1.r;
+ }
+ if(progp->startinst->type == BOL)
+ j.starttype = BOL;
+
+ /* mark space */
+ j.relist[0] = relist0;
+ j.relist[1] = relist1;
+ j.reliste[0] = relist0 + nelem(relist0) - 2;
+ j.reliste[1] = relist1 + nelem(relist1) - 2;
+
+ rv = regexec1(progp, bol, mp, ms, &j);
+ if(rv >= 0)
+ return rv;
+ rv = regexec2(progp, bol, mp, ms, &j);
+ if(rv >= 0)
+ return rv;
+ return -1;
+}
diff --git a/lib9/regex/regsub.c b/lib9/regex/regsub.c
@@ -0,0 +1,63 @@
+#include "lib9.h"
+#include "regexp9.h"
+
+/* substitute into one string using the matches from the last regexec() */
+extern void
+regsub(char *sp, /* source string */
+ char *dp, /* destination string */
+ int dlen,
+ Resub *mp, /* subexpression elements */
+ int ms) /* number of elements pointed to by mp */
+{
+ char *ssp, *ep;
+ int i;
+
+ ep = dp+dlen-1;
+ while(*sp != '\0'){
+ if(*sp == '\\'){
+ switch(*++sp){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ i = *sp-'0';
+ if(mp[i].s.sp != 0 && mp!=0 && ms>i)
+ for(ssp = mp[i].s.sp;
+ ssp < mp[i].e.ep;
+ ssp++)
+ if(dp < ep)
+ *dp++ = *ssp;
+ break;
+ case '\\':
+ if(dp < ep)
+ *dp++ = '\\';
+ break;
+ case '\0':
+ sp--;
+ break;
+ default:
+ if(dp < ep)
+ *dp++ = *sp;
+ break;
+ }
+ }else if(*sp == '&'){
+ if(mp[0].s.sp != 0 && mp!=0 && ms>0)
+ if(mp[0].s.sp != 0)
+ for(ssp = mp[0].s.sp;
+ ssp < mp[0].e.ep; ssp++)
+ if(dp < ep)
+ *dp++ = *ssp;
+ }else{
+ if(dp < ep)
+ *dp++ = *sp;
+ }
+ sp++;
+ }
+ *dp = '\0';
+}
diff --git a/lib9/regex/rregexec.c b/lib9/regex/rregexec.c
@@ -0,0 +1,213 @@
+#include "lib9.h"
+#include "regexp9.h"
+#include "regcomp.h"
+
+/*
+ * return 0 if no match
+ * >0 if a match
+ * <0 if we ran out of _relist space
+ */
+static int
+rregexec1(Reprog *progp, /* program to run */
+ Rune *bol, /* string to run machine on */
+ Resub *mp, /* subexpression elements */
+ int ms, /* number of elements at mp */
+ Reljunk *j)
+{
+ int flag=0;
+ Reinst *inst;
+ Relist *tlp;
+ Rune *s;
+ int i, checkstart;
+ Rune r, *rp, *ep;
+ Relist* tl; /* This list, next list */
+ Relist* nl;
+ Relist* tle; /* ends of this and next list */
+ Relist* nle;
+ int match;
+
+ match = 0;
+ checkstart = j->startchar;
+ if(mp)
+ for(i=0; i<ms; i++) {
+ mp[i].s.rsp = 0;
+ mp[i].e.rep = 0;
+ }
+ j->relist[0][0].inst = 0;
+ j->relist[1][0].inst = 0;
+
+ /* Execute machine once for each character, including terminal NUL */
+ s = j->rstarts;
+ do{
+
+ /* fast check for first char */
+ if(checkstart) {
+ switch(j->starttype) {
+ case RUNE:
+ while(*s != j->startchar) {
+ if(*s == 0 || s == j->reol)
+ return match;
+ s++;
+ }
+ break;
+ case BOL:
+ if(s == bol)
+ break;
+ while(*s != '\n') {
+ if(*s == 0 || s == j->reol)
+ return match;
+ s++;
+ }
+ break;
+ }
+ }
+
+ r = *s;
+
+ /* switch run lists */
+ tl = j->relist[flag];
+ tle = j->reliste[flag];
+ nl = j->relist[flag^=1];
+ nle = j->reliste[flag];
+ nl->inst = 0;
+
+ /* Add first instruction to current list */
+ _rrenewemptythread(tl, progp->startinst, ms, s);
+
+ /* Execute machine until current list is empty */
+ for(tlp=tl; tlp->inst; tlp++){
+ for(inst=tlp->inst; ; inst = inst->u2.next){
+ switch(inst->type){
+ case RUNE: /* regular character */
+ if(inst->u1.r == r)
+ if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
+ return -1;
+ break;
+ case LBRA:
+ tlp->se.m[inst->u1.subid].s.rsp = s;
+ continue;
+ case RBRA:
+ tlp->se.m[inst->u1.subid].e.rep = s;
+ continue;
+ case ANY:
+ if(r != '\n')
+ if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
+ return -1;
+ break;
+ case ANYNL:
+ if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
+ return -1;
+ break;
+ case BOL:
+ if(s == bol || *(s-1) == '\n')
+ continue;
+ break;
+ case EOL:
+ if(s == j->reol || r == 0 || r == '\n')
+ continue;
+ break;
+ case CCLASS:
+ ep = inst->u1.cp->end;
+ for(rp = inst->u1.cp->spans; rp < ep; rp += 2)
+ if(r >= rp[0] && r <= rp[1]){
+ if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
+ return -1;
+ break;
+ }
+ break;
+ case NCCLASS:
+ ep = inst->u1.cp->end;
+ for(rp = inst->u1.cp->spans; rp < ep; rp += 2)
+ if(r >= rp[0] && r <= rp[1])
+ break;
+ if(rp == ep)
+ if(_renewthread(nl, inst->u2.next, ms, &tlp->se)==nle)
+ return -1;
+ break;
+ case OR:
+ /* evaluate right choice later */
+ if(_renewthread(tlp, inst->u1.right, ms, &tlp->se) == tle)
+ return -1;
+ /* efficiency: advance and re-evaluate */
+ continue;
+ case END: /* Match! */
+ match = 1;
+ tlp->se.m[0].e.rep = s;
+ if(mp != 0)
+ _renewmatch(mp, ms, &tlp->se);
+ break;
+ }
+ break;
+ }
+ }
+ if(s == j->reol)
+ break;
+ checkstart = j->startchar && nl->inst==0;
+ s++;
+ }while(r);
+ return match;
+}
+
+static int
+rregexec2(Reprog *progp, /* program to run */
+ Rune *bol, /* string to run machine on */
+ Resub *mp, /* subexpression elements */
+ int ms, /* number of elements at mp */
+ Reljunk *j
+)
+{
+ Relist relist0[5*LISTSIZE], relist1[5*LISTSIZE];
+
+ /* mark space */
+ j->relist[0] = relist0;
+ j->relist[1] = relist1;
+ j->reliste[0] = relist0 + nelem(relist0) - 2;
+ j->reliste[1] = relist1 + nelem(relist1) - 2;
+
+ return rregexec1(progp, bol, mp, ms, j);
+}
+
+extern int
+rregexec(Reprog *progp, /* program to run */
+ Rune *bol, /* string to run machine on */
+ Resub *mp, /* subexpression elements */
+ int ms) /* number of elements at mp */
+{
+ Reljunk j;
+ Relist relist0[LISTSIZE], relist1[LISTSIZE];
+ int rv;
+
+ /*
+ * use user-specified starting/ending location if specified
+ */
+ j.rstarts = bol;
+ j.reol = 0;
+ if(mp && ms>0){
+ if(mp->s.sp)
+ j.rstarts = mp->s.rsp;
+ if(mp->e.ep)
+ j.reol = mp->e.rep;
+ }
+ j.starttype = 0;
+ j.startchar = 0;
+ if(progp->startinst->type == RUNE && progp->startinst->u1.r < Runeself) {
+ j.starttype = RUNE;
+ j.startchar = progp->startinst->u1.r;
+ }
+ if(progp->startinst->type == BOL)
+ j.starttype = BOL;
+
+ /* mark space */
+ j.relist[0] = relist0;
+ j.relist[1] = relist1;
+ j.reliste[0] = relist0 + nelem(relist0) - 2;
+ j.reliste[1] = relist1 + nelem(relist1) - 2;
+
+ rv = rregexec1(progp, bol, mp, ms, &j);
+ if(rv >= 0)
+ return rv;
+ rv = rregexec2(progp, bol, mp, ms, &j);
+ if(rv >= 0)
+ return rv;
+ return -1;
+}
diff --git a/lib9/regex/rregsub.c b/lib9/regex/rregsub.c
@@ -0,0 +1,63 @@
+#include "lib9.h"
+#include "regexp9.h"
+
+/* substitute into one string using the matches from the last regexec() */
+extern void
+rregsub(Rune *sp, /* source string */
+ Rune *dp, /* destination string */
+ int dlen,
+ Resub *mp, /* subexpression elements */
+ int ms) /* number of elements pointed to by mp */
+{
+ Rune *ssp, *ep;
+ int i;
+
+ ep = dp+(dlen/sizeof(Rune))-1;
+ while(*sp != '\0'){
+ if(*sp == '\\'){
+ switch(*++sp){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ i = *sp-'0';
+ if(mp[i].s.rsp != 0 && mp!=0 && ms>i)
+ for(ssp = mp[i].s.rsp;
+ ssp < mp[i].e.rep;
+ ssp++)
+ if(dp < ep)
+ *dp++ = *ssp;
+ break;
+ case '\\':
+ if(dp < ep)
+ *dp++ = '\\';
+ break;
+ case '\0':
+ sp--;
+ break;
+ default:
+ if(dp < ep)
+ *dp++ = *sp;
+ break;
+ }
+ }else if(*sp == '&'){
+ if(mp[0].s.rsp != 0 && mp!=0 && ms>0)
+ if(mp[0].s.rsp != 0)
+ for(ssp = mp[0].s.rsp;
+ ssp < mp[0].e.rep; ssp++)
+ if(dp < ep)
+ *dp++ = *ssp;
+ }else{
+ if(dp < ep)
+ *dp++ = *sp;
+ }
+ sp++;
+ }
+ *dp = '\0';
+}
diff --git a/lib9/regex/test.c b/lib9/regex/test.c
@@ -0,0 +1,46 @@
+#include "lib9.h"
+#include <regexp9.h>
+
+struct x
+{
+ char *re;
+ char *s;
+ Reprog *p;
+};
+
+struct x t[] = {
+ { "^[^!@]+$", "/bin/upas/aliasmail '&'", 0 },
+ { "^local!(.*)$", "/mail/box/\\1/mbox", 0 },
+ { "^plan9!(.*)$", "\\1", 0 },
+ { "^helix!(.*)$", "\\1", 0 },
+ { "^([^!]+)@([^!@]+)$", "\\2!\\1", 0 },
+ { "^(uk\\.[^!]*)(!.*)$", "/bin/upas/uk2uk '\\1' '\\2'", 0 },
+ { "^[^!]*\\.[^!]*!.*$", "inet!&", 0 },
+ { "^\xE2\x98\xBA$", "smiley", 0 },
+ { "^(coma|research|pipe|pyxis|inet|hunny|gauss)!(.*)$", "/mail/lib/qmail '\\s' 'net!\\1' '\\2'", 0 },
+ { "^.*$", "/mail/lib/qmail '\\s' 'net!research' '&'", 0 },
+ { 0, 0, 0 },
+};
+
+main(int ac, char **av)
+{
+ Resub rs[10];
+ char dst[128];
+ int n;
+ struct x *tp;
+
+ for(tp = t; tp->re; tp++)
+ tp->p = regcomp(tp->re);
+
+
+ for(tp = t; tp->re; tp++){
+ print("%s VIA %s", av[1], tp->re);
+ memset(rs, 0, sizeof rs);
+ if(regexec(tp->p, av[1], rs, 10)){
+ regsub(tp->s, dst, sizeof dst, rs, 10);
+ print(" sub %s -> %s", tp->s, dst);
+ }
+ print("\n");
+ }
+ exit(0);
+}
diff --git a/lib9/regex/test2.c b/lib9/regex/test2.c
@@ -0,0 +1,20 @@
+#include "lib9.h"
+#include <regexp9.h>
+
+
+main(int ac, char **av)
+{
+ Resub rs[10];
+ Reprog *p;
+ char *s;
+ int i;
+
+ p = regcomp("[^a-z]");
+ s = "\n";
+ if(regexec(p, s, rs, 10))
+ print("%s %lux %lux %lux\n", s, s, rs[0].sp, rs[0].ep);
+ s = "0";
+ if(regexec(p, s, rs, 10))
+ print("%s %lux %lux %lux\n", s, s, rs[0].sp, rs[0].ep);
+ exit(0);
+}
diff --git a/lib9/regexp.7 b/lib9/regexp.7
@@ -0,0 +1,133 @@
+.TH REGEXP 7
+.SH NAME
+regexp \- Plan 9 regular expression notation
+.SH DESCRIPTION
+This manual page describes the regular expression
+syntax used by the Plan 9 regular expression library
+.IR regexp (3).
+It is the form used by
+.IR egrep (1)
+before
+.I egrep
+got complicated.
+.PP
+A
+.I "regular expression"
+specifies
+a set of strings of characters.
+A member of this set of strings is said to be
+.I matched
+by the regular expression. In many applications
+a delimiter character, commonly
+.LR / ,
+bounds a regular expression.
+In the following specification for regular expressions
+the word `character' means any character (rune) but newline.
+.PP
+The syntax for a regular expression
+.B e0
+is
+.IP
+.EX
+e3: literal | charclass | '.' | '^' | '$' | '(' e0 ')'
+
+e2: e3
+ | e2 REP
+
+REP: '*' | '+' | '?'
+
+e1: e2
+ | e1 e2
+
+e0: e1
+ | e0 '|' e1
+.EE
+.PP
+A
+.B literal
+is any non-metacharacter, or a metacharacter
+(one of
+.BR .*+?[]()|\e^$ ),
+or the delimiter
+preceded by
+.LR \e .
+.PP
+A
+.B charclass
+is a nonempty string
+.I s
+bracketed
+.BI [ \|s\| ]
+(or
+.BI [^ s\| ]\fR);
+it matches any character in (or not in)
+.IR s .
+A negated character class never
+matches newline.
+A substring
+.IB a - b\f1,
+with
+.I a
+and
+.I b
+in ascending
+order, stands for the inclusive
+range of
+characters between
+.I a
+and
+.IR b .
+In
+.IR s ,
+the metacharacters
+.LR - ,
+.LR ] ,
+an initial
+.LR ^ ,
+and the regular expression delimiter
+must be preceded by a
+.LR \e ;
+other metacharacters
+have no special meaning and
+may appear unescaped.
+.PP
+A
+.L .
+matches any character.
+.PP
+A
+.L ^
+matches the beginning of a line;
+.L $
+matches the end of the line.
+.PP
+The
+.B REP
+operators match zero or more
+.RB ( * ),
+one or more
+.RB ( + ),
+zero or one
+.RB ( ? ),
+instances respectively of the preceding regular expression
+.BR e2 .
+.PP
+A concatenated regular expression,
+.BR "e1\|e2" ,
+matches a match to
+.B e1
+followed by a match to
+.BR e2 .
+.PP
+An alternative regular expression,
+.BR "e0\||\|e1" ,
+matches either a match to
+.B e0
+or a match to
+.BR e1 .
+.PP
+A match to any part of a regular expression
+extends as far as possible without preventing
+a match to the remainder of the regular expression.
+.SH "SEE ALSO"
+.IR regexp (3)
diff --git a/lib9/regexp.h b/lib9/regexp.h
@@ -0,0 +1 @@
+#include <regexp9.h>
diff --git a/lib9/regexp9.h b/lib9/regexp9.h
@@ -0,0 +1,96 @@
+#ifndef _REGEXP9_H_
+#define _REGEXP9_H_ 1
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifdef AUTOLIB
+AUTOLIB(regexp9)
+#endif
+
+#include <utf.h>
+
+typedef struct Resub Resub;
+typedef struct Reclass Reclass;
+typedef struct Reinst Reinst;
+typedef struct Reprog Reprog;
+
+/*
+ * Sub expression matches
+ */
+struct Resub{
+ union
+ {
+ char *sp;
+ Rune *rsp;
+ }s;
+ union
+ {
+ char *ep;
+ Rune *rep;
+ }e;
+};
+
+/*
+ * character class, each pair of rune's defines a range
+ */
+struct Reclass{
+ Rune *end;
+ Rune spans[64];
+};
+
+/*
+ * Machine instructions
+ */
+struct Reinst{
+ int type;
+ union {
+ Reclass *cp; /* class pointer */
+ Rune r; /* character */
+ int subid; /* sub-expression id for RBRA and LBRA */
+ Reinst *right; /* right child of OR */
+ }u1;
+ union { /* regexp relies on these two being in the same union */
+ Reinst *left; /* left child of OR */
+ Reinst *next; /* next instruction for CAT & LBRA */
+ }u2;
+};
+
+/*
+ * Reprogram definition
+ */
+struct Reprog{
+ Reinst *startinst; /* start pc */
+ Reclass class[16]; /* .data */
+ Reinst firstinst[5]; /* .text */
+};
+
+extern Reprog *regcomp9(char*);
+extern Reprog *regcomplit9(char*);
+extern Reprog *regcompnl9(char*);
+extern void regerror9(char*);
+extern int regexec9(Reprog*, char*, Resub*, int);
+extern void regsub9(char*, char*, int, Resub*, int);
+
+extern int rregexec9(Reprog*, Rune*, Resub*, int);
+extern void rregsub9(Rune*, Rune*, int, Resub*, int);
+
+/*
+ * Darwin simply cannot handle having routines that
+ * override other library routines.
+ */
+#ifndef NOPLAN9DEFINES
+#define regcomp regcomp9
+#define regcomplit regcomplit9
+#define regcompnl regcompnl9
+#define regerror regerror9
+#define regexec regexec9
+#define regsub regsub9
+#define rregexec rregexec9
+#define rregsub rregsub9
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+#endif
diff --git a/lib9/rfork.c b/lib9/rfork.c
@@ -0,0 +1,124 @@
+#include <u.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <libc.h>
+#undef rfork
+
+static void
+nop(int x)
+{
+ USED(x);
+}
+
+int
+p9rfork(int flags)
+{
+ int pid, status;
+ int p[2];
+ int n;
+ char buf[128], *q;
+
+ if((flags&(RFPROC|RFFDG|RFMEM)) == (RFPROC|RFFDG)){
+ /* check other flags before we commit */
+ flags &= ~(RFPROC|RFFDG);
+ n = (flags & ~(RFNOTEG|RFNAMEG|RFNOWAIT));
+ if(n){
+ werrstr("unknown flags %08ux in rfork", n);
+ return -1;
+ }
+ if(flags&RFNOWAIT){
+ /*
+ * BUG - should put the signal handler back after we
+ * finish, but I just don't care. If a program calls with
+ * NOWAIT once, they're not likely to want child notes
+ * after that.
+ */
+ signal(SIGCHLD, nop);
+ if(pipe(p) < 0)
+ return -1;
+ }
+ pid = fork();
+ if(pid == -1)
+ return -1;
+ if(flags&RFNOWAIT){
+ flags &= ~RFNOWAIT;
+ if(pid){
+ /*
+ * Parent - wait for child to fork wait-free child.
+ * Then read pid from pipe. Assume pipe buffer can absorb the write.
+ */
+ close(p[1]);
+ status = 0;
+ if(wait4(pid, &status, 0, 0) < 0){
+ werrstr("pipe dance - wait4 - %r");
+ close(p[0]);
+ return -1;
+ }
+ n = readn(p[0], buf, sizeof buf-1);
+ close(p[0]);
+ if(!WIFEXITED(status) || WEXITSTATUS(status)!=0 || n <= 0){
+ if(!WIFEXITED(status))
+ werrstr("pipe dance - !exited 0x%ux", status);
+ else if(WEXITSTATUS(status) != 0)
+ werrstr("pipe dance - non-zero status 0x%ux", status);
+ else if(n < 0)
+ werrstr("pipe dance - pipe read error - %r");
+ else if(n == 0)
+ werrstr("pipe dance - pipe read eof");
+ else
+ werrstr("pipe dance - unknown failure");
+ return -1;
+ }
+ buf[n] = 0;
+ if(buf[0] == 'x'){
+ werrstr("%s", buf+2);
+ return -1;
+ }
+ pid = strtol(buf, &q, 0);
+ }else{
+ /*
+ * Child - fork a new child whose wait message can't
+ * get back to the parent because we're going to exit!
+ */
+ signal(SIGCHLD, SIG_IGN);
+ close(p[0]);
+ pid = fork();
+ if(pid){
+ /* Child parent - send status over pipe and exit. */
+ if(pid > 0)
+ fprint(p[1], "%d", pid);
+ else
+ fprint(p[1], "x %r");
+ close(p[1]);
+ _exit(0);
+ }else{
+ /* Child child - close pipe. */
+ close(p[1]);
+ }
+ }
+ }
+ if(pid != 0)
+ return pid;
+ }
+ if(flags&RFPROC){
+ werrstr("cannot use rfork for shared memory -- use ffork");
+ return -1;
+ }
+ if(flags&RFNAMEG){
+ /* XXX set $NAMESPACE to a new directory */
+ flags &= ~RFNAMEG;
+ }
+ if(flags&RFNOTEG){
+ setpgid(0, getpid());
+ flags &= ~RFNOTEG;
+ }
+ if(flags&RFNOWAIT){
+ werrstr("cannot use RFNOWAIT without RFPROC");
+ return -1;
+ }
+ if(flags){
+ werrstr("unknown flags %08ux in rfork", flags);
+ return -1;
+ }
+ return 0;
+}
diff --git a/lib9/searchpath.c b/lib9/searchpath.c
@@ -0,0 +1,62 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * Search $PATH for an executable with the given name.
+ * Like in rc, mid-name slashes do not disable search.
+ * Should probably handle escaped colons,
+ * but I don't know what the syntax is.
+ */
+char*
+searchpath(char *name)
+{
+ char *path, *p, *next;
+ char *s, *ss;
+ int ns, l;
+
+ s = nil;
+ ns = 0;
+ if((name[0] == '.' && name[1] == '/')
+ || (name[0] == '.' && name[1] == '.' && name[2] == '/')
+ || (name[0] == '/')){
+ if(access(name, AEXEC) >= 0)
+ return strdup(name);
+ return nil;
+ }
+
+ path = getenv("PATH");
+ for(p=path; p && *p; p=next){
+ if((next = strchr(p, ':')) != nil)
+ *next++ = 0;
+ if(*p == 0){
+ if(access(name, AEXEC) >= 0){
+ free(s);
+ free(path);
+ return strdup(name);
+ }
+ }else{
+ l = strlen(p)+1+strlen(name)+1;
+ if(l > ns){
+ ss = realloc(s, l);
+ if(ss == nil){
+ free(s);
+ free(path);
+ return nil;
+ }
+ s = ss;
+ ns = l;
+ }
+ strcpy(s, p);
+ strcat(s, "/");
+ strcat(s, name);
+ if(access(s, AEXEC) >= 0){
+ free(path);
+ return s;
+ }
+ }
+ }
+ free(s);
+ free(path);
+ return nil;
+}
+
diff --git a/lib9/seek.c b/lib9/seek.c
@@ -0,0 +1,8 @@
+#include <u.h>
+#include <libc.h>
+
+vlong
+seek(int fd, vlong offset, int whence)
+{
+ return lseek(fd, offset, whence);
+}
diff --git a/lib9/sendfd.c b/lib9/sendfd.c
@@ -0,0 +1,85 @@
+#include <u.h>
+#define NOPLAN9DEFINES
+#include <libc.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifndef CMSG_ALIGN
+# ifdef __sun__
+# define CMSG_ALIGN _CMSG_DATA_ALIGN
+# else
+# define CMSG_ALIGN(len) (((len)+sizeof(long)-1) & ~(sizeof(long)-1))
+# endif
+#endif
+
+#ifndef CMSG_SPACE
+# define CMSG_SPACE(len) (CMSG_ALIGN(sizeof(struct cmsghdr))+CMSG_ALIGN(len))
+#endif
+
+#ifndef CMSG_LEN
+# define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr))+(len))
+#endif
+
+int
+sendfd(int s, int fd)
+{
+ char buf[1];
+ struct iovec iov;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ int n;
+ char cms[CMSG_SPACE(sizeof(int))];
+
+ buf[0] = 0;
+ iov.iov_base = buf;
+ iov.iov_len = 1;
+
+ memset(&msg, 0, sizeof msg);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = (caddr_t)cms;
+ msg.msg_controllen = CMSG_LEN(sizeof(int));
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *(int*)CMSG_DATA(cmsg) = fd;
+
+ if((n=sendmsg(s, &msg, 0)) != iov.iov_len)
+ return -1;
+ return 0;
+}
+
+int
+recvfd(int s)
+{
+ int n;
+ int fd;
+ char buf[1];
+ struct iovec iov;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ char cms[CMSG_SPACE(sizeof(int))];
+
+ iov.iov_base = buf;
+ iov.iov_len = 1;
+
+ memset(&msg, 0, sizeof msg);
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ msg.msg_control = (caddr_t)cms;
+ msg.msg_controllen = sizeof cms;
+
+ if((n=recvmsg(s, &msg, 0)) < 0)
+ return -1;
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ fd = *(int*)CMSG_DATA(cmsg);
+ return fd;
+}
diff --git a/lib9/sleep.c b/lib9/sleep.c
@@ -0,0 +1,35 @@
+#include <u.h>
+#define NOPLAN9DEFINES
+#include <sys/time.h>
+#include <sched.h>
+#include <libc.h>
+
+int
+p9sleep(long milli)
+{
+ struct timeval tv;
+
+ if(milli == 0){
+ sched_yield();
+ return 0;
+ }
+
+ tv.tv_sec = milli/1000;
+ tv.tv_usec = (milli%1000)*1000;
+ return select(0, 0, 0, 0, &tv);
+}
+
+long
+p9alarm(ulong milli)
+{
+ struct itimerval itv;
+ struct itimerval oitv;
+
+ itv.it_interval.tv_sec = 0;
+ itv.it_interval.tv_usec = 0;
+ itv.it_value.tv_sec = milli/1000;
+ itv.it_value.tv_usec = (milli%1000)*1000;
+ if(setitimer(ITIMER_REAL, &itv, &oitv) < 0)
+ return -1;
+ return oitv.it_value.tv_sec*1000+oitv.it_value.tv_usec/1000;
+}
diff --git a/lib9/st4nkO6D b/lib9/st4nkO6D
@@ -0,0 +1 @@
+!<arch>
diff --git a/lib9/strdup.c b/lib9/strdup.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+strdup(char *s)
+{
+ char *t;
+ int l;
+
+ l = strlen(s);
+ t = malloc(l+1);
+ if(t == nil)
+ return nil;
+ memmove(t, s, l+1);
+ return t;
+}
+
diff --git a/lib9/strecpy.c b/lib9/strecpy.c
@@ -0,0 +1,16 @@
+#include <lib9.h>
+
+char*
+strecpy(char *to, char *e, char *from)
+{
+ if(to >= e)
+ return to;
+ to = memccpy(to, from, '\0', e - to);
+ if(to == nil){
+ to = e - 1;
+ *to = '\0';
+ }else{
+ to--;
+ }
+ return to;
+}
diff --git a/lib9/sysfatal.c b/lib9/sysfatal.c
@@ -0,0 +1,26 @@
+#include <lib9.h>
+#include <stdarg.h>
+#include "fmt.h"
+
+extern char *argv0;
+extern void __fixargv0(void);
+extern void exits(char*);
+void (*_sysfatal)(char*, ...);
+
+void
+sysfatal(char *fmt, ...)
+{
+ char buf[256];
+ va_list arg;
+
+ va_start(arg, fmt);
+ if(_sysfatal)
+ (*_sysfatal)(fmt, arg);
+ vseprint(buf, buf+sizeof buf, fmt, arg);
+ va_end(arg);
+
+ __fixargv0();
+ fprint(2, "%s: %s\n", argv0 ? argv0 : "<prog>", buf);
+ exits("fatal");
+}
+
diff --git a/lib9/syslog.c b/lib9/syslog.c
@@ -0,0 +1,119 @@
+#include <u.h>
+#include <libc.h>
+
+static struct
+{
+ int fd;
+ int consfd;
+ char *name;
+ Dir *d;
+ Dir *consd;
+ Lock lk;
+} sl =
+{
+ -1, -1,
+};
+
+static void
+_syslogopen(void)
+{
+ char buf[1024], *p;
+
+ if(sl.fd >= 0)
+ close(sl.fd);
+ snprint(buf, sizeof(buf), "#9/log/%s", sl.name);
+ p = unsharp(buf);
+ sl.fd = open(p, OWRITE|OCEXEC|OAPPEND);
+ free(p);
+}
+
+/*
+ * Print
+ * sysname: time: mesg
+ * on /sys/log/logname.
+ * If cons or log file can't be opened, print on the system console, too.
+ */
+void
+syslog(int cons, char *logname, char *fmt, ...)
+{
+ char buf[1024];
+ char *ctim, *p;
+ va_list arg;
+ int n;
+ Dir *d;
+ char err[ERRMAX];
+
+ err[0] = '\0';
+ errstr(err, sizeof err);
+ lock(&sl.lk);
+
+ /*
+ * paranoia makes us stat to make sure a fork+close
+ * hasn't broken our fd's
+ */
+ d = dirfstat(sl.fd);
+ if(sl.fd < 0
+ || sl.name == nil
+ || strcmp(sl.name, logname)!=0
+ || sl.d == nil
+ || d == nil
+ || d->dev != sl.d->dev
+ || d->type != sl.d->type
+ || d->qid.path != sl.d->qid.path){
+ free(sl.name);
+ sl.name = strdup(logname);
+ if(sl.name == nil)
+ cons = 1;
+ else{
+ _syslogopen();
+ if(sl.fd < 0)
+ cons = 1;
+ free(sl.d);
+ sl.d = d;
+ d = nil; /* don't free it */
+ }
+ }
+ free(d);
+ if(cons){
+ d = dirfstat(sl.consfd);
+ if(sl.consfd < 0
+ || d == nil
+ || sl.consd == nil
+ || d->dev != sl.consd->dev
+ || d->type != sl.consd->type
+ || d->qid.path != sl.consd->qid.path){
+ sl.consfd = open("/dev/tty", OWRITE|OCEXEC);
+ free(sl.consd);
+ sl.consd = d;
+ d = nil; /* don't free it */
+ }
+ free(d);
+ }
+
+ if(fmt == nil){
+ unlock(&sl.lk);
+ return;
+ }
+
+ ctim = ctime(time(0));
+ werrstr(err);
+ p = buf + snprint(buf, sizeof(buf)-1, "%s ", sysname());
+ strncpy(p, ctim+4, 15);
+ p += 15;
+ *p++ = ' ';
+ va_start(arg, fmt);
+ p = vseprint(p, buf+sizeof(buf)-1, fmt, arg);
+ va_end(arg);
+ *p++ = '\n';
+ n = p - buf;
+
+ if(sl.fd >= 0){
+ seek(sl.fd, 0, 2);
+ write(sl.fd, buf, n);
+ }
+
+ if(cons && sl.consfd >=0)
+ write(sl.consfd, buf, n);
+
+ unlock(&sl.lk);
+}
diff --git a/lib9/sysname.c b/lib9/sysname.c
@@ -0,0 +1,30 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+sysname(void)
+{
+ static char buf[512];
+ char *p, *q;
+
+ if(buf[0])
+ return buf;
+
+ if((q = getenv("sysname")) != nil && q[0] != 0){
+ utfecpy(buf, buf+sizeof buf, q);
+ free(q);
+ return buf;
+ }
+ if(q)
+ free(q);
+
+ if(gethostname(buf, sizeof buf) >= 0){
+ buf[sizeof buf-1] = 0;
+ if((p = strchr(buf, '.')) != nil)
+ *p = 0;
+ return buf;
+ }
+
+ strcpy(buf, "gnot");
+ return buf;
+}
diff --git a/lib9/testfork.c b/lib9/testfork.c
@@ -0,0 +1,21 @@
+#include <lib9.h>
+
+void
+sayhi(void *v)
+{
+ USED(v);
+
+ print("hello from subproc\n");
+ print("rendez got %lu from main\n", rendezvous(0x1234, 1234));
+ exits(0);
+}
+
+int
+main(int argc, char **argv)
+{
+ print("hello from main\n");
+ ffork(RFMEM|RFPROC, sayhi, nil);
+
+ print("rendez got %lu from subproc\n", rendezvous(0x1234, 0));
+ exits(0);
+}
diff --git a/lib9/time.c b/lib9/time.c
@@ -0,0 +1,58 @@
+#include <u.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/resource.h>
+#define NOPLAN9DEFINES
+#include <libc.h>
+
+long
+p9times(long *t)
+{
+ struct rusage ru, cru;
+
+ if(getrusage(0, &ru) < 0 || getrusage(-1, &cru) < 0)
+ return -1;
+
+ t[0] = ru.ru_utime.tv_sec*1000 + ru.ru_utime.tv_usec/1000;
+ t[1] = ru.ru_stime.tv_sec*1000 + ru.ru_stime.tv_usec/1000;
+ t[2] = cru.ru_utime.tv_sec*1000 + cru.ru_utime.tv_usec/1000;
+ t[3] = cru.ru_stime.tv_sec*1000 + cru.ru_stime.tv_usec/1000;
+
+ /* BUG */
+ return t[0]+t[1]+t[2]+t[3];
+}
+
+double
+p9cputime(void)
+{
+ long t[4];
+ double d;
+
+ if(p9times(t) < 0)
+ return -1.0;
+
+ d = (double)t[0]+(double)t[1]+(double)t[2]+(double)t[3];
+ return d/1000.0;
+}
+
+vlong
+p9nsec(void)
+{
+ struct timeval tv;
+
+ if(gettimeofday(&tv, 0) < 0)
+ return -1;
+
+ return (vlong)tv.tv_sec*1000*1000*1000 + tv.tv_usec*1000;
+}
+
+long
+p9time(long *tt)
+{
+ long t;
+ t = time(0);
+ if(tt)
+ *tt = t;
+ return t;
+}
+
diff --git a/lib9/tokenize.c b/lib9/tokenize.c
@@ -0,0 +1,106 @@
+#include <lib9.h>
+
+static char qsep[] = " \t\r\n";
+
+static char*
+qtoken(char *s, char *sep)
+{
+ int quoting;
+ char *t;
+
+ quoting = 0;
+ t = s; /* s is output string, t is input string */
+ while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){
+ if(*t != '\''){
+ *s++ = *t++;
+ continue;
+ }
+ /* *t is a quote */
+ if(!quoting){
+ quoting = 1;
+ t++;
+ continue;
+ }
+ /* quoting and we're on a quote */
+ if(t[1] != '\''){
+ /* end of quoted section; absorb closing quote */
+ t++;
+ quoting = 0;
+ continue;
+ }
+ /* doubled quote; fold one quote into two */
+ t++;
+ *s++ = *t++;
+ }
+ if(*s != '\0'){
+ *s = '\0';
+ if(t == s)
+ t++;
+ }
+ return t;
+}
+
+static char*
+etoken(char *t, char *sep)
+{
+ int quoting;
+
+ /* move to end of next token */
+ quoting = 0;
+ while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){
+ if(*t != '\''){
+ t++;
+ continue;
+ }
+ /* *t is a quote */
+ if(!quoting){
+ quoting = 1;
+ t++;
+ continue;
+ }
+ /* quoting and we're on a quote */
+ if(t[1] != '\''){
+ /* end of quoted section; absorb closing quote */
+ t++;
+ quoting = 0;
+ continue;
+ }
+ /* doubled quote; fold one quote into two */
+ t += 2;
+ }
+ return t;
+}
+
+int
+gettokens(char *s, char **args, int maxargs, char *sep)
+{
+ int nargs;
+
+ for(nargs=0; nargs<maxargs; nargs++){
+ while(*s!='\0' && utfrune(sep, *s)!=nil)
+ *s++ = '\0';
+ if(*s == '\0')
+ break;
+ args[nargs] = s;
+ s = etoken(s, sep);
+ }
+
+ return nargs;
+}
+
+int
+tokenize(char *s, char **args, int maxargs)
+{
+ int nargs;
+
+ for(nargs=0; nargs<maxargs; nargs++){
+ while(*s!='\0' && utfrune(qsep, *s)!=nil)
+ s++;
+ if(*s == '\0')
+ break;
+ args[nargs] = s;
+ s = qtoken(s, qsep);
+ }
+
+ return nargs;
+}
diff --git a/lib9/truerand.c b/lib9/truerand.c
@@ -0,0 +1,25 @@
+#include <u.h>
+#include <libc.h>
+
+ulong
+truerand(void)
+{
+ int i, n;
+ uchar buf[sizeof(ulong)];
+ static int randfd = -1;
+ static char *randfile;
+
+ if(randfd < 0){
+ randfd = open(randfile="/dev/random", OREAD);
+ /* OpenBSD lets you open /dev/random but not read it! */
+ if(randfd < 0 || read(randfd, buf, 1) != 1)
+ randfd = open(randfile="/dev/srandom", OREAD); /* OpenBSD */
+ if(randfd < 0)
+ sysfatal("can't open /dev/random: %r");
+ fcntl(randfd, F_SETFD, FD_CLOEXEC);
+ }
+ for(i=0; i<sizeof(buf); i += n)
+ if((n = readn(randfd, buf+i, sizeof(buf)-i)) < 0)
+ sysfatal("can't read %s: %r", randfile);
+ return *((ulong*)buf);
+}
diff --git a/lib9/u.h b/lib9/u.h
@@ -0,0 +1,170 @@
+#ifndef _U_H_
+#define _U_H_ 1
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define __BSD_VISIBLE 1 /* FreeBSD 5.x */
+#if defined(__sun__)
+# define __EXTENSIONS__ 1 /* SunOS */
+# if defined(__SunOS5_6__) || defined(__SunOS5_7__) || defined(__SunOS5_8__)
+ /* NOT USING #define __MAKECONTEXT_V2_SOURCE 1 / * SunOS */
+# else
+# define __MAKECONTEXT_V2_SOURCE 1
+# endif
+#endif
+#define _BSD_SOURCE 1
+#define _NETBSD_SOURCE 1 /* NetBSD */
+#define _SVID_SOURCE 1
+#if !defined(__APPLE__) && !defined(__OpenBSD__)
+# define _XOPEN_SOURCE 1000
+# define _XOPEN_SOURCE_EXTENDED 1
+#endif
+#define _LARGEFILE64_SOURCE 1
+#define _FILE_OFFSET_BITS 64
+
+#include <inttypes.h>
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <setjmp.h>
+#include <stddef.h>
+#include <utf.h>
+#include <fmt.h>
+#include <math.h>
+#include <ctype.h> /* for tolower */
+
+/*
+ * OS-specific crap
+ */
+#define _NEEDUCHAR 1
+#define _NEEDUSHORT 1
+#define _NEEDUINT 1
+#define _NEEDULONG 1
+
+typedef long p9jmp_buf[sizeof(sigjmp_buf)/sizeof(long)];
+
+#if defined(__linux__)
+# include <sys/types.h>
+# if defined(__Linux26__)
+# include <pthread.h>
+# define PLAN9PORT_USING_PTHREADS 1
+# endif
+# if defined(__USE_MISC)
+# undef _NEEDUSHORT
+# undef _NEEDUINT
+# undef _NEEDULONG
+# endif
+#elif defined(__sun__)
+# include <sys/types.h>
+# include <pthread.h>
+# define PLAN9PORT_USING_PTHREADS 1
+# undef _NEEDUSHORT
+# undef _NEEDUINT
+# undef _NEEDULONG
+# define nil 0 /* no cast to void* */
+#elif defined(__FreeBSD__)
+# include <sys/types.h>
+# include <osreldate.h>
+# if __FreeBSD_version >= 500000
+# define PLAN9PORT_USING_PTHREADS 1
+# include <pthread.h>
+# endif
+# if !defined(_POSIX_SOURCE)
+# undef _NEEDUSHORT
+# undef _NEEDUINT
+# endif
+#elif defined(__APPLE__)
+# include <sys/types.h>
+# include <pthread.h>
+# define PLAN9PORT_USING_PTHREADS 1
+# if __GNUC__ < 4
+# undef _NEEDUSHORT
+# undef _NEEDUINT
+# endif
+# undef _ANSI_SOURCE
+# undef _POSIX_C_SOURCE
+# undef _XOPEN_SOURCE
+# if !defined(NSIG)
+# define NSIG 32
+# endif
+# define _NEEDLL 1
+#elif defined(__NetBSD__)
+# include <sched.h>
+# include <sys/types.h>
+# undef _NEEDUSHORT
+# undef _NEEDUINT
+# undef _NEEDULONG
+#elif defined(__OpenBSD__)
+# include <sys/types.h>
+# undef _NEEDUSHORT
+# undef _NEEDUINT
+# undef _NEEDULONG
+#else
+ /* No idea what system this is -- try some defaults */
+# include <pthread.h>
+# define PLAN9PORT_USING_PTHREADS 1
+#endif
+
+#ifndef O_DIRECT
+#define O_DIRECT 0
+#endif
+
+typedef signed char schar;
+
+#ifdef _NEEDUCHAR
+ typedef unsigned char uchar;
+#endif
+#ifdef _NEEDUSHORT
+ typedef unsigned short ushort;
+#endif
+#ifdef _NEEDUINT
+ typedef unsigned int uint;
+#endif
+#ifdef _NEEDULONG
+ typedef unsigned long ulong;
+#endif
+typedef unsigned long long uvlong;
+typedef long long vlong;
+
+typedef uint64_t u64int;
+typedef int64_t s64int;
+typedef uint8_t u8int;
+typedef int8_t s8int;
+typedef uint16_t u16int;
+typedef int16_t s16int;
+typedef uintptr_t uintptr;
+typedef uint32_t u32int;
+typedef int32_t s32int;
+
+#undef _NEEDUCHAR
+#undef _NEEDUSHORT
+#undef _NEEDUINT
+#undef _NEEDULONG
+
+/*
+ * Funny-named symbols to tip off 9l to autolink.
+ */
+#define AUTOLIB(x) static int __p9l_autolib_ ## x = 1;
+
+/*
+ * Gcc is too smart for its own good.
+ */
+#if defined(__GNUC__)
+# if __GNUC__ >= 4 || (__GNUC__==3 && !defined(__APPLE_CC__))
+# undef AUTOLIB
+# define AUTOLIB(x) int __p9l_autolib_ ## x __attribute__ ((weak));
+# else
+# undef AUTOLIB
+# define AUTOLIB(x) static int __p9l_autolib_ ## x __attribute__ ((unused));
+# endif
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+#endif
diff --git a/lib9/u16.c b/lib9/u16.c
@@ -0,0 +1,52 @@
+#include <lib9.h>
+static char t16e[] = "0123456789ABCDEF";
+
+int
+dec16(uchar *out, int lim, char *in, int n)
+{
+ int c, w = 0, i = 0;
+ uchar *start = out;
+ uchar *eout = out + lim;
+
+ while(n-- > 0){
+ c = *in++;
+ if('0' <= c && c <= '9')
+ c = c - '0';
+ else if('a' <= c && c <= 'z')
+ c = c - 'a' + 10;
+ else if('A' <= c && c <= 'Z')
+ c = c - 'A' + 10;
+ else
+ continue;
+ w = (w<<4) + c;
+ i++;
+ if(i == 2){
+ if(out + 1 > eout)
+ goto exhausted;
+ *out++ = w;
+ w = 0;
+ i = 0;
+ }
+ }
+exhausted:
+ return out - start;
+}
+
+int
+enc16(char *out, int lim, uchar *in, int n)
+{
+ uint c;
+ char *eout = out + lim;
+ char *start = out;
+
+ while(n-- > 0){
+ c = *in++;
+ if(out + 2 >= eout)
+ goto exhausted;
+ *out++ = t16e[c>>4];
+ *out++ = t16e[c&0xf];
+ }
+exhausted:
+ *out = 0;
+ return out - start;
+}
diff --git a/lib9/u32.c b/lib9/u32.c
@@ -0,0 +1,109 @@
+#include <lib9.h>
+
+int
+dec32(uchar *dest, int ndest, char *src, int nsrc)
+{
+ char *s, *tab;
+ uchar *start;
+ int i, u[8];
+
+ if(ndest+1 < (5*nsrc+7)/8)
+ return -1;
+ start = dest;
+ tab = "23456789abcdefghijkmnpqrstuvwxyz";
+ while(nsrc>=8){
+ for(i=0; i<8; i++){
+ s = strchr(tab,(int)src[i]);
+ u[i] = s ? s-tab : 0;
+ }
+ *dest++ = (u[0]<<3) | (0x7 & (u[1]>>2));
+ *dest++ = ((0x3 & u[1])<<6) | (u[2]<<1) | (0x1 & (u[3]>>4));
+ *dest++ = ((0xf & u[3])<<4) | (0xf & (u[4]>>1));
+ *dest++ = ((0x1 & u[4])<<7) | (u[5]<<2) | (0x3 & (u[6]>>3));
+ *dest++ = ((0x7 & u[6])<<5) | u[7];
+ src += 8;
+ nsrc -= 8;
+ }
+ if(nsrc > 0){
+ if(nsrc == 1 || nsrc == 3 || nsrc == 6)
+ return -1;
+ for(i=0; i<nsrc; i++){
+ s = strchr(tab,(int)src[i]);
+ u[i] = s ? s-tab : 0;
+ }
+ *dest++ = (u[0]<<3) | (0x7 & (u[1]>>2));
+ if(nsrc == 2)
+ goto out;
+ *dest++ = ((0x3 & u[1])<<6) | (u[2]<<1) | (0x1 & (u[3]>>4));
+ if(nsrc == 4)
+ goto out;
+ *dest++ = ((0xf & u[3])<<4) | (0xf & (u[4]>>1));
+ if(nsrc == 5)
+ goto out;
+ *dest++ = ((0x1 & u[4])<<7) | (u[5]<<2) | (0x3 & (u[6]>>3));
+ }
+out:
+ return dest-start;
+}
+
+int
+enc32(char *dest, int ndest, uchar *src, int nsrc)
+{
+ char *tab, *start;
+ int j;
+
+ if(ndest <= (8*nsrc+4)/5 )
+ return -1;
+ start = dest;
+ tab = "23456789abcdefghijkmnpqrstuvwxyz";
+ while(nsrc>=5){
+ j = (0x1f & (src[0]>>3));
+ *dest++ = tab[j];
+ j = (0x1c & (src[0]<<2)) | (0x03 & (src[1]>>6));
+ *dest++ = tab[j];
+ j = (0x1f & (src[1]>>1));
+ *dest++ = tab[j];
+ j = (0x10 & (src[1]<<4)) | (0x0f & (src[2]>>4));
+ *dest++ = tab[j];
+ j = (0x1e & (src[2]<<1)) | (0x01 & (src[3]>>7));
+ *dest++ = tab[j];
+ j = (0x1f & (src[3]>>2));
+ *dest++ = tab[j];
+ j = (0x18 & (src[3]<<3)) | (0x07 & (src[4]>>5));
+ *dest++ = tab[j];
+ j = (0x1f & (src[4]));
+ *dest++ = tab[j];
+ src += 5;
+ nsrc -= 5;
+ }
+ if(nsrc){
+ j = (0x1f & (src[0]>>3));
+ *dest++ = tab[j];
+ j = (0x1c & (src[0]<<2));
+ if(nsrc == 1)
+ goto out;
+ j |= (0x03 & (src[1]>>6));
+ *dest++ = tab[j];
+ j = (0x1f & (src[1]>>1));
+ if(nsrc == 2)
+ goto out;
+ *dest++ = tab[j];
+ j = (0x10 & (src[1]<<4));
+ if(nsrc == 3)
+ goto out;
+ j |= (0x0f & (src[2]>>4));
+ *dest++ = tab[j];
+ j = (0x1e & (src[2]<<1));
+ if(nsrc == 4)
+ goto out;
+ j |= (0x01 & (src[3]>>7));
+ *dest++ = tab[j];
+ j = (0x1f & (src[3]>>2));
+ *dest++ = tab[j];
+ j = (0x18 & (src[3]<<3));
+out:
+ *dest++ = tab[j];
+ }
+ *dest = 0;
+ return dest-start;
+}
diff --git a/lib9/u64.c b/lib9/u64.c
@@ -0,0 +1,126 @@
+#include <lib9.h>
+
+enum {
+ INVAL= 255
+};
+
+static uchar t64d[256] = {
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL, 62,INVAL,INVAL,INVAL, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL, 0, 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,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL
+};
+static char t64e[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+int
+dec64(uchar *out, int lim, char *in, int n)
+{
+ ulong b24;
+ uchar *start = out;
+ uchar *e = out + lim;
+ int i, c;
+
+ b24 = 0;
+ i = 0;
+ while(n-- > 0){
+
+ c = t64d[*(uchar*)in++];
+ if(c == INVAL)
+ continue;
+ switch(i){
+ case 0:
+ b24 = c<<18;
+ break;
+ case 1:
+ b24 |= c<<12;
+ break;
+ case 2:
+ b24 |= c<<6;
+ break;
+ case 3:
+ if(out + 3 > e)
+ goto exhausted;
+
+ b24 |= c;
+ *out++ = b24>>16;
+ *out++ = b24>>8;
+ *out++ = b24;
+ i = -1;
+ break;
+ }
+ i++;
+ }
+ switch(i){
+ case 2:
+ if(out + 1 > e)
+ goto exhausted;
+ *out++ = b24>>16;
+ break;
+ case 3:
+ if(out + 2 > e)
+ goto exhausted;
+ *out++ = b24>>16;
+ *out++ = b24>>8;
+ break;
+ }
+exhausted:
+ return out - start;
+}
+
+int
+enc64(char *out, int lim, uchar *in, int n)
+{
+ int i;
+ ulong b24;
+ char *start = out;
+ char *e = out + lim;
+
+ for(i = n/3; i > 0; i--){
+ b24 = (*in++)<<16;
+ b24 |= (*in++)<<8;
+ b24 |= *in++;
+ if(out + 4 >= e)
+ goto exhausted;
+ *out++ = t64e[(b24>>18)];
+ *out++ = t64e[(b24>>12)&0x3f];
+ *out++ = t64e[(b24>>6)&0x3f];
+ *out++ = t64e[(b24)&0x3f];
+ }
+
+ switch(n%3){
+ case 2:
+ b24 = (*in++)<<16;
+ b24 |= (*in)<<8;
+ if(out + 4 >= e)
+ goto exhausted;
+ *out++ = t64e[(b24>>18)];
+ *out++ = t64e[(b24>>12)&0x3f];
+ *out++ = t64e[(b24>>6)&0x3f];
+ *out++ = '=';
+ break;
+ case 1:
+ b24 = (*in)<<16;
+ if(out + 4 >= e)
+ goto exhausted;
+ *out++ = t64e[(b24>>18)];
+ *out++ = t64e[(b24>>12)&0x3f];
+ *out++ = '=';
+ *out++ = '=';
+ break;
+ }
+exhausted:
+ *out = 0;
+ return out - start;
+}
diff --git a/lib9/udp.c b/lib9/udp.c
@@ -0,0 +1,52 @@
+#include <u.h>
+#define NOPLAN9DEFINES
+#include <libc.h>
+#include <ip.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+/*
+ * prefix of all v4 addresses
+ * copied from libip because libc cannot depend on libip
+ */
+static uchar v4prefix[IPaddrlen] = {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0xff, 0xff,
+ 0, 0, 0, 0
+};
+
+long
+udpread(int fd, Udphdr *hdr, void *buf, long n)
+{
+ struct sockaddr_in sin;
+ socklen_t len;
+
+ len = sizeof sin;
+ n = recvfrom(fd, buf, n, 0, (struct sockaddr*)&sin, &len);
+ if(len != sizeof sin){
+ werrstr("recvfrom acting weird");
+ return -1;
+ }
+ if(n < 0)
+ return -1;
+ memset(hdr, 0, sizeof *hdr);
+ memmove(hdr->raddr, v4prefix, IPaddrlen);
+ *(u32int*)(hdr->raddr+12) = *(u32int*)&sin.sin_addr;
+ *(u16int*)hdr->rport = *(u16int*)&sin.sin_port;
+ return n;
+}
+
+long
+udpwrite(int fd, Udphdr *hdr, void *buf, long n)
+{
+ struct sockaddr_in sin;
+
+ memset(&sin, 0, sizeof sin);
+ sin.sin_family = AF_INET;
+ *(u32int*)&sin.sin_addr = *(u32int*)(hdr->raddr+12);
+ *(u16int*)&sin.sin_port = *(u16int*)hdr->rport;
+ return sendto(fd, buf, n, 0, (struct sockaddr*)&sin, sizeof sin);
+}
+
diff --git a/lib9/unsharp.c b/lib9/unsharp.c
@@ -0,0 +1,44 @@
+#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*
+unsharp(char *old)
+{
+ char *new;
+ int i, olen, nlen, len;
+
+ if(replace[0].new == nil)
+ replace[0].new = get9root();
+
+ 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)
+ /* Most callers won't check the return value... */
+ sysfatal("out of memory translating %s to %s%s", old, replace[i].new, old+olen);
+ strcpy(new, replace[i].new);
+ strcpy(new+nlen, old+olen);
+ assert(strlen(new) == len);
+ return new;
+ }
+ return old;
+}
diff --git a/lib9/utf.h b/lib9/utf.h
@@ -0,0 +1,53 @@
+#ifndef _UTF_H_
+#define _UTF_H_ 1
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef unsigned short Rune; /* 16 bits */
+
+enum
+{
+ UTFmax = 3, /* maximum bytes per rune */
+ Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */
+ Runeself = 0x80, /* rune and UTF sequences are the same (<) */
+ Runeerror = 0xFFFD, /* decoding error in UTF */
+};
+
+/* Edit .+1,/^$/ | cfn $PLAN9/src/lib9/utf/?*.c | grep -v static |grep -v __ */
+int chartorune(Rune *rune, char *str);
+int fullrune(char *str, int n);
+int isalpharune(Rune c);
+int islowerrune(Rune c);
+int isspacerune(Rune c);
+int istitlerune(Rune c);
+int isupperrune(Rune c);
+int runelen(long c);
+int runenlen(Rune *r, int nrune);
+Rune* runestrcat(Rune *s1, Rune *s2);
+Rune* runestrchr(Rune *s, Rune c);
+int runestrcmp(Rune *s1, Rune *s2);
+Rune* runestrcpy(Rune *s1, Rune *s2);
+Rune* runestrdup(Rune *s) ;
+Rune* runestrecpy(Rune *s1, Rune *es1, Rune *s2);
+long runestrlen(Rune *s);
+Rune* runestrncat(Rune *s1, Rune *s2, long n);
+int runestrncmp(Rune *s1, Rune *s2, long n);
+Rune* runestrncpy(Rune *s1, Rune *s2, long n);
+Rune* runestrrchr(Rune *s, Rune c);
+Rune* runestrstr(Rune *s1, Rune *s2);
+int runetochar(char *str, Rune *rune);
+Rune tolowerrune(Rune c);
+Rune totitlerune(Rune c);
+Rune toupperrune(Rune c);
+char* utfecpy(char *to, char *e, char *from);
+int utflen(char *s);
+int utfnlen(char *s, long m);
+char* utfrrune(char *s, long c);
+char* utfrune(char *s, long c);
+char* utfutf(char *s1, char *s2);
+
+#if defined(__cplusplus)
+}
+#endif
+#endif
diff --git a/lib9/utf/LICENSE b/lib9/utf/LICENSE
@@ -0,0 +1,13 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 1998-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.
+ */
diff --git a/lib9/utf/plan9.h b/lib9/utf/plan9.h
@@ -0,0 +1,29 @@
+/*
+ * compiler directive on Plan 9
+ */
+#ifndef USED
+#define USED(x) if(x);else
+#endif
+
+/*
+ * easiest way to make sure these are defined
+ */
+#define uchar _utfuchar
+#define ushort _utfushort
+#define uint _utfuint
+#define ulong _utfulong
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+
+/*
+ * nil cannot be ((void*)0) on ANSI C,
+ * because it is used for function pointers
+ */
+#undef nil
+#define nil 0
+
+#undef nelem
+#define nelem(x) (sizeof (x)/sizeof (x)[0])
+
diff --git a/lib9/utf/portdate b/lib9/utf/portdate
@@ -0,0 +1,20 @@
+rune.c 2004/1225
+runestrcat.c 2004/1225
+runestrchr.c 2004/1225
+runestrcmp.c 2004/1225
+runestrcpy.c 2004/1225
+runestrdup.c 2004/1225
+runestrecpy.c 2004/1225
+runestrlen.c 2004/1225
+runestrncat.c 2004/1225
+runestrncmp.c 2004/1225
+runestrncpy.c 2004/1225
+runestrrchr.c 2004/1225
+runestrstr.c 2004/1225
+runetype.c 2004/1225
+utfecpy.c 2004/1225
+utflen.c 2004/1225
+utfnlen.c 2004/1225
+utfrrune.c 2004/1225
+utfrune.c 2004/1225
+utfutf.c 2004/1225
diff --git a/lib9/utf/rune.c b/lib9/utf/rune.c
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+enum
+{
+ Bit1 = 7,
+ Bitx = 6,
+ Bit2 = 5,
+ Bit3 = 4,
+ Bit4 = 3,
+
+ T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */
+ Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */
+ T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */
+ T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */
+ T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */
+
+ Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */
+ Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */
+ Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */
+
+ Maskx = (1<<Bitx)-1, /* 0011 1111 */
+ Testx = Maskx ^ 0xFF, /* 1100 0000 */
+
+ Bad = Runeerror,
+};
+
+int
+chartorune(Rune *rune, char *str)
+{
+ int c, c1, c2;
+ long l;
+
+ /*
+ * one character sequence
+ * 00000-0007F => T1
+ */
+ c = *(uchar*)str;
+ if(c < Tx) {
+ *rune = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ c1 = *(uchar*)(str+1) ^ Tx;
+ if(c1 & Testx)
+ goto bad;
+ if(c < T3) {
+ if(c < T2)
+ goto bad;
+ l = ((c << Bitx) | c1) & Rune2;
+ if(l <= Rune1)
+ goto bad;
+ *rune = l;
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ c2 = *(uchar*)(str+2) ^ Tx;
+ if(c2 & Testx)
+ goto bad;
+ if(c < T4) {
+ l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
+ if(l <= Rune2)
+ goto bad;
+ *rune = l;
+ return 3;
+ }
+
+ /*
+ * bad decoding
+ */
+bad:
+ *rune = Bad;
+ return 1;
+}
+
+int
+runetochar(char *str, Rune *rune)
+{
+ long c;
+
+ /*
+ * one character sequence
+ * 00000-0007F => 00-7F
+ */
+ c = *rune;
+ if(c <= Rune1) {
+ str[0] = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ if(c <= Rune2) {
+ str[0] = T2 | (c >> 1*Bitx);
+ str[1] = Tx | (c & Maskx);
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ str[0] = T3 | (c >> 2*Bitx);
+ str[1] = Tx | ((c >> 1*Bitx) & Maskx);
+ str[2] = Tx | (c & Maskx);
+ return 3;
+}
+
+int
+runelen(long c)
+{
+ Rune rune;
+ char str[10];
+
+ rune = c;
+ return runetochar(str, &rune);
+}
+
+int
+runenlen(Rune *r, int nrune)
+{
+ int nb, c;
+
+ nb = 0;
+ while(nrune--) {
+ c = *r++;
+ if(c <= Rune1)
+ nb++;
+ else
+ if(c <= Rune2)
+ nb += 2;
+ else
+ nb += 3;
+ }
+ return nb;
+}
+
+int
+fullrune(char *str, int n)
+{
+ int c;
+
+ if(n > 0) {
+ c = *(uchar*)str;
+ if(c < Tx)
+ return 1;
+ if(n > 1)
+ if(c < T3 || n > 2)
+ return 1;
+ }
+ return 0;
+}
diff --git a/lib9/utf/runestrcat.c b/lib9/utf/runestrcat.c
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+Rune*
+runestrcat(Rune *s1, Rune *s2)
+{
+
+ runestrcpy(runestrchr(s1, 0), s2);
+ return s1;
+}
diff --git a/lib9/utf/runestrchr.c b/lib9/utf/runestrchr.c
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+Rune*
+runestrchr(Rune *s, Rune c)
+{
+ Rune c0 = c;
+ Rune c1;
+
+ if(c == 0) {
+ while(*s++)
+ ;
+ return s-1;
+ }
+
+ while(c1 = *s++)
+ if(c1 == c0)
+ return s-1;
+ return 0;
+}
diff --git a/lib9/utf/runestrcmp.c b/lib9/utf/runestrcmp.c
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+int
+runestrcmp(Rune *s1, Rune *s2)
+{
+ Rune c1, c2;
+
+ for(;;) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if(c1 != c2) {
+ if(c1 > c2)
+ return 1;
+ return -1;
+ }
+ if(c1 == 0)
+ return 0;
+ }
+}
diff --git a/lib9/utf/runestrcpy.c b/lib9/utf/runestrcpy.c
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+Rune*
+runestrcpy(Rune *s1, Rune *s2)
+{
+ Rune *os1;
+
+ os1 = s1;
+ while(*s1++ = *s2++)
+ ;
+ return os1;
+}
diff --git a/lib9/utf/runestrdup.c b/lib9/utf/runestrdup.c
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include "plan9.h"
+#include "utf.h"
+
+Rune*
+runestrdup(Rune *s)
+{
+ Rune *ns;
+
+ ns = malloc(sizeof(Rune)*(runestrlen(s) + 1));
+ if(ns == 0)
+ return 0;
+
+ return runestrcpy(ns, s);
+}
diff --git a/lib9/utf/runestrecpy.c b/lib9/utf/runestrecpy.c
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+Rune*
+runestrecpy(Rune *s1, Rune *es1, Rune *s2)
+{
+ if(s1 >= es1)
+ return s1;
+
+ while(*s1++ = *s2++){
+ if(s1 == es1){
+ *--s1 = '\0';
+ break;
+ }
+ }
+ return s1;
+}
diff --git a/lib9/utf/runestrlen.c b/lib9/utf/runestrlen.c
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+long
+runestrlen(Rune *s)
+{
+
+ return runestrchr(s, 0) - s;
+}
diff --git a/lib9/utf/runestrncat.c b/lib9/utf/runestrncat.c
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+Rune*
+runestrncat(Rune *s1, Rune *s2, long n)
+{
+ Rune *os1;
+
+ os1 = s1;
+ s1 = runestrchr(s1, 0);
+ while(*s1++ = *s2++)
+ if(--n < 0) {
+ s1[-1] = 0;
+ break;
+ }
+ return os1;
+}
diff --git a/lib9/utf/runestrncmp.c b/lib9/utf/runestrncmp.c
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+int
+runestrncmp(Rune *s1, Rune *s2, long n)
+{
+ Rune c1, c2;
+
+ while(n > 0) {
+ c1 = *s1++;
+ c2 = *s2++;
+ n--;
+ if(c1 != c2) {
+ if(c1 > c2)
+ return 1;
+ return -1;
+ }
+ if(c1 == 0)
+ break;
+ }
+ return 0;
+}
diff --git a/lib9/utf/runestrncpy.c b/lib9/utf/runestrncpy.c
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+Rune*
+runestrncpy(Rune *s1, Rune *s2, long n)
+{
+ int i;
+ Rune *os1;
+
+ os1 = s1;
+ for(i = 0; i < n; i++)
+ if((*s1++ = *s2++) == 0) {
+ while(++i < n)
+ *s1++ = 0;
+ return os1;
+ }
+ return os1;
+}
diff --git a/lib9/utf/runestrrchr.c b/lib9/utf/runestrrchr.c
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+Rune*
+runestrrchr(Rune *s, Rune c)
+{
+ Rune *r;
+
+ if(c == 0)
+ return runestrchr(s, 0);
+ r = 0;
+ while(s = runestrchr(s, c))
+ r = s++;
+ return r;
+}
diff --git a/lib9/utf/runestrstr.c b/lib9/utf/runestrstr.c
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+/*
+ * Return pointer to first occurrence of s2 in s1,
+ * 0 if none
+ */
+Rune*
+runestrstr(Rune *s1, Rune *s2)
+{
+ Rune *p, *pa, *pb;
+ int c0, c;
+
+ c0 = *s2;
+ if(c0 == 0)
+ return s1;
+ s2++;
+ for(p=runestrchr(s1, c0); p; p=runestrchr(p+1, c0)) {
+ pa = p;
+ for(pb=s2;; pb++) {
+ c = *pb;
+ if(c == 0)
+ return p;
+ if(c != *++pa)
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/lib9/utf/runetype.c b/lib9/utf/runetype.c
@@ -0,0 +1,1151 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+/*
+ * alpha ranges -
+ * only covers ranges not in lower||upper
+ */
+static
+Rune __alpha2[] =
+{
+ 0x00d8, 0x00f6, /* Ø - ö */
+ 0x00f8, 0x01f5, /* ø - ǵ */
+ 0x0250, 0x02a8, /* ɐ - ʨ */
+ 0x038e, 0x03a1, /* Ύ - Ρ */
+ 0x03a3, 0x03ce, /* Σ - ώ */
+ 0x03d0, 0x03d6, /* ϐ - ϖ */
+ 0x03e2, 0x03f3, /* Ϣ - ϳ */
+ 0x0490, 0x04c4, /* Ґ - ӄ */
+ 0x0561, 0x0587, /* ա - և */
+ 0x05d0, 0x05ea, /* א - ת */
+ 0x05f0, 0x05f2, /* װ - ײ */
+ 0x0621, 0x063a, /* ء - غ */
+ 0x0640, 0x064a, /* ـ - ي */
+ 0x0671, 0x06b7, /* ٱ - ڷ */
+ 0x06ba, 0x06be, /* ں - ھ */
+ 0x06c0, 0x06ce, /* ۀ - ێ */
+ 0x06d0, 0x06d3, /* ې - ۓ */
+ 0x0905, 0x0939, /* अ - ह */
+ 0x0958, 0x0961, /* क़ - ॡ */
+ 0x0985, 0x098c, /* অ - ঌ */
+ 0x098f, 0x0990, /* এ - ঐ */
+ 0x0993, 0x09a8, /* ও - ন */
+ 0x09aa, 0x09b0, /* প - র */
+ 0x09b6, 0x09b9, /* শ - হ */
+ 0x09dc, 0x09dd, /* ড় - ঢ় */
+ 0x09df, 0x09e1, /* য় - ৡ */
+ 0x09f0, 0x09f1, /* ৰ - ৱ */
+ 0x0a05, 0x0a0a, /* ਅ - ਊ */
+ 0x0a0f, 0x0a10, /* ਏ - ਐ */
+ 0x0a13, 0x0a28, /* ਓ - ਨ */
+ 0x0a2a, 0x0a30, /* ਪ - ਰ */
+ 0x0a32, 0x0a33, /* ਲ - ਲ਼ */
+ 0x0a35, 0x0a36, /* ਵ - ਸ਼ */
+ 0x0a38, 0x0a39, /* ਸ - ਹ */
+ 0x0a59, 0x0a5c, /* ਖ਼ - ੜ */
+ 0x0a85, 0x0a8b, /* અ - ઋ */
+ 0x0a8f, 0x0a91, /* એ - ઑ */
+ 0x0a93, 0x0aa8, /* ઓ - ન */
+ 0x0aaa, 0x0ab0, /* પ - ર */
+ 0x0ab2, 0x0ab3, /* લ - ળ */
+ 0x0ab5, 0x0ab9, /* વ - હ */
+ 0x0b05, 0x0b0c, /* ଅ - ଌ */
+ 0x0b0f, 0x0b10, /* ଏ - ଐ */
+ 0x0b13, 0x0b28, /* ଓ - ନ */
+ 0x0b2a, 0x0b30, /* ପ - ର */
+ 0x0b32, 0x0b33, /* ଲ - ଳ */
+ 0x0b36, 0x0b39, /* ଶ - ହ */
+ 0x0b5c, 0x0b5d, /* ଡ଼ - ଢ଼ */
+ 0x0b5f, 0x0b61, /* ୟ - ୡ */
+ 0x0b85, 0x0b8a, /* அ - ஊ */
+ 0x0b8e, 0x0b90, /* எ - ஐ */
+ 0x0b92, 0x0b95, /* ஒ - க */
+ 0x0b99, 0x0b9a, /* ங - ச */
+ 0x0b9e, 0x0b9f, /* ஞ - ட */
+ 0x0ba3, 0x0ba4, /* ண - த */
+ 0x0ba8, 0x0baa, /* ந - ப */
+ 0x0bae, 0x0bb5, /* ம - வ */
+ 0x0bb7, 0x0bb9, /* ஷ - ஹ */
+ 0x0c05, 0x0c0c, /* అ - ఌ */
+ 0x0c0e, 0x0c10, /* ఎ - ఐ */
+ 0x0c12, 0x0c28, /* ఒ - న */
+ 0x0c2a, 0x0c33, /* ప - ళ */
+ 0x0c35, 0x0c39, /* వ - హ */
+ 0x0c60, 0x0c61, /* ౠ - ౡ */
+ 0x0c85, 0x0c8c, /* ಅ - ಌ */
+ 0x0c8e, 0x0c90, /* ಎ - ಐ */
+ 0x0c92, 0x0ca8, /* ಒ - ನ */
+ 0x0caa, 0x0cb3, /* ಪ - ಳ */
+ 0x0cb5, 0x0cb9, /* ವ - ಹ */
+ 0x0ce0, 0x0ce1, /* ೠ - ೡ */
+ 0x0d05, 0x0d0c, /* അ - ഌ */
+ 0x0d0e, 0x0d10, /* എ - ഐ */
+ 0x0d12, 0x0d28, /* ഒ - ന */
+ 0x0d2a, 0x0d39, /* പ - ഹ */
+ 0x0d60, 0x0d61, /* ൠ - ൡ */
+ 0x0e01, 0x0e30, /* ก - ะ */
+ 0x0e32, 0x0e33, /* า - ำ */
+ 0x0e40, 0x0e46, /* เ - ๆ */
+ 0x0e5a, 0x0e5b, /* ๚ - ๛ */
+ 0x0e81, 0x0e82, /* ກ - ຂ */
+ 0x0e87, 0x0e88, /* ງ - ຈ */
+ 0x0e94, 0x0e97, /* ດ - ທ */
+ 0x0e99, 0x0e9f, /* ນ - ຟ */
+ 0x0ea1, 0x0ea3, /* ມ - ຣ */
+ 0x0eaa, 0x0eab, /* ສ - ຫ */
+ 0x0ead, 0x0eae, /* ອ - ຮ */
+ 0x0eb2, 0x0eb3, /* າ - ຳ */
+ 0x0ec0, 0x0ec4, /* ເ - ໄ */
+ 0x0edc, 0x0edd, /* ໜ - ໝ */
+ 0x0f18, 0x0f19, /* ༘ - ༙ */
+ 0x0f40, 0x0f47, /* ཀ - ཇ */
+ 0x0f49, 0x0f69, /* ཉ - ཀྵ */
+ 0x10d0, 0x10f6, /* ა - ჶ */
+ 0x1100, 0x1159, /* ᄀ - ᅙ */
+ 0x115f, 0x11a2, /* ᅟ - ᆢ */
+ 0x11a8, 0x11f9, /* ᆨ - ᇹ */
+ 0x1e00, 0x1e9b, /* Ḁ - ẛ */
+ 0x1f50, 0x1f57, /* ὐ - ὗ */
+ 0x1f80, 0x1fb4, /* ᾀ - ᾴ */
+ 0x1fb6, 0x1fbc, /* ᾶ - ᾼ */
+ 0x1fc2, 0x1fc4, /* ῂ - ῄ */
+ 0x1fc6, 0x1fcc, /* ῆ - ῌ */
+ 0x1fd0, 0x1fd3, /* ῐ - ΐ */
+ 0x1fd6, 0x1fdb, /* ῖ - Ί */
+ 0x1fe0, 0x1fec, /* ῠ - Ῥ */
+ 0x1ff2, 0x1ff4, /* ῲ - ῴ */
+ 0x1ff6, 0x1ffc, /* ῶ - ῼ */
+ 0x210a, 0x2113, /* ℊ - ℓ */
+ 0x2115, 0x211d, /* ℕ - ℝ */
+ 0x2120, 0x2122, /* ℠ - ™ */
+ 0x212a, 0x2131, /* K - ℱ */
+ 0x2133, 0x2138, /* ℳ - ℸ */
+ 0x3041, 0x3094, /* ぁ - ゔ */
+ 0x30a1, 0x30fa, /* ァ - ヺ */
+ 0x3105, 0x312c, /* ㄅ - ㄬ */
+ 0x3131, 0x318e, /* ㄱ - ㆎ */
+ 0x3192, 0x319f, /* ㆒ - ㆟ */
+ 0x3260, 0x327b, /* ㉠ - ㉻ */
+ 0x328a, 0x32b0, /* ㊊ - ㊰ */
+ 0x32d0, 0x32fe, /* ㋐ - ㋾ */
+ 0x3300, 0x3357, /* ㌀ - ㍗ */
+ 0x3371, 0x3376, /* ㍱ - ㍶ */
+ 0x337b, 0x3394, /* ㍻ - ㎔ */
+ 0x3399, 0x339e, /* ㎙ - ㎞ */
+ 0x33a9, 0x33ad, /* ㎩ - ㎭ */
+ 0x33b0, 0x33c1, /* ㎰ - ㏁ */
+ 0x33c3, 0x33c5, /* ㏃ - ㏅ */
+ 0x33c7, 0x33d7, /* ㏇ - ㏗ */
+ 0x33d9, 0x33dd, /* ㏙ - ㏝ */
+ 0x4e00, 0x9fff, /* 一 - 鿿 */
+ 0xac00, 0xd7a3, /* 가 - 힣 */
+ 0xf900, 0xfb06, /* 豈 - st */
+ 0xfb13, 0xfb17, /* ﬓ - ﬗ */
+ 0xfb1f, 0xfb28, /* ײַ - ﬨ */
+ 0xfb2a, 0xfb36, /* שׁ - זּ */
+ 0xfb38, 0xfb3c, /* טּ - לּ */
+ 0xfb40, 0xfb41, /* נּ - סּ */
+ 0xfb43, 0xfb44, /* ףּ - פּ */
+ 0xfb46, 0xfbb1, /* צּ - ﮱ */
+ 0xfbd3, 0xfd3d, /* ﯓ - ﴽ */
+ 0xfd50, 0xfd8f, /* ﵐ - ﶏ */
+ 0xfd92, 0xfdc7, /* ﶒ - ﷇ */
+ 0xfdf0, 0xfdf9, /* ﷰ - ﷹ */
+ 0xfe70, 0xfe72, /* ﹰ - ﹲ */
+ 0xfe76, 0xfefc, /* ﹶ - ﻼ */
+ 0xff66, 0xff6f, /* ヲ - ッ */
+ 0xff71, 0xff9d, /* ア - ン */
+ 0xffa0, 0xffbe, /* ᅠ - ᄒ */
+ 0xffc2, 0xffc7, /* ᅡ - ᅦ */
+ 0xffca, 0xffcf, /* ᅧ - ᅬ */
+ 0xffd2, 0xffd7, /* ᅭ - ᅲ */
+ 0xffda, 0xffdc, /* ᅳ - ᅵ */
+};
+
+/*
+ * alpha singlets -
+ * only covers ranges not in lower||upper
+ */
+static
+Rune __alpha1[] =
+{
+ 0x00aa, /* ª */
+ 0x00b5, /* µ */
+ 0x00ba, /* º */
+ 0x03da, /* Ϛ */
+ 0x03dc, /* Ϝ */
+ 0x03de, /* Ϟ */
+ 0x03e0, /* Ϡ */
+ 0x06d5, /* ە */
+ 0x09b2, /* ল */
+ 0x0a5e, /* ਫ਼ */
+ 0x0a8d, /* ઍ */
+ 0x0ae0, /* ૠ */
+ 0x0b9c, /* ஜ */
+ 0x0cde, /* ೞ */
+ 0x0e4f, /* ๏ */
+ 0x0e84, /* ຄ */
+ 0x0e8a, /* ຊ */
+ 0x0e8d, /* ຍ */
+ 0x0ea5, /* ລ */
+ 0x0ea7, /* ວ */
+ 0x0eb0, /* ະ */
+ 0x0ebd, /* ຽ */
+ 0x1fbe, /* ι */
+ 0x207f, /* ⁿ */
+ 0x20a8, /* ₨ */
+ 0x2102, /* ℂ */
+ 0x2107, /* ℇ */
+ 0x2124, /* ℤ */
+ 0x2126, /* Ω */
+ 0x2128, /* ℨ */
+ 0xfb3e, /* מּ */
+ 0xfe74, /* ﹴ */
+};
+
+/*
+ * space ranges
+ */
+static
+Rune __space2[] =
+{
+ 0x0009, 0x000a, /* tab and newline */
+ 0x0020, 0x0020, /* space */
+ 0x00a0, 0x00a0, /* */
+ 0x2000, 0x200b, /* - */
+ 0x2028, 0x2029, /*
-
*/
+ 0x3000, 0x3000, /* */
+ 0xfeff, 0xfeff, /* */
+};
+
+/*
+ * lower case ranges
+ * 3rd col is conversion excess 500
+ */
+static
+Rune __toupper2[] =
+{
+ 0x0061, 0x007a, 468, /* a-z A-Z */
+ 0x00e0, 0x00f6, 468, /* à-ö À-Ö */
+ 0x00f8, 0x00fe, 468, /* ø-þ Ø-Þ */
+ 0x0256, 0x0257, 295, /* ɖ-ɗ Ɖ-Ɗ */
+ 0x0258, 0x0259, 298, /* ɘ-ə Ǝ-Ə */
+ 0x028a, 0x028b, 283, /* ʊ-ʋ Ʊ-Ʋ */
+ 0x03ad, 0x03af, 463, /* έ-ί Έ-Ί */
+ 0x03b1, 0x03c1, 468, /* α-ρ Α-Ρ */
+ 0x03c3, 0x03cb, 468, /* σ-ϋ Σ-Ϋ */
+ 0x03cd, 0x03ce, 437, /* ύ-ώ Ύ-Ώ */
+ 0x0430, 0x044f, 468, /* а-я А-Я */
+ 0x0451, 0x045c, 420, /* ё-ќ Ё-Ќ */
+ 0x045e, 0x045f, 420, /* ў-џ Ў-Џ */
+ 0x0561, 0x0586, 452, /* ա-ֆ Ա-Ֆ */
+ 0x1f00, 0x1f07, 508, /* ἀ-ἇ Ἀ-Ἇ */
+ 0x1f10, 0x1f15, 508, /* ἐ-ἕ Ἐ-Ἕ */
+ 0x1f20, 0x1f27, 508, /* ἠ-ἧ Ἠ-Ἧ */
+ 0x1f30, 0x1f37, 508, /* ἰ-ἷ Ἰ-Ἷ */
+ 0x1f40, 0x1f45, 508, /* ὀ-ὅ Ὀ-Ὅ */
+ 0x1f60, 0x1f67, 508, /* ὠ-ὧ Ὠ-Ὧ */
+ 0x1f70, 0x1f71, 574, /* ὰ-ά Ὰ-Ά */
+ 0x1f72, 0x1f75, 586, /* ὲ-ή Ὲ-Ή */
+ 0x1f76, 0x1f77, 600, /* ὶ-ί Ὶ-Ί */
+ 0x1f78, 0x1f79, 628, /* ὸ-ό Ὸ-Ό */
+ 0x1f7a, 0x1f7b, 612, /* ὺ-ύ Ὺ-Ύ */
+ 0x1f7c, 0x1f7d, 626, /* ὼ-ώ Ὼ-Ώ */
+ 0x1f80, 0x1f87, 508, /* ᾀ-ᾇ ᾈ-ᾏ */
+ 0x1f90, 0x1f97, 508, /* ᾐ-ᾗ ᾘ-ᾟ */
+ 0x1fa0, 0x1fa7, 508, /* ᾠ-ᾧ ᾨ-ᾯ */
+ 0x1fb0, 0x1fb1, 508, /* ᾰ-ᾱ Ᾰ-Ᾱ */
+ 0x1fd0, 0x1fd1, 508, /* ῐ-ῑ Ῐ-Ῑ */
+ 0x1fe0, 0x1fe1, 508, /* ῠ-ῡ Ῠ-Ῡ */
+ 0x2170, 0x217f, 484, /* ⅰ-ⅿ Ⅰ-Ⅿ */
+ 0x24d0, 0x24e9, 474, /* ⓐ-ⓩ Ⓐ-Ⓩ */
+ 0xff41, 0xff5a, 468, /* a-z A-Z */
+};
+
+/*
+ * lower case singlets
+ * 2nd col is conversion excess 500
+ */
+static
+Rune __toupper1[] =
+{
+ 0x00ff, 621, /* ÿ Ÿ */
+ 0x0101, 499, /* ā Ā */
+ 0x0103, 499, /* ă Ă */
+ 0x0105, 499, /* ą Ą */
+ 0x0107, 499, /* ć Ć */
+ 0x0109, 499, /* ĉ Ĉ */
+ 0x010b, 499, /* ċ Ċ */
+ 0x010d, 499, /* č Č */
+ 0x010f, 499, /* ď Ď */
+ 0x0111, 499, /* đ Đ */
+ 0x0113, 499, /* ē Ē */
+ 0x0115, 499, /* ĕ Ĕ */
+ 0x0117, 499, /* ė Ė */
+ 0x0119, 499, /* ę Ę */
+ 0x011b, 499, /* ě Ě */
+ 0x011d, 499, /* ĝ Ĝ */
+ 0x011f, 499, /* ğ Ğ */
+ 0x0121, 499, /* ġ Ġ */
+ 0x0123, 499, /* ģ Ģ */
+ 0x0125, 499, /* ĥ Ĥ */
+ 0x0127, 499, /* ħ Ħ */
+ 0x0129, 499, /* ĩ Ĩ */
+ 0x012b, 499, /* ī Ī */
+ 0x012d, 499, /* ĭ Ĭ */
+ 0x012f, 499, /* į Į */
+ 0x0131, 268, /* ı I */
+ 0x0133, 499, /* ij IJ */
+ 0x0135, 499, /* ĵ Ĵ */
+ 0x0137, 499, /* ķ Ķ */
+ 0x013a, 499, /* ĺ Ĺ */
+ 0x013c, 499, /* ļ Ļ */
+ 0x013e, 499, /* ľ Ľ */
+ 0x0140, 499, /* ŀ Ŀ */
+ 0x0142, 499, /* ł Ł */
+ 0x0144, 499, /* ń Ń */
+ 0x0146, 499, /* ņ Ņ */
+ 0x0148, 499, /* ň Ň */
+ 0x014b, 499, /* ŋ Ŋ */
+ 0x014d, 499, /* ō Ō */
+ 0x014f, 499, /* ŏ Ŏ */
+ 0x0151, 499, /* ő Ő */
+ 0x0153, 499, /* œ Œ */
+ 0x0155, 499, /* ŕ Ŕ */
+ 0x0157, 499, /* ŗ Ŗ */
+ 0x0159, 499, /* ř Ř */
+ 0x015b, 499, /* ś Ś */
+ 0x015d, 499, /* ŝ Ŝ */
+ 0x015f, 499, /* ş Ş */
+ 0x0161, 499, /* š Š */
+ 0x0163, 499, /* ţ Ţ */
+ 0x0165, 499, /* ť Ť */
+ 0x0167, 499, /* ŧ Ŧ */
+ 0x0169, 499, /* ũ Ũ */
+ 0x016b, 499, /* ū Ū */
+ 0x016d, 499, /* ŭ Ŭ */
+ 0x016f, 499, /* ů Ů */
+ 0x0171, 499, /* ű Ű */
+ 0x0173, 499, /* ų Ų */
+ 0x0175, 499, /* ŵ Ŵ */
+ 0x0177, 499, /* ŷ Ŷ */
+ 0x017a, 499, /* ź Ź */
+ 0x017c, 499, /* ż Ż */
+ 0x017e, 499, /* ž Ž */
+ 0x017f, 200, /* ſ S */
+ 0x0183, 499, /* ƃ Ƃ */
+ 0x0185, 499, /* ƅ Ƅ */
+ 0x0188, 499, /* ƈ Ƈ */
+ 0x018c, 499, /* ƌ Ƌ */
+ 0x0192, 499, /* ƒ Ƒ */
+ 0x0199, 499, /* ƙ Ƙ */
+ 0x01a1, 499, /* ơ Ơ */
+ 0x01a3, 499, /* ƣ Ƣ */
+ 0x01a5, 499, /* ƥ Ƥ */
+ 0x01a8, 499, /* ƨ Ƨ */
+ 0x01ad, 499, /* ƭ Ƭ */
+ 0x01b0, 499, /* ư Ư */
+ 0x01b4, 499, /* ƴ Ƴ */
+ 0x01b6, 499, /* ƶ Ƶ */
+ 0x01b9, 499, /* ƹ Ƹ */
+ 0x01bd, 499, /* ƽ Ƽ */
+ 0x01c5, 499, /* Dž DŽ */
+ 0x01c6, 498, /* dž DŽ */
+ 0x01c8, 499, /* Lj LJ */
+ 0x01c9, 498, /* lj LJ */
+ 0x01cb, 499, /* Nj NJ */
+ 0x01cc, 498, /* nj NJ */
+ 0x01ce, 499, /* ǎ Ǎ */
+ 0x01d0, 499, /* ǐ Ǐ */
+ 0x01d2, 499, /* ǒ Ǒ */
+ 0x01d4, 499, /* ǔ Ǔ */
+ 0x01d6, 499, /* ǖ Ǖ */
+ 0x01d8, 499, /* ǘ Ǘ */
+ 0x01da, 499, /* ǚ Ǚ */
+ 0x01dc, 499, /* ǜ Ǜ */
+ 0x01df, 499, /* ǟ Ǟ */
+ 0x01e1, 499, /* ǡ Ǡ */
+ 0x01e3, 499, /* ǣ Ǣ */
+ 0x01e5, 499, /* ǥ Ǥ */
+ 0x01e7, 499, /* ǧ Ǧ */
+ 0x01e9, 499, /* ǩ Ǩ */
+ 0x01eb, 499, /* ǫ Ǫ */
+ 0x01ed, 499, /* ǭ Ǭ */
+ 0x01ef, 499, /* ǯ Ǯ */
+ 0x01f2, 499, /* Dz DZ */
+ 0x01f3, 498, /* dz DZ */
+ 0x01f5, 499, /* ǵ Ǵ */
+ 0x01fb, 499, /* ǻ Ǻ */
+ 0x01fd, 499, /* ǽ Ǽ */
+ 0x01ff, 499, /* ǿ Ǿ */
+ 0x0201, 499, /* ȁ Ȁ */
+ 0x0203, 499, /* ȃ Ȃ */
+ 0x0205, 499, /* ȅ Ȅ */
+ 0x0207, 499, /* ȇ Ȇ */
+ 0x0209, 499, /* ȉ Ȉ */
+ 0x020b, 499, /* ȋ Ȋ */
+ 0x020d, 499, /* ȍ Ȍ */
+ 0x020f, 499, /* ȏ Ȏ */
+ 0x0211, 499, /* ȑ Ȑ */
+ 0x0213, 499, /* ȓ Ȓ */
+ 0x0215, 499, /* ȕ Ȕ */
+ 0x0217, 499, /* ȗ Ȗ */
+ 0x0253, 290, /* ɓ Ɓ */
+ 0x0254, 294, /* ɔ Ɔ */
+ 0x025b, 297, /* ɛ Ɛ */
+ 0x0260, 295, /* ɠ Ɠ */
+ 0x0263, 293, /* ɣ Ɣ */
+ 0x0268, 291, /* ɨ Ɨ */
+ 0x0269, 289, /* ɩ Ɩ */
+ 0x026f, 289, /* ɯ Ɯ */
+ 0x0272, 287, /* ɲ Ɲ */
+ 0x0283, 282, /* ʃ Ʃ */
+ 0x0288, 282, /* ʈ Ʈ */
+ 0x0292, 281, /* ʒ Ʒ */
+ 0x03ac, 462, /* ά Ά */
+ 0x03cc, 436, /* ό Ό */
+ 0x03d0, 438, /* ϐ Β */
+ 0x03d1, 443, /* ϑ Θ */
+ 0x03d5, 453, /* ϕ Φ */
+ 0x03d6, 446, /* ϖ Π */
+ 0x03e3, 499, /* ϣ Ϣ */
+ 0x03e5, 499, /* ϥ Ϥ */
+ 0x03e7, 499, /* ϧ Ϧ */
+ 0x03e9, 499, /* ϩ Ϩ */
+ 0x03eb, 499, /* ϫ Ϫ */
+ 0x03ed, 499, /* ϭ Ϭ */
+ 0x03ef, 499, /* ϯ Ϯ */
+ 0x03f0, 414, /* ϰ Κ */
+ 0x03f1, 420, /* ϱ Ρ */
+ 0x0461, 499, /* ѡ Ѡ */
+ 0x0463, 499, /* ѣ Ѣ */
+ 0x0465, 499, /* ѥ Ѥ */
+ 0x0467, 499, /* ѧ Ѧ */
+ 0x0469, 499, /* ѩ Ѩ */
+ 0x046b, 499, /* ѫ Ѫ */
+ 0x046d, 499, /* ѭ Ѭ */
+ 0x046f, 499, /* ѯ Ѯ */
+ 0x0471, 499, /* ѱ Ѱ */
+ 0x0473, 499, /* ѳ Ѳ */
+ 0x0475, 499, /* ѵ Ѵ */
+ 0x0477, 499, /* ѷ Ѷ */
+ 0x0479, 499, /* ѹ Ѹ */
+ 0x047b, 499, /* ѻ Ѻ */
+ 0x047d, 499, /* ѽ Ѽ */
+ 0x047f, 499, /* ѿ Ѿ */
+ 0x0481, 499, /* ҁ Ҁ */
+ 0x0491, 499, /* ґ Ґ */
+ 0x0493, 499, /* ғ Ғ */
+ 0x0495, 499, /* ҕ Ҕ */
+ 0x0497, 499, /* җ Җ */
+ 0x0499, 499, /* ҙ Ҙ */
+ 0x049b, 499, /* қ Қ */
+ 0x049d, 499, /* ҝ Ҝ */
+ 0x049f, 499, /* ҟ Ҟ */
+ 0x04a1, 499, /* ҡ Ҡ */
+ 0x04a3, 499, /* ң Ң */
+ 0x04a5, 499, /* ҥ Ҥ */
+ 0x04a7, 499, /* ҧ Ҧ */
+ 0x04a9, 499, /* ҩ Ҩ */
+ 0x04ab, 499, /* ҫ Ҫ */
+ 0x04ad, 499, /* ҭ Ҭ */
+ 0x04af, 499, /* ү Ү */
+ 0x04b1, 499, /* ұ Ұ */
+ 0x04b3, 499, /* ҳ Ҳ */
+ 0x04b5, 499, /* ҵ Ҵ */
+ 0x04b7, 499, /* ҷ Ҷ */
+ 0x04b9, 499, /* ҹ Ҹ */
+ 0x04bb, 499, /* һ Һ */
+ 0x04bd, 499, /* ҽ Ҽ */
+ 0x04bf, 499, /* ҿ Ҿ */
+ 0x04c2, 499, /* ӂ Ӂ */
+ 0x04c4, 499, /* ӄ Ӄ */
+ 0x04c8, 499, /* ӈ Ӈ */
+ 0x04cc, 499, /* ӌ Ӌ */
+ 0x04d1, 499, /* ӑ Ӑ */
+ 0x04d3, 499, /* ӓ Ӓ */
+ 0x04d5, 499, /* ӕ Ӕ */
+ 0x04d7, 499, /* ӗ Ӗ */
+ 0x04d9, 499, /* ә Ә */
+ 0x04db, 499, /* ӛ Ӛ */
+ 0x04dd, 499, /* ӝ Ӝ */
+ 0x04df, 499, /* ӟ Ӟ */
+ 0x04e1, 499, /* ӡ Ӡ */
+ 0x04e3, 499, /* ӣ Ӣ */
+ 0x04e5, 499, /* ӥ Ӥ */
+ 0x04e7, 499, /* ӧ Ӧ */
+ 0x04e9, 499, /* ө Ө */
+ 0x04eb, 499, /* ӫ Ӫ */
+ 0x04ef, 499, /* ӯ Ӯ */
+ 0x04f1, 499, /* ӱ Ӱ */
+ 0x04f3, 499, /* ӳ Ӳ */
+ 0x04f5, 499, /* ӵ Ӵ */
+ 0x04f9, 499, /* ӹ Ӹ */
+ 0x1e01, 499, /* ḁ Ḁ */
+ 0x1e03, 499, /* ḃ Ḃ */
+ 0x1e05, 499, /* ḅ Ḅ */
+ 0x1e07, 499, /* ḇ Ḇ */
+ 0x1e09, 499, /* ḉ Ḉ */
+ 0x1e0b, 499, /* ḋ Ḋ */
+ 0x1e0d, 499, /* ḍ Ḍ */
+ 0x1e0f, 499, /* ḏ Ḏ */
+ 0x1e11, 499, /* ḑ Ḑ */
+ 0x1e13, 499, /* ḓ Ḓ */
+ 0x1e15, 499, /* ḕ Ḕ */
+ 0x1e17, 499, /* ḗ Ḗ */
+ 0x1e19, 499, /* ḙ Ḙ */
+ 0x1e1b, 499, /* ḛ Ḛ */
+ 0x1e1d, 499, /* ḝ Ḝ */
+ 0x1e1f, 499, /* ḟ Ḟ */
+ 0x1e21, 499, /* ḡ Ḡ */
+ 0x1e23, 499, /* ḣ Ḣ */
+ 0x1e25, 499, /* ḥ Ḥ */
+ 0x1e27, 499, /* ḧ Ḧ */
+ 0x1e29, 499, /* ḩ Ḩ */
+ 0x1e2b, 499, /* ḫ Ḫ */
+ 0x1e2d, 499, /* ḭ Ḭ */
+ 0x1e2f, 499, /* ḯ Ḯ */
+ 0x1e31, 499, /* ḱ Ḱ */
+ 0x1e33, 499, /* ḳ Ḳ */
+ 0x1e35, 499, /* ḵ Ḵ */
+ 0x1e37, 499, /* ḷ Ḷ */
+ 0x1e39, 499, /* ḹ Ḹ */
+ 0x1e3b, 499, /* ḻ Ḻ */
+ 0x1e3d, 499, /* ḽ Ḽ */
+ 0x1e3f, 499, /* ḿ Ḿ */
+ 0x1e41, 499, /* ṁ Ṁ */
+ 0x1e43, 499, /* ṃ Ṃ */
+ 0x1e45, 499, /* ṅ Ṅ */
+ 0x1e47, 499, /* ṇ Ṇ */
+ 0x1e49, 499, /* ṉ Ṉ */
+ 0x1e4b, 499, /* ṋ Ṋ */
+ 0x1e4d, 499, /* ṍ Ṍ */
+ 0x1e4f, 499, /* ṏ Ṏ */
+ 0x1e51, 499, /* ṑ Ṑ */
+ 0x1e53, 499, /* ṓ Ṓ */
+ 0x1e55, 499, /* ṕ Ṕ */
+ 0x1e57, 499, /* ṗ Ṗ */
+ 0x1e59, 499, /* ṙ Ṙ */
+ 0x1e5b, 499, /* ṛ Ṛ */
+ 0x1e5d, 499, /* ṝ Ṝ */
+ 0x1e5f, 499, /* ṟ Ṟ */
+ 0x1e61, 499, /* ṡ Ṡ */
+ 0x1e63, 499, /* ṣ Ṣ */
+ 0x1e65, 499, /* ṥ Ṥ */
+ 0x1e67, 499, /* ṧ Ṧ */
+ 0x1e69, 499, /* ṩ Ṩ */
+ 0x1e6b, 499, /* ṫ Ṫ */
+ 0x1e6d, 499, /* ṭ Ṭ */
+ 0x1e6f, 499, /* ṯ Ṯ */
+ 0x1e71, 499, /* ṱ Ṱ */
+ 0x1e73, 499, /* ṳ Ṳ */
+ 0x1e75, 499, /* ṵ Ṵ */
+ 0x1e77, 499, /* ṷ Ṷ */
+ 0x1e79, 499, /* ṹ Ṹ */
+ 0x1e7b, 499, /* ṻ Ṻ */
+ 0x1e7d, 499, /* ṽ Ṽ */
+ 0x1e7f, 499, /* ṿ Ṿ */
+ 0x1e81, 499, /* ẁ Ẁ */
+ 0x1e83, 499, /* ẃ Ẃ */
+ 0x1e85, 499, /* ẅ Ẅ */
+ 0x1e87, 499, /* ẇ Ẇ */
+ 0x1e89, 499, /* ẉ Ẉ */
+ 0x1e8b, 499, /* ẋ Ẋ */
+ 0x1e8d, 499, /* ẍ Ẍ */
+ 0x1e8f, 499, /* ẏ Ẏ */
+ 0x1e91, 499, /* ẑ Ẑ */
+ 0x1e93, 499, /* ẓ Ẓ */
+ 0x1e95, 499, /* ẕ Ẕ */
+ 0x1ea1, 499, /* ạ Ạ */
+ 0x1ea3, 499, /* ả Ả */
+ 0x1ea5, 499, /* ấ Ấ */
+ 0x1ea7, 499, /* ầ Ầ */
+ 0x1ea9, 499, /* ẩ Ẩ */
+ 0x1eab, 499, /* ẫ Ẫ */
+ 0x1ead, 499, /* ậ Ậ */
+ 0x1eaf, 499, /* ắ Ắ */
+ 0x1eb1, 499, /* ằ Ằ */
+ 0x1eb3, 499, /* ẳ Ẳ */
+ 0x1eb5, 499, /* ẵ Ẵ */
+ 0x1eb7, 499, /* ặ Ặ */
+ 0x1eb9, 499, /* ẹ Ẹ */
+ 0x1ebb, 499, /* ẻ Ẻ */
+ 0x1ebd, 499, /* ẽ Ẽ */
+ 0x1ebf, 499, /* ế Ế */
+ 0x1ec1, 499, /* ề Ề */
+ 0x1ec3, 499, /* ể Ể */
+ 0x1ec5, 499, /* ễ Ễ */
+ 0x1ec7, 499, /* ệ Ệ */
+ 0x1ec9, 499, /* ỉ Ỉ */
+ 0x1ecb, 499, /* ị Ị */
+ 0x1ecd, 499, /* ọ Ọ */
+ 0x1ecf, 499, /* ỏ Ỏ */
+ 0x1ed1, 499, /* ố Ố */
+ 0x1ed3, 499, /* ồ Ồ */
+ 0x1ed5, 499, /* ổ Ổ */
+ 0x1ed7, 499, /* ỗ Ỗ */
+ 0x1ed9, 499, /* ộ Ộ */
+ 0x1edb, 499, /* ớ Ớ */
+ 0x1edd, 499, /* ờ Ờ */
+ 0x1edf, 499, /* ở Ở */
+ 0x1ee1, 499, /* ỡ Ỡ */
+ 0x1ee3, 499, /* ợ Ợ */
+ 0x1ee5, 499, /* ụ Ụ */
+ 0x1ee7, 499, /* ủ Ủ */
+ 0x1ee9, 499, /* ứ Ứ */
+ 0x1eeb, 499, /* ừ Ừ */
+ 0x1eed, 499, /* ử Ử */
+ 0x1eef, 499, /* ữ Ữ */
+ 0x1ef1, 499, /* ự Ự */
+ 0x1ef3, 499, /* ỳ Ỳ */
+ 0x1ef5, 499, /* ỵ Ỵ */
+ 0x1ef7, 499, /* ỷ Ỷ */
+ 0x1ef9, 499, /* ỹ Ỹ */
+ 0x1f51, 508, /* ὑ Ὑ */
+ 0x1f53, 508, /* ὓ Ὓ */
+ 0x1f55, 508, /* ὕ Ὕ */
+ 0x1f57, 508, /* ὗ Ὗ */
+ 0x1fb3, 509, /* ᾳ ᾼ */
+ 0x1fc3, 509, /* ῃ ῌ */
+ 0x1fe5, 507, /* ῥ Ῥ */
+ 0x1ff3, 509, /* ῳ ῼ */
+};
+
+/*
+ * upper case ranges
+ * 3rd col is conversion excess 500
+ */
+static
+Rune __tolower2[] =
+{
+ 0x0041, 0x005a, 532, /* A-Z a-z */
+ 0x00c0, 0x00d6, 532, /* À-Ö à-ö */
+ 0x00d8, 0x00de, 532, /* Ø-Þ ø-þ */
+ 0x0189, 0x018a, 705, /* Ɖ-Ɗ ɖ-ɗ */
+ 0x018e, 0x018f, 702, /* Ǝ-Ə ɘ-ə */
+ 0x01b1, 0x01b2, 717, /* Ʊ-Ʋ ʊ-ʋ */
+ 0x0388, 0x038a, 537, /* Έ-Ί έ-ί */
+ 0x038e, 0x038f, 563, /* Ύ-Ώ ύ-ώ */
+ 0x0391, 0x03a1, 532, /* Α-Ρ α-ρ */
+ 0x03a3, 0x03ab, 532, /* Σ-Ϋ σ-ϋ */
+ 0x0401, 0x040c, 580, /* Ё-Ќ ё-ќ */
+ 0x040e, 0x040f, 580, /* Ў-Џ ў-џ */
+ 0x0410, 0x042f, 532, /* А-Я а-я */
+ 0x0531, 0x0556, 548, /* Ա-Ֆ ա-ֆ */
+ 0x10a0, 0x10c5, 548, /* Ⴀ-Ⴥ ა-ჵ */
+ 0x1f08, 0x1f0f, 492, /* Ἀ-Ἇ ἀ-ἇ */
+ 0x1f18, 0x1f1d, 492, /* Ἐ-Ἕ ἐ-ἕ */
+ 0x1f28, 0x1f2f, 492, /* Ἠ-Ἧ ἠ-ἧ */
+ 0x1f38, 0x1f3f, 492, /* Ἰ-Ἷ ἰ-ἷ */
+ 0x1f48, 0x1f4d, 492, /* Ὀ-Ὅ ὀ-ὅ */
+ 0x1f68, 0x1f6f, 492, /* Ὠ-Ὧ ὠ-ὧ */
+ 0x1f88, 0x1f8f, 492, /* ᾈ-ᾏ ᾀ-ᾇ */
+ 0x1f98, 0x1f9f, 492, /* ᾘ-ᾟ ᾐ-ᾗ */
+ 0x1fa8, 0x1faf, 492, /* ᾨ-ᾯ ᾠ-ᾧ */
+ 0x1fb8, 0x1fb9, 492, /* Ᾰ-Ᾱ ᾰ-ᾱ */
+ 0x1fba, 0x1fbb, 426, /* Ὰ-Ά ὰ-ά */
+ 0x1fc8, 0x1fcb, 414, /* Ὲ-Ή ὲ-ή */
+ 0x1fd8, 0x1fd9, 492, /* Ῐ-Ῑ ῐ-ῑ */
+ 0x1fda, 0x1fdb, 400, /* Ὶ-Ί ὶ-ί */
+ 0x1fe8, 0x1fe9, 492, /* Ῠ-Ῡ ῠ-ῡ */
+ 0x1fea, 0x1feb, 388, /* Ὺ-Ύ ὺ-ύ */
+ 0x1ff8, 0x1ff9, 372, /* Ὸ-Ό ὸ-ό */
+ 0x1ffa, 0x1ffb, 374, /* Ὼ-Ώ ὼ-ώ */
+ 0x2160, 0x216f, 516, /* Ⅰ-Ⅿ ⅰ-ⅿ */
+ 0x24b6, 0x24cf, 526, /* Ⓐ-Ⓩ ⓐ-ⓩ */
+ 0xff21, 0xff3a, 532, /* A-Z a-z */
+};
+
+/*
+ * upper case singlets
+ * 2nd col is conversion excess 500
+ */
+static
+Rune __tolower1[] =
+{
+ 0x0100, 501, /* Ā ā */
+ 0x0102, 501, /* Ă ă */
+ 0x0104, 501, /* Ą ą */
+ 0x0106, 501, /* Ć ć */
+ 0x0108, 501, /* Ĉ ĉ */
+ 0x010a, 501, /* Ċ ċ */
+ 0x010c, 501, /* Č č */
+ 0x010e, 501, /* Ď ď */
+ 0x0110, 501, /* Đ đ */
+ 0x0112, 501, /* Ē ē */
+ 0x0114, 501, /* Ĕ ĕ */
+ 0x0116, 501, /* Ė ė */
+ 0x0118, 501, /* Ę ę */
+ 0x011a, 501, /* Ě ě */
+ 0x011c, 501, /* Ĝ ĝ */
+ 0x011e, 501, /* Ğ ğ */
+ 0x0120, 501, /* Ġ ġ */
+ 0x0122, 501, /* Ģ ģ */
+ 0x0124, 501, /* Ĥ ĥ */
+ 0x0126, 501, /* Ħ ħ */
+ 0x0128, 501, /* Ĩ ĩ */
+ 0x012a, 501, /* Ī ī */
+ 0x012c, 501, /* Ĭ ĭ */
+ 0x012e, 501, /* Į į */
+ 0x0130, 301, /* İ i */
+ 0x0132, 501, /* IJ ij */
+ 0x0134, 501, /* Ĵ ĵ */
+ 0x0136, 501, /* Ķ ķ */
+ 0x0139, 501, /* Ĺ ĺ */
+ 0x013b, 501, /* Ļ ļ */
+ 0x013d, 501, /* Ľ ľ */
+ 0x013f, 501, /* Ŀ ŀ */
+ 0x0141, 501, /* Ł ł */
+ 0x0143, 501, /* Ń ń */
+ 0x0145, 501, /* Ņ ņ */
+ 0x0147, 501, /* Ň ň */
+ 0x014a, 501, /* Ŋ ŋ */
+ 0x014c, 501, /* Ō ō */
+ 0x014e, 501, /* Ŏ ŏ */
+ 0x0150, 501, /* Ő ő */
+ 0x0152, 501, /* Œ œ */
+ 0x0154, 501, /* Ŕ ŕ */
+ 0x0156, 501, /* Ŗ ŗ */
+ 0x0158, 501, /* Ř ř */
+ 0x015a, 501, /* Ś ś */
+ 0x015c, 501, /* Ŝ ŝ */
+ 0x015e, 501, /* Ş ş */
+ 0x0160, 501, /* Š š */
+ 0x0162, 501, /* Ţ ţ */
+ 0x0164, 501, /* Ť ť */
+ 0x0166, 501, /* Ŧ ŧ */
+ 0x0168, 501, /* Ũ ũ */
+ 0x016a, 501, /* Ū ū */
+ 0x016c, 501, /* Ŭ ŭ */
+ 0x016e, 501, /* Ů ů */
+ 0x0170, 501, /* Ű ű */
+ 0x0172, 501, /* Ų ų */
+ 0x0174, 501, /* Ŵ ŵ */
+ 0x0176, 501, /* Ŷ ŷ */
+ 0x0178, 379, /* Ÿ ÿ */
+ 0x0179, 501, /* Ź ź */
+ 0x017b, 501, /* Ż ż */
+ 0x017d, 501, /* Ž ž */
+ 0x0181, 710, /* Ɓ ɓ */
+ 0x0182, 501, /* Ƃ ƃ */
+ 0x0184, 501, /* Ƅ ƅ */
+ 0x0186, 706, /* Ɔ ɔ */
+ 0x0187, 501, /* Ƈ ƈ */
+ 0x018b, 501, /* Ƌ ƌ */
+ 0x0190, 703, /* Ɛ ɛ */
+ 0x0191, 501, /* Ƒ ƒ */
+ 0x0193, 705, /* Ɠ ɠ */
+ 0x0194, 707, /* Ɣ ɣ */
+ 0x0196, 711, /* Ɩ ɩ */
+ 0x0197, 709, /* Ɨ ɨ */
+ 0x0198, 501, /* Ƙ ƙ */
+ 0x019c, 711, /* Ɯ ɯ */
+ 0x019d, 713, /* Ɲ ɲ */
+ 0x01a0, 501, /* Ơ ơ */
+ 0x01a2, 501, /* Ƣ ƣ */
+ 0x01a4, 501, /* Ƥ ƥ */
+ 0x01a7, 501, /* Ƨ ƨ */
+ 0x01a9, 718, /* Ʃ ʃ */
+ 0x01ac, 501, /* Ƭ ƭ */
+ 0x01ae, 718, /* Ʈ ʈ */
+ 0x01af, 501, /* Ư ư */
+ 0x01b3, 501, /* Ƴ ƴ */
+ 0x01b5, 501, /* Ƶ ƶ */
+ 0x01b7, 719, /* Ʒ ʒ */
+ 0x01b8, 501, /* Ƹ ƹ */
+ 0x01bc, 501, /* Ƽ ƽ */
+ 0x01c4, 502, /* DŽ dž */
+ 0x01c5, 501, /* Dž dž */
+ 0x01c7, 502, /* LJ lj */
+ 0x01c8, 501, /* Lj lj */
+ 0x01ca, 502, /* NJ nj */
+ 0x01cb, 501, /* Nj nj */
+ 0x01cd, 501, /* Ǎ ǎ */
+ 0x01cf, 501, /* Ǐ ǐ */
+ 0x01d1, 501, /* Ǒ ǒ */
+ 0x01d3, 501, /* Ǔ ǔ */
+ 0x01d5, 501, /* Ǖ ǖ */
+ 0x01d7, 501, /* Ǘ ǘ */
+ 0x01d9, 501, /* Ǚ ǚ */
+ 0x01db, 501, /* Ǜ ǜ */
+ 0x01de, 501, /* Ǟ ǟ */
+ 0x01e0, 501, /* Ǡ ǡ */
+ 0x01e2, 501, /* Ǣ ǣ */
+ 0x01e4, 501, /* Ǥ ǥ */
+ 0x01e6, 501, /* Ǧ ǧ */
+ 0x01e8, 501, /* Ǩ ǩ */
+ 0x01ea, 501, /* Ǫ ǫ */
+ 0x01ec, 501, /* Ǭ ǭ */
+ 0x01ee, 501, /* Ǯ ǯ */
+ 0x01f1, 502, /* DZ dz */
+ 0x01f2, 501, /* Dz dz */
+ 0x01f4, 501, /* Ǵ ǵ */
+ 0x01fa, 501, /* Ǻ ǻ */
+ 0x01fc, 501, /* Ǽ ǽ */
+ 0x01fe, 501, /* Ǿ ǿ */
+ 0x0200, 501, /* Ȁ ȁ */
+ 0x0202, 501, /* Ȃ ȃ */
+ 0x0204, 501, /* Ȅ ȅ */
+ 0x0206, 501, /* Ȇ ȇ */
+ 0x0208, 501, /* Ȉ ȉ */
+ 0x020a, 501, /* Ȋ ȋ */
+ 0x020c, 501, /* Ȍ ȍ */
+ 0x020e, 501, /* Ȏ ȏ */
+ 0x0210, 501, /* Ȑ ȑ */
+ 0x0212, 501, /* Ȓ ȓ */
+ 0x0214, 501, /* Ȕ ȕ */
+ 0x0216, 501, /* Ȗ ȗ */
+ 0x0386, 538, /* Ά ά */
+ 0x038c, 564, /* Ό ό */
+ 0x03e2, 501, /* Ϣ ϣ */
+ 0x03e4, 501, /* Ϥ ϥ */
+ 0x03e6, 501, /* Ϧ ϧ */
+ 0x03e8, 501, /* Ϩ ϩ */
+ 0x03ea, 501, /* Ϫ ϫ */
+ 0x03ec, 501, /* Ϭ ϭ */
+ 0x03ee, 501, /* Ϯ ϯ */
+ 0x0460, 501, /* Ѡ ѡ */
+ 0x0462, 501, /* Ѣ ѣ */
+ 0x0464, 501, /* Ѥ ѥ */
+ 0x0466, 501, /* Ѧ ѧ */
+ 0x0468, 501, /* Ѩ ѩ */
+ 0x046a, 501, /* Ѫ ѫ */
+ 0x046c, 501, /* Ѭ ѭ */
+ 0x046e, 501, /* Ѯ ѯ */
+ 0x0470, 501, /* Ѱ ѱ */
+ 0x0472, 501, /* Ѳ ѳ */
+ 0x0474, 501, /* Ѵ ѵ */
+ 0x0476, 501, /* Ѷ ѷ */
+ 0x0478, 501, /* Ѹ ѹ */
+ 0x047a, 501, /* Ѻ ѻ */
+ 0x047c, 501, /* Ѽ ѽ */
+ 0x047e, 501, /* Ѿ ѿ */
+ 0x0480, 501, /* Ҁ ҁ */
+ 0x0490, 501, /* Ґ ґ */
+ 0x0492, 501, /* Ғ ғ */
+ 0x0494, 501, /* Ҕ ҕ */
+ 0x0496, 501, /* Җ җ */
+ 0x0498, 501, /* Ҙ ҙ */
+ 0x049a, 501, /* Қ қ */
+ 0x049c, 501, /* Ҝ ҝ */
+ 0x049e, 501, /* Ҟ ҟ */
+ 0x04a0, 501, /* Ҡ ҡ */
+ 0x04a2, 501, /* Ң ң */
+ 0x04a4, 501, /* Ҥ ҥ */
+ 0x04a6, 501, /* Ҧ ҧ */
+ 0x04a8, 501, /* Ҩ ҩ */
+ 0x04aa, 501, /* Ҫ ҫ */
+ 0x04ac, 501, /* Ҭ ҭ */
+ 0x04ae, 501, /* Ү ү */
+ 0x04b0, 501, /* Ұ ұ */
+ 0x04b2, 501, /* Ҳ ҳ */
+ 0x04b4, 501, /* Ҵ ҵ */
+ 0x04b6, 501, /* Ҷ ҷ */
+ 0x04b8, 501, /* Ҹ ҹ */
+ 0x04ba, 501, /* Һ һ */
+ 0x04bc, 501, /* Ҽ ҽ */
+ 0x04be, 501, /* Ҿ ҿ */
+ 0x04c1, 501, /* Ӂ ӂ */
+ 0x04c3, 501, /* Ӄ ӄ */
+ 0x04c7, 501, /* Ӈ ӈ */
+ 0x04cb, 501, /* Ӌ ӌ */
+ 0x04d0, 501, /* Ӑ ӑ */
+ 0x04d2, 501, /* Ӓ ӓ */
+ 0x04d4, 501, /* Ӕ ӕ */
+ 0x04d6, 501, /* Ӗ ӗ */
+ 0x04d8, 501, /* Ә ә */
+ 0x04da, 501, /* Ӛ ӛ */
+ 0x04dc, 501, /* Ӝ ӝ */
+ 0x04de, 501, /* Ӟ ӟ */
+ 0x04e0, 501, /* Ӡ ӡ */
+ 0x04e2, 501, /* Ӣ ӣ */
+ 0x04e4, 501, /* Ӥ ӥ */
+ 0x04e6, 501, /* Ӧ ӧ */
+ 0x04e8, 501, /* Ө ө */
+ 0x04ea, 501, /* Ӫ ӫ */
+ 0x04ee, 501, /* Ӯ ӯ */
+ 0x04f0, 501, /* Ӱ ӱ */
+ 0x04f2, 501, /* Ӳ ӳ */
+ 0x04f4, 501, /* Ӵ ӵ */
+ 0x04f8, 501, /* Ӹ ӹ */
+ 0x1e00, 501, /* Ḁ ḁ */
+ 0x1e02, 501, /* Ḃ ḃ */
+ 0x1e04, 501, /* Ḅ ḅ */
+ 0x1e06, 501, /* Ḇ ḇ */
+ 0x1e08, 501, /* Ḉ ḉ */
+ 0x1e0a, 501, /* Ḋ ḋ */
+ 0x1e0c, 501, /* Ḍ ḍ */
+ 0x1e0e, 501, /* Ḏ ḏ */
+ 0x1e10, 501, /* Ḑ ḑ */
+ 0x1e12, 501, /* Ḓ ḓ */
+ 0x1e14, 501, /* Ḕ ḕ */
+ 0x1e16, 501, /* Ḗ ḗ */
+ 0x1e18, 501, /* Ḙ ḙ */
+ 0x1e1a, 501, /* Ḛ ḛ */
+ 0x1e1c, 501, /* Ḝ ḝ */
+ 0x1e1e, 501, /* Ḟ ḟ */
+ 0x1e20, 501, /* Ḡ ḡ */
+ 0x1e22, 501, /* Ḣ ḣ */
+ 0x1e24, 501, /* Ḥ ḥ */
+ 0x1e26, 501, /* Ḧ ḧ */
+ 0x1e28, 501, /* Ḩ ḩ */
+ 0x1e2a, 501, /* Ḫ ḫ */
+ 0x1e2c, 501, /* Ḭ ḭ */
+ 0x1e2e, 501, /* Ḯ ḯ */
+ 0x1e30, 501, /* Ḱ ḱ */
+ 0x1e32, 501, /* Ḳ ḳ */
+ 0x1e34, 501, /* Ḵ ḵ */
+ 0x1e36, 501, /* Ḷ ḷ */
+ 0x1e38, 501, /* Ḹ ḹ */
+ 0x1e3a, 501, /* Ḻ ḻ */
+ 0x1e3c, 501, /* Ḽ ḽ */
+ 0x1e3e, 501, /* Ḿ ḿ */
+ 0x1e40, 501, /* Ṁ ṁ */
+ 0x1e42, 501, /* Ṃ ṃ */
+ 0x1e44, 501, /* Ṅ ṅ */
+ 0x1e46, 501, /* Ṇ ṇ */
+ 0x1e48, 501, /* Ṉ ṉ */
+ 0x1e4a, 501, /* Ṋ ṋ */
+ 0x1e4c, 501, /* Ṍ ṍ */
+ 0x1e4e, 501, /* Ṏ ṏ */
+ 0x1e50, 501, /* Ṑ ṑ */
+ 0x1e52, 501, /* Ṓ ṓ */
+ 0x1e54, 501, /* Ṕ ṕ */
+ 0x1e56, 501, /* Ṗ ṗ */
+ 0x1e58, 501, /* Ṙ ṙ */
+ 0x1e5a, 501, /* Ṛ ṛ */
+ 0x1e5c, 501, /* Ṝ ṝ */
+ 0x1e5e, 501, /* Ṟ ṟ */
+ 0x1e60, 501, /* Ṡ ṡ */
+ 0x1e62, 501, /* Ṣ ṣ */
+ 0x1e64, 501, /* Ṥ ṥ */
+ 0x1e66, 501, /* Ṧ ṧ */
+ 0x1e68, 501, /* Ṩ ṩ */
+ 0x1e6a, 501, /* Ṫ ṫ */
+ 0x1e6c, 501, /* Ṭ ṭ */
+ 0x1e6e, 501, /* Ṯ ṯ */
+ 0x1e70, 501, /* Ṱ ṱ */
+ 0x1e72, 501, /* Ṳ ṳ */
+ 0x1e74, 501, /* Ṵ ṵ */
+ 0x1e76, 501, /* Ṷ ṷ */
+ 0x1e78, 501, /* Ṹ ṹ */
+ 0x1e7a, 501, /* Ṻ ṻ */
+ 0x1e7c, 501, /* Ṽ ṽ */
+ 0x1e7e, 501, /* Ṿ ṿ */
+ 0x1e80, 501, /* Ẁ ẁ */
+ 0x1e82, 501, /* Ẃ ẃ */
+ 0x1e84, 501, /* Ẅ ẅ */
+ 0x1e86, 501, /* Ẇ ẇ */
+ 0x1e88, 501, /* Ẉ ẉ */
+ 0x1e8a, 501, /* Ẋ ẋ */
+ 0x1e8c, 501, /* Ẍ ẍ */
+ 0x1e8e, 501, /* Ẏ ẏ */
+ 0x1e90, 501, /* Ẑ ẑ */
+ 0x1e92, 501, /* Ẓ ẓ */
+ 0x1e94, 501, /* Ẕ ẕ */
+ 0x1ea0, 501, /* Ạ ạ */
+ 0x1ea2, 501, /* Ả ả */
+ 0x1ea4, 501, /* Ấ ấ */
+ 0x1ea6, 501, /* Ầ ầ */
+ 0x1ea8, 501, /* Ẩ ẩ */
+ 0x1eaa, 501, /* Ẫ ẫ */
+ 0x1eac, 501, /* Ậ ậ */
+ 0x1eae, 501, /* Ắ ắ */
+ 0x1eb0, 501, /* Ằ ằ */
+ 0x1eb2, 501, /* Ẳ ẳ */
+ 0x1eb4, 501, /* Ẵ ẵ */
+ 0x1eb6, 501, /* Ặ ặ */
+ 0x1eb8, 501, /* Ẹ ẹ */
+ 0x1eba, 501, /* Ẻ ẻ */
+ 0x1ebc, 501, /* Ẽ ẽ */
+ 0x1ebe, 501, /* Ế ế */
+ 0x1ec0, 501, /* Ề ề */
+ 0x1ec2, 501, /* Ể ể */
+ 0x1ec4, 501, /* Ễ ễ */
+ 0x1ec6, 501, /* Ệ ệ */
+ 0x1ec8, 501, /* Ỉ ỉ */
+ 0x1eca, 501, /* Ị ị */
+ 0x1ecc, 501, /* Ọ ọ */
+ 0x1ece, 501, /* Ỏ ỏ */
+ 0x1ed0, 501, /* Ố ố */
+ 0x1ed2, 501, /* Ồ ồ */
+ 0x1ed4, 501, /* Ổ ổ */
+ 0x1ed6, 501, /* Ỗ ỗ */
+ 0x1ed8, 501, /* Ộ ộ */
+ 0x1eda, 501, /* Ớ ớ */
+ 0x1edc, 501, /* Ờ ờ */
+ 0x1ede, 501, /* Ở ở */
+ 0x1ee0, 501, /* Ỡ ỡ */
+ 0x1ee2, 501, /* Ợ ợ */
+ 0x1ee4, 501, /* Ụ ụ */
+ 0x1ee6, 501, /* Ủ ủ */
+ 0x1ee8, 501, /* Ứ ứ */
+ 0x1eea, 501, /* Ừ ừ */
+ 0x1eec, 501, /* Ử ử */
+ 0x1eee, 501, /* Ữ ữ */
+ 0x1ef0, 501, /* Ự ự */
+ 0x1ef2, 501, /* Ỳ ỳ */
+ 0x1ef4, 501, /* Ỵ ỵ */
+ 0x1ef6, 501, /* Ỷ ỷ */
+ 0x1ef8, 501, /* Ỹ ỹ */
+ 0x1f59, 492, /* Ὑ ὑ */
+ 0x1f5b, 492, /* Ὓ ὓ */
+ 0x1f5d, 492, /* Ὕ ὕ */
+ 0x1f5f, 492, /* Ὗ ὗ */
+ 0x1fbc, 491, /* ᾼ ᾳ */
+ 0x1fcc, 491, /* ῌ ῃ */
+ 0x1fec, 493, /* Ῥ ῥ */
+ 0x1ffc, 491, /* ῼ ῳ */
+};
+
+/*
+ * title characters are those between
+ * upper and lower case. ie DZ Dz dz
+ */
+static
+Rune __totitle1[] =
+{
+ 0x01c4, 501, /* DŽ Dž */
+ 0x01c6, 499, /* dž Dž */
+ 0x01c7, 501, /* LJ Lj */
+ 0x01c9, 499, /* lj Lj */
+ 0x01ca, 501, /* NJ Nj */
+ 0x01cc, 499, /* nj Nj */
+ 0x01f1, 501, /* DZ Dz */
+ 0x01f3, 499, /* dz Dz */
+};
+
+static Rune*
+bsearch(Rune c, Rune *t, int n, int ne)
+{
+ Rune *p;
+ int m;
+
+ while(n > 1) {
+ m = n/2;
+ p = t + m*ne;
+ if(c >= p[0]) {
+ t = p;
+ n = n-m;
+ } else
+ n = m;
+ }
+ if(n && c >= t[0])
+ return t;
+ return 0;
+}
+
+Rune
+tolowerrune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, __tolower2, nelem(__tolower2)/3, 3);
+ if(p && c >= p[0] && c <= p[1])
+ return c + p[2] - 500;
+ p = bsearch(c, __tolower1, nelem(__tolower1)/2, 2);
+ if(p && c == p[0])
+ return c + p[1] - 500;
+ return c;
+}
+
+Rune
+toupperrune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, __toupper2, nelem(__toupper2)/3, 3);
+ if(p && c >= p[0] && c <= p[1])
+ return c + p[2] - 500;
+ p = bsearch(c, __toupper1, nelem(__toupper1)/2, 2);
+ if(p && c == p[0])
+ return c + p[1] - 500;
+ return c;
+}
+
+Rune
+totitlerune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, __totitle1, nelem(__totitle1)/2, 2);
+ if(p && c == p[0])
+ return c + p[1] - 500;
+ return c;
+}
+
+int
+islowerrune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, __toupper2, nelem(__toupper2)/3, 3);
+ if(p && c >= p[0] && c <= p[1])
+ return 1;
+ p = bsearch(c, __toupper1, nelem(__toupper1)/2, 2);
+ if(p && c == p[0])
+ return 1;
+ return 0;
+}
+
+int
+isupperrune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, __tolower2, nelem(__tolower2)/3, 3);
+ if(p && c >= p[0] && c <= p[1])
+ return 1;
+ p = bsearch(c, __tolower1, nelem(__tolower1)/2, 2);
+ if(p && c == p[0])
+ return 1;
+ return 0;
+}
+
+int
+isalpharune(Rune c)
+{
+ Rune *p;
+
+ if(isupperrune(c) || islowerrune(c))
+ return 1;
+ p = bsearch(c, __alpha2, nelem(__alpha2)/2, 2);
+ if(p && c >= p[0] && c <= p[1])
+ return 1;
+ p = bsearch(c, __alpha1, nelem(__alpha1), 1);
+ if(p && c == p[0])
+ return 1;
+ return 0;
+}
+
+int
+istitlerune(Rune c)
+{
+ return isupperrune(c) && islowerrune(c);
+}
+
+int
+isspacerune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, __space2, nelem(__space2)/2, 2);
+ if(p && c >= p[0] && c <= p[1])
+ return 1;
+ return 0;
+}
diff --git a/lib9/utf/utfdef.h b/lib9/utf/utfdef.h
@@ -0,0 +1,33 @@
+/*
+ * compiler directive on Plan 9
+ */
+#ifndef USED
+#define USED(x) if(x);else
+#endif
+
+/*
+ * easiest way to make sure these are defined
+ */
+#define uchar _fmtuchar
+#define ushort _fmtushort
+#define uint _fmtuint
+#define ulong _fmtulong
+#define vlong _fmtvlong
+#define uvlong _fmtuvlong
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+typedef unsigned long long uvlong;
+typedef long long vlong;
+
+/*
+ * nil cannot be ((void*)0) on ANSI C,
+ * because it is used for function pointers
+ */
+#undef nil
+#define nil 0
+
+#undef nelem
+#define nelem ((void*)0)
+
diff --git a/lib9/utf/utfecpy.c b/lib9/utf/utfecpy.c
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+char*
+utfecpy(char *to, char *e, char *from)
+{
+ char *end;
+
+ if(to >= e)
+ return to;
+ end = memccpy(to, from, '\0', e - to);
+ if(end == nil){
+ end = e-1;
+ while(end>to && (*--end&0xC0)==0x80)
+ ;
+ *end = '\0';
+ }else{
+ end--;
+ }
+ return end;
+}
diff --git a/lib9/utf/utflen.c b/lib9/utf/utflen.c
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+int
+utflen(char *s)
+{
+ int c;
+ long n;
+ Rune rune;
+
+ n = 0;
+ for(;;) {
+ c = *(uchar*)s;
+ if(c < Runeself) {
+ if(c == 0)
+ return n;
+ s++;
+ } else
+ s += chartorune(&rune, s);
+ n++;
+ }
+}
diff --git a/lib9/utf/utfnlen.c b/lib9/utf/utfnlen.c
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+int
+utfnlen(char *s, long m)
+{
+ int c;
+ long n;
+ Rune rune;
+ char *es;
+
+ es = s + m;
+ for(n = 0; s < es; n++) {
+ c = *(uchar*)s;
+ if(c < Runeself){
+ if(c == '\0')
+ break;
+ s++;
+ continue;
+ }
+ if(!fullrune(s, es-s))
+ break;
+ s += chartorune(&rune, s);
+ }
+ return n;
+}
diff --git a/lib9/utf/utfrrune.c b/lib9/utf/utfrrune.c
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+char*
+utfrrune(char *s, long c)
+{
+ long c1;
+ Rune r;
+ char *s1;
+
+ if(c < Runesync) /* not part of utf sequence */
+ return strrchr(s, c);
+
+ s1 = 0;
+ for(;;) {
+ c1 = *(uchar*)s;
+ if(c1 < Runeself) { /* one byte rune */
+ if(c1 == 0)
+ return s1;
+ if(c1 == c)
+ s1 = s;
+ s++;
+ continue;
+ }
+ c1 = chartorune(&r, s);
+ if(r == c)
+ s1 = s;
+ s += c1;
+ }
+}
diff --git a/lib9/utf/utfrune.c b/lib9/utf/utfrune.c
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+char*
+utfrune(char *s, long c)
+{
+ long c1;
+ Rune r;
+ int n;
+
+ if(c < Runesync) /* not part of utf sequence */
+ return strchr(s, c);
+
+ for(;;) {
+ c1 = *(uchar*)s;
+ if(c1 < Runeself) { /* one byte rune */
+ if(c1 == 0)
+ return 0;
+ if(c1 == c)
+ return s;
+ s++;
+ continue;
+ }
+ n = chartorune(&r, s);
+ if(r == c)
+ return s;
+ s += n;
+ }
+}
diff --git a/lib9/utf/utfutf.c b/lib9/utf/utfutf.c
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+#include <stdarg.h>
+#include <string.h>
+#include "plan9.h"
+#include "utf.h"
+
+
+/*
+ * Return pointer to first occurrence of s2 in s1,
+ * 0 if none
+ */
+char*
+utfutf(char *s1, char *s2)
+{
+ char *p;
+ long f, n1, n2;
+ Rune r;
+
+ n1 = chartorune(&r, s2);
+ f = r;
+ if(f <= Runesync) /* represents self */
+ return strstr(s1, s2);
+
+ n2 = strlen(s2);
+ for(p=s1; p=utfrune(p, f); p+=n1)
+ if(strncmp(p, s2, n2) == 0)
+ return p;
+ return 0;
+}
diff --git a/lib9/wait.c b/lib9/wait.c
@@ -0,0 +1,54 @@
+#include <u.h>
+#include <libc.h>
+
+static Waitmsg*
+_wait(int n, char *buf)
+{
+ int l;
+ char *fld[5];
+ Waitmsg *w;
+
+ if(n <= 0)
+ return nil;
+ buf[n] = '\0';
+ if(tokenize(buf, fld, nelem(fld)) != nelem(fld)){
+ werrstr("couldn't parse wait message");
+ return nil;
+ }
+ l = strlen(fld[4])+1;
+ w = malloc(sizeof(Waitmsg)+l);
+ if(w == nil)
+ return nil;
+ w->pid = atoi(fld[0]);
+ w->time[0] = atoi(fld[1]);
+ w->time[1] = atoi(fld[2]);
+ w->time[2] = atoi(fld[3]);
+ w->msg = (char*)&w[1];
+ memmove(w->msg, fld[4], l);
+ return w;
+}
+
+Waitmsg*
+wait(void)
+{
+ char buf[256];
+
+ return _wait(await(buf, sizeof buf-1), buf);
+}
+
+Waitmsg*
+waitnohang(void)
+{
+ char buf[256];
+
+ return _wait(awaitnohang(buf, sizeof buf-1), buf);
+}
+
+Waitmsg*
+waitfor(int pid)
+{
+ char buf[256];
+
+ return _wait(awaitfor(pid, buf, sizeof buf-1), buf);
+}
+
diff --git a/lib9/waitpid.c b/lib9/waitpid.c
@@ -0,0 +1,20 @@
+#include <u.h>
+#include <libc.h>
+
+int
+waitpid(void)
+{
+ int n;
+ char buf[512], *fld[5];
+
+ n = await(buf, sizeof buf-1);
+ if(n <= 0)
+ return -1;
+ buf[n] = '\0';
+ if(tokenize(buf, fld, nelem(fld)) != nelem(fld)){
+ werrstr("couldn't parse wait message");
+ return -1;
+ }
+ return atoi(fld[0]);
+}
+
diff --git a/rc/Makefile b/rc/Makefile
@@ -0,0 +1,54 @@
+# rc - rc shell unix port from plan9
+# Depends on ../lib9
+
+include ../config.mk
+
+TARG = rc
+
+OFILES = code.o exec.o getflags.o glob.o here.o io.o lex.o \
+ pcmd.o pfnc.o simple.o subr.o trap.o tree.o unixcrap.o \
+ var.o y.tab.o plan9ish.o
+
+YFILES = syn.y
+
+MANFILES = rc.1
+
+RCMAIN = rcmain
+
+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}${PREFIX}/lib
+ @cp -f ${RCMAIN} ${DESTDIR}${PREFIX}/lib
+ @chmod 755 ${DESTDIR}${PREFIX}/lib/${RCMAIN}
+ @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}/lib/${RCMAIN}
+ 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 x.tab.h
+
+${TARG}: ${OFILES}
+ @echo LD ${TARG}
+ @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9
diff --git a/rc/code.c b/rc/code.c
@@ -0,0 +1,430 @@
+#include "rc.h"
+#include "io.h"
+#include "exec.h"
+#include "fns.h"
+#include "getflags.h"
+#define c0 t->child[0]
+#define c1 t->child[1]
+#define c2 t->child[2]
+int codep, ncode;
+#define emitf(x) ((void)(codep!=ncode || morecode()), codebuf[codep].f=(x), codep++)
+#define emiti(x) ((void)(codep!=ncode || morecode()), codebuf[codep].i=(x), codep++)
+#define emits(x) ((void)(codep!=ncode || morecode()), codebuf[codep].s=(x), codep++)
+void stuffdot(int);
+char *fnstr(tree*);
+void outcode(tree*, int);
+void codeswitch(tree*, int);
+int iscase(tree*);
+code *codecopy(code*);
+void codefree(code*);
+int morecode(void){
+ ncode+=100;
+ codebuf=(code *)realloc((char *)codebuf, ncode*sizeof codebuf[0]);
+ if(codebuf==0) panic("Can't realloc %d bytes in morecode!",
+ ncode*sizeof codebuf[0]);
+ return 0;
+}
+void stuffdot(int a){
+ if(a<0 || codep<=a) panic("Bad address %d in stuffdot", a);
+ codebuf[a].i=codep;
+}
+int compile(tree *t)
+{
+ ncode=100;
+ codebuf=(code *)emalloc(ncode*sizeof codebuf[0]);
+ codep=0;
+ emiti(0); /* reference count */
+ outcode(t, flag['e']?1:0);
+ if(nerror){
+ efree((char *)codebuf);
+ return 0;
+ }
+ readhere();
+ emitf(Xreturn);
+ emitf(0);
+ return 1;
+}
+void cleanhere(char *f)
+{
+ emitf(Xdelhere);
+ emits(strdup(f));
+}
+char *fnstr(tree *t)
+{
+ io *f=openstr();
+ char *v;
+ extern char nl;
+ char svnl=nl;
+ nl=';';
+ pfmt(f, "%t", t);
+ nl=svnl;
+ v=f->strp;
+ f->strp=0;
+ closeio(f);
+ return v;
+}
+void outcode(tree *t, int eflag)
+{
+ int p, q;
+ tree *tt;
+ if(t==0) return;
+ if(t->type!=NOT && t->type!=';') runq->iflast=0;
+ switch(t->type){
+ default:
+ pfmt(err, "bad type %d in outcode\n", t->type);
+ break;
+ case '$':
+ emitf(Xmark);
+ outcode(c0, eflag);
+ emitf(Xdol);
+ break;
+ case '"':
+ emitf(Xmark);
+ outcode(c0, eflag);
+ emitf(Xqdol);
+ break;
+ case SUB:
+ emitf(Xmark);
+ outcode(c0, eflag);
+ emitf(Xmark);
+ outcode(c1, eflag);
+ emitf(Xsub);
+ break;
+ case '&':
+ emitf(Xasync);
+ p=emiti(0);
+ outcode(c0, eflag);
+ emitf(Xexit);
+ stuffdot(p);
+ break;
+ case ';':
+ outcode(c0, eflag);
+ outcode(c1, eflag);
+ break;
+ case '^':
+ emitf(Xmark);
+ outcode(c1, eflag);
+ emitf(Xmark);
+ outcode(c0, eflag);
+ emitf(Xconc);
+ break;
+ case '`':
+ emitf(Xbackq);
+ p=emiti(0);
+ outcode(c0, 0);
+ emitf(Xexit);
+ stuffdot(p);
+ break;
+ case ANDAND:
+ outcode(c0, 0);
+ emitf(Xtrue);
+ p=emiti(0);
+ outcode(c1, eflag);
+ stuffdot(p);
+ break;
+ case ARGLIST:
+ outcode(c1, eflag);
+ outcode(c0, eflag);
+ break;
+ case BANG:
+ outcode(c0, eflag);
+ emitf(Xbang);
+ break;
+ case PCMD:
+ case BRACE:
+ outcode(c0, eflag);
+ break;
+ case COUNT:
+ emitf(Xmark);
+ outcode(c0, eflag);
+ emitf(Xcount);
+ break;
+ case FN:
+ emitf(Xmark);
+ outcode(c0, eflag);
+ if(c1){
+ emitf(Xfn);
+ p=emiti(0);
+ emits(fnstr(c1));
+ outcode(c1, eflag);
+ emitf(Xunlocal); /* get rid of $* */
+ emitf(Xreturn);
+ stuffdot(p);
+ }
+ else
+ emitf(Xdelfn);
+ break;
+ case IF:
+ outcode(c0, 0);
+ emitf(Xif);
+ p=emiti(0);
+ outcode(c1, eflag);
+ emitf(Xwastrue);
+ stuffdot(p);
+ break;
+ case NOT:
+ if(!runq->iflast) yyerror("`if not' does not follow `if(...)'");
+ emitf(Xifnot);
+ p=emiti(0);
+ outcode(c0, eflag);
+ stuffdot(p);
+ break;
+ case OROR:
+ outcode(c0, 0);
+ emitf(Xfalse);
+ p=emiti(0);
+ outcode(c1, eflag);
+ stuffdot(p);
+ break;
+ case PAREN:
+ outcode(c0, eflag);
+ break;
+ case SIMPLE:
+ emitf(Xmark);
+ outcode(c0, eflag);
+ emitf(Xsimple);
+ if(eflag) emitf(Xeflag);
+ break;
+ case SUBSHELL:
+ emitf(Xsubshell);
+ p=emiti(0);
+ outcode(c0, eflag);
+ emitf(Xexit);
+ stuffdot(p);
+ if(eflag) emitf(Xeflag);
+ break;
+ case SWITCH:
+ codeswitch(t, eflag);
+ break;
+ case TWIDDLE:
+ emitf(Xmark);
+ outcode(c1, eflag);
+ emitf(Xmark);
+ outcode(c0, eflag);
+ emitf(Xmatch);
+ if(eflag) emitf(Xeflag);
+ break;
+ case WHILE:
+ q=codep;
+ outcode(c0, 0);
+ if(q==codep) emitf(Xsettrue); /* empty condition == while(true) */
+ emitf(Xtrue);
+ p=emiti(0);
+ outcode(c1, eflag);
+ emitf(Xjump);
+ emiti(q);
+ stuffdot(p);
+ break;
+ case WORDS:
+ outcode(c1, eflag);
+ outcode(c0, eflag);
+ break;
+ case FOR:
+ emitf(Xmark);
+ if(c1){
+ outcode(c1, eflag);
+ emitf(Xglob);
+ }
+ else{
+ emitf(Xmark);
+ emitf(Xword);
+ emits(strdup("*"));
+ emitf(Xdol);
+ }
+ emitf(Xmark); /* dummy value for Xlocal */
+ emitf(Xmark);
+ outcode(c0, eflag);
+ emitf(Xlocal);
+ p=emitf(Xfor);
+ q=emiti(0);
+ outcode(c2, eflag);
+ emitf(Xjump);
+ emiti(p);
+ stuffdot(q);
+ emitf(Xunlocal);
+ break;
+ case WORD:
+ emitf(Xword);
+ emits(strdup(t->str));
+ break;
+ case DUP:
+ if(t->rtype==DUPFD){
+ emitf(Xdup);
+ emiti(t->fd0);
+ emiti(t->fd1);
+ }
+ else{
+ emitf(Xclose);
+ emiti(t->fd0);
+ }
+ outcode(c1, eflag);
+ emitf(Xpopredir);
+ break;
+ case PIPEFD:
+ emitf(Xpipefd);
+ emiti(t->rtype);
+ p=emiti(0);
+ outcode(c0, eflag);
+ emitf(Xexit);
+ stuffdot(p);
+ break;
+ case REDIR:
+ emitf(Xmark);
+ outcode(c0, eflag);
+ emitf(Xglob);
+ switch(t->rtype){
+ case APPEND:
+ emitf(Xappend);
+ break;
+ case WRITE:
+ emitf(Xwrite);
+ break;
+ case READ:
+ case HERE:
+ emitf(Xread);
+ break;
+ }
+ emiti(t->fd0);
+ outcode(c1, eflag);
+ emitf(Xpopredir);
+ break;
+ case '=':
+ tt=t;
+ for(;t && t->type=='=';t=c2);
+ if(t){
+ for(t=tt;t->type=='=';t=c2){
+ emitf(Xmark);
+ outcode(c1, eflag);
+ emitf(Xmark);
+ outcode(c0, eflag);
+ emitf(Xlocal);
+ }
+ t=tt;
+ outcode(c2, eflag);
+ for(;t->type=='=';t=c2) emitf(Xunlocal);
+ }
+ else{
+ for(t=tt;t;t=c2){
+ emitf(Xmark);
+ outcode(c1, eflag);
+ emitf(Xmark);
+ outcode(c0, eflag);
+ emitf(Xassign);
+ }
+ }
+ t=tt; /* so tests below will work */
+ break;
+ case PIPE:
+ emitf(Xpipe);
+ emiti(t->fd0);
+ emiti(t->fd1);
+ p=emiti(0);
+ q=emiti(0);
+ outcode(c0, eflag);
+ emitf(Xexit);
+ stuffdot(p);
+ outcode(c1, eflag);
+ emitf(Xreturn);
+ stuffdot(q);
+ emitf(Xpipewait);
+ break;
+ }
+ if(t->type!=NOT && t->type!=';')
+ runq->iflast=t->type==IF;
+ else if(c0) runq->iflast=c0->type==IF;
+}
+/*
+ * switch code looks like this:
+ * Xmark
+ * (get switch value)
+ * Xjump 1f
+ * out: Xjump leave
+ * 1: Xmark
+ * (get case values)
+ * Xcase 1f
+ * (commands)
+ * Xjump out
+ * 1: Xmark
+ * (get case values)
+ * Xcase 1f
+ * (commands)
+ * Xjump out
+ * 1:
+ * leave:
+ * Xpopm
+ */
+void codeswitch(tree *t, int eflag)
+{
+ int leave; /* patch jump address to leave switch */
+ int out; /* jump here to leave switch */
+ int nextcase; /* patch jump address to next case */
+ tree *tt;
+ if(c1->child[0]==nil
+ || c1->child[0]->type!=';'
+ || !iscase(c1->child[0]->child[0])){
+ yyerror("case missing in switch");
+ return;
+ }
+ emitf(Xmark);
+ outcode(c0, eflag);
+ emitf(Xjump);
+ nextcase=emiti(0);
+ out=emitf(Xjump);
+ leave=emiti(0);
+ stuffdot(nextcase);
+ t=c1->child[0];
+ while(t->type==';'){
+ tt=c1;
+ emitf(Xmark);
+ for(t=c0->child[0];t->type==ARGLIST;t=c0) outcode(c1, eflag);
+ emitf(Xcase);
+ nextcase=emiti(0);
+ t=tt;
+ for(;;){
+ if(t->type==';'){
+ if(iscase(c0)) break;
+ outcode(c0, eflag);
+ t=c1;
+ }
+ else{
+ if(!iscase(t)) outcode(t, eflag);
+ break;
+ }
+ }
+ emitf(Xjump);
+ emiti(out);
+ stuffdot(nextcase);
+ }
+ stuffdot(leave);
+ emitf(Xpopm);
+}
+int iscase(tree *t)
+{
+ if(t->type!=SIMPLE) return 0;
+ do t=c0; while(t->type==ARGLIST);
+ return t->type==WORD && !t->quoted && strcmp(t->str, "case")==0;
+}
+code *codecopy(code *cp)
+{
+ cp[0].i++;
+ return cp;
+}
+void codefree(code *cp)
+{
+ code *p;
+ if(--cp[0].i!=0) return;
+ for(p=cp+1;p->f;p++){
+ if(p->f==Xappend || p->f==Xclose || p->f==Xread || p->f==Xwrite
+ || p->f==Xasync || p->f==Xbackq || p->f==Xcase || p->f==Xfalse
+ || p->f==Xfor || p->f==Xjump
+ || p->f==Xsubshell || p->f==Xtrue) p++;
+ else if(p->f==Xdup || p->f==Xpipefd) p+=2;
+ else if(p->f==Xpipe) p+=4;
+ else if(p->f==Xword || p->f==Xdelhere) efree((++p)->s);
+ else if(p->f==Xfn){
+ efree(p[2].s);
+ p+=2;
+ }
+ }
+ efree((char *)cp);
+}
diff --git a/rc/exec.c b/rc/exec.c
@@ -0,0 +1,946 @@
+#include <u.h>
+#include <signal.h>
+#if defined(PLAN9PORT) && defined(__sun__)
+# define BSD_COMP /* sigh. for TIOCNOTTY */
+#endif
+#include <sys/ioctl.h>
+#include "rc.h"
+#include "getflags.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+/*
+ * Start executing the given code at the given pc with the given redirection
+ */
+char *argv0="rc";
+void start(code *c, int pc, var *local)
+{
+ struct thread *p=new(struct thread);
+ p->code=codecopy(c);
+ p->pc=pc;
+ p->argv=0;
+ p->redir=p->startredir=runq?runq->redir:0;
+ p->local=local;
+ p->cmdfile=0;
+ p->cmdfd=0;
+ p->eof=0;
+ p->iflag=0;
+ p->lineno=1;
+ p->pid=-1;
+ p->ret=runq;
+ runq=p;
+}
+word *newword(char *wd, word *next)
+{
+ word *p=new(word);
+ p->word=strdup(wd);
+ p->next=next;
+ return p;
+}
+void pushword(char *wd)
+{
+ if(runq->argv==0) panic("pushword but no argv!", 0);
+ runq->argv->words=newword(wd, runq->argv->words);
+}
+void popword(void){
+ word *p;
+ if(runq->argv==0) panic("popword but no argv!", 0);
+ p=runq->argv->words;
+ if(p==0) panic("popword but no word!", 0);
+ runq->argv->words=p->next;
+ efree(p->word);
+ efree((char *)p);
+}
+void freelist(word *w)
+{
+ word *nw;
+ while(w){
+ nw=w->next;
+ efree(w->word);
+ efree((char *)w);
+ w=nw;
+ }
+}
+void pushlist(void){
+ list *p=new(list);
+ p->next=runq->argv;
+ p->words=0;
+ runq->argv=p;
+}
+void poplist(void){
+ list *p=runq->argv;
+ if(p==0) panic("poplist but no argv", 0);
+ freelist(p->words);
+ runq->argv=p->next;
+ efree((char *)p);
+}
+int count(word *w)
+{
+ int n;
+ for(n=0;w;n++) w=w->next;
+ return n;
+}
+void pushredir(int type, int from, int to){
+ redir * rp=new(redir);
+ rp->type=type;
+ rp->from=from;
+ rp->to=to;
+ rp->next=runq->redir;
+ runq->redir=rp;
+}
+var *newvar(char *name, var *next)
+{
+ var *v=new(var);
+ v->name=name;
+ v->val=0;
+ v->fn=0;
+ v->changed=0;
+ v->fnchanged=0;
+ v->next=next;
+ v->changefn = 0;
+ return v;
+}
+/*
+ * get command line flags, initialize keywords & traps.
+ * get values from environment.
+ * set $pid, $cflag, $*
+ * fabricate bootstrap code and start it (*=(argv);. /usr/lib/rcmain $*)
+ * start interpreting code
+ */
+int
+main(int argc, char *argv[])
+{
+ code bootstrap[32];
+ char num[12], *rcmain;
+ int i;
+
+ argc=getflags(argc, argv, "srdiIlxepvVc:1m:1[command]", 1);
+ if(argc==-1) usage("[file [arg ...]]");
+ if(argv[0][0]=='-') flag['l']=flagset;
+ if(flag['I']) flag['i'] = 0;
+ else if(flag['i']==0 && argc==1 && Isatty(0)) flag['i'] = flagset;
+ rcmain=flag['m']?flag['m'][0]:Rcmain();
+ err=openfd(2);
+ kinit();
+ Trapinit();
+ Vinit();
+ itoa(num, mypid=getpid());
+ pathinit();
+ setvar("pid", newword(num, (word *)0));
+ setvar("cflag", flag['c']?newword(flag['c'][0], (word *)0)
+ :(word *)0);
+ setvar("rcname", newword(argv[0], (word *)0));
+ i=0;
+ bootstrap[i++].i=1;
+ bootstrap[i++].f=Xmark;
+ bootstrap[i++].f=Xword;
+ bootstrap[i++].s="*";
+ bootstrap[i++].f=Xassign;
+ bootstrap[i++].f=Xmark;
+ bootstrap[i++].f=Xmark;
+ bootstrap[i++].f=Xword;
+ bootstrap[i++].s="*";
+ bootstrap[i++].f=Xdol;
+ bootstrap[i++].f=Xword;
+ bootstrap[i++].s=rcmain;
+ bootstrap[i++].f=Xword;
+ bootstrap[i++].s=".";
+ bootstrap[i++].f=Xsimple;
+ bootstrap[i++].f=Xexit;
+ bootstrap[i].i=0;
+ start(bootstrap, 1, (var *)0);
+ /* prime bootstrap argv */
+ pushlist();
+ argv0 = strdup(argv[0]);
+ for(i=argc-1;i!=0;--i) pushword(argv[i]);
+ for(;;){
+ if(flag['r']) pfnc(err, runq);
+ runq->pc++;
+ (*runq->code[runq->pc-1].f)();
+ if(ntrap) dotrap();
+ }
+ return 0;
+}
+/*
+ * Opcode routines
+ * Arguments on stack (...)
+ * Arguments in line [...]
+ * Code in line with jump around {...}
+ *
+ * Xappend(file)[fd] open file to append
+ * Xassign(name, val) assign val to name
+ * Xasync{... Xexit} make thread for {}, no wait
+ * Xbackq{... Xreturn} make thread for {}, push stdout
+ * Xbang complement condition
+ * Xcase(pat, value){...} exec code on match, leave (value) on
+ * stack
+ * Xclose[i] close file descriptor
+ * Xconc(left, right) concatenate, push results
+ * Xcount(name) push var count
+ * Xdelfn(name) delete function definition
+ * Xdeltraps(names) delete named traps
+ * Xdol(name) get variable value
+ * Xqdol(name) concatenate variable components
+ * Xdup[i j] dup file descriptor
+ * Xexit rc exits with status
+ * Xfalse{...} execute {} if false
+ * Xfn(name){... Xreturn} define function
+ * Xfor(var, list){... Xreturn} for loop
+ * Xjump[addr] goto
+ * Xlocal(name, val) create local variable, assign value
+ * Xmark mark stack
+ * Xmatch(pat, str) match pattern, set status
+ * Xpipe[i j]{... Xreturn}{... Xreturn} construct a pipe between 2 new threads,
+ * wait for both
+ * Xpipefd[type]{... Xreturn} connect {} to pipe (input or output,
+ * depending on type), push /dev/fd/??
+ * Xpopm(value) pop value from stack
+ * Xread(file)[fd] open file to read
+ * Xsettraps(names){... Xreturn} define trap functions
+ * Xshowtraps print trap list
+ * Xsimple(args) run command and wait
+ * Xreturn kill thread
+ * Xsubshell{... Xexit} execute {} in a subshell and wait
+ * Xtrue{...} execute {} if true
+ * Xunlocal delete local variable
+ * Xword[string] push string
+ * Xwrite(file)[fd] open file to write
+ */
+void Xappend(void){
+ char *file;
+ int f;
+ switch(count(runq->argv->words)){
+ default: Xerror1(">> requires singleton"); return;
+ case 0: Xerror1(">> requires file"); return;
+ case 1: break;
+ }
+ file=runq->argv->words->word;
+ if((f=open(file, 1))<0 && (f=Creat(file))<0){
+ pfmt(err, "%s: ", file);
+ Xerror("can't open");
+ return;
+ }
+ Seek(f, 0L, 2);
+ pushredir(ROPEN, f, runq->code[runq->pc].i);
+ runq->pc++;
+ poplist();
+}
+void Xasync(void){
+ int null=open("/dev/null", 0);
+ int tty;
+ int pid;
+ char npid[10];
+ if(null<0){
+ Xerror("Can't open /dev/null\n");
+ return;
+ }
+ switch(pid=rfork(RFFDG|RFPROC|RFNOTEG)){
+ case -1:
+ close(null);
+ Xerror("try again");
+ break;
+ case 0:
+ /*
+ * I don't know what the right thing to do here is,
+ * so this is all experimentally determined.
+ * If we just dup /dev/null onto 0, then running
+ * ssh foo & will reopen /dev/tty, try to read a password,
+ * get a signal, and repeat, in a tight loop, forever.
+ * Arguably this is a bug in ssh (it behaves the same
+ * way under bash as under rc) but I'm fixing it here
+ * anyway. If we dissociate the process from the tty,
+ * then it won't be able to open /dev/tty ever again.
+ * The SIG_IGN on SIGTTOU makes writing the tty
+ * (via fd 1 or 2, for example) succeed even though
+ * our pgrp is not the terminal's controlling pgrp.
+ */
+ if((tty=open("/dev/tty", OREAD)) >= 0){
+ /*
+ * Should make reads of tty fail, writes succeed.
+ */
+ signal(SIGTTIN, SIG_IGN);
+ signal(SIGTTOU, SIG_IGN);
+ ioctl(tty, TIOCNOTTY);
+ close(tty);
+ }
+ if(isatty(0))
+ pushredir(ROPEN, null, 0);
+ else
+ close(null);
+ start(runq->code, runq->pc+1, runq->local);
+ runq->ret=0;
+ break;
+ default:
+ close(null);
+ runq->pc=runq->code[runq->pc].i;
+ itoa(npid, pid);
+ setvar("apid", newword(npid, (word *)0));
+ break;
+ }
+}
+void Xsettrue(void){
+ setstatus("");
+}
+void Xbang(void){
+ setstatus(truestatus()?"false":"");
+}
+void Xclose(void){
+ pushredir(RCLOSE, runq->code[runq->pc].i, 0);
+ runq->pc++;
+}
+void Xdup(void){
+ pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i);
+ runq->pc+=2;
+}
+void Xeflag(void){
+ if(eflagok && !truestatus()) Xexit();
+}
+void Xexit(void){
+ struct var *trapreq;
+ struct word *starval;
+ static int beenhere=0;
+ if(getpid()==mypid && !beenhere){
+ trapreq=vlook("sigexit");
+ if(trapreq->fn){
+ beenhere=1;
+ --runq->pc;
+ starval=vlook("*")->val;
+ start(trapreq->fn, trapreq->pc, (struct var *)0);
+ runq->local=newvar(strdup("*"), runq->local);
+ runq->local->val=copywords(starval, (struct word *)0);
+ runq->local->changed=1;
+ runq->redir=runq->startredir=0;
+ return;
+ }
+ }
+ Exit(getstatus());
+}
+void Xfalse(void){
+ if(truestatus()) runq->pc=runq->code[runq->pc].i;
+ else runq->pc++;
+}
+int ifnot; /* dynamic if not flag */
+void Xifnot(void){
+ if(ifnot)
+ runq->pc++;
+ else
+ runq->pc=runq->code[runq->pc].i;
+}
+void Xjump(void){
+ runq->pc=runq->code[runq->pc].i;
+}
+void Xmark(void){
+ pushlist();
+}
+void Xpopm(void){
+ poplist();
+}
+void Xread(void){
+ char *file;
+ int f;
+ switch(count(runq->argv->words)){
+ default: Xerror1("< requires singleton\n"); return;
+ case 0: Xerror1("< requires file\n"); return;
+ case 1: break;
+ }
+ file=runq->argv->words->word;
+ if((f=open(file, 0))<0){
+ pfmt(err, "%s: ", file);
+ Xerror("can't open");
+ return;
+ }
+ pushredir(ROPEN, f, runq->code[runq->pc].i);
+ runq->pc++;
+ poplist();
+}
+void turfredir(void){
+ while(runq->redir!=runq->startredir)
+ Xpopredir();
+}
+void Xpopredir(void){
+ struct redir *rp=runq->redir;
+ if(rp==0) panic("turfredir null!", 0);
+ runq->redir=rp->next;
+ if(rp->type==ROPEN) close(rp->from);
+ efree((char *)rp);
+}
+void Xreturn(void){
+ struct thread *p=runq;
+ turfredir();
+ while(p->argv) poplist();
+ codefree(p->code);
+ runq=p->ret;
+ efree((char *)p);
+ if(runq==0) Exit(getstatus());
+}
+void Xtrue(void){
+ if(truestatus()) runq->pc++;
+ else runq->pc=runq->code[runq->pc].i;
+}
+void Xif(void){
+ ifnot=1;
+ if(truestatus()) runq->pc++;
+ else runq->pc=runq->code[runq->pc].i;
+}
+void Xwastrue(void){
+ ifnot=0;
+}
+void Xword(void){
+ pushword(runq->code[runq->pc++].s);
+}
+void Xwrite(void){
+ char *file;
+ int f;
+ switch(count(runq->argv->words)){
+ default: Xerror1("> requires singleton\n"); return;
+ case 0: Xerror1("> requires file\n"); return;
+ case 1: break;
+ }
+ file=runq->argv->words->word;
+ if((f=Creat(file))<0){
+ pfmt(err, "%s: ", file);
+ Xerror("can't open");
+ return;
+ }
+ pushredir(ROPEN, f, runq->code[runq->pc].i);
+ runq->pc++;
+ poplist();
+}
+char *_list2str(word *words, int c){
+ char *value, *s, *t;
+ int len=0;
+ word *ap;
+ for(ap=words;ap;ap=ap->next)
+ len+=1+strlen(ap->word);
+ value=emalloc(len+1);
+ s=value;
+ for(ap=words;ap;ap=ap->next){
+ for(t=ap->word;*t;) *s++=*t++;
+ *s++=c;
+ }
+ if(s==value) *s='\0';
+ else s[-1]='\0';
+ return value;
+}
+char *list2str(word *words){
+ return _list2str(words, ' ');
+}
+void Xmatch(void){
+ word *p;
+ char *subject;
+ subject=list2str(runq->argv->words);
+ setstatus("no match");
+ for(p=runq->argv->next->words;p;p=p->next)
+ if(match(subject, p->word, '\0')){
+ setstatus("");
+ break;
+ }
+ efree(subject);
+ poplist();
+ poplist();
+}
+void Xcase(void){
+ word *p;
+ char *s;
+ int ok=0;
+ s=list2str(runq->argv->next->words);
+ for(p=runq->argv->words;p;p=p->next){
+ if(match(s, p->word, '\0')){
+ ok=1;
+ break;
+ }
+ }
+ efree(s);
+ if(ok)
+ runq->pc++;
+ else
+ runq->pc=runq->code[runq->pc].i;
+ poplist();
+}
+word *conclist(word *lp, word *rp, word *tail)
+{
+ char *buf;
+ word *v;
+ if(lp->next || rp->next)
+ tail=conclist(lp->next==0?lp:lp->next, rp->next==0?rp:rp->next,
+ tail);
+ buf=emalloc(strlen(lp->word)+strlen(rp->word)+1);
+ strcpy(buf, lp->word);
+ strcat(buf, rp->word);
+ v=newword(buf, tail);
+ efree(buf);
+ return v;
+}
+void Xconc(void){
+ word *lp=runq->argv->words;
+ word *rp=runq->argv->next->words;
+ word *vp=runq->argv->next->next->words;
+ int lc=count(lp), rc=count(rp);
+ if(lc!=0 || rc!=0){
+ if(lc==0 || rc==0){
+ Xerror1("null list in concatenation");
+ return;
+ }
+ if(lc!=1 && rc!=1 && lc!=rc){
+ Xerror1("mismatched list lengths in concatenation");
+ return;
+ }
+ vp=conclist(lp, rp, vp);
+ }
+ poplist();
+ poplist();
+ runq->argv->words=vp;
+}
+void Xassign(void){
+ var *v;
+ if(count(runq->argv->words)!=1){
+ Xerror1("variable name not singleton!");
+ return;
+ }
+ deglob(runq->argv->words->word);
+ v=vlook(runq->argv->words->word);
+ poplist();
+ globlist();
+ freewords(v->val);
+ v->val=runq->argv->words;
+ v->changed=1;
+ if(v->changefn)
+ v->changefn(v);
+ runq->argv->words=0;
+ poplist();
+}
+/*
+ * copy arglist a, adding the copy to the front of tail
+ */
+word *copywords(word *a, word *tail)
+{
+ word *v=0, **end;
+ for(end=&v;a;a=a->next,end=&(*end)->next)
+ *end=newword(a->word, 0);
+ *end=tail;
+ return v;
+}
+void Xdol(void){
+ word *a, *star;
+ char *s, *t;
+ int n;
+ if(count(runq->argv->words)!=1){
+ Xerror1("variable name not singleton!");
+ return;
+ }
+ s=runq->argv->words->word;
+ deglob(s);
+ n=0;
+ for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0';
+ a=runq->argv->next->words;
+ if(n==0 || *t)
+ a=copywords(vlook(s)->val, a);
+ else{
+ star=vlook("*")->val;
+ if(star && 1<=n && n<=count(star)){
+ while(--n) star=star->next;
+ a=newword(star->word, a);
+ }
+ }
+ poplist();
+ runq->argv->words=a;
+}
+void Xqdol(void){
+ word *a, *p;
+ char *s;
+ int n;
+ if(count(runq->argv->words)!=1){
+ Xerror1("variable name not singleton!");
+ return;
+ }
+ s=runq->argv->words->word;
+ deglob(s);
+ a=vlook(s)->val;
+ poplist();
+ n=count(a);
+ if(n==0){
+ pushword("");
+ return;
+ }
+ for(p=a;p;p=p->next) n+=strlen(p->word);
+ s=emalloc(n);
+ if(a){
+ strcpy(s, a->word);
+ for(p=a->next;p;p=p->next){
+ strcat(s, " ");
+ strcat(s, p->word);
+ }
+ }
+ else
+ s[0]='\0';
+ pushword(s);
+ efree(s);
+}
+word *subwords(word *val, int len, word *sub, word *a)
+{
+ int n;
+ char *s;
+ if(!sub) return a;
+ a=subwords(val, len, sub->next, a);
+ s=sub->word;
+ deglob(s);
+ n=0;
+ while('0'<=*s && *s<='9') n=n*10+ *s++ -'0';
+ if(n<1 || len<n) return a;
+ for(;n!=1;--n) val=val->next;
+ return newword(val->word, a);
+}
+void Xsub(void){
+ word *a, *v;
+ char *s;
+ if(count(runq->argv->next->words)!=1){
+ Xerror1("variable name not singleton!");
+ return;
+ }
+ s=runq->argv->next->words->word;
+ deglob(s);
+ a=runq->argv->next->next->words;
+ v=vlook(s)->val;
+ a=subwords(v, count(v), runq->argv->words, a);
+ poplist();
+ poplist();
+ runq->argv->words=a;
+}
+void Xcount(void){
+ word *a;
+ char *s, *t;
+ int n;
+ char num[12];
+ if(count(runq->argv->words)!=1){
+ Xerror1("variable name not singleton!");
+ return;
+ }
+ s=runq->argv->words->word;
+ deglob(s);
+ n=0;
+ for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0';
+ if(n==0 || *t){
+ a=vlook(s)->val;
+ itoa(num, count(a));
+ }
+ else{
+ a=vlook("*")->val;
+ itoa(num, a && 1<=n && n<=count(a)?1:0);
+ }
+ poplist();
+ pushword(num);
+}
+void Xlocal(void){
+ if(count(runq->argv->words)!=1){
+ Xerror1("variable name must be singleton\n");
+ return;
+ }
+ deglob(runq->argv->words->word);
+ runq->local=newvar(strdup(runq->argv->words->word), runq->local);
+ runq->local->val=copywords(runq->argv->next->words, (word *)0);
+ runq->local->changed=1;
+ poplist();
+ poplist();
+}
+void Xunlocal(void){
+ var *v=runq->local, *hid;
+ if(v==0) panic("Xunlocal: no locals!", 0);
+ runq->local=v->next;
+ hid=vlook(v->name);
+ hid->changed=1;
+ efree(v->name);
+ freewords(v->val);
+ efree((char *)v);
+}
+void freewords(word *w)
+{
+ word *nw;
+ while(w){
+ efree(w->word);
+ nw=w->next;
+ efree((char *)w);
+ w=nw;
+ }
+}
+void Xfn(void){
+ var *v;
+ word *a;
+ int end;
+ end=runq->code[runq->pc].i;
+ for(a=runq->argv->words;a;a=a->next){
+ v=gvlook(a->word);
+ if(v->fn) codefree(v->fn);
+ v->fn=codecopy(runq->code);
+ v->pc=runq->pc+2;
+ v->fnchanged=1;
+ }
+ runq->pc=end;
+ poplist();
+}
+void Xdelfn(void){
+ var *v;
+ word *a;
+ for(a=runq->argv->words;a;a=a->next){
+ v=gvlook(a->word);
+ if(v->fn) codefree(v->fn);
+ v->fn=0;
+ v->fnchanged=1;
+ }
+ poplist();
+}
+void Xpipe(void){
+ struct thread *p=runq;
+ int pc=p->pc, forkid;
+ int lfd=p->code[pc++].i;
+ int rfd=p->code[pc++].i;
+ int pfd[2];
+ if(pipe(pfd)<0){
+ Xerror("can't get pipe");
+ return;
+ }
+ switch(forkid=fork()){
+ case -1:
+ Xerror("try again");
+ break;
+ case 0:
+ start(p->code, pc+2, runq->local);
+ runq->ret=0;
+ close(pfd[PRD]);
+ pushredir(ROPEN, pfd[PWR], lfd);
+ break;
+ default:
+ start(p->code, p->code[pc].i, runq->local);
+ close(pfd[PWR]);
+ pushredir(ROPEN, pfd[PRD], rfd);
+ p->pc=p->code[pc+1].i;
+ p->pid=forkid;
+ break;
+ }
+}
+char *concstatus(char *s, char *t)
+{
+ static char v[NSTATUS+1];
+ int n=strlen(s);
+ strncpy(v, s, NSTATUS);
+ if(n<NSTATUS){
+ v[n]='|';
+ strncpy(v+n+1, t, NSTATUS-n-1);
+ }
+ v[NSTATUS]='\0';
+ return v;
+}
+void Xpipewait(void){
+ char status[NSTATUS+1];
+ if(runq->pid==-1)
+ setstatus(concstatus(runq->status, getstatus()));
+ else{
+ strncpy(status, getstatus(), NSTATUS);
+ status[NSTATUS]='\0';
+ Waitfor(runq->pid, 1);
+ runq->pid=-1;
+ setstatus(concstatus(getstatus(), status));
+ }
+}
+void Xrdcmds(void){
+ struct thread *p=runq;
+ word *prompt;
+ flush(err);
+ nerror=0;
+ if(flag['s'] && !truestatus())
+ pfmt(err, "status=%v\n", vlook("status")->val);
+ if(runq->iflag){
+ prompt=vlook("prompt")->val;
+ if(prompt)
+ promptstr=prompt->word;
+ else
+ promptstr="% ";
+ }
+ Noerror();
+ if(yyparse()){
+ if(!p->iflag || p->eof && !Eintr()){
+ if(p->cmdfile) efree(p->cmdfile);
+ closeio(p->cmdfd);
+ Xreturn(); /* should this be omitted? */
+ }
+ else{
+ if(Eintr()){
+ pchr(err, '\n');
+ p->eof=0;
+ }
+ --p->pc; /* go back for next command */
+ }
+ }
+ else{
+ ntrap = 0; /* avoid double-interrupts during blocked writes */
+ --p->pc; /* re-execute Xrdcmds after codebuf runs */
+ start(codebuf, 1, runq->local);
+ }
+ freenodes();
+}
+void Xerror(char *s)
+{
+ if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0)
+ pfmt(err, "rc: %s: %r\n", s);
+ else
+ pfmt(err, "rc (%s): %s: %r\n", argv0, s);
+ flush(err);
+ setstatus("error");
+ while(!runq->iflag) Xreturn();
+}
+void Xerror1(char *s)
+{
+ if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0)
+ pfmt(err, "rc: %s\n", s);
+ else
+ pfmt(err, "rc (%s): %s\n", argv0, s);
+ flush(err);
+ setstatus("error");
+ while(!runq->iflag) Xreturn();
+}
+void Xbackq(void){
+ char wd[8193];
+ int c;
+ char *s, *ewd=&wd[8192], *stop;
+ struct io *f;
+ var *ifs=vlook("ifs");
+ word *v, *nextv;
+ int pfd[2];
+ int pid;
+ stop=ifs->val?ifs->val->word:"";
+ if(pipe(pfd)<0){
+ Xerror("can't make pipe");
+ return;
+ }
+ switch(pid=fork()){
+ case -1: Xerror("try again");
+ close(pfd[PRD]);
+ close(pfd[PWR]);
+ return;
+ case 0:
+ close(pfd[PRD]);
+ start(runq->code, runq->pc+1, runq->local);
+ pushredir(ROPEN, pfd[PWR], 1);
+ return;
+ default:
+ close(pfd[PWR]);
+ f=openfd(pfd[PRD]);
+ s=wd;
+ v=0;
+ while((c=rchr(f))!=EOF){
+ if(strchr(stop, c) || s==ewd){
+ if(s!=wd){
+ *s='\0';
+ v=newword(wd, v);
+ s=wd;
+ }
+ }
+ else *s++=c;
+ }
+ if(s!=wd){
+ *s='\0';
+ v=newword(wd, v);
+ }
+ closeio(f);
+ Waitfor(pid, 0);
+ /* v points to reversed arglist -- reverse it onto argv */
+ while(v){
+ nextv=v->next;
+ v->next=runq->argv->words;
+ runq->argv->words=v;
+ v=nextv;
+ }
+ runq->pc=runq->code[runq->pc].i;
+ return;
+ }
+}
+/*
+ * Who should wait for the exit from the fork?
+ */
+void Xpipefd(void){
+ struct thread *p=runq;
+ int pc=p->pc;
+ char name[40];
+ int pfd[2];
+ int sidefd, mainfd;
+ if(pipe(pfd)<0){
+ Xerror("can't get pipe");
+ return;
+ }
+ if(p->code[pc].i==READ){
+ sidefd=pfd[PWR];
+ mainfd=pfd[PRD];
+ }
+ else{
+ sidefd=pfd[PRD];
+ mainfd=pfd[PWR];
+ }
+ switch(fork()){
+ case -1:
+ Xerror("try again");
+ break;
+ case 0:
+ start(p->code, pc+2, runq->local);
+ close(mainfd);
+ pushredir(ROPEN, sidefd, p->code[pc].i==READ?1:0);
+ runq->ret=0;
+ break;
+ default:
+ close(sidefd);
+ pushredir(ROPEN, mainfd, mainfd); /* isn't this a noop? */
+ strcpy(name, Fdprefix);
+ itoa(name+strlen(name), mainfd);
+ pushword(name);
+ p->pc=p->code[pc+1].i;
+ break;
+ }
+}
+void Xsubshell(void){
+ int pid;
+ switch(pid=fork()){
+ case -1:
+ Xerror("try again");
+ break;
+ case 0:
+ start(runq->code, runq->pc+1, runq->local);
+ runq->ret=0;
+ break;
+ default:
+ Waitfor(pid, 1);
+ runq->pc=runq->code[runq->pc].i;
+ break;
+ }
+}
+void setstatus(char *s)
+{
+ setvar("status", newword(s, (word *)0));
+}
+char *getstatus(void){
+ var *status=vlook("status");
+ return status->val?status->val->word:"";
+}
+int truestatus(void){
+ char *s;
+ for(s=getstatus();*s;s++)
+ if(*s!='|' && *s!='0') return 0;
+ return 1;
+}
+void Xdelhere(void){
+ Unlink(runq->code[runq->pc++].s);
+}
+void Xfor(void){
+ if(runq->argv->words==0){
+ poplist();
+ runq->pc=runq->code[runq->pc].i;
+ }
+ else{
+ freelist(runq->local->val);
+ runq->local->val=runq->argv->words;
+ runq->local->changed=1;
+ runq->argv->words=runq->argv->words->next;
+ runq->local->val->next=0;
+ runq->pc++;
+ }
+}
+void Xglob(void){
+ globlist();
+}
diff --git a/rc/exec.h b/rc/exec.h
@@ -0,0 +1,72 @@
+/*
+ * Definitions used in the interpreter
+ */
+extern void Xappend(void), Xasync(void), Xbackq(void), Xbang(void), Xclose(void);
+extern void Xconc(void), Xcount(void), Xdelfn(void), Xdol(void), Xqdol(void), Xdup(void);
+extern void Xexit(void), Xfalse(void), Xfn(void), Xfor(void), Xglob(void);
+extern void Xjump(void), Xmark(void), Xmatch(void), Xpipe(void), Xread(void);
+extern void Xrdfn(void), Xunredir(void), Xstar(void), Xreturn(void), Xsubshell(void);
+extern void Xtrue(void), Xword(void), Xwrite(void), Xpipefd(void), Xcase(void);
+extern void Xlocal(void), Xunlocal(void), Xassign(void), Xsimple(void), Xpopm(void);
+extern void Xrdcmds(void), Xwastrue(void), Xif(void), Xifnot(void), Xpipewait(void);
+extern void Xdelhere(void), Xpopredir(void), Xsub(void), Xeflag(void), Xsettrue(void);
+extern void Xerror(char*);
+extern void Xerror1(char*);
+/*
+ * word lists are in correct order,
+ * i.e. word0->word1->word2->word3->0
+ */
+struct word{
+ char *word;
+ word *next;
+};
+struct list{
+ word *words;
+ list *next;
+};
+word *newword(char *, word *), *copywords(word *, word *);
+struct redir{
+ char type; /* what to do */
+ short from, to; /* what to do it to */
+ struct redir *next; /* what else to do (reverse order) */
+};
+#define NSTATUS ERRMAX /* length of status (from plan 9) */
+/*
+ * redir types
+ */
+#define ROPEN 1 /* dup2(from, to); close(from); */
+#define RDUP 2 /* dup2(from, to); */
+#define RCLOSE 3 /* close(from); */
+struct thread{
+ union code *code; /* code for this thread */
+ int pc; /* code[pc] is the next instruction */
+ struct list *argv; /* argument stack */
+ struct redir *redir; /* redirection stack */
+ struct redir *startredir; /* redir inheritance point */
+ struct var *local; /* list of local variables */
+ char *cmdfile; /* file name in Xrdcmd */
+ struct io *cmdfd; /* file descriptor for Xrdcmd */
+ int iflast; /* static `if not' checking */
+ int eof; /* is cmdfd at eof? */
+ int iflag; /* interactive? */
+ int lineno; /* linenumber */
+ int pid; /* process for Xpipewait to wait for */
+ int done; /* have we seen a wait message for this process? */
+ char status[NSTATUS]; /* status for Xpipewait */
+ tree *treenodes; /* tree nodes created by this process */
+ thread *ret; /* who continues when this finishes */
+};
+thread *runq;
+code *codecopy(code*);
+code *codebuf; /* compiler output */
+int ntrap; /* number of outstanding traps */
+int trap[NSIG]; /* number of outstanding traps per type */
+extern struct builtin{
+ char *name;
+ void (*fnc)(void);
+}Builtin[];
+int eflagok; /* kludge flag so that -e doesn't exit in startup */
+void execcd(void), execwhatis(void), execeval(void), execexec(void);
+void execexit(void), execshift(void);
+void execwait(void), execumask(void), execdot(void), execflag(void);
+void execfunc(var*), execcmds(io *);
diff --git a/rc/fmtquote.c b/rc/fmtquote.c
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+#include <u.h>
+#include <libc.h>
+#include "fmt.h"
+#include "fmtdef.h"
+
+extern int (*doquote)(int);
+
+/*
+ * How many bytes of output UTF will be produced by quoting (if necessary) this string?
+ * How many runes? How much of the input will be consumed?
+ * The parameter q is filled in by _quotesetup.
+ * The string may be UTF or Runes (s or r).
+ * Return count does not include NUL.
+ * Terminate the scan at the first of:
+ * NUL in input
+ * count exceeded in input
+ * count exceeded on output
+ * *ninp is set to number of input bytes accepted.
+ * nin may be <0 initially, to avoid checking input by count.
+ */
+void
+__quotesetup(char *s, int nin, int nout, Quoteinfo *q, int sharp)
+{
+ int c;
+
+ q->quoted = 0;
+ q->nbytesout = 0;
+ q->nrunesout = 0;
+ q->nbytesin = 0;
+ q->nrunesin = 0;
+ if(sharp || nin==0 || *s=='\0'){
+ if(nout < 2)
+ return;
+ q->quoted = 1;
+ q->nbytesout = 2;
+ q->nrunesout = 2;
+ }
+ for(; nin!=0; nin-=1){
+ c = *s;
+
+ if(c == '\0')
+ break;
+ if(q->nrunesout+1 > nout)
+ break;
+
+ if((c <= L' ') || (c == L'\'') || (doquote!=nil && doquote(c))){
+ if(!q->quoted){
+ if(1+q->nrunesout+1+1 > nout) /* no room for quotes */
+ break;
+ q->nrunesout += 2; /* include quotes */
+ q->nbytesout += 2; /* include quotes */
+ q->quoted = 1;
+ }
+ if(c == '\'') {
+ q->nbytesout++;
+ q->nrunesout++; /* quotes reproduce as two characters */
+ }
+ }
+
+ /* advance input */
+ s++;
+ q->nbytesin++;
+ q->nrunesin++;
+
+ /* advance output */
+ q->nbytesout++;
+ q->nrunesout++;
+ }
+}
+
+static int
+qstrfmt(char *sin, Quoteinfo *q, Fmt *f)
+{
+ int r;
+ char *t, *s, *m, *me;
+ ulong fl;
+ int nc, w;
+
+ m = sin;
+ me = m + q->nbytesin;
+
+ w = f->width;
+ fl = f->flags;
+ if(!(fl & FmtLeft) && __fmtpad(f, w - q->nbytesout) < 0)
+ return -1;
+ t = f->to;
+ s = f->stop;
+ FMTCHAR(f, t, s, '\'');
+ for(nc = q->nrunesin; nc > 0; nc--){
+ r = *(uchar*)m++;
+ FMTCHAR(f, t, s, r);
+ if(r == '\'')
+ FMTCHAR(f, t, s, r);
+ }
+
+ FMTCHAR(f, t, s, '\'');
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ if(fl & FmtLeft && __fmtpad(f, w - q->nbytesout) < 0)
+ return -1;
+ return 0;
+}
+
+int
+__quotestrfmt(int runesin, Fmt *f)
+{
+ int outlen;
+ char *s;
+ Quoteinfo q;
+
+ f->flags &= ~FmtPrec; /* ignored for %q %Q, so disable for %s %S in easy case */
+ s = va_arg(f->args, char *);
+ if(!s)
+ return __fmtcpy(f, "<nil>", 5, 5);
+
+ if(f->flush)
+ outlen = 0x7FFFFFFF; /* if we can flush, no output limit */
+ else
+ outlen = (char*)f->stop - (char*)f->to;
+
+ __quotesetup(s, -1, outlen, &q, f->flags&FmtSharp);
+
+ if(!q.quoted)
+ return __fmtcpy(f, s, q.nrunesin, q.nbytesin);
+ return qstrfmt(s, &q, f);
+}
+
+int
+quotestrfmt(Fmt *f)
+{
+ return __quotestrfmt(0, f);
+}
+
+void
+quotefmtinstall(void)
+{
+ fmtinstall('q', quotestrfmt);
+}
+
+int
+__needsquotes(char *s, int *quotelenp)
+{
+ Quoteinfo q;
+
+ __quotesetup(s, -1, 0x7FFFFFFF, &q, 0);
+ *quotelenp = q.nbytesout;
+
+ return q.quoted;
+}
diff --git a/rc/fns.h b/rc/fns.h
@@ -0,0 +1,61 @@
+void Abort(void);
+void Closedir(int);
+int Creat(char*);
+int Dup(int, int);
+int Dup1(int);
+int Eintr(void);
+int Executable(char*);
+void Execute(word*, word*);
+void Exit(char*);
+int Globsize(char*);
+int Isatty(int);
+void Memcpy(char*, char*, long);
+void Noerror(void);
+int Opendir(char*);
+long Read(int, char*, long);
+int Readdir(int, char*);
+long Seek(int, long, long);
+void Trapinit(void);
+void Unlink(char*);
+void Updenv(void);
+void Vinit(void);
+int Waitfor(int, int);
+long Write(int, char*, long);
+int advance(void);
+int back(int);
+void cleanhere(char*);
+void codefree(code*);
+int compile(tree*);
+char * list2str(word*);
+char * _list2str(word*, int);
+int count(word*);
+void deglob(char*);
+void dotrap(void);
+void freenodes(void);
+void freewords(word*);
+void globlist(void);
+int idchr(int);
+void itoa(char*, long);
+void kinit(void);
+int match(char*, char*, int);
+int matchfn(char*, char*);
+void panic(char*, int);
+void pathinit(void);
+void poplist(void);
+void popword(void);
+void pprompt(void);
+void pushlist(void);
+void pushredir(int, int, int);
+void pushword(char*);
+void readhere(void);
+void setstatus(char*);
+void setvar(char*, word*);
+void _setvar(char*, word*, int);
+void skipnl(void);
+void start(code*, int, var*);
+int truestatus(void);
+void usage(char*);
+int wordchr(int);
+void yyerror(char*);
+int yylex(void);
+int yyparse(void);
diff --git a/rc/getflags.c b/rc/getflags.c
@@ -0,0 +1,217 @@
+/*% cyntax -DTEST % && cc -DTEST -go # %
+ */
+#include "rc.h"
+#include "getflags.h"
+#include "fns.h"
+char *flagset[]={"<flag>"};
+char **flag[NFLAG];
+char cmdline[NCMDLINE+1];
+char *cmdname;
+static char *flagarg="";
+static void reverse(char**, char**);
+static int scanflag(int, char*);
+static void errn(char*, int);
+static void errs(char*);
+static void errc(int);
+static int reason;
+#define RESET 1
+#define FEWARGS 2
+#define FLAGSYN 3
+#define BADFLAG 4
+static int badflag;
+int getflags(int argc, char *argv[], char *flags, int stop)
+{
+ char *s, *t;
+ int i, j, c, count;
+ flagarg=flags;
+ if(cmdname==0) cmdname=argv[0];
+ s=cmdline;
+ for(i=0;i!=argc;i++){
+ for(t=argv[i];*t;t++)
+ if(s!=&cmdline[NCMDLINE])
+ *s++=*t;
+ if(i!=argc-1 && s!=&cmdline[NCMDLINE])
+ *s++=' ';
+ }
+ *s='\0';
+ i=1;
+ while(i!=argc){
+ if(argv[i][0]!='-' || argv[i][1]=='\0'){
+ if(stop) return argc;
+ i++;
+ continue;
+ }
+ s=argv[i]+1;
+ while(*s){
+ c=*s++;
+ count=scanflag(c, flags);
+ if(count==-1) return -1;
+ if(flag[c]){ reason=RESET; badflag=c; return -1; }
+ if(count==0){
+ flag[c]=flagset;
+ if(*s=='\0'){
+ for(j=i+1;j<=argc;j++)
+ argv[j-1]=argv[j];
+ --argc;
+ }
+ }
+ else{
+ if(*s=='\0'){
+ for(j=i+1;j<=argc;j++)
+ argv[j-1]=argv[j];
+ --argc;
+ s=argv[i];
+ }
+ if(argc-i<count){
+ reason=FEWARGS;
+ badflag=c;
+ return -1;
+ }
+ reverse(argv+i, argv+argc);
+ reverse(argv+i, argv+argc-count);
+ reverse(argv+argc-count+1, argv+argc);
+ argc-=count;
+ flag[c]=argv+argc+1;
+ flag[c][0]=s;
+ s="";
+ }
+ }
+ }
+ return argc;
+}
+static void reverse(char **p, char **q)
+{
+ char *t;
+ for(;p<q;p++,--q){ t=*p; *p=*q; *q=t; }
+}
+static int scanflag(int c, char *f)
+{
+ int fc, count;
+ if(0<=c && c<NFLAG) while(*f){
+ if(*f==' '){
+ f++;
+ continue;
+ }
+ fc=*f++;
+ if(*f==':'){
+ f++;
+ if(*f<'0' || '9'<*f){ reason=FLAGSYN; return -1; }
+ count=0;
+ while('0'<=*f && *f<='9') count=count*10+*f++-'0';
+ }
+ else
+ count=0;
+ if(*f=='['){
+ do{
+ f++;
+ if(*f=='\0'){ reason=FLAGSYN; return -1; }
+ }while(*f!=']');
+ f++;
+ }
+ if(c==fc) return count;
+ }
+ reason=BADFLAG;
+ badflag=c;
+ return -1;
+}
+void usage(char *tail)
+{
+ char *s, *t, c;
+ int count, nflag=0;
+ switch(reason){
+ case RESET:
+ errs("Flag -");
+ errc(badflag);
+ errs(": set twice\n");
+ break;
+ case FEWARGS:
+ errs("Flag -");
+ errc(badflag);
+ errs(": too few arguments\n");
+ break;
+ case FLAGSYN:
+ errs("Bad argument to getflags!\n");
+ break;
+ case BADFLAG:
+ errs("Illegal flag -");
+ errc(badflag);
+ errc('\n');
+ break;
+ }
+ errs("Usage: ");
+ errs(cmdname);
+ for(s=flagarg;*s;){
+ c=*s;
+ if(*s++==' ') continue;
+ if(*s==':'){
+ s++;
+ count=0;
+ while('0'<=*s && *s<='9') count=count*10+*s++-'0';
+ }
+ else count=0;
+ if(count==0){
+ if(nflag==0) errs(" [-");
+ nflag++;
+ errc(c);
+ }
+ if(*s=='['){
+ s++;
+ while(*s!=']' && *s!='\0') s++;
+ if(*s==']') s++;
+ }
+ }
+ if(nflag) errs("]");
+ for(s=flagarg;*s;){
+ c=*s;
+ if(*s++==' ') continue;
+ if(*s==':'){
+ s++;
+ count=0;
+ while('0'<=*s && *s<='9') count=count*10+*s++-'0';
+ }
+ else count=0;
+ if(count!=0){
+ errs(" [-");
+ errc(c);
+ if(*s=='['){
+ s++;
+ t=s;
+ while(*s!=']' && *s!='\0') s++;
+ errs(" ");
+ errn(t, s-t);
+ if(*s==']') s++;
+ }
+ else
+ while(count--) errs(" arg");
+ errs("]");
+ }
+ else if(*s=='['){
+ s++;
+ while(*s!=']' && *s!='\0') s++;
+ if(*s==']') s++;
+ }
+ }
+ if(tail){
+ errs(" ");
+ errs(tail);
+ }
+ errs("\n");
+ Exit("bad flags");
+}
+static void errn(char *s, int count)
+{
+ while(count){ errc(*s++); --count; }
+}
+static void errs(char *s)
+{
+ while(*s) errc(*s++);
+}
+#define NBUF 80
+static char buf[NBUF], *bufp=buf;
+static void errc(int c){
+ *bufp++=c;
+ if(bufp==&buf[NBUF] || c=='\n'){
+ Write(2, buf, bufp-buf);
+ bufp=buf;
+ }
+}
diff --git a/rc/getflags.h b/rc/getflags.h
@@ -0,0 +1,7 @@
+#define NFLAG 128
+#define NCMDLINE 512
+extern char **flag[NFLAG];
+extern char cmdline[NCMDLINE+1];
+extern char *cmdname;
+extern char *flagset[];
+int getflags(int, char*[], char*, int);
diff --git a/rc/glob.c b/rc/glob.c
@@ -0,0 +1,211 @@
+#include "rc.h"
+#include "exec.h"
+#include "fns.h"
+char *globname;
+struct word *globv;
+/*
+ * delete all the GLOB marks from s, in place
+ */
+void deglob(char *s)
+{
+ char *t=s;
+ do{
+ if(*t==GLOB) t++;
+ *s++=*t;
+ }while(*t++);
+}
+int globcmp(const void *s, const void *t)
+{
+ return strcmp(*(char**)s, *(char**)t);
+}
+void globsort(word *left, word *right)
+{
+ char **list;
+ word *a;
+ int n=0;
+ for(a=left;a!=right;a=a->next) n++;
+ list=(char **)emalloc(n*sizeof(char *));
+ for(a=left,n=0;a!=right;a=a->next,n++) list[n]=a->word;
+ qsort((char *)list, n, sizeof(char *), globcmp);
+ for(a=left,n=0;a!=right;a=a->next,n++) a->word=list[n];
+ efree((char *)list);
+}
+/*
+ * Push names prefixed by globname and suffixed by a match of p onto the astack.
+ * namep points to the end of the prefix in globname.
+ */
+void globdir(char *p, char *namep)
+{
+ char *t, *newp;
+ int f;
+ /* scan the pattern looking for a component with a metacharacter in it */
+ if(*p=='\0'){
+ globv=newword(globname, globv);
+ return;
+ }
+ t=namep;
+ newp=p;
+ while(*newp){
+ if(*newp==GLOB)
+ break;
+ *t=*newp++;
+ if(*t++=='/'){
+ namep=t;
+ p=newp;
+ }
+ }
+ /* If we ran out of pattern, append the name if accessible */
+ if(*newp=='\0'){
+ *t='\0';
+ if(access(globname, 0)==0)
+ globv=newword(globname, globv);
+ return;
+ }
+ /* read the directory and recur for any entry that matches */
+ *namep='\0';
+ if((f=Opendir(globname[0]?globname:"."))<0) return;
+ while(*newp!='/' && *newp!='\0') newp++;
+ while(Readdir(f, namep)){
+ if(matchfn(namep, p)){
+ for(t=namep;*t;t++);
+ globdir(newp, t);
+ }
+ }
+ Closedir(f);
+}
+/*
+ * Push all file names matched by p on the current thread's stack.
+ * If there are no matches, the list consists of p.
+ */
+void glob(char *p)
+{
+ word *svglobv=globv;
+ int globlen=Globsize(p);
+ if(!globlen){
+ deglob(p);
+ globv=newword(p, globv);
+ return;
+ }
+ globname=emalloc(globlen);
+ globname[0]='\0';
+ globdir(p, globname);
+ efree(globname);
+ if(svglobv==globv){
+ deglob(p);
+ globv=newword(p, globv);
+ }
+ else
+ globsort(globv, svglobv);
+}
+/*
+ * Do p and q point at equal utf codes
+ */
+int equtf(char *p, char *q){
+ if(*p!=*q) return 0;
+ if(twobyte(*p)) return p[1]==q[1];
+ if(threebyte(*p)){
+ if(p[1]!=q[1]) return 0;
+ if(p[1]=='\0') return 1; /* broken code at end of string! */
+ return p[2]==q[2];
+ }
+ return 1;
+}
+/*
+ * Return a pointer to the next utf code in the string,
+ * not jumping past nuls in broken utf codes!
+ */
+char *nextutf(char *p){
+ if(twobyte(*p)) return p[1]=='\0'?p+1:p+2;
+ if(threebyte(*p)) return p[1]=='\0'?p+1:p[2]=='\0'?p+2:p+3;
+ return p+1;
+}
+/*
+ * Convert the utf code at *p to a unicode value
+ */
+int unicode(char *p){
+ int u=*p&0xff;
+ if(twobyte(u)) return ((u&0x1f)<<6)|(p[1]&0x3f);
+ if(threebyte(u)) return (u<<12)|((p[1]&0x3f)<<6)|(p[2]&0x3f);
+ return u;
+}
+/*
+ * Does the string s match the pattern p
+ * . and .. are only matched by patterns starting with .
+ * * matches any sequence of characters
+ * ? matches any single character
+ * [...] matches the enclosed list of characters
+ */
+int matchfn(char *s, char *p)
+{
+ if(s[0]=='.' && (s[1]=='\0' || s[1]=='.' && s[2]=='\0') && p[0]!='.')
+ return 0;
+ return match(s, p, '/');
+}
+int match(char *s, char *p, int stop)
+{
+ int compl, hit, lo, hi, t, c;
+ for(;*p!=stop && *p!='\0';s=nextutf(s),p=nextutf(p)){
+ if(*p!=GLOB){
+ if(!equtf(p, s)) return 0;
+ }
+ else switch(*++p){
+ case GLOB:
+ if(*s!=GLOB) return 0;
+ break;
+ case '*':
+ for(;;){
+ if(match(s, nextutf(p), stop)) return 1;
+ if(!*s) break;
+ s=nextutf(s);
+ }
+ return 0;
+ case '?':
+ if(*s=='\0') return 0;
+ break;
+ case '[':
+ if(*s=='\0') return 0;
+ c=unicode(s);
+ p++;
+ compl=*p=='~';
+ if(compl) p++;
+ hit=0;
+ while(*p!=']'){
+ if(*p=='\0') return 0; /* syntax error */
+ lo=unicode(p);
+ p=nextutf(p);
+ if(*p!='-') hi=lo;
+ else{
+ p++;
+ if(*p=='\0') return 0; /* syntax error */
+ hi=unicode(p);
+ p=nextutf(p);
+ if(hi<lo){ t=lo; lo=hi; hi=t; }
+ }
+ if(lo<=c && c<=hi) hit=1;
+ }
+ if(compl) hit=!hit;
+ if(!hit) return 0;
+ break;
+ }
+ }
+ return *s=='\0';
+}
+void globlist1(word *gl)
+{
+ if(gl){
+ globlist1(gl->next);
+ glob(gl->word);
+ }
+}
+void globlist(void){
+ word *a;
+ globv=0;
+ globlist1(runq->argv->words);
+ poplist();
+ pushlist();
+ if(globv){
+ for(a=globv;a->next;a=a->next);
+ a->next=runq->argv->words;
+ runq->argv->words=globv;
+ }
+}
diff --git a/rc/havefork.c b/rc/havefork.c
@@ -0,0 +1,222 @@
+#include <u.h>
+#include <signal.h>
+#include "rc.h"
+#include "getflags.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+
+int havefork = 1;
+
+void
+Xasync(void)
+{
+ int null = open("/dev/null", 0);
+ int pid;
+ int tcpgrp, pgrp;
+ char npid[10];
+
+ if(null<0){
+ Xerror("Can't open /dev/null\n");
+ return;
+ }
+ switch(pid = rfork(RFFDG|RFPROC|RFNOTEG)){
+ case -1:
+ close(null);
+ Xerror("try again");
+ break;
+ case 0:
+ /*
+ * Should make reads of tty fail, writes succeed.
+ */
+ signal(SIGTTIN, SIG_IGN);
+ signal(SIGTTOU, SIG_IGN);
+
+ pushredir(ROPEN, null, 0);
+ start(runq->code, runq->pc+1, runq->local);
+ runq->ret = 0;
+ break;
+ default:
+ close(null);
+ runq->pc = runq->code[runq->pc].i;
+ inttoascii(npid, pid);
+ setvar("apid", newword(npid, (word *)0));
+ break;
+ }
+}
+
+void
+Xpipe(void)
+{
+ struct thread *p = runq;
+ int pc = p->pc, forkid;
+ int lfd = p->code[pc++].i;
+ int rfd = p->code[pc++].i;
+ int pfd[2];
+ if(pipe(pfd)<0){
+ Xerror("can't get pipe");
+ return;
+ }
+ switch(forkid = fork()){
+ case -1:
+ Xerror("try again");
+ break;
+ case 0:
+ start(p->code, pc+2, runq->local);
+ runq->ret = 0;
+ close(pfd[PRD]);
+ pushredir(ROPEN, pfd[PWR], lfd);
+ break;
+ default:
+ start(p->code, p->code[pc].i, runq->local);
+ close(pfd[PWR]);
+ pushredir(ROPEN, pfd[PRD], rfd);
+ p->pc = p->code[pc+1].i;
+ p->pid = forkid;
+ break;
+ }
+}
+
+/*
+ * Who should wait for the exit from the fork?
+ */
+void
+Xbackq(void)
+{
+ char wd[8193];
+ int c;
+ char *s, *ewd=&wd[8192], *stop;
+ struct io *f;
+ var *ifs = vlook("ifs");
+ word *v, *nextv;
+ int pfd[2];
+ int pid;
+ stop = ifs->val?ifs->val->word:"";
+ if(pipe(pfd)<0){
+ Xerror("can't make pipe");
+ return;
+ }
+ switch(pid = fork()){
+ case -1:
+ Xerror("try again");
+ close(pfd[PRD]);
+ close(pfd[PWR]);
+ return;
+ case 0:
+ close(pfd[PRD]);
+ start(runq->code, runq->pc+1, runq->local);
+ pushredir(ROPEN, pfd[PWR], 1);
+ return;
+ default:
+ close(pfd[PWR]);
+ f = openfd(pfd[PRD]);
+ s = wd;
+ v = 0;
+ while((c = rchr(f))!=EOF){
+ if(strchr(stop, c) || s==ewd){
+ if(s!=wd){
+ *s='\0';
+ v = newword(wd, v);
+ s = wd;
+ }
+ }
+ else *s++=c;
+ }
+ if(s!=wd){
+ *s='\0';
+ v = newword(wd, v);
+ }
+ closeio(f);
+ Waitfor(pid, 0);
+ /* v points to reversed arglist -- reverse it onto argv */
+ while(v){
+ nextv = v->next;
+ v->next = runq->argv->words;
+ runq->argv->words = v;
+ v = nextv;
+ }
+ runq->pc = runq->code[runq->pc].i;
+ return;
+ }
+}
+
+void
+Xpipefd(void)
+{
+ struct thread *p = runq;
+ int pc = p->pc;
+ char name[40];
+ int pfd[2];
+ int sidefd, mainfd;
+ if(pipe(pfd)<0){
+ Xerror("can't get pipe");
+ return;
+ }
+ if(p->code[pc].i==READ){
+ sidefd = pfd[PWR];
+ mainfd = pfd[PRD];
+ }
+ else{
+ sidefd = pfd[PRD];
+ mainfd = pfd[PWR];
+ }
+ switch(fork()){
+ case -1:
+ Xerror("try again");
+ break;
+ case 0:
+ start(p->code, pc+2, runq->local);
+ close(mainfd);
+ pushredir(ROPEN, sidefd, p->code[pc].i==READ?1:0);
+ runq->ret = 0;
+ break;
+ default:
+ close(sidefd);
+ pushredir(ROPEN, mainfd, mainfd); /* isn't this a noop? */
+ strcpy(name, Fdprefix);
+ inttoascii(name+strlen(name), mainfd);
+ pushword(name);
+ p->pc = p->code[pc+1].i;
+ break;
+ }
+}
+
+void
+Xsubshell(void)
+{
+ int pid;
+ switch(pid = fork()){
+ case -1:
+ Xerror("try again");
+ break;
+ case 0:
+ start(runq->code, runq->pc+1, runq->local);
+ runq->ret = 0;
+ break;
+ default:
+ Waitfor(pid, 1);
+ runq->pc = runq->code[runq->pc].i;
+ break;
+ }
+}
+
+int
+execforkexec(void)
+{
+ int pid;
+ int n;
+ char buf[ERRMAX];
+
+ switch(pid = fork()){
+ case -1:
+ return -1;
+ case 0:
+ pushword("exec");
+ execexec();
+ strcpy(buf, "can't exec: ");
+ n = strlen(buf);
+ errstr(buf+n, ERRMAX-n);
+ Exit(buf);
+ }
+ return pid;
+}
diff --git a/rc/haventfork.c b/rc/haventfork.c
@@ -0,0 +1,211 @@
+#include "rc.h"
+#include "getflags.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+
+int havefork = 0;
+
+static char **
+rcargv(char *s)
+{
+ int argc;
+ char **argv;
+ word *p;
+
+ p = vlook("*")->val;
+ argv = malloc((count(p)+6)*sizeof(char*));
+ argc = 0;
+ argv[argc++] = argv0;
+ if(flag['e'])
+ argv[argc++] = "-Se";
+ else
+ argv[argc++] = "-S";
+ argv[argc++] = "-c";
+ argv[argc++] = s;
+ for(p = vlook("*")->val; p; p = p->next)
+ argv[argc++] = p->word;
+ argv[argc] = 0;
+ return argv;
+}
+
+void
+Xasync(void)
+{
+ uint pid;
+ char buf[20], **argv;
+
+ Updenv();
+
+ argv = rcargv(runq->code[runq->pc].s);
+ pid = ForkExecute(argv0, argv, -1, 1, 2);
+ free(argv);
+
+ if(pid == 0) {
+ Xerror("proc failed");
+ return;
+ }
+
+ runq->pc++;
+ sprint(buf, "%d", pid);
+ setvar("apid", newword(buf, (word *)0));
+}
+
+void
+Xbackq(void)
+{
+ char wd[8193], **argv;
+ int c;
+ char *s, *ewd=&wd[8192], *stop;
+ struct io *f;
+ var *ifs = vlook("ifs");
+ word *v, *nextv;
+ int pfd[2];
+ int pid;
+
+ stop = ifs->val?ifs->val->word:"";
+ if(pipe(pfd)<0){
+ Xerror("can't make pipe");
+ return;
+ }
+
+ Updenv();
+
+ argv = rcargv(runq->code[runq->pc].s);
+ pid = ForkExecute(argv0, argv, -1, pfd[1], 2);
+ free(argv);
+
+ close(pfd[1]);
+
+ if(pid == 0) {
+ Xerror("proc failed");
+ close(pfd[0]);
+ return;
+ }
+
+ f = openfd(pfd[0]);
+ s = wd;
+ v = 0;
+ while((c=rchr(f))!=EOF){
+ if(strchr(stop, c) || s==ewd){
+ if(s!=wd){
+ *s='\0';
+ v=newword(wd, v);
+ s=wd;
+ }
+ }
+ else *s++=c;
+ }
+ if(s!=wd){
+ *s='\0';
+ v=newword(wd, v);
+ }
+ closeio(f);
+ Waitfor(pid, 1);
+ /* v points to reversed arglist -- reverse it onto argv */
+ while(v){
+ nextv=v->next;
+ v->next=runq->argv->words;
+ runq->argv->words=v;
+ v=nextv;
+ }
+ runq->pc++;
+}
+
+void
+Xpipe(void)
+{
+ thread *p=runq;
+ int pc=p->pc, pid;
+ int rfd=p->code[pc+1].i;
+ int pfd[2];
+ char **argv;
+
+ if(pipe(pfd)<0){
+ Xerror1("can't get pipe");
+ return;
+ }
+
+ Updenv();
+
+ argv = rcargv(runq->code[pc+2].s);
+ pid = ForkExecute(argv0, argv, 0, pfd[1], 2);
+ free(argv);
+ close(pfd[1]);
+
+ if(pid == 0) {
+ Xerror("proc failed");
+ close(pfd[0]);
+ return;
+ }
+
+ start(p->code, pc+4, runq->local);
+ pushredir(ROPEN, pfd[0], rfd);
+ p->pc=p->code[pc+3].i;
+ p->pid=pid;
+}
+
+void
+Xpipefd(void)
+{
+ Abort();
+}
+
+void
+Xsubshell(void)
+{
+ char **argv;
+ int pid;
+
+ Updenv();
+
+ argv = rcargv(runq->code[runq->pc].s);
+ pid = ForkExecute(argv0, argv, -1, 1, 2);
+ free(argv);
+
+ if(pid < 0) {
+ Xerror("proc failed");
+ return;
+ }
+
+ Waitfor(pid, 1);
+ runq->pc++;
+}
+
+/*
+ * start a process running the cmd on the stack and return its pid.
+ */
+int
+execforkexec(void)
+{
+ char **argv;
+ char file[1024];
+ int nc;
+ word *path;
+ int pid;
+
+ if(runq->argv->words==0)
+ return -1;
+ argv = mkargv(runq->argv->words);
+
+ for(path = searchpath(runq->argv->words->word);path;path = path->next){
+ nc = strlen(path->word);
+ if(nc<sizeof(file)){
+ strcpy(file, path->word);
+ if(file[0]){
+ strcat(file, "/");
+ nc++;
+ }
+ if(nc+strlen(argv[1])<sizeof(file)){
+ strcat(file, argv[1]);
+ pid = ForkExecute(file, argv+1, mapfd(0), mapfd(1), mapfd(2));
+ if(pid >= 0){
+ free(argv);
+ return pid;
+ }
+ }
+ }
+ }
+ free(argv);
+ return -1;
+}
diff --git a/rc/here.c b/rc/here.c
@@ -0,0 +1,131 @@
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+struct here *here, **ehere;
+int ser=0;
+char tmp[]="/tmp/here0000.0000";
+char hex[]="0123456789abcdef";
+void psubst(io*, char*);
+void pstrs(io*, word*);
+void hexnum(char *p, int n)
+{
+ *p++=hex[(n>>12)&0xF];
+ *p++=hex[(n>>8)&0xF];
+ *p++=hex[(n>>4)&0xF];
+ *p=hex[n&0xF];
+}
+tree *heredoc(tree *tag)
+{
+ struct here *h=new(struct here);
+ if(tag->type!=WORD) yyerror("Bad here tag");
+ h->next=0;
+ if(here)
+ *ehere=h;
+ else
+ here=h;
+ ehere=&h->next;
+ h->tag=tag;
+ hexnum(&tmp[9], getpid());
+ hexnum(&tmp[14], ser++);
+ h->name=strdup(tmp);
+ return token(tmp, WORD);
+}
+/*
+ * bug: lines longer than NLINE get split -- this can cause spurious
+ * missubstitution, or a misrecognized EOF marker.
+ */
+#define NLINE 4096
+void readhere(void){
+ struct here *h, *nexth;
+ io *f;
+ char *s, *tag;
+ int c, subst;
+ char line[NLINE+1];
+ for(h=here;h;h=nexth){
+ subst=!h->tag->quoted;
+ tag=h->tag->str;
+ c=Creat(h->name);
+ if(c<0) yyerror("can't create here document");
+ f=openfd(c);
+ s=line;
+ pprompt();
+ while((c=rchr(runq->cmdfd))!=EOF){
+ if(c=='\n' || s==&line[NLINE]){
+ *s='\0';
+ if(strcmp(line, tag)==0) break;
+ if(subst) psubst(f, line);
+ else pstr(f, line);
+ s=line;
+ if(c=='\n'){
+ pprompt();
+ pchr(f, c);
+ }
+ else *s++=c;
+ }
+ else *s++=c;
+ }
+ flush(f);
+ closeio(f);
+ cleanhere(h->name);
+ nexth=h->next;
+ efree((char *)h);
+ }
+ here=0;
+ doprompt=1;
+}
+void psubst(io *f, char *s)
+{
+ char *t, *u;
+ int savec, n;
+ word *star;
+ while(*s){
+ if(*s!='$'){
+ if(0xa0<=(*s&0xff) && (*s&0xff)<=0xf5){
+ pchr(f, *s++);
+ if(*s=='\0') break;
+ }
+ else if(0xf6<=(*s&0xff) && (*s&0xff)<=0xf7){
+ pchr(f, *s++);
+ if(*s=='\0') break;
+ pchr(f, *s++);
+ if(*s=='\0') break;
+ }
+ pchr(f, *s++);
+ }
+ else{
+ t=++s;
+ if(*t=='$') pchr(f, *t++);
+ else{
+ while(*t && idchr(*t)) t++;
+ savec=*t;
+ *t='\0';
+ n=0;
+ for(u=s;*u && '0'<=*u && *u<='9';u++) n=n*10+*u-'0';
+ if(n && *u=='\0'){
+ star=vlook("*")->val;
+ if(star && 1<=n && n<=count(star)){
+ while(--n) star=star->next;
+ pstr(f, star->word);
+ }
+ }
+ else
+ pstrs(f, vlook(s)->val);
+ *t=savec;
+ if(savec=='^') t++;
+ }
+ s=t;
+ }
+ }
+}
+void pstrs(io *f, word *a)
+{
+ if(a){
+ while(a->next && a->next->word){
+ pstr(f, a->word);
+ pchr(f, ' ');
+ a=a->next;
+ }
+ pstr(f, a->word);
+ }
+}
diff --git a/rc/io.c b/rc/io.c
@@ -0,0 +1,182 @@
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+int pfmtnest=0;
+void pfmt(io *f, char *fmt, ...){
+ va_list ap;
+ char err[ERRMAX];
+ va_start(ap, fmt);
+ pfmtnest++;
+ for(;*fmt;fmt++)
+ if(*fmt!='%') pchr(f, *fmt);
+ else switch(*++fmt){
+ case '\0': va_end(ap); return;
+ case 'c': pchr(f, va_arg(ap, int)); break;
+ case 'd': pdec(f, va_arg(ap, int)); break;
+ case 'o': poct(f, va_arg(ap, unsigned)); break;
+ case 'p': phex(f, (long)va_arg(ap, char *)); break; /*unportable*/
+ case 'Q': pquo(f, va_arg(ap, char *)); break;
+ case 'q': pwrd(f, va_arg(ap, char *)); break;
+ case 'r': errstr(err, sizeof err); pstr(f, err); break;
+ case 's': pstr(f, va_arg(ap, char *)); break;
+ case 't': pcmd(f, va_arg(ap, struct tree *)); break;
+ case 'v': pval(f, va_arg(ap, struct word *)); break;
+ default: pchr(f, *fmt); break;
+ }
+ va_end(ap);
+ if(--pfmtnest==0) flush(f);
+}
+void pchr(io *b, int c)
+{
+ if(b->bufp==b->ebuf) fullbuf(b, c);
+ else *b->bufp++=c;
+}
+int rchr(io *b)
+{
+ if(b->bufp==b->ebuf) return emptybuf(b);
+ return *b->bufp++ & 0xFF;
+}
+
+void pquo(io *f, char *s)
+{
+ pchr(f, '\'');
+ for(;*s;s++)
+ if(*s=='\'') pfmt(f, "''");
+ else pchr(f, *s);
+ pchr(f, '\'');
+}
+void pwrd(io *f, char *s)
+{
+ char *t;
+ for(t=s;*t;t++) if(!wordchr(*t)) break;
+ if(t==s || *t) pquo(f, s);
+ else pstr(f, s);
+}
+void phex(io *f, long p)
+{
+ int n;
+ for(n=28;n>=0;n-=4) pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]);
+}
+void pstr(io *f, char *s)
+{
+ if(s==0) s="(null)";
+ while(*s) pchr(f, *s++);
+}
+void pdec(io *f, long n)
+{
+ if(n<0){
+ n=-n;
+ if(n>=0){
+ pchr(f, '-');
+ pdec(f, n);
+ return;
+ }
+ /* n is two's complement minimum integer */
+ n=1-n;
+ pchr(f, '-');
+ pdec(f, n/10);
+ pchr(f, n%10+'1');
+ return;
+ }
+ if(n>9) pdec(f, n/10);
+ pchr(f, n%10+'0');
+}
+void poct(io *f, ulong n)
+{
+ if(n>7) poct(f, n>>3);
+ pchr(f, (n&7)+'0');
+}
+void pval(io *f, word *a)
+{
+ if(a){
+ while(a->next && a->next->word){
+ pwrd(f, a->word);
+ pchr(f, ' ');
+ a=a->next;
+ }
+ pwrd(f, a->word);
+ }
+}
+int fullbuf(io *f, int c)
+{
+ flush(f);
+ return *f->bufp++=c;
+}
+void flush(io *f)
+{
+ int n;
+ char *s;
+ if(f->strp){
+ n=f->ebuf-f->strp;
+ f->strp=realloc(f->strp, n+101);
+ if(f->strp==0) panic("Can't realloc %d bytes in flush!", n+101);
+ f->bufp=f->strp+n;
+ f->ebuf=f->bufp+100;
+ for(s=f->bufp;s<=f->ebuf;s++) *s='\0';
+ }
+ else{
+ n=f->bufp-f->buf;
+ if(n && Write(f->fd, f->buf, n) < 0){
+ Write(3, "Write error\n", 12);
+ if(ntrap) dotrap();
+ }
+ f->bufp=f->buf;
+ f->ebuf=f->buf+NBUF;
+ }
+}
+io *openfd(int fd){
+ io *f;
+ f=new(struct io);
+ f->fd=fd;
+ f->bufp=f->ebuf=f->buf;
+ f->strp=0;
+ return f;
+}
+io *openstr(void){
+ io *f=new(struct io);
+ char *s;
+ f->fd=-1;
+ f->bufp=f->strp=emalloc(101);
+ f->ebuf=f->bufp+100;
+ for(s=f->bufp;s<=f->ebuf;s++) *s='\0';
+ return f;
+}
+/*
+ * Open a corebuffer to read. EOF occurs after reading len
+ * characters from buf.
+ */
+io *opencore(char *s, int len)
+{
+ io *f=new(struct io);
+ char *buf=emalloc(len);
+ f->fd= -1 /*open("/dev/null", 0)*/;
+ f->bufp=f->strp=buf;
+ f->ebuf=buf+len;
+ Memcpy(buf, s, len);
+ return f;
+}
+/*
+void rewind(io *io)
+{
+ if(io->fd==-1) io->bufp=io->strp;
+ else{
+ io->bufp=io->ebuf=io->buf;
+ Seek(io->fd, 0L, 0);
+ }
+}
+*/
+void closeio(io *io)
+{
+ if(io->fd>=0) close(io->fd);
+ if(io->strp) efree(io->strp);
+ efree((char *)io);
+}
+int emptybuf(io *f)
+{
+ int n;
+ if(f->fd==-1 || (n=Read(f->fd, f->buf, NBUF))<=0) return EOF;
+ f->bufp=f->buf;
+ f->ebuf=f->buf+n;
+ return *f->bufp++&0xff;
+}
diff --git a/rc/io.h b/rc/io.h
@@ -0,0 +1,30 @@
+/*
+ * on Mac OS X, err is something else,
+ * and assigning to it causes a bus error.
+ * what a crappy linker.
+ */
+#define err rc_err
+#define EOF (-1)
+#define NBUF 512
+struct io{
+ int fd;
+ char *bufp, *ebuf, *strp, buf[NBUF];
+};
+io *err;
+io *openfd(int), *openstr(void), *opencore(char *, int);
+int emptybuf(io*);
+void pchr(io*, int);
+int rchr(io*);
+void closeio(io*);
+void flush(io*);
+int fullbuf(io*, int);
+void pdec(io*, long);
+void poct(io*, ulong);
+void phex(io*, long);
+void pquo(io*, char*);
+void pwrd(io*, char*);
+void pstr(io*, char*);
+void pcmd(io*, tree*);
+void pval(io*, word*);
+void pfnc(io*, thread*);
+void pfmt(io*, char*, ...);
diff --git a/rc/lex.c b/rc/lex.c
@@ -0,0 +1,324 @@
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "getflags.h"
+#include "fns.h"
+int getnext(void);
+int wordchr(int c)
+{
+ return !strchr("\n \t#;&|^$=`'{}()<>", c) && c!=EOF;
+}
+int idchr(int c)
+{
+ /*
+ * Formerly:
+ * return 'a'<=c && c<='z' || 'A'<=c && c<='Z' || '0'<=c && c<='9'
+ * || c=='_' || c=='*';
+ */
+ return c>' ' && !strchr("!\"#$%&'()+,-./:;<=>?@[\\]^`{|}~", c);
+}
+int future=EOF;
+int doprompt=1;
+int inquote;
+/*
+ * Look ahead in the input stream
+ */
+int nextc(void){
+ if(future==EOF) future=getnext();
+ return future;
+}
+/*
+ * Consume the lookahead character.
+ */
+int advance(void){
+ int c=nextc();
+ lastc=future;
+ future=EOF;
+ return c;
+}
+/*
+ * read a character from the input stream
+ */
+int getnext(void){
+ register int c;
+ static int peekc=EOF;
+ if(peekc!=EOF){
+ c=peekc;
+ peekc=EOF;
+ return c;
+ }
+ if(runq->eof) return EOF;
+ if(doprompt) pprompt();
+ c=rchr(runq->cmdfd);
+ if(!inquote && c=='\\'){
+ c=rchr(runq->cmdfd);
+ if(c=='\n'){
+ doprompt=1;
+ c=' ';
+ }
+ else{
+ peekc=c;
+ c='\\';
+ }
+ }
+ doprompt=doprompt || c=='\n' || c==EOF;
+ if(c==EOF) runq->eof++;
+ else if(flag['V'] || ndot>=2 && flag['v']) pchr(err, c);
+ return c;
+}
+void pprompt(void){
+ var *prompt;
+ if(runq->iflag){
+ pstr(err, promptstr);
+ flush(err);
+ prompt=vlook("prompt");
+ if(prompt->val && prompt->val->next)
+ promptstr=prompt->val->next->word;
+ else
+ promptstr="\t";
+ }
+ runq->lineno++;
+ doprompt=0;
+}
+void skipwhite(void){
+ int c;
+ for(;;){
+ c=nextc();
+ if(c=='#'){ /* Why did this used to be if(!inquote && c=='#') ?? */
+ for(;;){
+ c=nextc();
+ if(c=='\n' || c==EOF) break;
+ advance();
+ }
+ }
+ if(c==' ' || c=='\t') advance();
+ else return;
+ }
+}
+void skipnl(void){
+ register int c;
+ for(;;){
+ skipwhite();
+ c=nextc();
+ if(c!='\n') return;
+ advance();
+ }
+}
+int nextis(int c){
+ if(nextc()==c){
+ advance();
+ return 1;
+ }
+ return 0;
+}
+char *addtok(char *p, int val){
+ if(p==0) return 0;
+ if(p==&tok[NTOK]){
+ *p=0;
+ yyerror("token buffer too short");
+ return 0;
+ }
+ *p++=val;
+ return p;
+}
+char *addutf(char *p, int c){
+ p=addtok(p, c);
+ if(twobyte(c)) /* 2-byte escape */
+ return addtok(p, advance());
+ if(threebyte(c)){ /* 3-byte escape */
+ p=addtok(p, advance());
+ return addtok(p, advance());
+ }
+ return p;
+}
+int lastdol; /* was the last token read '$' or '$#' or '"'? */
+int lastword; /* was the last token read a word or compound word terminator? */
+int yylex(void){
+ register int c, d=nextc();
+ register char *w=tok;
+ register struct tree *t;
+ yylval.tree=0;
+ /*
+ * Embarassing sneakiness: if the last token read was a quoted or unquoted
+ * WORD then we alter the meaning of what follows. If the next character
+ * is `(', we return SUB (a subscript paren) and consume the `('. Otherwise,
+ * if the next character is the first character of a simple or compound word,
+ * we insert a `^' before it.
+ */
+ if(lastword){
+ lastword=0;
+ if(d=='('){
+ advance();
+ strcpy(tok, "( [SUB]");
+ return SUB;
+ }
+ if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){
+ strcpy(tok, "^");
+ return '^';
+ }
+ }
+ inquote=0;
+ skipwhite();
+ switch(c=advance()){
+ case EOF:
+ lastdol=0;
+ strcpy(tok, "EOF");
+ return EOF;
+ case '$':
+ lastdol=1;
+ if(nextis('#')){
+ strcpy(tok, "$#");
+ return COUNT;
+ }
+ if(nextis('"')){
+ strcpy(tok, "$\"");
+ return '"';
+ }
+ strcpy(tok, "$");
+ return '$';
+ case '&':
+ lastdol=0;
+ if(nextis('&')){
+ skipnl();
+ strcpy(tok, "&&");
+ return ANDAND;
+ }
+ strcpy(tok, "&");
+ return '&';
+ case '|':
+ lastdol=0;
+ if(nextis(c)){
+ skipnl();
+ strcpy(tok, "||");
+ return OROR;
+ }
+ case '<':
+ case '>':
+ lastdol=0;
+ /*
+ * funny redirection tokens:
+ * redir: arrow | arrow '[' fd ']'
+ * arrow: '<' | '<<' | '>' | '>>' | '|'
+ * fd: digit | digit '=' | digit '=' digit
+ * digit: '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'
+ * some possibilities are nonsensical and get a message.
+ */
+ *w++=c;
+ t=newtree();
+ switch(c){
+ case '|':
+ t->type=PIPE;
+ t->fd0=1;
+ t->fd1=0;
+ break;
+ case '>':
+ t->type=REDIR;
+ if(nextis(c)){
+ t->rtype=APPEND;
+ *w++=c;
+ }
+ else t->rtype=WRITE;
+ t->fd0=1;
+ break;
+ case '<':
+ t->type=REDIR;
+ if(nextis(c)){
+ t->rtype=HERE;
+ *w++=c;
+ }
+ else t->rtype=READ;
+ t->fd0=0;
+ break;
+ }
+ if(nextis('[')){
+ *w++='[';
+ c=advance();
+ if(c<'0' || '9'<c){
+ RedirErr:
+ *w++ = c;
+ *w=0;
+ yyerror(t->type==PIPE?"pipe syntax"
+ :"redirection syntax");
+ return EOF;
+ }
+ t->fd0=0;
+ do{
+ t->fd0=t->fd0*10+c-'0';
+ *w++=c;
+ c=advance();
+ }while('0'<=c && c<='9');
+ if(c=='='){
+ *w++='=';
+ if(t->type==REDIR) t->type=DUP;
+ c=advance();
+ if('0'<=c && c<='9'){
+ t->rtype=DUPFD;
+ t->fd1=t->fd0;
+ t->fd0=0;
+ do{
+ t->fd0=t->fd0*10+c-'0';
+ *w++=c;
+ c=advance();
+ }while('0'<=c && c<='9');
+ }
+ else{
+ if(t->type==PIPE) goto RedirErr;
+ t->rtype=CLOSE;
+ }
+ }
+ *w=0;
+ if(c!=']'
+ || t->type==DUP && (t->rtype==HERE || t->rtype==APPEND))
+ goto RedirErr;
+ *w++=']';
+ }
+ *w='\0';
+ yylval.tree=t;
+ if(t->type==PIPE) skipnl();
+ return t->type;
+ case '\'':
+ lastdol=0;
+ lastword=1;
+ inquote=1;
+ for(;;){
+ c=advance();
+ if(c==EOF) break;
+ if(c=='\''){
+ if(nextc()!='\'')
+ break;
+ advance();
+ }
+ w=addutf(w, c);
+ }
+ if(w!=0) *w='\0';
+ t=token(tok, WORD);
+ t->quoted=1;
+ yylval.tree=t;
+ return t->type;
+ }
+ if(!wordchr(c)){
+ lastdol=0;
+ tok[0]=c;
+ tok[1]='\0';
+ return c;
+ }
+ for(;;){
+ /* next line should have (char)c==GLOB, but ken's compiler is broken */
+ if(c=='*' || c=='[' || c=='?' || c==(unsigned char)GLOB)
+ w=addtok(w, GLOB);
+ w=addutf(w, c);
+ c=nextc();
+ if(lastdol?!idchr(c):!wordchr(c)) break;
+ advance();
+ }
+
+ lastword=1;
+ lastdol=0;
+ if(w!=0) *w='\0';
+ t=klook(tok);
+ if(t->type!=WORD) lastword=0;
+ t->quoted=0;
+ yylval.tree=t;
+ return t->type;
+}
+
diff --git a/rc/pcmd.c b/rc/pcmd.c
@@ -0,0 +1,108 @@
+#include "rc.h"
+#include "io.h"
+#include "fns.h"
+char nl='\n'; /* change to semicolon for bourne-proofing */
+#define c0 t->child[0]
+#define c1 t->child[1]
+#define c2 t->child[2]
+void pdeglob(io *f, char *s)
+{
+ while(*s){
+ if(*s==GLOB) s++;
+ pchr(f, *s++);
+ }
+}
+void pcmd(io *f, tree *t)
+{
+ if(t==0) return;
+ switch(t->type){
+ default: pfmt(f, "bad %d %p %p %p", t->type, c0, c1, c2); break;
+ case '$': pfmt(f, "$%t", c0); break;
+ case '"': pfmt(f, "$\"%t", c0); break;
+ case '&': pfmt(f, "%t&", c0); break;
+ case '^': pfmt(f, "%t^%t", c0, c1); break;
+ case '`': pfmt(f, "`%t", c0); break;
+ case ANDAND: pfmt(f, "%t && %t", c0, c1); break;
+ case BANG: pfmt(f, "! %t", c0); break;
+ case BRACE: pfmt(f, "{%t}", c0); break;
+ case COUNT: pfmt(f, "$#%t", c0); break;
+ case FN: pfmt(f, "fn %t %t", c0, c1); break;
+ case IF: pfmt(f, "if%t%t", c0, c1); break;
+ case NOT: pfmt(f, "if not %t", c0); break;
+ case OROR: pfmt(f, "%t || %t", c0, c1); break;
+ case PCMD:
+ case PAREN: pfmt(f, "(%t)", c0); break;
+ case SUB: pfmt(f, "$%t(%t)", c0, c1); break;
+ case SIMPLE: pfmt(f, "%t", c0); break;
+ case SUBSHELL: pfmt(f, "@ %t", c0); break;
+ case SWITCH: pfmt(f, "switch %t %t", c0, c1); break;
+ case TWIDDLE: pfmt(f, "~ %t %t", c0, c1); break;
+ case WHILE: pfmt(f, "while %t%t", c0, c1); break;
+ case ARGLIST:
+ if(c0==0)
+ pfmt(f, "%t", c1);
+ else if(c1==0)
+ pfmt(f, "%t", c0);
+ else
+ pfmt(f, "%t %t", c0, c1);
+ break;
+ case ';':
+ if(c0){
+ if(c1) pfmt(f, "%t%c%t", c0, nl, c1);
+ else pfmt(f, "%t", c0);
+ }
+ else pfmt(f, "%t", c1);
+ break;
+ case WORDS:
+ if(c0) pfmt(f, "%t ", c0);
+ pfmt(f, "%t", c1);
+ break;
+ case FOR:
+ pfmt(f, "for(%t", c0);
+ if(c1) pfmt(f, " in %t", c1);
+ pfmt(f, ")%t", c2);
+ break;
+ case WORD:
+ if(t->quoted) pfmt(f, "%Q", t->str);
+ else pdeglob(f, t->str);
+ break;
+ case DUP:
+ if(t->rtype==DUPFD)
+ pfmt(f, ">[%d=%d]", t->fd1, t->fd0); /* yes, fd1, then fd0; read lex.c */
+ else
+ pfmt(f, ">[%d=]", t->fd0);
+ pfmt(f, "%t", c1);
+ break;
+ case PIPEFD:
+ case REDIR:
+ switch(t->rtype){
+ case HERE:
+ pchr(f, '<');
+ case READ:
+ pchr(f, '<');
+ if(t->fd0!=0) pfmt(f, "[%d]", t->fd0);
+ break;
+ case APPEND:
+ pchr(f, '>');
+ case WRITE:
+ pchr(f, '>');
+ if(t->fd0!=1) pfmt(f, "[%d]", t->fd0);
+ break;
+ }
+ pfmt(f, "%t", c0);
+ if(c1) pfmt(f, " %t", c1);
+ break;
+ case '=':
+ pfmt(f, "%t=%t", c0, c1);
+ if(c2) pfmt(f, " %t", c2);
+ break;
+ case PIPE:
+ pfmt(f, "%t|", c0);
+ if(t->fd1==0){
+ if(t->fd0!=1) pfmt(f, "[%d]", t->fd0);
+ }
+ else pfmt(f, "[%d=%d]", t->fd0, t->fd1);
+ pfmt(f, "%t", c1);
+ break;
+ }
+}
diff --git a/rc/pfnc.c b/rc/pfnc.c
@@ -0,0 +1,67 @@
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+struct{
+ void (*f)(void);
+ char *name;
+}fname[]={
+ Xappend, "Xappend",
+ Xasync, "Xasync",
+ Xbang, "Xbang",
+ Xclose, "Xclose",
+ Xdup, "Xdup",
+ Xeflag, "Xeflag",
+ Xexit, "Xexit",
+ Xfalse, "Xfalse",
+ Xifnot, "Xifnot",
+ Xjump, "Xjump",
+ Xmark, "Xmark",
+ Xpopm, "Xpopm",
+ Xread, "Xread",
+ Xreturn, "Xreturn",
+ Xtrue, "Xtrue",
+ Xif, "Xif",
+ Xwastrue, "Xwastrue",
+ Xword, "Xword",
+ Xwrite, "Xwrite",
+ Xmatch, "Xmatch",
+ Xcase, "Xcase",
+ Xconc, "Xconc",
+ Xassign, "Xassign",
+ Xdol, "Xdol",
+ Xcount, "Xcount",
+ Xlocal, "Xlocal",
+ Xunlocal, "Xunlocal",
+ Xfn, "Xfn",
+ Xdelfn, "Xdelfn",
+ Xpipe, "Xpipe",
+ Xpipewait, "Xpipewait",
+ Xrdcmds, "Xrdcmds",
+ (void (*)(void))Xerror, "Xerror",
+ Xbackq, "Xbackq",
+ Xpipefd, "Xpipefd",
+ Xsubshell, "Xsubshell",
+ Xdelhere, "Xdelhere",
+ Xfor, "Xfor",
+ Xglob, "Xglob",
+ Xrdfn, "Xrdfn",
+ Xsimple, "Xsimple",
+ Xrdfn, "Xrdfn",
+ Xqdol, "Xqdol",
+0};
+void pfnc(io *fd, thread *t)
+{
+ int i;
+ void (*fn)(void)=t->code[t->pc].f;
+ list *a;
+ pfmt(fd, "pid %d cycle %p %d ", getpid(), t->code, t->pc);
+ for(i=0;fname[i].f;i++) if(fname[i].f==fn){
+ pstr(fd, fname[i].name);
+ break;
+ }
+ if(!fname[i].f) pfmt(fd, "%p", fn);
+ for(a=t->argv;a;a=a->next) pfmt(fd, " (%v)", a->words);
+ pchr(fd, '\n');
+ flush(fd);
+}
diff --git a/rc/plan9ish.c b/rc/plan9ish.c
@@ -0,0 +1,549 @@
+/*
+ * Plan 9 versions of system-specific functions
+ * By convention, exported routines herein have names beginning with an
+ * upper case letter.
+ */
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+#include "getflags.h"
+char *Signame[]={
+ "sigexit", "sighup", "sigint", "sigquit",
+ "sigalrm", "sigkill", "sigfpe", "sigterm",
+ 0
+};
+char *syssigname[]={
+ "exit", /* can't happen */
+ "hangup",
+ "interrupt",
+ "quit", /* can't happen */
+ "alarm",
+ "kill",
+ "sys: fp: ",
+ "term",
+ 0
+};
+char*
+Rcmain(void)
+{
+ static char Rcmain[] = PREFIX"/lib/rcmain";
+ char *rcmain = getenv("RCMAIN");
+ return rcmain ? rcmain : Rcmain;
+}
+
+char Fdprefix[]="/dev/fd/";
+void execfinit(void);
+void execbind(void);
+void execmount(void);
+void execulimit(void);
+void execumask(void);
+void execrfork(void);
+builtin Builtin[]={
+ "cd", execcd,
+ "whatis", execwhatis,
+ "eval", execeval,
+ "exec", execexec, /* but with popword first */
+ "exit", execexit,
+ "shift", execshift,
+ "wait", execwait,
+ ".", execdot,
+ "finit", execfinit,
+ "flag", execflag,
+ "ulimit", execulimit,
+ "umask", execumask,
+ "rfork", execrfork,
+ 0
+};
+
+void
+execrfork(void)
+{
+ int arg;
+ char *s;
+
+ switch(count(runq->argv->words)){
+ case 1:
+ arg = RFENVG|RFNOTEG|RFNAMEG;
+ break;
+ case 2:
+ arg = 0;
+ for(s = runq->argv->words->next->word;*s;s++) switch(*s){
+ default:
+ goto Usage;
+ case 'n':
+ arg|=RFNAMEG; break;
+ case 'N':
+ arg|=RFCNAMEG;
+ break;
+ case 'e':
+ /* arg|=RFENVG; */ break;
+ case 'E':
+ arg|=RFCENVG; break;
+ case 's':
+ arg|=RFNOTEG; break;
+ case 'f':
+ arg|=RFFDG; break;
+ case 'F':
+ arg|=RFCFDG; break;
+ }
+ break;
+ default:
+ Usage:
+ pfmt(err, "Usage: %s [nNeEsfF]\n", runq->argv->words->word);
+ setstatus("rfork usage");
+ poplist();
+ return;
+ }
+ if(rfork(arg)==-1){
+ pfmt(err, "rc: %s failed\n", runq->argv->words->word);
+ setstatus("rfork failed");
+ }
+ else
+ setstatus("");
+ poplist();
+}
+
+
+
+#define SEP '\1'
+char **environp;
+struct word *enval(s)
+register char *s;
+{
+ register char *t, c;
+ register struct word *v;
+ for(t=s;*t && *t!=SEP;t++);
+ c=*t;
+ *t='\0';
+ v=newword(s, c=='\0'?(struct word *)0:enval(t+1));
+ *t=c;
+ return v;
+}
+void Vinit(void){
+ extern char **environ;
+ register char *s;
+ register char **env=environ;
+ environp=env;
+ for(;*env;env++){
+ for(s=*env;*s && *s!='(' && *s!='=';s++);
+ switch(*s){
+ case '\0':
+ // pfmt(err, "rc: odd environment %q?\n", *env);
+ break;
+ case '=':
+ *s='\0';
+ setvar(*env, enval(s+1));
+ *s='=';
+ break;
+ case '(': /* ignore functions for now */
+ break;
+ }
+ }
+}
+char **envp;
+void Xrdfn(void){
+ char *p;
+ register char *s;
+ register int len;
+ for(;*envp;envp++){
+ s = *envp;
+ if(strncmp(s, "fn#", 3) == 0){
+ p = strchr(s, '=');
+ if(p == nil)
+ continue;
+ *p = ' ';
+ s[2] = ' ';
+ len = strlen(s);
+ execcmds(opencore(s, len));
+ s[len] = '\0';
+ return;
+ }
+#if 0
+ for(s=*envp;*s && *s!='(' && *s!='=';s++);
+ switch(*s){
+ case '\0':
+ pfmt(err, "environment %q?\n", *envp);
+ break;
+ case '=': /* ignore variables */
+ break;
+ case '(': /* Bourne again */
+ s=*envp+3;
+ envp++;
+ len=strlen(s);
+ s[len]='\n';
+ execcmds(opencore(s, len+1));
+ s[len]='\0';
+ return;
+ }
+#endif
+ }
+ Xreturn();
+}
+union code rdfns[4];
+void execfinit(void){
+ static int first=1;
+ if(first){
+ rdfns[0].i=1;
+ rdfns[1].f=Xrdfn;
+ rdfns[2].f=Xjump;
+ rdfns[3].i=1;
+ first=0;
+ }
+ Xpopm();
+ envp=environp;
+ start(rdfns, 1, runq->local);
+}
+extern int mapfd(int);
+int Waitfor(int pid, int unused0){
+ thread *p;
+ Waitmsg *w;
+ char errbuf[ERRMAX];
+
+ while((w = wait()) != nil){
+ if(w->pid==pid){
+ if(strncmp(w->msg, "signal: ", 8) == 0)
+ fprint(mapfd(2), "%d: %s\n", w->pid, w->msg);
+ setstatus(w->msg);
+ free(w);
+ return 0;
+ }
+ if(strncmp(w->msg, "signal: ", 8) == 0)
+ fprint(2, "%d: %s\n", w->pid, w->msg);
+ for(p=runq->ret;p;p=p->ret)
+ if(p->pid==w->pid){
+ p->pid=-1;
+ strcpy(p->status, w->msg);
+ }
+ free(w);
+ }
+
+ errstr(errbuf, sizeof errbuf);
+ if(strcmp(errbuf, "interrupted")==0) return -1;
+ return 0;
+}
+char **mkargv(word *a)
+{
+ char **argv=(char **)emalloc((count(a)+2)*sizeof(char *));
+ char **argp=argv+1; /* leave one at front for runcoms */
+ for(;a;a=a->next) *argp++=a->word;
+ *argp=0;
+ return argv;
+}
+/*
+void addenv(var *v)
+{
+ char envname[256];
+ word *w;
+ int f;
+ io *fd;
+ if(v->changed){
+ v->changed=0;
+ snprint(envname, sizeof envname, "/env/%s", v->name);
+ if((f=Creat(envname))<0)
+ pfmt(err, "rc: can't open %s: %r\n", envname);
+ else{
+ for(w=v->val;w;w=w->next)
+ write(f, w->word, strlen(w->word)+1L);
+ close(f);
+ }
+ }
+ if(v->fnchanged){
+ v->fnchanged=0;
+ snprint(envname, sizeof envname, "/env/fn#%s", v->name);
+ if((f=Creat(envname))<0)
+ pfmt(err, "rc: can't open %s: %r\n", envname);
+ else{
+ if(v->fn){
+ fd=openfd(f);
+ pfmt(fd, "fn %s %s\n", v->name, v->fn[v->pc-1].s);
+ closeio(fd);
+ }
+ close(f);
+ }
+ }
+}
+void updenvlocal(var *v)
+{
+ if(v){
+ updenvlocal(v->next);
+ addenv(v);
+ }
+}
+void Updenv(void){
+ var *v, **h;
+ for(h=gvar;h!=&gvar[NVAR];h++)
+ for(v=*h;v;v=v->next)
+ addenv(v);
+ if(runq) updenvlocal(runq->local);
+}
+*/
+int
+cmpenv(const void *a, const void *b)
+{
+ return strcmp(*(char**)a, *(char**)b);
+}
+char **mkenv(){
+ register char **env, **ep, *p, *q;
+ register struct var **h, *v;
+ register struct word *a;
+ register int nvar=0, nchr=0, sep;
+ /*
+ * Slightly kludgy loops look at locals then globals
+ */
+ for(h=gvar-1;h!=&gvar[NVAR];h++) for(v=h>=gvar?*h:runq->local;v;v=v->next){
+ if((v==vlook(v->name)) && v->val){
+ nvar++;
+ nchr+=strlen(v->name)+1;
+ for(a=v->val;a;a=a->next)
+ nchr+=strlen(a->word)+1;
+ }
+ if(v->fn){
+ nvar++;
+ nchr+=strlen(v->name)+strlen(v->fn[v->pc-1].s)+8;
+ }
+ }
+ env=(char **)emalloc((nvar+1)*sizeof(char *)+nchr);
+ ep=env;
+ p=(char *)&env[nvar+1];
+ for(h=gvar-1;h!=&gvar[NVAR];h++) for(v=h>=gvar?*h:runq->local;v;v=v->next){
+ if((v==vlook(v->name)) && v->val){
+ *ep++=p;
+ q=v->name;
+ while(*q) *p++=*q++;
+ sep='=';
+ for(a=v->val;a;a=a->next){
+ *p++=sep;
+ sep=SEP;
+ q=a->word;
+ while(*q) *p++=*q++;
+ }
+ *p++='\0';
+ }
+ if(v->fn){
+ *ep++=p;
+#if 0
+ *p++='#'; *p++='('; *p++=')'; /* to fool Bourne */
+ *p++='f'; *p++='n'; *p++=' ';
+ q=v->name;
+ while(*q) *p++=*q++;
+ *p++=' ';
+#endif
+ *p++='f'; *p++='n'; *p++='#';
+ q=v->name;
+ while(*q) *p++=*q++;
+ *p++='=';
+ q=v->fn[v->pc-1].s;
+ while(*q) *p++=*q++;
+ *p++='\n';
+ *p++='\0';
+ }
+ }
+ *ep=0;
+ qsort((char *)env, nvar, sizeof ep[0], cmpenv);
+ return env;
+}
+void Updenv(void){}
+void Execute(word *args, word *path)
+{
+ char **argv=mkargv(args);
+ char **env=mkenv();
+ char file[1024];
+ int nc;
+ Updenv();
+ for(;path;path=path->next){
+ nc=strlen(path->word);
+ if(nc<1024){
+ strcpy(file, path->word);
+ if(file[0]){
+ strcat(file, "/");
+ nc++;
+ }
+ if(nc+strlen(argv[1])<1024){
+ strcat(file, argv[1]);
+ execve(file, argv+1, env);
+ }
+ else werrstr("command name too long");
+ }
+ }
+ rerrstr(file, sizeof file);
+ pfmt(err, "%s: %s\n", argv[1], file);
+ efree((char *)argv);
+}
+#define NDIR 256 /* shoud be a better way */
+int Globsize(char *p)
+{
+ ulong isglob=0, globlen=NDIR+1;
+ for(;*p;p++){
+ if(*p==GLOB){
+ p++;
+ if(*p!=GLOB) isglob++;
+ globlen+=*p=='*'?NDIR:1;
+ }
+ else
+ globlen++;
+ }
+ return isglob?globlen:0;
+}
+#define NFD 50
+#define NDBUF 32
+struct{
+ Dir *dbuf;
+ int i;
+ int n;
+}dir[NFD];
+int Opendir(char *name)
+{
+ Dir *db;
+ int f;
+ f=open(name, 0);
+ if(f==-1)
+ return f;
+ db = dirfstat(f);
+ if(db!=nil && (db->mode&DMDIR)){
+ if(f<NFD){
+ dir[f].i=0;
+ dir[f].n=0;
+ }
+ free(db);
+ return f;
+ }
+ free(db);
+ close(f);
+ return -1;
+}
+int Readdir(int f, char *p)
+{
+ int n;
+ if(f<0 || f>=NFD)
+ return 0;
+ if(dir[f].i==dir[f].n){ /* read */
+ free(dir[f].dbuf);
+ dir[f].dbuf=0;
+ n=dirread(f, &dir[f].dbuf);
+ if(n>=0)
+ dir[f].n=n;
+ else
+ dir[f].n=0;
+ dir[f].i=0;
+ }
+ if(dir[f].i==dir[f].n)
+ return 0;
+ strcpy(p, dir[f].dbuf[dir[f].i].name);
+ dir[f].i++;
+ return 1;
+}
+void Closedir(int f){
+ if(f>=0 && f<NFD){
+ free(dir[f].dbuf);
+ dir[f].i=0;
+ dir[f].n=0;
+ dir[f].dbuf=0;
+ }
+ close(f);
+}
+int interrupted = 0;
+void
+notifyf(void *unused0, char *s)
+{
+ int i;
+ for(i=0;syssigname[i];i++)
+ if(strncmp(s, syssigname[i], strlen(syssigname[i]))==0){
+ if(strncmp(s, "sys: ", 5)!=0){
+ if(kidpid && !interrupted){
+ interrupted=1;
+ postnote(PNGROUP, kidpid, s);
+ }
+ interrupted = 1;
+ }
+ goto Out;
+ }
+ if(strcmp(s, "sys: window size change") != 0)
+ if(strcmp(s, "sys: write on closed pipe") != 0)
+ if(strcmp(s, "sys: child") != 0)
+ pfmt(err, "rc: note: %s\n", s);
+ noted(NDFLT);
+ return;
+Out:
+ if(strcmp(s, "interrupt")!=0 || trap[i]==0){
+ trap[i]++;
+ ntrap++;
+ }
+ if(ntrap>=32){ /* rc is probably in a trap loop */
+ pfmt(err, "rc: Too many traps (trap %s), aborting\n", s);
+ abort();
+ }
+ noted(NCONT);
+}
+void Trapinit(void){
+ notify(notifyf);
+}
+void Unlink(char *name)
+{
+ remove(name);
+}
+long Write(int fd, char *buf, long cnt)
+{
+ return write(fd, buf, (long)cnt);
+}
+long Read(int fd, char *buf, long cnt)
+{
+ int i;
+
+ i = read(fd, buf, cnt);
+ if(ntrap) dotrap();
+ return i;
+}
+long Seek(int fd, long cnt, long whence)
+{
+ return seek(fd, cnt, whence);
+}
+int Executable(char *file)
+{
+ Dir *statbuf;
+ int ret;
+
+ statbuf = dirstat(file);
+ if(statbuf == nil) return 0;
+ ret = ((statbuf->mode&0111)!=0 && (statbuf->mode&DMDIR)==0);
+ free(statbuf);
+ return ret;
+}
+int Creat(char *file)
+{
+ return create(file, 1, 0666L);
+}
+int Dup(int a, int b){
+ return dup(a, b);
+}
+int Dup1(int a){
+ return dup(a, -1);
+}
+void Exit(char *stat)
+{
+ Updenv();
+ setstatus(stat);
+ exits(truestatus()?"":getstatus());
+}
+int Eintr(void){
+ return interrupted;
+}
+void Noerror(void){
+ interrupted=0;
+}
+int
+Isatty(int fd){
+ return isatty(fd);
+}
+void Abort(void){
+ pfmt(err, "aborting\n");
+ flush(err);
+ Exit("aborting");
+}
+void Memcpy(char *a, char *b, long n)
+{
+ memmove(a, b, (long)n);
+}
+void *Malloc(ulong n){
+ return malloc(n);
+}
diff --git a/rc/rc.1 b/rc/rc.1
@@ -0,0 +1,992 @@
+.TH RC 1
+.SH NAME
+rc, cd, eval, exec, exit, flag, rfork, shift, wait, whatis, ., ~ \- command language
+.SH SYNOPSIS
+.B rc
+[
+.B -srdiIlxepvV
+]
+[
+.B -c command
+]
+[
+.I file
+[
+.I arg ...
+]]
+.SH DESCRIPTION
+.I Rc
+is the Plan 9 shell.
+It executes command lines read from a terminal or a file or, with the
+.B -c
+flag, from
+.I rc's
+argument list.
+.SS Command Lines
+A command line is a sequence of commands, separated by ampersands or semicolons
+.RB ( &
+or
+.BR ; ),
+terminated by a newline.
+The commands are executed in sequence
+from left to right.
+.I Rc
+does not wait for a command followed by
+.B &
+to finish executing before starting
+the following command.
+Whenever a command followed by
+.B &
+is executed, its process id is assigned to the
+.I rc
+variable
+.BR $apid .
+Whenever a command
+.I not
+followed by
+.B &
+exits or is terminated, the
+.I rc
+variable
+.B $status
+gets the process's wait message (see
+.IR wait (3));
+it will be the null string if the command was successful.
+.PP
+A long command line may be continued on subsequent lines by typing
+a backslash
+.RB ( \e )
+followed by a newline.
+This sequence is treated as though it were a blank.
+Backslash is not otherwise a special character.
+.PP
+A number-sign
+.RB ( # )
+and any following characters up to (but not including) the next newline
+are ignored, except in quotation marks.
+.SS Simple Commands
+A simple command is a sequence of arguments interspersed with I/O redirections.
+If the first argument is the name of an
+.I rc
+function or of one of
+.I rc's
+built-in commands, it is executed by
+.IR rc .
+Otherwise if the name starts with a slash
+.RB ( / ),
+it must be the path name of the program to be executed.
+Names containing no initial slash are searched for in
+a list of directory names stored in
+.BR $path .
+The first executable file of the given name found
+in a directory in
+.B $path
+is the program to be executed.
+To be executable, the user must have execute permission (see
+.IR stat (3))
+and the file must be either an executable binary
+for the current machine's CPU type, or a shell script.
+Shell scripts begin with a line containing the full path name of a shell
+(usually
+.BR /bin/rc ),
+prefixed by
+.LR #! .
+.PP
+The first word of a simple command cannot be a keyword unless it is
+quoted or otherwise disguised.
+The keywords are
+.EX
+ for in while if not switch fn ~ ! @
+.EE
+.SS Arguments and Variables
+A number of constructions may be used where
+.I rc's
+syntax requires an argument to appear.
+In many cases a construction's
+value will be a list of arguments rather than a single string.
+.PP
+The simplest kind of argument is the unquoted word:
+a sequence of one or more characters none of which is a blank, tab,
+newline, or any of the following:
+.EX
+ # ; & | ^ $ = ` ' { } ( ) < >
+.EE
+An unquoted word that contains any of the characters
+.B *
+.B ?
+.B [
+is a pattern for matching against file names.
+The character
+.B *
+matches any sequence of characters,
+.B ?
+matches any single character, and
+.BI [ class ]
+matches any character in the
+.IR class .
+If the first character of
+.I class
+is
+.BR ~ ,
+the class is complemented.
+The
+.I class
+may also contain pairs of characters separated by
+.BR - ,
+standing for all characters lexically between the two.
+The character
+.B /
+must appear explicitly in a pattern, as must the
+first character of the path name components
+.B .
+and
+.BR .. .
+A pattern is replaced by a list of arguments, one for each path name matched,
+except that a pattern matching no names is not replaced by the empty list,
+but rather stands for itself.
+Pattern matching is done after all other
+operations.
+Thus,
+.EX
+ x=/tmp echo $x^/*.c
+.EE
+matches
+.BR /tmp/*.c ,
+rather than matching
+.B "/*.c
+and then prefixing
+.BR /tmp .
+.PP
+A quoted word is a sequence of characters surrounded by single quotes
+.RB ( ' ).
+A single quote is represented in a quoted word by a pair of quotes
+.RB ( '' ).
+.PP
+Each of the following is an argument.
+.PD 0
+.HP
+.BI ( arguments )
+.br
+The value of a sequence of arguments enclosed in parentheses is
+a list comprising the members of each element of the sequence.
+Argument lists have no recursive structure, although their syntax may
+suggest it.
+The following are entirely equivalent:
+.EX
+ echo hi there everybody
+ ((echo) (hi there) everybody)
+.EE
+.HP
+.BI $ argument
+.HP
+.BI $ argument ( subscript )
+.br
+The
+.I argument
+after the
+.B $
+is the name of a variable whose value is substituted.
+Multiple levels
+of indirection are possible, but of questionable utility.
+Variable values
+are lists of strings.
+If
+.I argument
+is a number
+.IR n ,
+the value is the
+.IR n th
+element of
+.BR $* ,
+unless
+.B $*
+doesn't have
+.I n
+elements, in which case the value is empty.
+If
+.I argument
+is followed by a parenthesized list of subscripts, the
+value substituted is a list composed of the requested elements (origin 1).
+The parenthesis must follow the variable name with no spaces.
+Assignments to variables are described below.
+.HP
+.BI $# argument
+.br
+The value is the number of elements in the named variable.
+A variable
+never assigned a value has zero elements.
+.HP
+$"\c
+.I argument
+.br
+The value is a single string containing the components of the named variable
+separated by spaces. A variable with zero elements yields the empty string.
+.HP
+.BI `{ command }
+.br
+.I rc
+executes the
+.I command
+and reads its standard output, splitting it into a list of arguments,
+using characters in
+.B $ifs
+as separators.
+If
+.B $ifs
+is not otherwise set, its value is
+.BR "'\ \et\en'" .
+.HP
+.BI <{ command }
+.HP
+.BI >{ command }
+.br
+The
+.I command
+is executed asynchronously with its standard output or standard input
+connected to a pipe.
+The value of the argument is the name of a file
+referring to the other end of the pipe.
+This allows the construction of
+non-linear pipelines.
+For example, the following runs two commands
+.B old
+and
+.B new
+and uses
+.B cmp
+to compare their outputs
+.EX
+ cmp <{old} <{new}
+.EE
+.HP
+.IB argument ^ argument
+.br
+The
+.B ^
+operator concatenates its two operands.
+If the two operands
+have the same number of components, they are concatenated pairwise.
+If not,
+then one operand must have one component, and the other must be non-empty,
+and concatenation is distributive.
+.PD
+.SS Free Carets
+In most circumstances,
+.I rc
+will insert the
+.B ^
+operator automatically between words that are not separated by white space.
+Whenever one of
+.B $
+.B '
+.B `
+follows a quoted or unquoted word or an unquoted word follows a quoted word
+with no intervening blanks or tabs,
+a
+.B ^
+is inserted between the two.
+If an unquoted word immediately follows a
+.BR $
+and contains a character other than an alphanumeric, underscore,
+or
+.BR * ,
+a
+.B ^
+is inserted before the first such character.
+Thus
+.IP
+.B cc -$flags $stem.c
+.LP
+is equivalent to
+.IP
+.B cc -^$flags $stem^.c
+.SS I/O Redirections
+The sequence
+.BI > file
+redirects the standard output file (file descriptor 1, normally the
+terminal) to the named
+.IR file ;
+.BI >> file
+appends standard output to the file.
+The standard input file (file descriptor 0, also normally the terminal)
+may be redirected from a file by the sequence
+.BI < file \f1,
+or from an inline `here document'
+by the sequence
+.BI << eof-marker\f1.
+The contents of a here document are lines of text taken from the command
+input stream up to a line containing nothing but the
+.IR eof-marker ,
+which may be either a quoted or unquoted word.
+If
+.I eof-marker
+is unquoted, variable names of the form
+.BI $ word
+have their values substituted from
+.I rc's
+environment.
+If
+.BI $ word
+is followed by a caret
+.RB ( ^ ),
+the caret is deleted.
+If
+.I eof-marker
+is quoted, no substitution occurs.
+.PP
+Redirections may be applied to a file-descriptor other than standard input
+or output by qualifying the redirection operator
+with a number in square brackets.
+For example, the diagnostic output (file descriptor 2)
+may be redirected by writing
+.BR "cc junk.c >[2]junk" .
+.PP
+A file descriptor may be redirected to an already open descriptor by writing
+.BI >[ fd0 = fd1 ]
+or
+.BI <[ fd0 = fd1 ]\f1.
+.I Fd1
+is a previously opened file descriptor and
+.I fd0
+becomes a new copy (in the sense of
+.IR dup (3))
+of it.
+A file descriptor may be closed by writing
+.BI >[ fd0 =]
+or
+.BI <[ fd0 =]\f1.
+.PP
+Redirections are executed from left to right.
+Therefore,
+.B cc junk.c >/dev/null >[2=1]
+and
+.B cc junk.c >[2=1] >/dev/null
+have different effects: the first puts standard output in
+.BR /dev/null
+and then puts diagnostic output in the same place, where the second
+directs diagnostic output to the terminal and sends standard output to
+.BR /dev/null .
+.SS Compound Commands
+A pair of commands separated by a pipe operator
+.RB ( | )
+is a command.
+The standard output of the left command is sent through a pipe
+to the standard input of the right command.
+The pipe operator may be decorated
+to use different file descriptors.
+.BI |[ fd ]
+connects the output end of the pipe to file descriptor
+.I fd
+rather than 1.
+.BI |[ fd0 = fd1 ]
+connects output to
+.I fd1
+of the left command and input to
+.I fd0
+of the right command.
+.PP
+A pair of commands separated by
+.B &&
+or
+.B ||
+is a command.
+In either case, the left command is executed and its exit status examined.
+If the operator is
+.B &&
+the right command is executed if the left command's status is null.
+.B ||
+causes the right command to be executed if the left command's status is non-null.
+.PP
+The exit status of a command may be inverted (non-null is changed to null, null
+is changed to non-null) by preceding it with a
+.BR ! .
+.PP
+The
+.B |
+operator has highest precedence, and is left-associative (i.e. binds tighter
+to the left than the right).
+.B !
+has intermediate precedence, and
+.B &&
+and
+.B ||
+have the lowest precedence.
+.PP
+The unary
+.B @
+operator, with precedence equal to
+.BR ! ,
+causes its operand to be executed in a subshell.
+.PP
+Each of the following is a command.
+.PD 0
+.HP
+.B if (
+.I list
+.B )
+.I command
+.br
+A
+.I list
+is a sequence of commands, separated by
+.BR & ,
+.BR ; ,
+or newline.
+It is executed and
+if its exit status is null, the
+.I command
+is executed.
+.HP
+.B if not
+.I command
+.br
+The immediately preceding command must have been
+.BI if( list )
+.IR command .
+If its condition was non-zero, the
+.I command
+is executed.
+.HP
+.BI for( name
+.B in
+.IB arguments )
+.I command
+.HP
+.BI for( name )
+.I command
+.br
+The
+.I command
+is executed once for each
+.IR argument
+with that argument assigned to
+.IR name .
+If the argument list is omitted,
+.B $*
+is used.
+.HP
+.BI while( list )
+.I command
+.br
+The
+.I list
+is executed repeatedly until its exit status is non-null.
+Each time it returns null status, the
+.I command
+is executed.
+An empty
+.I list
+is taken to give null status.
+.HP
+.BI "switch(" argument "){" list }
+.br
+The
+.IR list
+is searched for simple commands beginning with the word
+.BR case .
+(The search is only at the `top level' of the
+.IR list .
+That is,
+.B cases
+in nested constructs are not found.)
+.I Argument
+is matched against each word following
+.B case
+using the pattern-matching algorithm described above, except that
+.B /
+and the first characters of
+.B .
+and
+.B ..
+need not be matched explicitly.
+When a match is found, commands in the list are executed up to the next
+following
+.B case
+command (at the top level) or the closing brace.
+.HP
+.BI { list }
+.br
+Braces serve to alter the grouping of commands implied by operator
+priorities.
+The
+.I body
+is a sequence of commands separated by
+.BR & ,
+.BR ; ,
+or newline.
+.HP
+.BI "fn " name { list }
+.HP
+.BI "fn " name
+.br
+The first form defines a function with the given
+.IR name .
+Subsequently, whenever a command whose first argument is
+.I name
+is encountered, the current value of
+the remainder of the command's argument list will be assigned to
+.BR $* ,
+after saving its current value, and
+.I rc
+will execute the
+.IR list .
+The second form removes
+.IR name 's
+function definition.
+.HP
+.BI "fn " note { list }
+.br
+.HP
+.BI "fn " note
+.br
+A function with a special name will be called when
+.I rc
+receives a corresponding note; see
+.IR notify (3).
+The valid note names (and corresponding notes) are
+.B sighup
+.RB ( hangup ),
+.B sigint
+.RB ( interrupt ),
+.BR sigalrm
+.RB ( alarm ),
+and
+.B sigfpe
+(floating point trap).
+By default
+.I rc
+exits on receiving any signal, except when run interactively,
+in which case interrupts and quits normally cause
+.I rc
+to stop whatever it's doing and start reading a new command.
+The second form causes
+.I rc
+to handle a signal in the default manner.
+.I Rc
+recognizes an artificial note,
+.BR sigexit ,
+which occurs when
+.I rc
+is about to finish executing.
+.HP
+.IB name = "argument command"
+.br
+Any command may be preceded by a sequence of assignments
+interspersed with redirections.
+The assignments remain in effect until the end of the command, unless
+the command is empty (i.e. the assignments stand alone), in which case
+they are effective until rescinded by later assignments.
+.PD
+.SS Built-in Commands
+These commands are executed internally by
+.IR rc ,
+usually because their execution changes or depends on
+.IR rc 's
+internal state.
+.PD 0
+.HP
+.BI . " file ..."
+.br
+Execute commands from
+.IR file .
+.B $*
+is set for the duration to the remainder of the argument list following
+.IR file .
+.I File
+is searched for using
+.BR $path .
+.HP
+.BI builtin " command ..."
+.br
+Execute
+.I command
+as usual except that any function named
+.I command
+is ignored in favor of the built-in meaning.
+.HP
+.BI "cd [" dir "]"
+.br
+Change the current directory to
+.IR dir .
+The default argument is
+.BR $home .
+.I dir
+is searched for in each of the directories mentioned in
+.BR $cdpath .
+.HP
+.BI "eval [" "arg ..." "]"
+.br
+The arguments are concatenated separated by spaces into a single string,
+read as input to
+.IR rc ,
+and executed.
+.HP
+.BI "exec [" "command ..." "]"
+.br
+This instance of
+.I rc
+replaces itself with the given (non-built-in)
+.IR command .
+.HP
+.BI "flag " f " [+-]"
+.br
+Either set
+.RB ( + ),
+clear
+.RB ( - ),
+or test (neither
+.B +
+nor
+.BR - )
+the flag
+.IR f ,
+where
+.I f
+is a single character, one of the command line flags (see Invocation, below).
+.HP
+.BI "exit [" status "]"
+.br
+Exit with the given exit status.
+If none is given, the current value of
+.B $status
+is used.
+.HP
+.BR "rfork " [ nNeEsfFm ]
+.br
+Become a new process group using
+.BI rfork( flags )
+where
+.I flags
+is composed of the bitwise OR of the
+.B rfork
+flags specified by the option letters
+(see
+.IR fork (2)).
+If no
+.I flags
+are given, they default to
+.BR ens .
+The
+.I flags
+and their meanings are:
+.B n
+is
+.BR RFNAMEG ;
+.B N
+is
+.BR RFCNAMEG ;
+.B e
+is
+.BR RFENVG ;
+.B E
+is
+.BR RFCENVG ;
+.B s
+is
+.BR RFNOTEG ;
+.B f
+is
+.BR RFFDG ;
+.B F
+is
+.BR RFCFDG ;
+and
+.B m
+is
+.BR RFNOMNT .
+.HP
+.BI "shift [" n "]"
+.br
+Delete the first
+.IR n
+(default 1)
+elements of
+.BR $* .
+.HP
+.BI "wait [" pid "]"
+.br
+Wait for the process with the given
+.I pid
+to exit.
+If no
+.I pid
+is given, all outstanding processes are waited for.
+.HP
+.BI whatis " name ..."
+.br
+Print the value of each
+.I name
+in a form suitable for input to
+.IR rc .
+The output is
+an assignment to any variable,
+the definition of any function,
+a call to
+.B builtin
+for any built-in command, or
+the completed pathname of any executable file.
+.HP
+.BI ~ " subject pattern ..."
+.br
+The
+.I subject
+is matched against each
+.I pattern
+in sequence.
+If it matches any pattern,
+.B $status
+is set to zero.
+Otherwise,
+.B $status
+is set to one.
+Patterns are the same as for file name matching, except that
+.B /
+and the first character of
+.B .
+and
+.B ..
+need not be matched explicitly.
+The
+.I patterns
+are not subjected to
+file name matching before the
+.B ~
+command is executed, so they need not be enclosed in quotation marks.
+.PD
+.SS Environment
+The
+.I environment
+is a list of strings made available to executing binaries by the
+kernel.
+.I Rc
+creates an environment entry for each variable whose value is non-empty,
+and for each function.
+The string for a variable entry has the variable's name followed by
+.B =
+and its value.
+If the value has more than one component, these
+are separated by SOH (001)
+characters.
+The string for a function is just the
+.I rc
+input that defines the function.
+The name of a function in the environment is the function name
+preceded by
+.LR fn# .
+.PP
+When
+.I rc
+starts executing it reads variable and function definitions from its
+environment.
+.SS Special Variables
+The following variables are set or used by
+.IR rc .
+.PD 0
+.TP \w'\fL$promptXX'u
+.B $*
+Set to
+.IR rc 's
+argument list during initialization.
+Whenever a
+.B .
+command or a function is executed, the current value is saved and
+.B $*
+receives the new argument list.
+The saved value is restored on completion of the
+.B .
+or function.
+.TP
+.B $apid
+Whenever a process is started asynchronously with
+.BR & ,
+.B $apid
+is set to its process id.
+.TP
+.B $home
+The default directory for
+.BR cd .
+.TP
+.B $ifs
+The input field separators used in backquote substitutions.
+If
+.B $ifs
+is not set in
+.IR rc 's
+environment, it is initialized to blank, tab and newline.
+.TP
+.B $path
+The search path used to find commands and input files
+for the
+.B .
+command.
+If not set in the environment, it is initialized by
+parsing the
+.B $PATH
+variable
+(as in
+.IR sh (1))
+or by
+.BR "path=(.\ /bin)" .
+The variables
+.B $path
+and
+.B $PATH
+are maintained together: changes to one will be reflected in the other.
+.\" Its use is discouraged; instead use
+.\" .IR bind (1)
+.\" to build a
+.\" .B /bin
+.\" containing what's needed.
+.TP
+.B $pid
+Set during initialization to
+.IR rc 's
+process id.
+.TP
+.B $prompt
+When
+.I rc
+is run interactively, the first component of
+.B $prompt
+is printed before reading each command.
+The second component is printed whenever a newline is typed and more lines
+are required to complete the command.
+If not set in the environment, it is initialized by
+.BR "prompt=('%\ '\ '\ ')" .
+.TP
+.B $status
+Set to the wait message of the last-executed program.
+(unless started with
+.BR &).
+.B !
+and
+.B ~
+also change
+.BR $status .
+Its value is used to control execution in
+.BR && ,
+.BR || ,
+.B if
+and
+.B while
+commands.
+When
+.I rc
+exits at end-of-file of its input or on executing an
+.B exit
+command with no argument,
+.B $status
+is its exit status.
+.PD
+.SS Invocation
+If
+.I rc
+is started with no arguments it reads commands from standard input.
+Otherwise its first non-flag argument is the name of a file from which
+to read commands (but see
+.B -c
+below).
+Subsequent arguments become the initial value of
+.BR $* .
+.I Rc
+accepts the following command-line flags.
+.PD 0
+.TP \w'\fL-c\ \fIstring\fLXX'u
+.BI -c " string"
+Commands are read from
+.IR string .
+.TP
+.B -s
+Print out exit status after any command where the status is non-null.
+.TP
+.B -e
+Exit if
+.B $status
+is non-null after executing a simple command.
+.TP
+.B -i
+If
+.B -i
+is present, or
+.I rc
+is given no arguments and its standard input is a terminal,
+it runs interactively.
+Commands are prompted for using
+.BR $prompt .
+.TP
+.B -I
+Makes sure
+.I rc
+is not run interactively.
+.TP
+.B -l
+If
+.B -l
+is given or the first character of argument zero is
+.BR - ,
+.I rc
+reads commands from
+.BR $home/lib/profile ,
+if it exists, before reading its normal input.
+.TP
+.B -p
+A no-op.
+.TP
+.B -d
+A no-op.
+.TP
+.B -v
+Echo input on file descriptor 2 as it is read.
+.TP
+.B -x
+Print each simple command before executing it.
+.TP
+.B -r
+Print debugging information (internal form of commands
+as they are executed).
+.PD
+.SH SOURCE
+.B \*9/src/cmd/rc
+.SH "SEE ALSO"
+Tom Duff,
+``Rc \- The Plan 9 Shell''.
+.SH BUGS
+There should be a way to match patterns against whole lists rather than
+just single strings.
+.PP
+Using
+.B ~
+to check the value of
+.B $status
+changes
+.BR $status .
+.PP
+Functions that use here documents don't work.
+.PP
+Free carets don't get inserted next to keywords.
+.PP
+The
+.BI <{ command }
+syntax depends on the underlying operating system
+providing a file descriptor device tree at
+.BR /dev/fd .
+.PP
+By default, FreeBSD 5
+does not provide file descriptors greater than 2
+in
+.BR /dev/fd .
+To fix this, add
+.IP
+.EX
+/fdescfs /dev/fd fdescfs rw 0 0
+.EE
+.LP
+to
+.BR /etc/fstab ,
+and then
+.B mount
+.BR /dev/fd .
+(Adding the line to
+.B fstab
+ensures causes FreeBSD to mount the file system
+automatically at boot time.)
diff --git a/rc/rc.h b/rc/rc.h
@@ -0,0 +1,145 @@
+/*
+ * Plan9 is defined for plan 9
+ * V9 is defined for 9th edition
+ * Sun is defined for sun-os
+ * Please don't litter the code with ifdefs. The three below (and one in
+ * getflags) should be enough.
+ */
+#define Plan9
+#ifdef Plan9
+#include <u.h>
+#include <libc.h>
+#undef NSIG
+#undef SIGINT
+#undef SIGQUIT
+#define NSIG 32
+#define SIGINT 2
+#define SIGQUIT 3
+#endif
+#ifdef V9
+#include <signal.h>
+#include <libc.h>
+#endif
+#ifdef Sun
+#include <signal.h>
+#endif
+#define YYMAXDEPTH 500
+#ifndef PAREN
+#ifndef YYMAJOR
+#include "y.tab.h"
+#endif
+#endif
+
+#undef pipe /* so that /dev/fd works */
+#define searchpath rcsearchpath /* avoid new libc function */
+
+typedef struct tree tree;
+typedef struct word word;
+typedef struct io io;
+typedef union code code;
+typedef struct var var;
+typedef struct list list;
+typedef struct redir redir;
+typedef struct thread thread;
+typedef struct builtin builtin;
+
+struct tree{
+ int type;
+ int rtype, fd0, fd1; /* details of REDIR PIPE DUP tokens */
+ char *str;
+ int quoted;
+ int iskw;
+ tree *child[3];
+ tree *next;
+};
+tree *newtree(void);
+tree *token(char*, int), *klook(char*), *tree1(int, tree*);
+tree *tree2(int, tree*, tree*), *tree3(int, tree*, tree*, tree*);
+tree *mung1(tree*, tree*), *mung2(tree*, tree*, tree*);
+tree *mung3(tree*, tree*, tree*, tree*), *epimung(tree*, tree*);
+tree *simplemung(tree*), *heredoc(tree*);
+void freetree(tree*);
+tree *cmdtree;
+/*
+ * The first word of any code vector is a reference count.
+ * Always create a new reference to a code vector by calling codecopy(.).
+ * Always call codefree(.) when deleting a reference.
+ */
+union code{
+ void (*f)(void);
+ int i;
+ char *s;
+};
+char *promptstr;
+int doprompt;
+#define NTOK 8192
+char tok[NTOK];
+#define APPEND 1
+#define WRITE 2
+#define READ 3
+#define HERE 4
+#define DUPFD 5
+#define CLOSE 6
+struct var{
+ char *name; /* ascii name */
+ word *val; /* value */
+ int changed;
+ code *fn; /* pointer to function's code vector */
+ int fnchanged;
+ int pc; /* pc of start of function */
+ var *next; /* next on hash or local list */
+ void (*changefn)(var*);
+};
+var *vlook(char*), *gvlook(char*), *newvar(char*, var*);
+#define NVAR 521
+var *gvar[NVAR]; /* hash for globals */
+#define new(type) ((type *)emalloc(sizeof(type)))
+char *emalloc(long);
+void *Malloc(ulong);
+void efree(char*);
+#define NOFILE 128 /* should come from <param.h> */
+struct here{
+ tree *tag;
+ char *name;
+ struct here *next;
+};
+int mypid;
+/*
+ * Glob character escape in strings:
+ * In a string, GLOB must be followed by *?[ or GLOB.
+ * GLOB* matches any string
+ * GLOB? matches any single character
+ * GLOB[...] matches anything in the brackets
+ * GLOBGLOB matches GLOB
+ */
+#define GLOB ((char)0x01)
+/*
+ * onebyte(c), twobyte(c), threebyte(c)
+ * Is c the first character of a one- two- or three-byte utf sequence?
+ */
+#define onebyte(c) ((c&0x80)==0x00)
+#define twobyte(c) ((c&0xe0)==0xc0)
+#define threebyte(c) ((c&0xf0)==0xe0)
+char **argp;
+char **args;
+int nerror; /* number of errors encountered during compilation */
+int doprompt; /* is it time for a prompt? */
+/*
+ * Which fds are the reading/writing end of a pipe?
+ * Unfortunately, this can vary from system to system.
+ * 9th edition Unix doesn't care, the following defines
+ * work on plan 9.
+ */
+#define PRD 0
+#define PWR 1
+extern char *Rcmain(), Fdprefix[];
+#define register
+/*
+ * How many dot commands have we executed?
+ * Used to ensure that -v flag doesn't print rcmain.
+ */
+int ndot;
+char *getstatus(void);
+int lastc;
+int lastword;
+int kidpid;
diff --git a/rc/rcmain b/rc/rcmain
@@ -0,0 +1,39 @@
+# rcmain: Plan 9 on Unix version
+if(~ $#home 0) home=$HOME
+if(~ $#home 0) home=/
+if(~ $#ifs 0) ifs='
+'
+switch($#prompt){
+case 0
+ prompt=('; ' ' ')
+case 1
+ prompt=($prompt ' ')
+}
+if(~ $rcname ?.out ?.rc */?.rc */?.out) prompt=('broken! ' ' ')
+if(flag p) path=(/bin /usr/bin)
+if not{
+ finit
+ # should be taken care of by rc now, but leave just in case
+}
+fn sigexit
+if(! ~ $#cflag 0){
+ if(flag l && test -r $home/lib/profile) . $home/lib/profile
+ status=''
+ eval $cflag
+ exit $status
+}
+if(flag i){
+ if(flag l && test -r $home/lib/profile) . $home/lib/profile
+ status=''
+ if(! ~ $#* 0) . $*
+ . -i '/dev/stdin'
+ exit $status
+}
+if(flag l && test -r $home/lib/profile) . $home/lib/profile
+if(~ $#* 0){
+ . /dev/stdin
+ exit $status
+}
+status=''
+. $*
+exit $status
diff --git a/rc/simple.c b/rc/simple.c
@@ -0,0 +1,445 @@
+/*
+ * Maybe `simple' is a misnomer.
+ */
+#include "rc.h"
+#include "getflags.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+/*
+ * Search through the following code to see if we're just going to exit.
+ */
+int
+exitnext(void){
+ union code *c=&runq->code[runq->pc];
+ while(c->f==Xpopredir) c++;
+ return c->f==Xexit;
+}
+void Xsimple(void){
+ word *a;
+ thread *p=runq;
+ var *v;
+ struct builtin *bp;
+ int pid, n;
+ char buf[ERRMAX];
+ globlist();
+ a=runq->argv->words;
+ if(a==0){
+ Xerror1("empty argument list");
+ return;
+ }
+ if(flag['x'])
+ pfmt(err, "%v\n", p->argv->words); /* wrong, should do redirs */
+ v=gvlook(a->word);
+ if(v->fn)
+ execfunc(v);
+ else{
+ if(strcmp(a->word, "builtin")==0){
+ if(count(a)==1){
+ pfmt(err, "builtin: empty argument list\n");
+ setstatus("empty arg list");
+ poplist();
+ return;
+ }
+ a=a->next;
+ popword();
+ }
+ for(bp=Builtin;bp->name;bp++)
+ if(strcmp(a->word, bp->name)==0){
+ (*bp->fnc)();
+ return;
+ }
+ if(exitnext()){
+ /* fork and wait is redundant */
+ pushword("exec");
+ execexec();
+ Xexit();
+ }
+ else{
+ flush(err);
+ Updenv(); /* necessary so changes don't go out again */
+ switch(pid=fork()){
+ case -1:
+ Xerror("try again");
+ return;
+ case 0:
+ pushword("exec");
+ execexec();
+ strcpy(buf, "can't exec: ");
+ n = strlen(buf);
+ errstr(buf+n, ERRMAX-n);
+ Exit(buf);
+ default:
+ kidpid = pid;
+ poplist();
+ /* interrupts don't get us out */
+ while(Waitfor(pid, 1) < 0)
+ ;
+ kidpid = 0;
+ }
+ }
+ }
+}
+struct word nullpath={ "", 0};
+void doredir(redir *rp)
+{
+ if(rp){
+ doredir(rp->next);
+ switch(rp->type){
+ case ROPEN:
+ if(rp->from!=rp->to){
+ Dup(rp->from, rp->to);
+ close(rp->from);
+ }
+ break;
+ case RDUP: Dup(rp->from, rp->to); break;
+ case RCLOSE: close(rp->from); break;
+ }
+ }
+}
+word *searchpath(char *w){
+ word *path;
+ if(strncmp(w, "/", 1)==0
+/* || strncmp(w, "#", 1)==0 */
+ || strncmp(w, "./", 2)==0
+ || strncmp(w, "../", 3)==0
+ || (path=vlook("path")->val)==0)
+ path=&nullpath;
+ return path;
+}
+void execexec(void){
+ popword(); /* "exec" */
+ if(runq->argv->words==0){
+ Xerror1("empty argument list");
+ return;
+ }
+ doredir(runq->redir);
+ Execute(runq->argv->words, searchpath(runq->argv->words->word));
+ poplist();
+}
+void execfunc(var *func)
+{
+ word *starval;
+ popword();
+ starval=runq->argv->words;
+ runq->argv->words=0;
+ poplist();
+ start(func->fn, func->pc, (struct var *)0);
+ runq->local=newvar(strdup("*"), runq->local);
+ runq->local->val=starval;
+ runq->local->changed=1;
+}
+int dochdir(char *word){
+ /* report to /dev/wdir if it exists and we're interactive */
+ static int wdirfd = -2;
+ if(chdir(word)<0) return -1;
+ if(flag['i']!=0){
+ if(wdirfd==-2) /* try only once */
+ wdirfd = open("/dev/wdir", OWRITE|OCEXEC);
+ if(wdirfd>=0)
+ write(wdirfd, word, strlen(word));
+ }
+ return 1;
+}
+void execcd(void){
+ word *a=runq->argv->words;
+ word *cdpath;
+ char dir[512];
+ setstatus("can't cd");
+ cdpath=vlook("cdpath")->val;
+ switch(count(a)){
+ default:
+ pfmt(err, "Usage: cd [directory]\n");
+ break;
+ case 2:
+ if(a->next->word[0]=='/' || cdpath==0) cdpath=&nullpath;
+ for(;cdpath;cdpath=cdpath->next){
+ strcpy(dir, cdpath->word);
+ if(dir[0]) strcat(dir, "/");
+ strcat(dir, a->next->word);
+ if(dochdir(dir)>=0){
+ if(strlen(cdpath->word)
+ && strcmp(cdpath->word, ".")!=0)
+ pfmt(err, "%s\n", dir);
+ setstatus("");
+ break;
+ }
+ }
+ if(cdpath==0) pfmt(err, "Can't cd %s: %r\n", a->next->word);
+ break;
+ case 1:
+ a=vlook("HOME")->val;
+ if(count(a)>=1){
+ if(dochdir(a->word)>=0)
+ setstatus("");
+ else
+ pfmt(err, "Can't cd %s: %r\n", a->word);
+ }
+ else
+ pfmt(err, "Can't cd -- $home empty\n");
+ break;
+ }
+ poplist();
+}
+void execexit(void){
+ switch(count(runq->argv->words)){
+ default: pfmt(err, "Usage: exit [status]\nExiting anyway\n");
+ case 2: setstatus(runq->argv->words->next->word);
+ case 1: Xexit();
+ }
+}
+void execshift(void){
+ int n;
+ word *a;
+ var *star;
+ switch(count(runq->argv->words)){
+ default:
+ pfmt(err, "Usage: shift [n]\n");
+ setstatus("shift usage");
+ poplist();
+ return;
+ case 2: n=atoi(runq->argv->words->next->word); break;
+ case 1: n=1; break;
+ }
+ star=vlook("*");
+ for(;n && star->val;--n){
+ a=star->val->next;
+ efree(star->val->word);
+ efree((char *)star->val);
+ star->val=a;
+ star->changed=1;
+ }
+ setstatus("");
+ poplist();
+}
+int octal(char *s)
+{
+ int n=0;
+ while(*s==' ' || *s=='\t' || *s=='\n') s++;
+ while('0'<=*s && *s<='7') n=n*8+*s++-'0';
+ return n;
+}
+int mapfd(int fd)
+{
+ redir *rp;
+ for(rp=runq->redir;rp;rp=rp->next){
+ switch(rp->type){
+ case RCLOSE:
+ if(rp->from==fd) fd=-1;
+ break;
+ case RDUP:
+ case ROPEN:
+ if(rp->to==fd) fd=rp->from;
+ break;
+ }
+ }
+ return fd;
+}
+union code rdcmds[4];
+void execcmds(io *f)
+{
+ static int first=1;
+ if(first){
+ rdcmds[0].i=1;
+ rdcmds[1].f=Xrdcmds;
+ rdcmds[2].f=Xreturn;
+ first=0;
+ }
+ start(rdcmds, 1, runq->local);
+ runq->cmdfd=f;
+ runq->iflast=0;
+}
+void execeval(void){
+ char *cmdline, *s, *t;
+ int len=0;
+ word *ap;
+ if(count(runq->argv->words)<=1){
+ Xerror1("Usage: eval cmd ...");
+ return;
+ }
+ eflagok=1;
+ for(ap=runq->argv->words->next;ap;ap=ap->next)
+ len+=1+strlen(ap->word);
+ cmdline=emalloc(len);
+ s=cmdline;
+ for(ap=runq->argv->words->next;ap;ap=ap->next){
+ for(t=ap->word;*t;) *s++=*t++;
+ *s++=' ';
+ }
+ s[-1]='\n';
+ poplist();
+ execcmds(opencore(cmdline, len));
+ efree(cmdline);
+}
+union code dotcmds[14];
+void execdot(void){
+ int iflag=0;
+ int fd;
+ list *av;
+ thread *p=runq;
+ char *zero;
+ static int first=1;
+ char file[512];
+ word *path;
+ if(first){
+ dotcmds[0].i=1;
+ dotcmds[1].f=Xmark;
+ dotcmds[2].f=Xword;
+ dotcmds[3].s="0";
+ dotcmds[4].f=Xlocal;
+ dotcmds[5].f=Xmark;
+ dotcmds[6].f=Xword;
+ dotcmds[7].s="*";
+ dotcmds[8].f=Xlocal;
+ dotcmds[9].f=Xrdcmds;
+ dotcmds[10].f=Xunlocal;
+ dotcmds[11].f=Xunlocal;
+ dotcmds[12].f=Xreturn;
+ first=0;
+ }
+ else
+ eflagok=1;
+ popword();
+ if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){
+ iflag=1;
+ popword();
+ }
+ /* get input file */
+ if(p->argv->words==0){
+ Xerror1("Usage: . [-i] file [arg ...]");
+ return;
+ }
+ zero=strdup(p->argv->words->word);
+ popword();
+ fd=-1;
+ for(path=searchpath(zero);path;path=path->next){
+ strcpy(file, path->word);
+ if(file[0]) strcat(file, "/");
+ strcat(file, zero);
+ if(strcmp(file, "/dev/stdin")==0){ /* for sun & ucb */
+ fd=Dup1(0);
+ if(fd>=0) break;
+ }
+ if((fd=open(file, 0))>=0) break;
+ }
+ if(fd<0){
+ pfmt(err, "%s: ", zero);
+ setstatus("can't open");
+ Xerror(".: can't open");
+ return;
+ }
+ /* set up for a new command loop */
+ start(dotcmds, 1, (struct var *)0);
+ pushredir(RCLOSE, fd, 0);
+ runq->cmdfile=zero;
+ runq->cmdfd=openfd(fd);
+ runq->iflag=iflag;
+ runq->iflast=0;
+ /* push $* value */
+ pushlist();
+ runq->argv->words=p->argv->words;
+ /* free caller's copy of $* */
+ av=p->argv;
+ p->argv=av->next;
+ efree((char *)av);
+ /* push $0 value */
+ pushlist();
+ pushword(zero);
+ ndot++;
+}
+void execflag(void){
+ char *letter, *val;
+ switch(count(runq->argv->words)){
+ case 2:
+ setstatus(flag[(uchar)runq->argv->words->next->word[0]]?"":"flag not set");
+ break;
+ case 3:
+ letter=runq->argv->words->next->word;
+ val=runq->argv->words->next->next->word;
+ if(strlen(letter)==1){
+ if(strcmp(val, "+")==0){
+ flag[(uchar)letter[0]]=flagset;
+ break;
+ }
+ if(strcmp(val, "-")==0){
+ flag[(uchar)letter[0]]=0;
+ break;
+ }
+ }
+ default:
+ Xerror1("Usage: flag [letter] [+-]");
+ return;
+ }
+ poplist();
+}
+void execwhatis(void){ /* mildly wrong -- should fork before writing */
+ word *a, *b, *path;
+ var *v;
+ struct builtin *bp;
+ char file[512];
+ struct io out[1];
+ int found, sep;
+ a=runq->argv->words->next;
+ if(a==0){
+ Xerror1("Usage: whatis name ...");
+ return;
+ }
+ setstatus("");
+ out->fd=mapfd(1);
+ out->bufp=out->buf;
+ out->ebuf=&out->buf[NBUF];
+ out->strp=0;
+ for(;a;a=a->next){
+ v=vlook(a->word);
+ if(v->val){
+ pfmt(out, "%s=", a->word);
+ if(v->val->next==0)
+ pfmt(out, "%q\n", v->val->word);
+ else{
+ sep='(';
+ for(b=v->val;b && b->word;b=b->next){
+ pfmt(out, "%c%q", sep, b->word);
+ sep=' ';
+ }
+ pfmt(out, ")\n");
+ }
+ found=1;
+ }
+ else
+ found=0;
+ v=gvlook(a->word);
+ if(v->fn) pfmt(out, "fn %s %s\n", v->name, v->fn[v->pc-1].s);
+ else{
+ for(bp=Builtin;bp->name;bp++)
+ if(strcmp(a->word, bp->name)==0){
+ pfmt(out, "builtin %s\n", a->word);
+ break;
+ }
+ if(!bp->name){
+ for(path=searchpath(a->word);path;path=path->next){
+ strcpy(file, path->word);
+ if(file[0]) strcat(file, "/");
+ strcat(file, a->word);
+ if(Executable(file)){
+ pfmt(out, "%s\n", file);
+ break;
+ }
+ }
+ if(!path && !found){
+ pfmt(err, "%s: not found\n", a->word);
+ setstatus("not found");
+ }
+ }
+ }
+ }
+ poplist();
+ flush(err);
+}
+void execwait(void){
+ switch(count(runq->argv->words)){
+ default: Xerror1("Usage: wait [pid]"); return;
+ case 2: Waitfor(atoi(runq->argv->words->next->word), 0); break;
+ case 1: Waitfor(-1, 0); break;
+ }
+ poplist();
+}
diff --git a/rc/subr.c b/rc/subr.c
@@ -0,0 +1,59 @@
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+char *emalloc(long n){
+ char *p=(char *)Malloc(n);
+ if(p==0) panic("Can't malloc %d bytes", n);
+/* if(err){ pfmt(err, "malloc %d->%p\n", n, p); flush(err); } *//**/
+ return p;
+}
+void efree(char *p)
+{
+/* pfmt(err, "free %p\n", p); flush(err); *//**/
+ if(p) free(p);
+ else pfmt(err, "free 0\n");
+}
+extern int lastword, lastdol;
+void yyerror(char *m)
+{
+ pfmt(err, "rc: ");
+ if(runq->cmdfile && !runq->iflag)
+ pfmt(err, "%s:%d: ", runq->cmdfile, runq->lineno);
+ else if(runq->cmdfile)
+ pfmt(err, "%s: ", runq->cmdfile);
+ else if(!runq->iflag)
+ pfmt(err, "line %d: ", runq->lineno);
+ if(tok[0] && tok[0]!='\n') pfmt(err, "token %q: ", tok);
+ pfmt(err, "%s\n", m);
+ flush(err);
+ lastword=0;
+ lastdol=0;
+ while(lastc!='\n' && lastc!=EOF) advance();
+ nerror++;
+ setvar("status", newword(m, (word *)0));
+}
+char *bp;
+void iacvt(int n){
+ if(n<0){
+ *bp++='-';
+ n=-n; /* doesn't work for n==-inf */
+ }
+ if(n/10)
+ iacvt(n/10);
+ *bp++=n%10+'0';
+}
+void itoa(char *s, long n)
+{
+ bp=s;
+ iacvt(n);
+ *bp='\0';
+}
+void panic(char *s, int n)
+{
+ pfmt(err, "rc: ");
+ pfmt(err, s, n);
+ pchr(err, '\n');
+ flush(err);
+ Abort();
+}
diff --git a/rc/syn.y b/rc/syn.y
@@ -0,0 +1,91 @@
+%term FOR IN WHILE IF NOT TWIDDLE BANG SUBSHELL SWITCH FN
+%term WORD REDIR DUP PIPE SUB
+%term SIMPLE ARGLIST WORDS BRACE PAREN PCMD PIPEFD /* not used in syntax */
+/* operator priorities -- lowest first */
+%left IF WHILE FOR SWITCH ')' NOT
+%left ANDAND OROR
+%left BANG SUBSHELL
+%left PIPE
+%left '^'
+%right '$' COUNT '"'
+%left SUB
+%{
+#include "rc.h"
+#include "fns.h"
+%}
+%union{
+ struct tree *tree;
+};
+%type<tree> line paren brace body cmdsa cmdsan assign epilog redir
+%type<tree> cmd simple first word comword keyword words
+%type<tree> NOT FOR IN WHILE IF TWIDDLE BANG SUBSHELL SWITCH FN
+%type<tree> WORD REDIR DUP PIPE
+%%
+rc: { return 1;}
+| line '\n' {return !compile($1);}
+line: cmd
+| cmdsa line {$$=tree2(';', $1, $2);}
+body: cmd
+| cmdsan body {$$=tree2(';', $1, $2);}
+cmdsa: cmd ';'
+| cmd '&' {$$=tree1('&', $1);}
+cmdsan: cmdsa
+| cmd '\n'
+brace: '{' body '}' {$$=tree1(BRACE, $2);}
+paren: '(' body ')' {$$=tree1(PCMD, $2);}
+assign: first '=' word {$$=tree2('=', $1, $3);}
+epilog: {$$=0;}
+| redir epilog {$$=mung2($1, $1->child[0], $2);}
+redir: REDIR word {$$=mung1($1, $1->rtype==HERE?heredoc($2):$2);}
+| DUP
+cmd: {$$=0;}
+| brace epilog {$$=epimung($1, $2);}
+| IF paren {skipnl();} cmd
+ {$$=mung2($1, $2, $4);}
+| IF NOT {skipnl();} cmd {$$=mung1($2, $4);}
+| FOR '(' word IN words ')' {skipnl();} cmd
+ /*
+ * if ``words'' is nil, we need a tree element to distinguish between
+ * for(i in ) and for(i), the former being a loop over the empty set
+ * and the latter being the implicit argument loop. so if $5 is nil
+ * (the empty set), we represent it as "()". don't parenthesize non-nil
+ * functions, to avoid growing parentheses every time we reread the
+ * definition.
+ */
+ {$$=mung3($1, $3, $5 ? $5 : tree1(PAREN, $5), $8);}
+| FOR '(' word ')' {skipnl();} cmd
+ {$$=mung3($1, $3, (struct tree *)0, $6);}
+| WHILE paren {skipnl();} cmd
+ {$$=mung2($1, $2, $4);}
+| SWITCH word {skipnl();} brace
+ {$$=tree2(SWITCH, $2, $4);}
+| simple {$$=simplemung($1);}
+| TWIDDLE word words {$$=mung2($1, $2, $3);}
+| cmd ANDAND cmd {$$=tree2(ANDAND, $1, $3);}
+| cmd OROR cmd {$$=tree2(OROR, $1, $3);}
+| cmd PIPE cmd {$$=mung2($2, $1, $3);}
+| redir cmd %prec BANG {$$=mung2($1, $1->child[0], $2);}
+| assign cmd %prec BANG {$$=mung3($1, $1->child[0], $1->child[1], $2);}
+| BANG cmd {$$=mung1($1, $2);}
+| SUBSHELL cmd {$$=mung1($1, $2);}
+| FN words brace {$$=tree2(FN, $2, $3);}
+| FN words {$$=tree1(FN, $2);}
+simple: first
+| simple word {$$=tree2(ARGLIST, $1, $2);}
+| simple redir {$$=tree2(ARGLIST, $1, $2);}
+first: comword
+| first '^' word {$$=tree2('^', $1, $3);}
+word: keyword {lastword=1; $1->type=WORD;}
+| comword
+| word '^' word {$$=tree2('^', $1, $3);}
+comword: '$' word {$$=tree1('$', $2);}
+| '$' word SUB words ')' {$$=tree2(SUB, $2, $4);}
+| '"' word {$$=tree1('"', $2);}
+| COUNT word {$$=tree1(COUNT, $2);}
+| WORD
+| '`' brace {$$=tree1('`', $2);}
+| '(' words ')' {$$=tree1(PAREN, $2);}
+| REDIR brace {$$=mung1($1, $2); $$->type=PIPEFD;}
+keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN
+words: {$$=(struct tree*)0;}
+| words word {$$=tree2(WORDS, $1, $2);}
diff --git a/rc/trap.c b/rc/trap.c
@@ -0,0 +1,34 @@
+#include "rc.h"
+#include "exec.h"
+#include "fns.h"
+#include "io.h"
+extern char *Signame[];
+void dotrap(void){
+ register int i;
+ register struct var *trapreq;
+ register struct word *starval;
+ starval=vlook("*")->val;
+ while(ntrap) for(i=0;i!=NSIG;i++) while(trap[i]){
+ --trap[i];
+ --ntrap;
+ if(getpid()!=mypid) Exit(getstatus());
+ trapreq=vlook(Signame[i]);
+ if(trapreq->fn){
+ start(trapreq->fn, trapreq->pc, (struct var *)0);
+ runq->local=newvar(strdup("*"), runq->local);
+ runq->local->val=copywords(starval, (struct word *)0);
+ runq->local->changed=1;
+ runq->redir=runq->startredir=0;
+ }
+ else if(i==SIGINT || i==SIGQUIT){
+ /*
+ * run the stack down until we uncover the
+ * command reading loop. Xreturn will exit
+ * if there is none (i.e. if this is not
+ * an interactive rc.)
+ */
+ while(!runq->iflag) Xreturn();
+ }
+ else Exit(getstatus());
+ }
+}
diff --git a/rc/tree.c b/rc/tree.c
@@ -0,0 +1,114 @@
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+tree *treenodes;
+/*
+ * create and clear a new tree node, and add it
+ * to the node list.
+ */
+tree *newtree(void){
+ tree *t=new(tree);
+ t->iskw=0;
+ t->str=0;
+ t->child[0]=t->child[1]=t->child[2]=0;
+ t->next=treenodes;
+ treenodes=t;
+ return t;
+}
+void freenodes(void){
+ tree *t, *u;
+ for(t=treenodes;t;t=u){
+ u=t->next;
+ if(t->str) efree(t->str);
+ efree((char *)t);
+ }
+ treenodes=0;
+}
+tree *tree1(int type, tree *c0)
+{
+ return tree3(type, c0, (tree *)0, (tree *)0);
+}
+tree *tree2(int type, tree *c0, tree *c1)
+{
+ return tree3(type, c0, c1, (tree *)0);
+}
+tree *tree3(int type, tree *c0, tree *c1, tree *c2)
+{
+ tree *t;
+ if(type==';'){
+ if(c0==0) return c1;
+ if(c1==0) return c0;
+ }
+ t=newtree();
+ t->type=type;
+ t->child[0]=c0;
+ t->child[1]=c1;
+ t->child[2]=c2;
+ return t;
+}
+tree *mung1(tree *t, tree *c0)
+{
+ t->child[0]=c0;
+ return t;
+}
+tree *mung2(tree *t, tree *c0, tree *c1)
+{
+ t->child[0]=c0;
+ t->child[1]=c1;
+ return t;
+}
+tree *mung3(tree *t, tree *c0, tree *c1, tree *c2)
+{
+ t->child[0]=c0;
+ t->child[1]=c1;
+ t->child[2]=c2;
+ return t;
+}
+tree *epimung(tree *comp, tree *epi)
+{
+ tree *p;
+ if(epi==0) return comp;
+ for(p=epi;p->child[1];p=p->child[1]);
+ p->child[1]=comp;
+ return epi;
+}
+/*
+ * Add a SIMPLE node at the root of t and percolate all the redirections
+ * up to the root.
+ */
+tree *simplemung(tree *t)
+{
+ tree *u;
+ struct io *s;
+ t=tree1(SIMPLE, t);
+ s=openstr();
+ pfmt(s, "%t", t);
+ t->str=strdup(s->strp);
+ closeio(s);
+ for(u=t->child[0];u->type==ARGLIST;u=u->child[0]){
+ if(u->child[1]->type==DUP
+ || u->child[1]->type==REDIR){
+ u->child[1]->child[1]=t;
+ t=u->child[1];
+ u->child[1]=0;
+ }
+ }
+ return t;
+}
+tree *token(char *str, int type)
+{
+ tree *t=newtree();
+ t->type=type;
+ t->str=strdup(str);
+ return t;
+}
+void freetree(tree *p)
+{
+ if(p==0) return;
+ freetree(p->child[0]);
+ freetree(p->child[1]);
+ freetree(p->child[2]);
+ if(p->str) efree(p->str);
+ efree((char *)p);
+}
diff --git a/rc/unixcrap.c b/rc/unixcrap.c
@@ -0,0 +1,211 @@
+#include <u.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <libc.h>
+#include "rc.h"
+#include "exec.h"
+#include "io.h"
+#include "fns.h"
+#include "getflags.h"
+
+extern char **mkargv(word*);
+extern int mapfd(int);
+
+static char *eargs = "cdflmnstuv";
+static int rlx[] = {
+ RLIMIT_CORE,
+ RLIMIT_DATA,
+ RLIMIT_FSIZE,
+#ifdef RLIMIT_MEMLOCK
+ RLIMIT_MEMLOCK,
+#else
+ 0,
+#endif
+#ifdef RLIMIT_RSS
+ RLIMIT_RSS,
+#else
+ 0,
+#endif
+ RLIMIT_NOFILE,
+ RLIMIT_STACK,
+ RLIMIT_CPU,
+#ifdef RLIMIT_NPROC
+ RLIMIT_NPROC,
+#else
+ 0,
+#endif
+#ifdef RLIMIT_RSS
+ RLIMIT_RSS,
+#else
+ 0,
+#endif
+};
+
+static void
+eusage(void)
+{
+ fprint(mapfd(2), "usage: ulimit [-SHa%s [limit]]\n", eargs);
+}
+
+#define Notset -4
+#define Unlimited -3
+#define Hard -2
+#define Soft -1
+
+void
+execulimit(void)
+{
+ int fd, n, argc, sethard, setsoft, limit;
+ int flag[256];
+ char **argv, **oargv, *p;
+ char *argv0;
+ struct rlimit rl;
+
+ argv0 = nil;
+ setstatus("");
+ oargv = mkargv(runq->argv->words);
+ argv = oargv+1;
+ for(argc=0; argv[argc]; argc++)
+ ;
+
+ memset(flag, 0, sizeof flag);
+ ARGBEGIN{
+ default:
+ if(strchr(eargs, ARGC()) == nil){
+ eusage();
+ return;
+ }
+ case 'S':
+ case 'H':
+ case 'a':
+ flag[ARGC()] = 1;
+ break;
+ }ARGEND
+
+ if(argc > 1){
+ eusage();
+ goto out;
+ }
+
+ fd = mapfd(1);
+
+ sethard = 1;
+ setsoft = 1;
+ if(flag['S'] && flag['H'])
+ ;
+ else if(flag['S'])
+ sethard = 0;
+ else if(flag['H'])
+ setsoft = 0;
+
+ limit = Notset;
+ if(argc>0){
+ if(strcmp(argv[0], "unlimited") == 0)
+ limit = Unlimited;
+ else if(strcmp(argv[0], "hard") == 0)
+ limit = Hard;
+ else if(strcmp(argv[0], "soft") == 0)
+ limit = Soft;
+ else if((limit = strtol(argv[0], &p, 0)) < 0 || *p != 0){
+ eusage();
+ goto out;
+ }
+ }
+ if(flag['a']){
+ for(p=eargs; *p; p++){
+ getrlimit(rlx[p-eargs], &rl);
+ n = flag['H'] ? rl.rlim_max : rl.rlim_cur;
+ if(n == -1)
+ fprint(fd, "ulimit -%c unlimited\n", *p);
+ else
+ fprint(fd, "ulimit -%c %d\n", *p, n);
+ }
+ goto out;
+ }
+ for(p=eargs; *p; p++){
+ if(flag[(uchar)*p]){
+ n = 0;
+ getrlimit(rlx[p-eargs], &rl);
+ switch(limit){
+ case Notset:
+ n = flag['H'] ? rl.rlim_max : rl.rlim_cur;
+ if(n == -1)
+ fprint(fd, "ulimit -%c unlimited\n", *p);
+ else
+ fprint(fd, "ulimit -%c %d\n", *p, n);
+ break;
+ case Hard:
+ n = rl.rlim_max;
+ goto set;
+ case Soft:
+ n = rl.rlim_cur;
+ goto set;
+ case Unlimited:
+ n = -1;
+ goto set;
+ default:
+ n = limit;
+ set:
+ if(setsoft)
+ rl.rlim_cur = n;
+ if(sethard)
+ rl.rlim_max = n;
+ if(setrlimit(rlx[p-eargs], &rl) < 0)
+ fprint(mapfd(2), "setrlimit: %r\n");
+ }
+ }
+ }
+
+out:
+ free(oargv);
+ poplist();
+ flush(err);
+}
+
+void
+execumask(void)
+{
+ int n, argc;
+ char **argv, **oargv, *p;
+ char *argv0;
+
+ argv0 = nil;
+ setstatus("");
+ oargv = mkargv(runq->argv->words);
+ argv = oargv+1;
+ for(argc=0; argv[argc]; argc++)
+ ;
+
+ ARGBEGIN{
+ default:
+ usage:
+ fprint(mapfd(2), "usage: umask [mode]\n");
+ goto out;
+ }ARGEND
+
+ if(argc > 1)
+ goto usage;
+
+ if(argc == 1){
+ n = strtol(argv[0], &p, 8);
+ if(*p != 0 || p == argv[0])
+ goto usage;
+ umask(n);
+ goto out;
+ }
+
+ n = umask(0);
+ umask(n);
+ if(n < 0){
+ fprint(mapfd(2), "umask: %r\n");
+ goto out;
+ }
+
+ fprint(mapfd(1), "umask %03o\n", n);
+
+out:
+ free(oargv);
+ poplist();
+ flush(err);
+}
diff --git a/rc/var.c b/rc/var.c
@@ -0,0 +1,131 @@
+#include "rc.h"
+#include "exec.h"
+#include "fns.h"
+int hash(char *s, int n)
+{
+ register int h=0, i=1;
+ while(*s) h+=*s++*i++;
+ h%=n;
+ return h<0?h+n:h;
+}
+#define NKW 30
+struct kw{
+ char *name;
+ int type;
+ struct kw *next;
+}*kw[NKW];
+void kenter(int type, char *name)
+{
+ register int h=hash(name, NKW);
+ register struct kw *p=new(struct kw);
+ p->type=type;
+ p->name=name;
+ p->next=kw[h];
+ kw[h]=p;
+}
+void kinit(void){
+ kenter(FOR, "for");
+ kenter(IN, "in");
+ kenter(WHILE, "while");
+ kenter(IF, "if");
+ kenter(NOT, "not");
+ kenter(TWIDDLE, "~");
+ kenter(BANG, "!");
+ kenter(SUBSHELL, "@");
+ kenter(SWITCH, "switch");
+ kenter(FN, "fn");
+}
+tree *klook(char *name)
+{
+ struct kw *p;
+ tree *t=token(name, WORD);
+ for(p=kw[hash(name, NKW)];p;p=p->next)
+ if(strcmp(p->name, name)==0){
+ t->type=p->type;
+ t->iskw=1;
+ break;
+ }
+ return t;
+}
+var *gvlook(char *name)
+{
+ int h=hash(name, NVAR);
+ var *v;
+ for(v=gvar[h];v;v=v->next) if(strcmp(v->name, name)==0) return v;
+ return gvar[h]=newvar(strdup(name), gvar[h]);
+}
+var *vlook(char *name)
+{
+ var *v;
+ if(runq)
+ for(v=runq->local;v;v=v->next)
+ if(strcmp(v->name, name)==0) return v;
+ return gvlook(name);
+}
+void _setvar(char *name, word *val, int callfn)
+{
+ register struct var *v=vlook(name);
+ freewords(v->val);
+ v->val=val;
+ v->changed=1;
+ if(callfn && v->changefn)
+ v->changefn(v);
+}
+void setvar(char *name, word *val)
+{
+ _setvar(name, val, 1);
+}
+void bigpath(var *v)
+{
+ /* convert $PATH to $path */
+ char *p, *q;
+ word **l, *w;
+
+ if(v->val == nil){
+ _setvar("path", nil, 0);
+ return;
+ }
+ p = v->val->word;
+ w = nil;
+ l = &w;
+ /*
+ * Doesn't handle escaped colon nonsense.
+ */
+ if(p[0] == 0)
+ p = nil;
+ while(p){
+ q = strchr(p, ':');
+ if(q)
+ *q = 0;
+ *l = newword(p[0] ? p : ".", nil);
+ l = &(*l)->next;
+ if(q){
+ *q = ':';
+ p = q+1;
+ }else
+ p = nil;
+ }
+ _setvar("path", w, 0);
+}
+void littlepath(var *v)
+{
+ /* convert $path to $PATH */
+ char *p;
+ word *w;
+
+ p = _list2str(v->val, ':');
+ w = new(word);
+ w->word = p;
+ w->next = nil;
+ _setvar("PATH", w, 1); /* 1: recompute $path to expose colon problems */
+}
+void pathinit(void)
+{
+ var *v;
+
+ v = gvlook("path");
+ v->changefn = littlepath;
+ v = gvlook("PATH");
+ v->changefn = bigpath;
+ bigpath(v);
+}
diff --git a/rc/y.tab.h b/rc/y.tab.h
@@ -0,0 +1,30 @@
+#define FOR 57346
+#define IN 57347
+#define WHILE 57348
+#define IF 57349
+#define NOT 57350
+#define TWIDDLE 57351
+#define BANG 57352
+#define SUBSHELL 57353
+#define SWITCH 57354
+#define FN 57355
+#define WORD 57356
+#define REDIR 57357
+#define DUP 57358
+#define PIPE 57359
+#define SUB 57360
+#define SIMPLE 57361
+#define ARGLIST 57362
+#define WORDS 57363
+#define BRACE 57364
+#define PAREN 57365
+#define PCMD 57366
+#define PIPEFD 57367
+#define ANDAND 57368
+#define OROR 57369
+#define COUNT 57370
+
+typedef union {
+ struct tree *tree;
+} YYSTYPE;
+extern YYSTYPE yylval;
diff --git a/sed/Makefile b/sed/Makefile
@@ -0,0 +1,36 @@
+# sed - sed unix port from plan9
+# Depends on ../lib9
+
+include ../config.mk
+
+TARG = sed
+
+OFILES = sed.o
+
+MANFILES = sed.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
diff --git a/sed/sed.1 b/sed/sed.1
@@ -0,0 +1,385 @@
+.TH SED 1
+.SH NAME
+sed \- stream editor
+.SH SYNOPSIS
+.B sed
+[
+.B -n
+]
+[
+.B -g
+]
+[
+.B -e
+.I script
+]
+[
+.B -f
+.I sfile
+]
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Sed
+copies the named
+.I files
+(standard input default) to the standard output,
+edited according to a script of commands.
+The
+.B -f
+option causes the script to be taken from file
+.IR sfile ;
+these options accumulate.
+If there is just one
+.B -e
+option and no
+.BR -f 's,
+the flag
+.B -e
+may be omitted.
+The
+.B -n
+option suppresses the default output;
+.B -g
+causes all substitutions to be global, as if suffixed
+.BR g .
+.PP
+A script consists of editing commands, one per line,
+of the following form:
+.IP
+[\fIaddress\fR [\fL,\fI address\fR] ] \fIfunction\fR [\fIargument\fR ...]
+.PP
+In normal operation
+.I sed
+cyclically copies a line of input into a
+.I pattern space
+(unless there is something left after
+a
+.L D
+command),
+applies in sequence
+all commands whose
+.I addresses
+select that pattern space,
+and at the end of the script copies the pattern space
+to the standard output (except under
+.BR -n )
+and deletes the pattern space.
+.PP
+An
+.I address
+is either a decimal number that counts
+input lines cumulatively across files, a
+.L $
+that
+addresses the last line of input, or a context address,
+.BI / regular-expression / \f1,
+in the style of
+.IR regexp (7),
+with the added convention that
+.L \en
+matches a
+newline embedded in the pattern space.
+.PP
+A command line with no addresses selects every pattern space.
+.PP
+A command line with
+one address selects each pattern space that matches the address.
+.PP
+A command line with
+two addresses selects the inclusive range from the first
+pattern space that matches the first address through
+the next pattern space that matches
+the second.
+(If the second address is a number less than or equal
+to the line number first selected, only one
+line is selected.)
+Thereafter the process is repeated, looking again for the
+first address.
+.PP
+Editing commands can be applied to non-selected pattern
+spaces by use of the negation function
+.L !
+(below).
+.PP
+An argument denoted
+.I text
+consists of one or more lines,
+all but the last of which end with
+.L \e
+to hide the
+newline.
+Backslashes in text are treated like backslashes
+in the replacement string of an
+.L s
+command,
+and may be used to protect initial blanks and tabs
+against the stripping that is done on
+every script line.
+.PP
+An argument denoted
+.I rfile
+or
+.I wfile
+must terminate the command
+line and must be preceded by exactly one blank.
+Each
+.I wfile
+is created before processing begins.
+There can be at most 120 distinct
+.I wfile
+arguments.
+.TP \w'\fL!\ \fIfunction\fLXXX'u
+.B a\e
+.br
+.ns
+.TP
+.I text
+Append.
+Place
+.I text
+on the output before
+reading the next input line.
+.TP
+.BI b " label"
+Branch to the
+.B :
+command bearing the
+.IR label .
+If
+.I label
+is empty, branch to the end of the script.
+.TP
+.B c\e
+.br
+.ns
+.TP
+.I text
+Change.
+Delete the pattern space.
+With 0 or 1 address or at the end of a 2-address range, place
+.I text
+on the output.
+Start the next cycle.
+.TP
+.B d
+Delete the pattern space.
+Start the next cycle.
+.TP
+.B D
+Delete the initial segment of the
+pattern space through the first newline.
+Start the next cycle.
+.TP
+.B g
+Replace the contents of the pattern space
+by the contents of the hold space.
+.TP
+.B G
+Append the contents of the hold space to the pattern space.
+.TP
+.B h
+Replace the contents of the hold space by the contents of the pattern space.
+.TP
+.B H
+Append the contents of the pattern space to the hold space.
+.ne 3
+.TP
+.B i\e
+.br
+.ns
+.TP
+.I text
+Insert.
+Place
+.I text
+on the standard output.
+.TP
+.B n
+Copy the pattern space to the standard output.
+Replace the pattern space with the next line of input.
+.TP
+.B N
+Append the next line of input to the pattern space
+with an embedded newline.
+(The current line number changes.)
+.TP
+.B p
+Print.
+Copy the pattern space to the standard output.
+.TP
+.B P
+Copy the initial segment of the pattern space through
+the first newline to the standard output.
+.TP
+.B q
+Quit.
+Branch to the end of the script.
+Do not start a new cycle.
+.TP
+.BI r " rfile"
+Read the contents of
+.IR rfile .
+Place them on the output before reading
+the next input line.
+.TP
+.B s/\fIregular-expression\fP/\fIreplacement\fP/\fIflags
+Substitute the
+.I replacement
+string for instances of the
+.I regular-expression
+in the pattern space.
+Any character may be used instead of
+.LR / .
+For a fuller description see
+.IR regexp (7).
+.I Flags
+is zero or more of
+.RS
+.TP
+.B g
+Global.
+Substitute for all non-overlapping instances of the
+.I regular expression
+rather than just the
+first one.
+.TP
+.B p
+Print the pattern space if a replacement was made.
+.TP
+.BI w " wfile"
+Write.
+Append the pattern space to
+.I wfile
+if a replacement
+was made.
+.RE
+.TP
+.BI t " label"
+Test.
+Branch to the
+.L :
+command bearing the
+.I label
+if any
+substitutions have been made since the most recent
+reading of an input line or execution of a
+.LR t .
+If
+.I label
+is empty, branch to the end of the script.
+.TP
+.B w
+.I wfile
+.br
+Write.
+Append the pattern space to
+.IR wfile .
+.TP
+.B x
+Exchange the contents of the pattern and hold spaces.
+.TP
+.B y/\fIstring1\fP/\fIstring2\fP/
+Transform.
+Replace all occurrences of characters in
+.I string1
+with the corresponding character in
+.IR string2 .
+The lengths of
+.I
+string1
+and
+.I string2
+must be equal.
+.TP
+.BI ! "function"
+Don't.
+Apply the
+.I function
+(or group, if
+.I function
+is
+.LR { )
+only to lines
+.I not
+selected by the address(es).
+.TP
+.BI : " label"
+This command does nothing; it bears a
+.I label
+for
+.B b
+and
+.B t
+commands to branch to.
+.TP
+.B =
+Place the current line number on the standard output as a line.
+.TP
+.B {
+Execute the following commands through a matching
+.L }
+only when the pattern space is selected.
+.TP
+.B " "
+An empty command is ignored.
+.ne 4
+.SH EXAMPLES
+.TP
+.B sed 10q file
+Print the first 10 lines of the file.
+.TP
+.B sed '/^$/d'
+Delete empty lines from standard input.
+.TP
+.B sed 's/UNIX/& system/g'
+Replace every instance of
+.L UNIX
+by
+.LR "UNIX system" .
+.PP
+.EX
+sed 's/ *$// \fRdrop trailing blanks\fP
+/^$/d \fRdrop empty lines\fP
+s/ */\e \fRreplace blanks by newlines\fP
+/g
+/^$/d' chapter*
+.EE
+.ns
+.IP
+Print the files
+.BR chapter1 ,
+.BR chapter2 ,
+etc. one word to a line.
+.PP
+.EX
+nroff -ms manuscript | sed '
+${
+ /^$/p \fRif last line of file is empty, print it\fP
+}
+//N \fRif current line is empty, append next line\fP
+/^\en$/D' \fRif two lines are empty, delete the first\fP
+.EE
+.ns
+.IP
+Delete all but one of each group of empty lines from a
+formatted manuscript.
+.SH SOURCE
+.B \*9/src/cmd/sed.c
+.SH SEE ALSO
+.IR ed (1),
+.IR grep (1),
+.IR awk (1),
+.IR lex (1),
+.IR sam (1),
+.IR regexp (7)
+.br
+L. E. McMahon,
+`SED \(em A Non-interactive Text Editor',
+Unix Research System Programmer's Manual, Volume 2.
+.SH BUGS
+If input is from a pipe, buffering may consume
+characters beyond a line on which a
+.L q
+command is executed.
diff --git a/sed/sed.c b/sed/sed.c
@@ -0,0 +1,1447 @@
+/*
+ * sed -- stream editor
+ *
+ *
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <regexp.h>
+
+enum {
+ DEPTH = 20, /* max nesting depth of {} */
+ MAXCMDS = 512, /* max sed commands */
+ ADDSIZE = 10000, /* size of add & read buffer */
+ MAXADDS = 20, /* max pending adds and reads */
+ LBSIZE = 8192, /* input line size */
+ LABSIZE = 50, /* max label name size */
+ MAXSUB = 10, /* max number of sub reg exp */
+ MAXFILES = 120, /* max output files */
+};
+ /* An address is a line #, a R.E., "$", a reference to the last
+ * R.E., or nothing.
+ */
+typedef struct {
+ enum {
+ A_NONE,
+ A_DOL,
+ A_LINE,
+ A_RE,
+ A_LAST,
+ }type;
+ union {
+ long line; /* Line # */
+ Reprog *rp; /* Compiled R.E. */
+ } u;
+} Addr;
+
+typedef struct SEDCOM {
+ Addr ad1; /* optional start address */
+ Addr ad2; /* optional end address */
+ union {
+ Reprog *re1; /* compiled R.E. */
+ Rune *text; /* added text or file name */
+ struct SEDCOM *lb1; /* destination command of branch */
+ } u;
+ Rune *rhs; /* Right-hand side of substitution */
+ Biobuf* fcode; /* File ID for read and write */
+ char command; /* command code -see below */
+ char gfl; /* 'Global' flag for substitutions */
+ char pfl; /* 'print' flag for substitutions */
+ char active; /* 1 => data between start and end */
+ char negfl; /* negation flag */
+} SedCom;
+
+ /* Command Codes for field SedCom.command */
+#define ACOM 01
+#define BCOM 020
+#define CCOM 02
+#define CDCOM 025
+#define CNCOM 022
+#define COCOM 017
+#define CPCOM 023
+#define DCOM 03
+#define ECOM 015
+#define EQCOM 013
+#define FCOM 016
+#define GCOM 027
+#define CGCOM 030
+#define HCOM 031
+#define CHCOM 032
+#define ICOM 04
+#define LCOM 05
+#define NCOM 012
+#define PCOM 010
+#define QCOM 011
+#define RCOM 06
+#define SCOM 07
+#define TCOM 021
+#define WCOM 014
+#define CWCOM 024
+#define YCOM 026
+#define XCOM 033
+
+
+typedef struct label { /* Label symbol table */
+ Rune asc[9]; /* Label name */
+ SedCom *chain;
+ SedCom *address; /* Command associated with label */
+} Label;
+
+typedef struct FILE_CACHE { /* Data file control block */
+ struct FILE_CACHE *next; /* Forward Link */
+ char *name; /* Name of file */
+} FileCache;
+
+SedCom pspace[MAXCMDS]; /* Command storage */
+SedCom *pend = pspace+MAXCMDS; /* End of command storage */
+SedCom *rep = pspace; /* Current fill point */
+
+Reprog *lastre = 0; /* Last regular expression */
+Resub subexp[MAXSUB]; /* sub-patterns of pattern match*/
+
+Rune addspace[ADDSIZE]; /* Buffer for a, c, & i commands */
+Rune *addend = addspace+ADDSIZE;
+
+SedCom *abuf[MAXADDS]; /* Queue of pending adds & reads */
+SedCom **aptr = abuf;
+
+struct { /* Sed program input control block */
+ enum PTYPE /* Either on command line or in file */
+ { P_ARG,
+ P_FILE
+ } type;
+ union PCTL { /* Pointer to data */
+ Biobuf *bp;
+ char *curr;
+ } pctl;
+} prog;
+
+Rune genbuf[LBSIZE]; /* Miscellaneous buffer */
+
+FileCache *fhead = 0; /* Head of File Cache Chain */
+FileCache *ftail = 0; /* Tail of File Cache Chain */
+
+Rune *loc1; /* Start of pattern match */
+Rune *loc2; /* End of pattern match */
+Rune seof; /* Pattern delimiter char */
+
+Rune linebuf[LBSIZE+1]; /* Input data buffer */
+Rune *lbend = linebuf+LBSIZE; /* End of buffer */
+Rune *spend = linebuf; /* End of input data */
+Rune *cp; /* Current scan point in linebuf */
+
+Rune holdsp[LBSIZE+1]; /* Hold buffer */
+Rune *hend = holdsp+LBSIZE; /* End of hold buffer */
+Rune *hspend = holdsp; /* End of hold data */
+
+int nflag; /* Command line flags */
+int gflag;
+
+int dolflag; /* Set when at true EOF */
+int sflag; /* Set when substitution done */
+int jflag; /* Set when jump required */
+int delflag; /* Delete current line when set */
+
+long lnum = 0; /* Input line count */
+
+char fname[MAXFILES][40]; /* File name cache */
+Biobuf *fcode[MAXFILES]; /* File ID cache */
+int nfiles = 0; /* Cache fill point */
+
+Biobuf fout; /* Output stream */
+Biobuf bstdin; /* Default input */
+Biobuf* f = 0; /* Input data */
+
+Label ltab[LABSIZE]; /* Label name symbol table */
+Label *labend = ltab+LABSIZE; /* End of label table */
+Label *lab = ltab+1; /* Current Fill point */
+
+int depth = 0; /* {} stack pointer */
+
+Rune bad; /* Dummy err ptr reference */
+Rune *badp = &bad;
+
+
+char CGMES[] = "Command garbled: %S";
+char TMMES[] = "Too much text: %S";
+char LTL[] = "Label too long: %S";
+char AD0MES[] = "No addresses allowed: %S";
+char AD1MES[] = "Only one address allowed: %S";
+
+void address(Addr *);
+void arout(void);
+int cmp(char *, char *);
+int rcmp(Rune *, Rune *);
+void command(SedCom *);
+Reprog *compile(void);
+Rune *compsub(Rune *, Rune *);
+void dechain(void);
+void dosub(Rune *);
+int ecmp(Rune *, Rune *, int);
+void enroll(char *);
+void errexit(void);
+int executable(SedCom *);
+void execute(void);
+void fcomp(void);
+long getrune(void);
+Rune *gline(Rune *);
+int match(Reprog *, Rune *);
+void newfile(enum PTYPE, char *);
+int opendata(void);
+Biobuf *open_file(char *);
+Rune *place(Rune *, Rune *, Rune *);
+void quit(char *, char *);
+int rline(Rune *, Rune *);
+Label *search(Label *);
+int substitute(SedCom *);
+char *text(char *);
+Rune *stext(Rune *, Rune *);
+int ycomp(SedCom *);
+char * trans(int c);
+void putline(Biobuf *bp, Rune *buf, int n);
+
+void
+main(int argc, char **argv)
+{
+ int compfl;
+
+ lnum = 0;
+ Binit(&fout, 1, OWRITE);
+ fcode[nfiles++] = &fout;
+ compfl = 0;
+
+ if(argc == 1)
+ exits(0);
+ ARGBEGIN{
+ case 'n':
+ nflag++;
+ continue;
+ case 'f':
+ if(argc <= 1)
+ quit("no pattern-file", 0);
+ newfile(P_FILE, ARGF());
+ fcomp();
+ compfl = 1;
+ continue;
+ case 'e':
+ if (argc <= 1)
+ quit("missing pattern", 0);
+ newfile(P_ARG, ARGF());
+ fcomp();
+ compfl = 1;
+ continue;
+ case 'g':
+ gflag++;
+ continue;
+ default:
+ fprint(2, "sed: Unknown flag: %c\n", ARGC());
+ continue;
+ } ARGEND
+
+ if(compfl == 0) {
+ if (--argc < 0)
+ quit("missing pattern", 0);
+ newfile(P_ARG, *argv++);
+ fcomp();
+ }
+
+ if(depth)
+ quit("Too many {'s", 0);
+
+ ltab[0].address = rep;
+
+ dechain();
+
+ if(argc <= 0)
+ enroll(0); /* Add stdin to cache */
+ else while(--argc >= 0) {
+ enroll(*argv++);
+ }
+ execute();
+ exits(0);
+}
+void
+fcomp(void)
+{
+ Rune *tp;
+ SedCom *pt, *pt1;
+ int i;
+ Label *lpt;
+
+ static Rune *p = addspace;
+ static SedCom **cmpend[DEPTH]; /* stack of {} operations */
+
+ while (rline(linebuf, lbend) >= 0) {
+ cp = linebuf;
+comploop:
+ while(*cp == ' ' || *cp == '\t')
+ cp++;
+ if(*cp == '\0' || *cp == '#')
+ continue;
+ if(*cp == ';') {
+ cp++;
+ goto comploop;
+ }
+
+ address(&rep->ad1);
+ if (rep->ad1.type != A_NONE) {
+ if (rep->ad1.type == A_LAST) {
+ if (!lastre)
+ quit("First RE may not be null", 0);
+ rep->ad1.type = A_RE;
+ rep->ad1.u.rp = lastre;
+ }
+ if(*cp == ',' || *cp == ';') {
+ cp++;
+ address(&rep->ad2);
+ if (rep->ad2.type == A_LAST) {
+ rep->ad1.type = A_RE;
+ rep->ad2.u.rp = lastre;
+ }
+ } else
+ rep->ad2.type = A_NONE;
+ }
+ while(*cp == ' ' || *cp == '\t')
+ cp++;
+
+swit:
+ switch(*cp++) {
+
+ default:
+ quit("Unrecognized command: %S", (char *)linebuf);
+
+ case '!':
+ rep->negfl = 1;
+ goto swit;
+
+ case '{':
+ rep->command = BCOM;
+ rep->negfl = !(rep->negfl);
+ cmpend[depth++] = &rep->u.lb1;
+ if(++rep >= pend)
+ quit("Too many commands: %S", (char *) linebuf);
+ if(*cp == '\0') continue;
+ goto comploop;
+
+ case '}':
+ if(rep->ad1.type != A_NONE)
+ quit(AD0MES, (char *) linebuf);
+ if(--depth < 0)
+ quit("Too many }'s", 0);
+ *cmpend[depth] = rep;
+ if(*cp == 0) continue;
+ goto comploop;
+
+ case '=':
+ rep->command = EQCOM;
+ if(rep->ad2.type != A_NONE)
+ quit(AD1MES, (char *) linebuf);
+ break;
+
+ case ':':
+ if(rep->ad1.type != A_NONE)
+ quit(AD0MES, (char *) linebuf);
+
+ while(*cp == ' ')
+ cp++;
+ tp = lab->asc;
+ while (*cp && *cp != ';' && *cp != ' ' && *cp != '\t' && *cp != '#') {
+ *tp++ = *cp++;
+ if(tp >= &(lab->asc[8]))
+ quit(LTL, (char *) linebuf);
+ }
+ *tp = '\0';
+
+ if(lpt = search(lab)) {
+ if(lpt->address)
+ quit("Duplicate labels: %S", (char *) linebuf);
+ } else {
+ lab->chain = 0;
+ lpt = lab;
+ if(++lab >= labend)
+ quit("Too many labels: %S", (char *) linebuf);
+ }
+ lpt->address = rep;
+ if (*cp == '#')
+ continue;
+ rep--; /* reuse this slot */
+ break;
+
+ case 'a':
+ rep->command = ACOM;
+ if(rep->ad2.type != A_NONE)
+ quit(AD1MES, (char *) linebuf);
+ if(*cp == '\\') cp++;
+ if(*cp++ != '\n')
+ quit(CGMES, (char *) linebuf);
+ rep->u.text = p;
+ p = stext(p, addend);
+ break;
+ case 'c':
+ rep->command = CCOM;
+ if(*cp == '\\') cp++;
+ if(*cp++ != '\n')
+ quit(CGMES, (char *) linebuf);
+ rep->u.text = p;
+ p = stext(p, addend);
+ break;
+ case 'i':
+ rep->command = ICOM;
+ if(rep->ad2.type != A_NONE)
+ quit(AD1MES, (char *) linebuf);
+ if(*cp == '\\') cp++;
+ if(*cp++ != '\n')
+ quit(CGMES, (char *) linebuf);
+ rep->u.text = p;
+ p = stext(p, addend);
+ break;
+
+ case 'g':
+ rep->command = GCOM;
+ break;
+
+ case 'G':
+ rep->command = CGCOM;
+ break;
+
+ case 'h':
+ rep->command = HCOM;
+ break;
+
+ case 'H':
+ rep->command = CHCOM;
+ break;
+
+ case 't':
+ rep->command = TCOM;
+ goto jtcommon;
+
+ case 'b':
+ rep->command = BCOM;
+jtcommon:
+ while(*cp == ' ')cp++;
+ if(*cp == '\0') {
+ if(pt = ltab[0].chain) {
+ while(pt1 = pt->u.lb1)
+ pt = pt1;
+ pt->u.lb1 = rep;
+ } else
+ ltab[0].chain = rep;
+ break;
+ }
+ tp = lab->asc;
+ while((*tp++ = *cp++))
+ if(tp >= &(lab->asc[8]))
+ quit(LTL, (char *) linebuf);
+ cp--;
+ tp[-1] = '\0';
+
+ if(lpt = search(lab)) {
+ if(lpt->address) {
+ rep->u.lb1 = lpt->address;
+ } else {
+ pt = lpt->chain;
+ while(pt1 = pt->u.lb1)
+ pt = pt1;
+ pt->u.lb1 = rep;
+ }
+ } else {
+ lab->chain = rep;
+ lab->address = 0;
+ if(++lab >= labend)
+ quit("Too many labels: %S",
+ (char *) linebuf);
+ }
+ break;
+
+ case 'n':
+ rep->command = NCOM;
+ break;
+
+ case 'N':
+ rep->command = CNCOM;
+ break;
+
+ case 'p':
+ rep->command = PCOM;
+ break;
+
+ case 'P':
+ rep->command = CPCOM;
+ break;
+
+ case 'r':
+ rep->command = RCOM;
+ if(rep->ad2.type != A_NONE)
+ quit(AD1MES, (char *) linebuf);
+ if(*cp++ != ' ')
+ quit(CGMES, (char *) linebuf);
+ rep->u.text = p;
+ p = stext(p, addend);
+ break;
+
+ case 'd':
+ rep->command = DCOM;
+ break;
+
+ case 'D':
+ rep->command = CDCOM;
+ rep->u.lb1 = pspace;
+ break;
+
+ case 'q':
+ rep->command = QCOM;
+ if(rep->ad2.type != A_NONE)
+ quit(AD1MES, (char *) linebuf);
+ break;
+
+ case 'l':
+ rep->command = LCOM;
+ break;
+
+ case 's':
+ rep->command = SCOM;
+ seof = *cp++;
+ if ((rep->u.re1 = compile()) == 0) {
+ if(!lastre)
+ quit("First RE may not be null.", 0);
+ rep->u.re1 = lastre;
+ }
+ rep->rhs = p;
+ if((p = compsub(p, addend)) == 0)
+ quit(CGMES, (char *) linebuf);
+ if(*cp == 'g') {
+ cp++;
+ rep->gfl++;
+ } else if(gflag)
+ rep->gfl++;
+
+ if(*cp == 'p') {
+ cp++;
+ rep->pfl = 1;
+ }
+
+ if(*cp == 'P') {
+ cp++;
+ rep->pfl = 2;
+ }
+
+ if(*cp == 'w') {
+ cp++;
+ if(*cp++ != ' ')
+ quit(CGMES, (char *) linebuf);
+ text(fname[nfiles]);
+ for(i = nfiles - 1; i >= 0; i--)
+ if(cmp(fname[nfiles],fname[i]) == 0) {
+ rep->fcode = fcode[i];
+ goto done;
+ }
+ if(nfiles >= MAXFILES)
+ quit("Too many files in w commands 1", 0);
+ rep->fcode = open_file(fname[nfiles]);
+ }
+ break;
+
+ case 'w':
+ rep->command = WCOM;
+ if(*cp++ != ' ')
+ quit(CGMES, (char *) linebuf);
+ text(fname[nfiles]);
+ for(i = nfiles - 1; i >= 0; i--)
+ if(cmp(fname[nfiles], fname[i]) == 0) {
+ rep->fcode = fcode[i];
+ goto done;
+ }
+ if(nfiles >= MAXFILES){
+ fprint(2, "sed: Too many files in w commands 2 \n");
+ fprint(2, "nfiles = %d; MAXF = %d\n", nfiles, MAXFILES);
+ errexit();
+ }
+ rep->fcode = open_file(fname[nfiles]);
+ break;
+
+ case 'x':
+ rep->command = XCOM;
+ break;
+
+ case 'y':
+ rep->command = YCOM;
+ seof = *cp++;
+ if (ycomp(rep) == 0)
+ quit(CGMES, (char *) linebuf);
+ break;
+
+ }
+done:
+ if(++rep >= pend)
+ quit("Too many commands, last: %S", (char *) linebuf);
+
+ if(*cp++ != '\0') {
+ if(cp[-1] == ';')
+ goto comploop;
+ quit(CGMES, (char *) linebuf);
+ }
+
+ }
+}
+
+Biobuf *
+open_file(char *name)
+{
+ Biobuf *bp;
+ int fd;
+
+ if ((bp = malloc(sizeof(Biobuf))) == 0)
+ quit("Out of memory", 0);
+ if ((fd = open(name, OWRITE)) < 0 &&
+ (fd = create(name, OWRITE, 0666)) < 0)
+ quit("Cannot create %s", name);
+ Binit(bp, fd, OWRITE);
+ Bseek(bp, 0, 2);
+ fcode[nfiles++] = bp;
+ return bp;
+}
+
+Rune *
+compsub(Rune *rhs, Rune *end)
+{
+ Rune r;
+
+ while ((r = *cp++) != '\0') {
+ if(r == '\\') {
+ if (rhs < end)
+ *rhs++ = 0xFFFF;
+ else
+ return 0;
+ r = *cp++;
+ if(r == 'n')
+ r = '\n';
+ } else {
+ if(r == seof) {
+ if (rhs < end)
+ *rhs++ = '\0';
+ else
+ return 0;
+ return rhs;
+ }
+ }
+ if (rhs < end)
+ *rhs++ = r;
+ else
+ return 0;
+
+ }
+ return 0;
+}
+
+Reprog *
+compile(void)
+{
+ Rune c;
+ char *ep;
+ char expbuf[512];
+
+ if((c = *cp++) == seof) /* '//' */
+ return 0;
+ ep = expbuf;
+ do {
+ if (c == 0 || c == '\n')
+ quit(TMMES, (char *) linebuf);
+ if (c == '\\') {
+ if (ep >= expbuf+sizeof(expbuf))
+ quit(TMMES, (char *) linebuf);
+ ep += runetochar(ep, &c);
+ if ((c = *cp++) == 'n')
+ c = '\n';
+ }
+ if (ep >= expbuf+sizeof(expbuf))
+ quit(TMMES, (char *) linebuf);
+ ep += runetochar(ep, &c);
+ } while ((c = *cp++) != seof);
+ *ep = 0;
+ return lastre = regcomp(expbuf);
+}
+
+void
+regerror(char *s)
+{
+ USED(s);
+ quit(CGMES, (char *) linebuf);
+}
+
+void
+newfile(enum PTYPE type, char *name)
+{
+ if (type == P_ARG)
+ prog.pctl.curr = name;
+ else if ((prog.pctl.bp = Bopen(name, OREAD)) == 0)
+ quit("Cannot open pattern-file: %s\n", name);
+ prog.type = type;
+}
+
+int
+rline(Rune *buf, Rune *end)
+{
+ long c;
+ Rune r;
+
+ while ((c = getrune()) >= 0) {
+ r = c;
+ if (r == '\\') {
+ if (buf <= end)
+ *buf++ = r;
+ if ((c = getrune()) < 0)
+ break;
+ r = c;
+ } else if (r == '\n') {
+ *buf = '\0';
+ return(1);
+ }
+ if (buf <= end)
+ *buf++ = r;
+ }
+ *buf = '\0';
+ return(-1);
+}
+
+long
+getrune(void)
+{
+ char *p;
+ long c;
+ Rune r;
+
+ if (prog.type == P_ARG) {
+ if ((p = prog.pctl.curr) != 0) {
+ if (*p) {
+ prog.pctl.curr += chartorune(&r, p);
+ c = r;
+ } else {
+ c = '\n'; /* fake an end-of-line */
+ prog.pctl.curr = 0;
+ }
+ } else
+ c = -1;
+ } else if ((c = Bgetrune(prog.pctl.bp)) < 0)
+ Bterm(prog.pctl.bp);
+ return c;
+}
+
+void
+address(Addr *ap)
+{
+ int c;
+ long lno;
+
+ if((c = *cp++) == '$')
+ ap->type = A_DOL;
+ else if(c == '/') {
+ seof = c;
+ if (ap->u.rp = compile())
+ ap->type = A_RE;
+ else
+ ap->type = A_LAST;
+ }
+ else if (c >= '0' && c <= '9') {
+ lno = c-'0';
+ while ((c = *cp) >= '0' && c <= '9')
+ lno = lno*10 + *cp++-'0';
+ if(!lno)
+ quit("line number 0 is illegal",0);
+ ap->type = A_LINE;
+ ap->u.line = lno;
+ }
+ else {
+ cp--;
+ ap->type = A_NONE;
+ }
+}
+
+int
+cmp(char *a, char *b) /* compare characters */
+{
+ while(*a == *b++)
+ if (*a == '\0')
+ return(0);
+ else a++;
+ return(1);
+}
+
+int
+rcmp(Rune *a, Rune *b) /* compare runes */
+{
+ while(*a == *b++)
+ if (*a == '\0')
+ return(0);
+ else a++;
+ return(1);
+}
+
+char *
+text(char *p) /* extract character string */
+{
+ Rune r;
+
+ while(*cp == '\t' || *cp == ' ')
+ cp++;
+ while (*cp) {
+ if ((r = *cp++) == '\\')
+ if ((r = *cp++) == 0)
+ break;;
+ if (r == '\n')
+ while (*cp == '\t' || *cp == ' ')
+ cp++;
+ p += runetochar(p, &r);
+ }
+ *p++ = '\0';
+ return p;
+}
+
+Rune *
+stext(Rune *p, Rune *end) /* extract rune string */
+{
+ while(*cp == '\t' || *cp == ' ')
+ cp++;
+ while (*cp) {
+ if (*cp == '\\')
+ if (*++cp == 0)
+ break;
+ if (p >= end-1)
+ quit(TMMES, (char *) linebuf);
+ if ((*p++ = *cp++) == '\n')
+ while(*cp == '\t' || *cp == ' ')
+ cp++;
+ }
+ *p++ = 0;
+ return p;
+}
+
+
+Label *
+search (Label *ptr)
+{
+ Label *rp;
+
+ for (rp = ltab; rp < ptr; rp++)
+ if(rcmp(rp->asc, ptr->asc) == 0)
+ return(rp);
+ return(0);
+}
+
+void
+dechain(void)
+{
+ Label *lptr;
+ SedCom *rptr, *trptr;
+
+ for(lptr = ltab; lptr < lab; lptr++) {
+
+ if(lptr->address == 0)
+ quit("Undefined label: %S", (char *) lptr->asc);
+
+ if(lptr->chain) {
+ rptr = lptr->chain;
+ while(trptr = rptr->u.lb1) {
+ rptr->u.lb1 = lptr->address;
+ rptr = trptr;
+ }
+ rptr->u.lb1 = lptr->address;
+ }
+ }
+}
+
+int
+ycomp(SedCom *r)
+{
+ int i;
+ Rune *rp;
+ Rune c, *tsp, highc;
+ Rune *sp;
+
+ highc = 0;
+ for(tsp = cp; *tsp != seof; tsp++) {
+ if(*tsp == '\\')
+ tsp++;
+ if(*tsp == '\n' || *tsp == '\0')
+ return(0);
+ if (*tsp > highc) highc = *tsp;
+ }
+ tsp++;
+ if ((rp = r->u.text = (Rune *) malloc(sizeof(Rune)*(highc+2))) == 0)
+ quit("Out of memory", 0);
+ *rp++ = highc; /* save upper bound */
+ for (i = 0; i <= highc; i++)
+ rp[i] = i;
+ sp = cp;
+ while((c = *sp++) != seof) {
+ if(c == '\\' && *sp == 'n') {
+ sp++;
+ c = '\n';
+ }
+ if((rp[c] = *tsp++) == '\\' && *tsp == 'n') {
+ rp[c] = '\n';
+ tsp++;
+ }
+ if(rp[c] == seof || rp[c] == '\0') {
+ free(r->u.re1);
+ r->u.re1 = 0;
+ return(0);
+ }
+ }
+ if(*tsp != seof) {
+ free(r->u.re1);
+ r->u.re1 = 0;
+ return(0);
+ }
+ cp = tsp+1;
+ return(1);
+}
+
+void
+execute(void)
+{
+ SedCom *ipc;
+
+ while (spend = gline(linebuf)){
+ for(ipc = pspace; ipc->command; ) {
+ if (!executable(ipc)) {
+ ipc++;
+ continue;
+ }
+ command(ipc);
+
+ if(delflag)
+ break;
+ if(jflag) {
+ jflag = 0;
+ if((ipc = ipc->u.lb1) == 0)
+ break;
+ } else
+ ipc++;
+
+ }
+ if(!nflag && !delflag)
+ putline(&fout, linebuf, spend-linebuf);
+ if(aptr > abuf) {
+ arout();
+ }
+ delflag = 0;
+ }
+}
+ /* determine if a statement should be applied to an input line */
+int
+executable(SedCom *ipc)
+{
+ if (ipc->active) { /* Addr1 satisfied - accept until Addr2 */
+ if (ipc->active == 1) /* Second line */
+ ipc->active = 2;
+ switch(ipc->ad2.type) {
+ case A_NONE: /* No second addr; use first */
+ ipc->active = 0;
+ break;
+ case A_DOL: /* Accept everything */
+ return !ipc->negfl;
+ case A_LINE: /* Line at end of range? */
+ if (lnum <= ipc->ad2.u.line) {
+ if (ipc->ad2.u.line == lnum)
+ ipc->active = 0;
+ return !ipc->negfl;
+ }
+ ipc->active = 0; /* out of range */
+ return ipc->negfl;
+ case A_RE: /* Check for matching R.E. */
+ if (match(ipc->ad2.u.rp, linebuf))
+ ipc->active = 0;
+ return !ipc->negfl;
+ default: /* internal error */
+ quit("Internal error", 0);
+ }
+ }
+ switch (ipc->ad1.type) { /* Check first address */
+ case A_NONE: /* Everything matches */
+ return !ipc->negfl;
+ case A_DOL: /* Only last line */
+ if (dolflag)
+ return !ipc->negfl;
+ break;
+ case A_LINE: /* Check line number */
+ if (ipc->ad1.u.line == lnum) {
+ ipc->active = 1; /* In range */
+ return !ipc->negfl;
+ }
+ break;
+ case A_RE: /* Check R.E. */
+ if (match(ipc->ad1.u.rp, linebuf)) {
+ ipc->active = 1; /* In range */
+ return !ipc->negfl;
+ }
+ break;
+ default:
+ quit("Internal error", 0);
+ }
+ return ipc->negfl;
+}
+
+int
+match(Reprog *pattern, Rune *buf)
+{
+ if (!pattern)
+ return 0;
+ subexp[0].s.rsp = buf;
+ subexp[0].e.rep = 0;
+ if (rregexec(pattern, linebuf, subexp, MAXSUB)) {
+ loc1 = subexp[0].s.rsp;
+ loc2 = subexp[0].e.rep;
+ return 1;
+ }
+ loc1 = loc2 = 0;
+ return 0;
+}
+
+int
+substitute(SedCom *ipc)
+{
+ int len;
+
+ if(!match(ipc->u.re1, linebuf))
+ return 0;
+
+ /*
+ * we have at least one match. some patterns, e.g. '$' or '^', can
+ * produce zero-length matches, so during a global substitute we
+ * must bump to the character after a zero-length match to keep from looping.
+ */
+ sflag = 1;
+ if(ipc->gfl == 0) /* single substitution */
+ dosub(ipc->rhs);
+ else
+ do{ /* global substitution */
+ len = loc2-loc1; /* length of match */
+ dosub(ipc->rhs); /* dosub moves loc2 */
+ if(*loc2 == 0) /* end of string */
+ break;
+ if(len == 0) /* zero-length R.E. match */
+ loc2++; /* bump over zero-length match */
+ if(*loc2 == 0) /* end of string */
+ break;
+ } while(match(ipc->u.re1, loc2));
+ return 1;
+}
+
+void
+dosub(Rune *rhsbuf)
+{
+ Rune *lp, *sp;
+ Rune *rp;
+ int c, n;
+
+ lp = linebuf;
+ sp = genbuf;
+ rp = rhsbuf;
+ while (lp < loc1)
+ *sp++ = *lp++;
+ while(c = *rp++) {
+ if (c == '&') {
+ sp = place(sp, loc1, loc2);
+ continue;
+ }
+ if (c == 0xFFFF && (c = *rp++) >= '1' && c < MAXSUB+'0') {
+ n = c-'0';
+ if (subexp[n].s.rsp && subexp[n].e.rep) {
+ sp = place(sp, subexp[n].s.rsp, subexp[n].e.rep);
+ continue;
+ }
+ else {
+ fprint(2, "sed: Invalid back reference \\%d\n",n);
+ errexit();
+ }
+ }
+ *sp++ = c;
+ if (sp >= &genbuf[LBSIZE])
+ fprint(2, "sed: Output line too long.\n");
+ }
+ lp = loc2;
+ loc2 = sp - genbuf + linebuf;
+ while (*sp++ = *lp++)
+ if (sp >= &genbuf[LBSIZE])
+ fprint(2, "sed: Output line too long.\n");
+ lp = linebuf;
+ sp = genbuf;
+ while (*lp++ = *sp++)
+ ;
+ spend = lp-1;
+}
+
+Rune *
+place(Rune *sp, Rune *l1, Rune *l2)
+{
+ while (l1 < l2) {
+ *sp++ = *l1++;
+ if (sp >= &genbuf[LBSIZE])
+ fprint(2, "sed: Output line too long.\n");
+ }
+ return(sp);
+}
+
+char *
+trans(int c)
+{
+ static char buf[] = "\\x0000";
+ static char hex[] = "0123456789abcdef";
+
+ switch(c) {
+ case '\b':
+ return "\\b";
+ case '\n':
+ return "\\n";
+ case '\r':
+ return "\\r";
+ case '\t':
+ return "\\t";
+ case '\\':
+ return "\\\\";
+ }
+ buf[2] = hex[(c>>12)&0xF];
+ buf[3] = hex[(c>>8)&0xF];
+ buf[4] = hex[(c>>4)&0xF];
+ buf[5] = hex[c&0xF];
+ return buf;
+}
+
+void
+command(SedCom *ipc)
+{
+ int i, c;
+ Rune *p1, *p2;
+ char *ucp;
+ Rune *rp;
+ Rune *execp;
+
+ switch(ipc->command) {
+
+ case ACOM:
+ *aptr++ = ipc;
+ if(aptr >= abuf+MAXADDS) {
+ quit("sed: Too many appends after line %ld\n",
+ (char *) lnum);
+ }
+ *aptr = 0;
+ break;
+ case CCOM:
+ delflag = 1;
+ if(ipc->active == 1) {
+ for(rp = ipc->u.text; *rp; rp++)
+ Bputrune(&fout, *rp);
+ Bputc(&fout, '\n');
+ }
+ break;
+ case DCOM:
+ delflag++;
+ break;
+ case CDCOM:
+ p1 = p2 = linebuf;
+ while(*p1 != '\n') {
+ if(*p1++ == 0) {
+ delflag++;
+ return;
+ }
+ }
+ p1++;
+ while(*p2++ = *p1++)
+ ;
+ spend = p2-1;
+ jflag++;
+ break;
+ case EQCOM:
+ Bprint(&fout, "%ld\n", lnum);
+ break;
+ case GCOM:
+ p1 = linebuf;
+ p2 = holdsp;
+ while(*p1++ = *p2++)
+ ;
+ spend = p1-1;
+ break;
+ case CGCOM:
+ *spend++ = '\n';
+ p1 = spend;
+ p2 = holdsp;
+ while(*p1++ = *p2++)
+ if(p1 >= lbend)
+ break;
+ spend = p1-1;
+ break;
+ case HCOM:
+ p1 = holdsp;
+ p2 = linebuf;
+ while(*p1++ = *p2++);
+ hspend = p1-1;
+ break;
+ case CHCOM:
+ *hspend++ = '\n';
+ p1 = hspend;
+ p2 = linebuf;
+ while(*p1++ = *p2++)
+ if(p1 >= hend)
+ break;
+ hspend = p1-1;
+ break;
+ case ICOM:
+ for(rp = ipc->u.text; *rp; rp++)
+ Bputrune(&fout, *rp);
+ Bputc(&fout, '\n');
+ break;
+ case BCOM:
+ jflag = 1;
+ break;
+ case LCOM:
+ c = 0;
+ for (i = 0, rp = linebuf; *rp; rp++) {
+ c = *rp;
+ if(c >= 0x20 && c < 0x7F && c != '\\') {
+ Bputc(&fout, c);
+ if(i++ > 71) {
+ Bprint(&fout, "\\\n");
+ i = 0;
+ }
+ } else {
+ for (ucp = trans(*rp); *ucp; ucp++){
+ c = *ucp;
+ Bputc(&fout, c);
+ if(i++ > 71) {
+ Bprint(&fout, "\\\n");
+ i = 0;
+ }
+ }
+ }
+ }
+ if(c == ' ')
+ Bprint(&fout, "\\n");
+ Bputc(&fout, '\n');
+ break;
+ case NCOM:
+ if(!nflag)
+ putline(&fout, linebuf, spend-linebuf);
+
+ if(aptr > abuf)
+ arout();
+ if((execp = gline(linebuf)) == 0) {
+ delflag = 1;
+ break;
+ }
+ spend = execp;
+ break;
+ case CNCOM:
+ if(aptr > abuf)
+ arout();
+ *spend++ = '\n';
+ if((execp = gline(spend)) == 0) {
+ delflag = 1;
+ break;
+ }
+ spend = execp;
+ break;
+ case PCOM:
+ putline(&fout, linebuf, spend-linebuf);
+ break;
+ case CPCOM:
+ cpcom:
+ for(rp = linebuf; *rp && *rp != '\n'; rp++)
+ Bputc(&fout, *rp);
+ Bputc(&fout, '\n');
+ break;
+ case QCOM:
+ if(!nflag)
+ putline(&fout, linebuf, spend-linebuf);
+ if(aptr > abuf)
+ arout();
+ exits(0);
+ case RCOM:
+ *aptr++ = ipc;
+ if(aptr >= &abuf[MAXADDS])
+ quit("sed: Too many reads after line %ld\n",
+ (char *) lnum);
+ *aptr = 0;
+ break;
+ case SCOM:
+ i = substitute(ipc);
+ if(i && ipc->pfl)
+ if(ipc->pfl == 1)
+ putline(&fout, linebuf, spend-linebuf);
+ else
+ goto cpcom;
+ if(i && ipc->fcode)
+ goto wcom;
+ break;
+
+ case TCOM:
+ if(sflag == 0) break;
+ sflag = 0;
+ jflag = 1;
+ break;
+
+ wcom:
+ case WCOM:
+ putline(ipc->fcode,linebuf, spend-linebuf);
+ break;
+ case XCOM:
+ p1 = linebuf;
+ p2 = genbuf;
+ while(*p2++ = *p1++);
+ p1 = holdsp;
+ p2 = linebuf;
+ while(*p2++ = *p1++);
+ spend = p2 - 1;
+ p1 = genbuf;
+ p2 = holdsp;
+ while(*p2++ = *p1++);
+ hspend = p2 - 1;
+ break;
+ case YCOM:
+ p1 = linebuf;
+ p2 = ipc->u.text;
+ for (i = *p2++; *p1; p1++){
+ if (*p1 <= i) *p1 = p2[*p1];
+ }
+ break;
+ }
+
+}
+
+void
+putline(Biobuf *bp, Rune *buf, int n)
+{
+ while (n--)
+ Bputrune(bp, *buf++);
+ Bputc(bp, '\n');
+}
+
+int
+ecmp(Rune *a, Rune *b, int count)
+{
+ while(count--)
+ if(*a++ != *b++) return(0);
+ return(1);
+}
+
+void
+arout(void)
+{
+ Rune *p1;
+ Biobuf *fi;
+ int c;
+ char *s;
+ char buf[128];
+
+ for (aptr = abuf; *aptr; aptr++) {
+ if((*aptr)->command == ACOM) {
+ for(p1 = (*aptr)->u.text; *p1; p1++ )
+ Bputrune(&fout, *p1);
+ Bputc(&fout, '\n');
+ } else {
+ for(s = buf, p1= (*aptr)->u.text; *p1; p1++)
+ s += runetochar(s, p1);
+ *s = '\0';
+ if((fi = Bopen(buf, OREAD)) == 0)
+ continue;
+ while((c = Bgetc(fi)) >= 0)
+ Bputc(&fout, c);
+ Bterm(fi);
+ }
+ }
+ aptr = abuf;
+ *aptr = 0;
+}
+
+void
+errexit(void)
+{
+ exits("error");
+}
+
+void
+quit (char *msg, char *arg)
+{
+ fprint(2, "sed: ");
+ fprint(2, msg, arg);
+ fprint(2, "\n");
+ errexit();
+}
+
+Rune *
+gline(Rune *addr)
+{
+ long c;
+ Rune *p;
+
+ static long peekc = 0;
+
+ if (f == 0 && opendata() < 0)
+ return 0;
+ sflag = 0;
+ lnum++;
+/* Bflush(&fout);********* dumped 4/30/92 - bobf****/
+ do {
+ p = addr;
+ for (c = (peekc ? peekc : Bgetrune(f)); c >= 0; c = Bgetrune(f)) {
+ if (c == '\n') {
+ if ((peekc = Bgetrune(f)) < 0) {
+ if (fhead == 0)
+ dolflag = 1;
+ }
+ *p = '\0';
+ return p;
+ }
+ if (c && p < lbend)
+ *p++ = c;
+ }
+ /* return partial final line, adding implicit newline */
+ if(p != addr) {
+ *p = '\0';
+ peekc = -1;
+ if (fhead == 0)
+ dolflag = 1;
+ return p;
+ }
+ peekc = 0;
+ Bterm(f);
+ } while (opendata() > 0); /* Switch to next stream */
+ f = 0;
+ return 0;
+}
+
+ /* Data file input section - the intent is to transparently
+ * catenate all data input streams.
+ */
+void
+enroll(char *filename) /* Add a file to the input file cache */
+{
+ FileCache *fp;
+
+ if ((fp = (FileCache *) malloc(sizeof (FileCache))) == 0)
+ quit("Out of memory", 0);
+ if (ftail == 0)
+ fhead = fp;
+ else
+ ftail->next = fp;
+ ftail = fp;
+ fp->next = 0;
+ fp->name = filename; /* 0 => stdin */
+}
+
+int
+opendata(void)
+{
+ if (fhead == 0)
+ return -1;
+ if (fhead->name) {
+ if ((f = Bopen(fhead->name, OREAD)) == 0)
+ quit("Can't open %s", fhead->name);
+ } else {
+ Binit(&bstdin, 0, OREAD);
+ f = &bstdin;
+ }
+ fhead = fhead->next;
+ return 1;
+}
diff --git a/seq/Makefile b/seq/Makefile
@@ -0,0 +1,36 @@
+# seq - seq unix port from plan9
+# Depends on ../lib9
+
+include ../config.mk
+
+TARG = seq
+
+OFILES = seq.o
+
+MANFILES = seq.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
diff --git a/seq/seq.1 b/seq/seq.1
@@ -0,0 +1,75 @@
+.TH SEQ 1
+.SH NAME
+seq \- print sequences of numbers
+.SH SYNOPSIS
+.B seq
+[
+.B -w
+]
+[
+.BI -f format
+]
+[
+.I first
+[
+.I incr
+]
+]
+.I last
+.SH DESCRIPTION
+.I Seq
+prints a sequence of numbers, one per line, from
+.I first
+(default 1) to as near
+.I last
+as possible, in increments of
+.I incr
+(default 1).
+The loop is:
+.sp
+.EX
+ for(val = min; val <= max; val += incr) print val;
+.EE
+.sp
+The numbers are interpreted as floating point.
+.PP
+Normally integer values are printed as decimal integers.
+The options are
+.TP "\w'\fL-f \fIformat\fLXX'u"
+.BI -f format
+Use the
+.IR print (3)-style
+.I format
+.IR print
+for printing each (floating point) number.
+The default is
+.LR %g .
+.TP
+.B -w
+Equalize the widths of all numbers by padding with
+leading zeros as necessary.
+Not effective with option
+.BR -f ,
+nor with numbers in exponential notation.
+.SH EXAMPLES
+.TP
+.L
+seq 0 .05 .1
+Print
+.BR "0 0.05 0.1"
+(on separate lines).
+.TP
+.L
+seq -w 0 .05 .1
+Print
+.BR "0.00 0.05 0.10" .
+.SH SOURCE
+.B \*9/src/cmd/seq.c
+.SH BUGS
+Option
+.B -w
+always surveys every value in advance.
+Thus
+.L
+seq -w 1000000000
+is a painful way to get an `infinite' sequence.
diff --git a/seq/seq.c b/seq/seq.c
@@ -0,0 +1,92 @@
+#include <u.h>
+#include <libc.h>
+
+double min = 1.0;
+double max = 0.0;
+double incr = 1.0;
+int constant = 0;
+int nsteps;
+char *format;
+
+void
+usage(void)
+{
+ fprint(2, "usage: seq [-fformat] [-w] [first [incr]] last\n");
+ exits("usage");
+}
+
+void
+buildfmt(void)
+{
+ int i;
+ char *dp;
+ int w, p, maxw, maxp;
+ static char fmt[16];
+ char buf[32];
+
+ format = "%g\n";
+ if(!constant)
+ return;
+ maxw = 0;
+ maxp = 0;
+ for(i=0; i<=nsteps; i++){
+ sprint(buf, "%g", min+i*incr);
+ if(strchr(buf, 'e')!=0)
+ return;
+ dp = strchr(buf,'.');
+ w = dp==0? strlen(buf): dp-buf;
+ p = dp==0? 0: strlen(strchr(buf,'.')+1);
+ if(w>maxw)
+ maxw = w;
+ if(p>maxp)
+ maxp = p;
+ }
+ if(maxp > 0)
+ maxw += maxp+1;
+ sprint(fmt,"%%%d.%df\n", maxw, maxp);
+ format = fmt;
+}
+
+void
+main(int argc, char *argv[]){
+ int i, j, n;
+ char buf[256], ffmt[4096];
+
+ ARGBEGIN{
+ case 'w':
+ constant++;
+ break;
+ case 'f':
+ format = ARGF();
+ if(format[strlen(format)-1] != '\n'){
+ sprint(ffmt, "%s\n", format);
+ format = ffmt;
+ }
+ break;
+ default:
+ goto out;
+ }ARGEND
+ out:
+ if(argc<1 || argc>3)
+ usage();
+ max = atof(argv[argc-1]);
+ if(argc > 1)
+ min = atof(argv[0]);
+ if(argc > 2)
+ incr = atof(argv[1]);
+ if(incr == 0){
+ fprint(2, "seq: zero increment\n");
+ exits("zero increment");
+ }
+ nsteps = (max-min)/incr+.5;
+ if(!format)
+ buildfmt();
+ for(i=0; i<=nsteps; i++){
+ n = sprint(buf, format, min+i*incr);
+ if(constant)
+ for(j=0; buf[j]==' '; j++)
+ buf[j] ='0';
+ write(1, buf, n);
+ }
+ exits(0);
+}
diff --git a/sleep/Makefile b/sleep/Makefile
@@ -0,0 +1,37 @@
+# sleep - sleep unix port from plan9
+# Depends on ../lib9
+
+include ../config.mk
+
+TARG = sleep
+
+OFILES = sleep.o
+
+MANFILES = sleep.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 ${MANFILES} ${DESTDIR}${MANPREFIX}/man1
+ @chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES}
+
+uninstall:
+ rm -f ${DESTDIR}${PREFIX}/bin/${TARG}
+ rm -f ${DESTDIR}${MANPREFIX}/man1/${MANFILES}
+
+.c.o:
+ @echo CC $*.c
+ @${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c
+
+clean:
+ rm -f ${OFILES} ${TARG}
+ @echo cleaned ${TARG}
+
+${TARG}: ${OFILES}
+ @echo LD ${TARG}
+ @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L${PREFIX}/lib -L../lib9 -l9
diff --git a/sleep/sleep.1 b/sleep/sleep.1
@@ -0,0 +1,31 @@
+.TH SLEEP 1
+.SH NAME
+sleep \- suspend execution for an interval
+.SH SYNOPSIS
+.B sleep
+.I time
+.SH DESCRIPTION
+.I Sleep
+suspends execution for
+.I time
+seconds.
+.SH EXAMPLES
+Execute a command
+100 seconds hence.
+.IP
+.EX
+{sleep 100; command}&
+.EE
+.PP
+Repeat a command every 30 seconds.
+.IP
+.EX
+while (){
+ command
+ sleep 30
+}
+.EE
+.SH SOURCE
+.B \*9/src/cmd/sleep.c
+.SH "SEE ALSO"
+.IR sleep (3)
diff --git a/sleep/sleep.c b/sleep/sleep.c
@@ -0,0 +1,13 @@
+#include <u.h>
+#include <libc.h>
+
+void
+main(int argc, char *argv[])
+{
+ long secs;
+
+ if(argc>1)
+ for(secs = atol(argv[1]); secs > 0; secs--)
+ sleep(1000);
+ exits(0);
+}
diff --git a/sort/Makefile b/sort/Makefile
@@ -0,0 +1,36 @@
+# sort - sort unix port from plan9
+# Depends on ../lib9
+
+include ../config.mk
+
+TARG = sort
+
+OFILES = sort.o
+
+MANFILES = sort.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
diff --git a/sort/sort.1 b/sort/sort.1
@@ -0,0 +1,262 @@
+.TH SORT 1
+.SH NAME
+sort \- sort and/or merge files
+.SH SYNOPSIS
+.B sort
+[
+.BI -cmuMbdf\&inrwt x
+]
+[
+.BI + pos1
+[
+.BI - pos2
+] ...
+] ...
+[
+.B -k
+.I pos1
+[
+.I ,pos2
+]
+] ...
+.br
+\h'0.5in'
+[
+.B -o
+.I output
+]
+[
+.B -T
+.I dir
+\&...
+]
+[
+.I option
+\&...
+]
+[
+.I file
+\&...
+]
+.SH DESCRIPTION
+.I Sort\^
+sorts
+lines of all the
+.I files
+together and writes the result on
+the standard output.
+If no input files are named, the standard input is sorted.
+.PP
+The default sort key is an entire line.
+Default ordering is
+lexicographic by runes.
+The ordering is affected globally by the following options,
+one or more of which may appear.
+.TP
+.B -M
+Compare as months.
+The first three
+non-white space characters
+of the field
+are folded
+to upper case
+and compared
+so that
+.L JAN
+precedes
+.LR FEB ,
+etc.
+Invalid fields
+compare low to
+.LR JAN .
+.TP
+.B -b
+Ignore leading white space (spaces and tabs) in field comparisons.
+.TP
+.B -d
+`Phone directory' order:
+only letters,
+accented letters,
+digits and white space
+are significant in comparisons.
+.TP
+.B -f
+Fold lower case
+letters onto upper case.
+Accented characters are folded to their
+non-accented upper case form.
+.TP
+.B -i
+Ignore characters outside the
+.SM ASCII
+range 040-0176
+in non-numeric comparisons.
+.TP
+.B -w
+Like
+.BR -i ,
+but ignore only tabs and spaces.
+.TP
+.B -n
+An initial numeric string,
+consisting of optional white space,
+optional plus or minus sign,
+and zero or more digits with optional decimal point,
+is sorted by arithmetic value.
+.TP
+.B -g
+Numbers, like
+.B -n
+but with optional
+.BR e -style
+exponents, are sorted by value.
+.TP
+.B -r
+Reverse the sense of comparisons.
+.TP
+.BI -t x\^
+`Tab character' separating fields is
+.IR x .
+.PP
+The notation
+.BI + "pos1\| " - pos2\^
+restricts a sort key to a field beginning at
+.I pos1\^
+and ending just before
+.IR pos2 .
+.I Pos1\^
+and
+.I pos2\^
+each have the form
+.IB m . n\f1,
+optionally followed by one or more of the flags
+.BR Mbdfginr ,
+where
+.I m\^
+tells a number of fields to skip from the beginning of the line and
+.I n\^
+tells a number of characters to skip further.
+If any flags are present they override all the global
+ordering options for this key.
+A missing
+.BI \&. n\^
+means
+.BR \&.0 ;
+a missing
+.BI - pos2\^
+means the end of the line.
+Under the
+.BI -t x\^
+option, fields are strings separated by
+.IR x ;
+otherwise fields are
+non-empty strings separated by white space.
+White space before a field
+is part of the field, except under option
+.BR -b .
+A
+.B b
+flag may be attached independently to
+.IR pos1
+and
+.IR pos2.
+.PP
+The notation
+.B -k
+.IR pos1 [, pos2 ]
+is how POSIX
+.I sort
+defines fields:
+.I pos1
+and
+.I pos2
+have the same format but different meanings.
+The value of
+.I m\^
+is origin 1 instead of origin 0
+and a missing
+.BI \&. n\^
+in
+.I pos2
+is the end of the field.
+.PP
+When there are multiple sort keys, later keys
+are compared only after all earlier keys
+compare equal.
+Lines that otherwise compare equal are ordered
+with all bytes significant.
+.PP
+These option arguments are also understood:
+.TP \w'\fL-z\fIrecsize\fLXX'u
+.B -c
+Check that the single input file is sorted according to the ordering rules;
+give no output unless the file is out of sort.
+.TP
+.B -m
+Merge; assume the input files are already sorted.
+.TP
+.B -u
+Suppress all but one in each
+set of equal lines.
+Ignored bytes
+and bytes outside keys
+do not participate in
+this comparison.
+.TP
+.B -o
+The next argument is the name of an output file
+to use instead of the standard output.
+This file may be the same as one of the inputs.
+.TP
+.BI -T dir
+Put temporary files in
+.I dir
+rather than in
+.BR /var/tmp .
+.ne 4
+.SH EXAMPLES
+.TP
+.L sort -u +0f +0 list
+Print in alphabetical order all the unique spellings
+in a list of words
+where capitalized words differ from uncapitalized.
+.TP
+.L sort -t: +1 /adm/users
+Print the users file
+sorted by user name
+(the second colon-separated field).
+.TP
+.L sort -umM dates
+Print the first instance of each month in an already sorted file.
+Options
+.B -um
+with just one input file make the choice of a
+unique representative from a set of equal lines predictable.
+.TP
+.L
+grep -n '^' input | sort -t: +1f +0n | sed 's/[0-9]*://'
+A stable sort: input lines that compare equal will
+come out in their original order.
+.SH FILES
+.BI /var/tmp/sort. <pid>.<ordinal>
+.SH SOURCE
+.B \*9/src/cmd/sort.c
+.SH SEE ALSO
+.IR uniq (1),
+.IR look (1)
+.SH DIAGNOSTICS
+.I Sort
+comments and exits with non-null status for various trouble
+conditions and for disorder discovered under option
+.BR -c .
+.SH BUGS
+An external null character can be confused
+with an internally generated end-of-field character.
+The result can make a sub-field not sort
+less than a longer field.
+.PP
+Some of the options, e.g.
+.B -i
+and
+.BR -M ,
+are hopelessly provincial.
diff --git a/sort/sort.c b/sort/sort.c
@@ -0,0 +1,1767 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+/*
+bugs:
+ 00/ff for end of file can conflict with 00/ff characters
+*/
+
+enum
+{
+ Nline = 500000, /* default max number of lines saved in memory */
+ Nmerge = 10, /* max number of temporary files merged */
+ Nfield = 20, /* max number of argument fields */
+
+ Bflag = 1<<0, /* flags per field */
+ B1flag = 1<<1,
+
+ Dflag = 1<<2,
+ Fflag = 1<<3,
+ Gflag = 1<<4,
+ Iflag = 1<<5,
+ Mflag = 1<<6,
+ Nflag = 1<<7,
+ Rflag = 1<<8,
+ Wflag = 1<<9,
+
+ NSstart = 0, /* states for number to key decoding */
+ NSsign,
+ NSzero,
+ NSdigit,
+ NSpoint,
+ NSfract,
+ NSzerofract,
+ NSexp,
+ NSexpsign,
+ NSexpdigit,
+};
+
+typedef struct Line Line;
+typedef struct Key Key;
+typedef struct Merge Merge;
+typedef struct Field Field;
+
+struct Line
+{
+ Key* key;
+ int llen; /* always >= 1 */
+ uchar line[1]; /* always ends in '\n' */
+};
+
+struct Merge
+{
+ Key* key; /* copy of line->key so (Line*) looks like (Merge*) */
+ Line* line; /* line at the head of a merged temp file */
+ int fd; /* file descriptor */
+ Biobuf b; /* iobuf for reading a temp file */
+};
+
+struct Key
+{
+ int klen;
+ uchar key[1];
+};
+
+struct Field
+{
+ int beg1;
+ int beg2;
+ int end1;
+ int end2;
+
+ long flags;
+ uchar mapto[256];
+
+ void (*dokey)(Key*, uchar*, uchar*, Field*);
+};
+
+struct args
+{
+ char* ofile;
+ char* tname;
+ Rune tabchar;
+ char cflag;
+ char uflag;
+ char vflag;
+ int nfield;
+ int nfile;
+ Field field[Nfield];
+
+ Line** linep;
+ long nline; /* number of lines in this temp file */
+ long lineno; /* overall ordinal for -s option */
+ int ntemp;
+ long mline; /* max lines per file */
+} args;
+
+extern int latinmap[];
+extern Rune* month[12];
+
+void buildkey(Line*);
+void doargs(int, char*[]);
+void dofield(char*, int*, int*, int, int);
+void dofile(Biobuf*);
+void dokey_(Key*, uchar*, uchar*, Field*);
+void dokey_dfi(Key*, uchar*, uchar*, Field*);
+void dokey_gn(Key*, uchar*, uchar*, Field*);
+void dokey_m(Key*, uchar*, uchar*, Field*);
+void dokey_r(Key*, uchar*, uchar*, Field*);
+void done(char*);
+int kcmp(Key*, Key*);
+void makemapd(Field*);
+void makemapm(Field*);
+void mergefiles(int, int, Biobuf*);
+void mergeout(Biobuf*);
+void newfield(void);
+Line* newline(Biobuf*);
+void nomem(void);
+void notifyf(void*, char*);
+void printargs(void);
+void printout(Biobuf*);
+void setfield(int, int);
+uchar* skip(uchar*, int, int, int, int);
+void sort4(void*, ulong);
+char* tempfile(int);
+void tempout(void);
+void lineout(Biobuf*, Line*);
+
+void
+main(int argc, char *argv[])
+{
+ int i, f;
+ char *s;
+ Biobuf bbuf;
+
+ notify(notifyf); /**/
+ doargs(argc, argv);
+ if(args.vflag)
+ printargs();
+
+ for(i=1; i<argc; i++) {
+ s = argv[i];
+ if(s == 0)
+ continue;
+ if(strcmp(s, "-") == 0) {
+ Binit(&bbuf, 0, OREAD);
+ dofile(&bbuf);
+ Bterm(&bbuf);
+ continue;
+ }
+ f = open(s, OREAD);
+ if(f < 0) {
+ fprint(2, "sort: open %s: %r\n", s);
+ done("open");
+ }
+ Binit(&bbuf, f, OREAD);
+ dofile(&bbuf);
+ Bterm(&bbuf);
+ close(f);
+ }
+ if(args.nfile == 0) {
+ Binit(&bbuf, 0, OREAD);
+ dofile(&bbuf);
+ Bterm(&bbuf);
+ }
+ if(args.cflag)
+ done(0);
+ if(args.vflag)
+ fprint(2, "=========\n");
+
+ f = 1;
+ if(args.ofile) {
+ f = create(args.ofile, OWRITE, 0666);
+ if(f < 0) {
+ fprint(2, "sort: create %s: %r\n", args.ofile);
+ done("create");
+ }
+ }
+
+ Binit(&bbuf, f, OWRITE);
+ if(args.ntemp) {
+ tempout();
+ mergeout(&bbuf);
+ } else {
+ printout(&bbuf);
+ }
+ Bterm(&bbuf);
+ done(0);
+}
+
+void
+dofile(Biobuf *b)
+{
+ Line *l, *ol;
+ int n;
+
+ if(args.cflag) {
+ ol = newline(b);
+ if(ol == 0)
+ return;
+ for(;;) {
+ l = newline(b);
+ if(l == 0)
+ break;
+ n = kcmp(ol->key, l->key);
+ if(n > 0 || (n == 0 && args.uflag)) {
+ fprint(2, "sort: -c file not in sort\n"); /**/
+ done("order");
+ }
+ free(ol->key);
+ free(ol);
+ ol = l;
+ }
+ return;
+ }
+
+ if(args.linep == 0) {
+ args.linep = malloc(args.mline * sizeof(args.linep));
+ if(args.linep == 0)
+ nomem();
+ }
+ for(;;) {
+ l = newline(b);
+ if(l == 0)
+ break;
+ if(args.nline >= args.mline)
+ tempout();
+ args.linep[args.nline] = l;
+ args.nline++;
+ args.lineno++;
+ }
+}
+
+void
+notifyf(void *a, char *s)
+{
+ USED(a);
+ if(strcmp(s, "interrupt") == 0)
+ done(0);
+ if(strcmp(s, "hangup") == 0)
+ done(0);
+ if(strcmp(s, "kill") == 0)
+ done(0);
+ if(strncmp(s, "sys: write on closed pipe", 25) == 0)
+ done(0);
+ fprint(2, "sort: note: %s\n", s);
+ abort();
+}
+
+Line*
+newline(Biobuf *b)
+{
+ Line *l;
+ char *p;
+ int n, c;
+
+ p = Brdline(b, '\n');
+ n = Blinelen(b);
+ if(p == 0) {
+ if(n == 0)
+ return 0;
+ l = 0;
+ for(n=0;;) {
+ if((n & 31) == 0) {
+ l = realloc(l, sizeof(Line) +
+ (n+31)*sizeof(l->line[0]));
+ if(l == 0)
+ nomem();
+ }
+ c = Bgetc(b);
+ if(c < 0) {
+ fprint(2, "sort: newline added\n");
+ c = '\n';
+ }
+ l->line[n++] = c;
+ if(c == '\n')
+ break;
+ }
+ l->llen = n;
+ buildkey(l);
+ return l;
+ }
+ l = malloc(sizeof(Line) +
+ (n-1)*sizeof(l->line[0]));
+ if(l == 0)
+ nomem();
+ l->llen = n;
+ memmove(l->line, p, n);
+ buildkey(l);
+ return l;
+}
+
+void
+lineout(Biobuf *b, Line *l)
+{
+ int n, m;
+
+ n = l->llen;
+ m = Bwrite(b, l->line, n);
+ if(n != m)
+ exits("write");
+}
+
+void
+tempout(void)
+{
+ long n;
+ Line **lp, *l;
+ char *tf;
+ int f;
+ Biobuf tb;
+
+ sort4(args.linep, args.nline);
+ tf = tempfile(args.ntemp);
+ args.ntemp++;
+ f = create(tf, OWRITE, 0666);
+ if(f < 0) {
+ fprint(2, "sort: create %s: %r\n", tf);
+ done("create");
+ }
+
+ Binit(&tb, f, OWRITE);
+ lp = args.linep;
+ for(n=args.nline; n>0; n--) {
+ l = *lp++;
+ lineout(&tb, l);
+ free(l->key);
+ free(l);
+ }
+ args.nline = 0;
+ Bterm(&tb);
+ close(f);
+}
+
+void
+done(char *xs)
+{
+ int i;
+
+ for(i=0; i<args.ntemp; i++)
+ remove(tempfile(i));
+ exits(xs);
+}
+
+void
+nomem(void)
+{
+ fprint(2, "sort: out of memory\n");
+ done("mem");
+}
+
+char*
+tempfile(int n)
+{
+ static char file[100];
+ static uint pid;
+ char *dir;
+
+ dir = "/var/tmp";
+ if(args.tname)
+ dir = args.tname;
+ if(strlen(dir) >= nelem(file)-20) {
+ fprint(2, "temp file directory name is too long: %s\n", dir);
+ done("tdir");
+ }
+
+ if(pid == 0) {
+ pid = getpid();
+ if(pid == 0) {
+ pid = time(0);
+ if(pid == 0)
+ pid = 1;
+ }
+ }
+
+ sprint(file, "%s/sort.%.4d.%.4d", dir, pid%10000, n);
+ return file;
+}
+
+void
+mergeout(Biobuf *b)
+{
+ int n, i, f;
+ char *tf;
+ Biobuf tb;
+
+ for(i=0; i<args.ntemp; i+=n) {
+ n = args.ntemp - i;
+ if(n > Nmerge) {
+ tf = tempfile(args.ntemp);
+ args.ntemp++;
+ f = create(tf, OWRITE, 0666);
+ if(f < 0) {
+ fprint(2, "sort: create %s: %r\n", tf);
+ done("create");
+ }
+ Binit(&tb, f, OWRITE);
+
+ n = Nmerge;
+ mergefiles(i, n, &tb);
+
+ Bterm(&tb);
+ close(f);
+ } else
+ mergefiles(i, n, b);
+ }
+}
+
+void
+mergefiles(int t, int n, Biobuf *b)
+{
+ Merge *m, *mp, **mmp;
+ Key *ok;
+ Line *l;
+ char *tf;
+ int i, f, nn;
+
+ mmp = malloc(n*sizeof(*mmp));
+ mp = malloc(n*sizeof(*mp));
+ if(mmp == 0 || mp == 0)
+ nomem();
+
+ nn = 0;
+ m = mp;
+ for(i=0; i<n; i++,m++) {
+ tf = tempfile(t+i);
+ f = open(tf, OREAD);
+ if(f < 0) {
+ fprint(2, "sort: reopen %s: %r\n", tf);
+ done("open");
+ }
+ m->fd = f;
+ Binit(&m->b, f, OREAD);
+ mmp[nn] = m;
+
+ l = newline(&m->b);
+ if(l == 0)
+ continue;
+ nn++;
+ m->line = l;
+ m->key = l->key;
+ }
+
+ ok = 0;
+ for(;;) {
+ sort4(mmp, nn);
+ m = *mmp;
+ if(nn == 0)
+ break;
+ for(;;) {
+ l = m->line;
+ if(args.uflag && ok && kcmp(ok, l->key) == 0) {
+ free(l->key);
+ free(l);
+ } else {
+ lineout(b, l);
+ if(ok)
+ free(ok);
+ ok = l->key;
+ free(l);
+ }
+
+ l = newline(&m->b);
+ if(l == 0) {
+ nn--;
+ mmp[0] = mmp[nn];
+ break;
+ }
+ m->line = l;
+ m->key = l->key;
+ if(nn > 1 && kcmp(mmp[0]->key, mmp[1]->key) > 0)
+ break;
+ }
+ }
+ if(ok)
+ free(ok);
+
+ m = mp;
+ for(i=0; i<n; i++,m++) {
+ Bterm(&m->b);
+ close(m->fd);
+ }
+
+ free(mp);
+ free(mmp);
+}
+
+int
+kcmp(Key *ka, Key *kb)
+{
+ int n, m;
+
+ /*
+ * set n to length of smaller key
+ */
+ n = ka->klen;
+ m = kb->klen;
+ if(n > m)
+ n = m;
+ return memcmp(ka->key, kb->key, n);
+}
+
+void
+printout(Biobuf *b)
+{
+ long n;
+ Line **lp, *l;
+ Key *ok;
+
+ sort4(args.linep, args.nline);
+ lp = args.linep;
+ ok = 0;
+ for(n=args.nline; n>0; n--) {
+ l = *lp++;
+ if(args.uflag && ok && kcmp(ok, l->key) == 0)
+ continue;
+ lineout(b, l);
+ ok = l->key;
+ }
+}
+
+void
+setfield(int n, int c)
+{
+ Field *f;
+
+ f = &args.field[n];
+ switch(c) {
+ default:
+ fprint(2, "sort: unknown option: field.%C\n", c);
+ done("option");
+ case 'b': /* skip blanks */
+ f->flags |= Bflag;
+ break;
+ case 'd': /* directory order */
+ f->flags |= Dflag;
+ break;
+ case 'f': /* fold case */
+ f->flags |= Fflag;
+ break;
+ case 'g': /* floating point -n case */
+ f->flags |= Gflag;
+ break;
+ case 'i': /* ignore non-ascii */
+ f->flags |= Iflag;
+ break;
+ case 'M': /* month */
+ f->flags |= Mflag;
+ break;
+ case 'n': /* numbers */
+ f->flags |= Nflag;
+ break;
+ case 'r': /* reverse */
+ f->flags |= Rflag;
+ break;
+ case 'w': /* ignore white */
+ f->flags |= Wflag;
+ break;
+ }
+}
+
+void
+dofield(char *s, int *n1, int *n2, int off1, int off2)
+{
+ int c, n;
+
+ c = *s++;
+ if(c >= '0' && c <= '9') {
+ n = 0;
+ while(c >= '0' && c <= '9') {
+ n = n*10 + (c-'0');
+ c = *s++;
+ }
+ n -= off1; /* posix committee: rot in hell */
+ if(n < 0) {
+ fprint(2, "sort: field offset must be positive\n");
+ done("option");
+ }
+ *n1 = n;
+ }
+ if(c == '.') {
+ c = *s++;
+ if(c >= '0' && c <= '9') {
+ n = 0;
+ while(c >= '0' && c <= '9') {
+ n = n*10 + (c-'0');
+ c = *s++;
+ }
+ n -= off2;
+ if(n < 0) {
+ fprint(2, "sort: character offset must be positive\n");
+ done("option");
+ }
+ *n2 = n;
+ }
+ }
+ while(c != 0) {
+ setfield(args.nfield, c);
+ c = *s++;
+ }
+}
+
+void
+printargs(void)
+{
+ int i, n;
+ Field *f;
+ char *prefix;
+
+ fprint(2, "sort");
+ for(i=0; i<=args.nfield; i++) {
+ f = &args.field[i];
+ prefix = " -";
+ if(i) {
+ n = f->beg1;
+ if(n >= 0)
+ fprint(2, " +%d", n);
+ else
+ fprint(2, " +*");
+ n = f->beg2;
+ if(n >= 0)
+ fprint(2, ".%d", n);
+ else
+ fprint(2, ".*");
+
+ if(f->flags & B1flag)
+ fprint(2, "b");
+
+ n = f->end1;
+ if(n >= 0)
+ fprint(2, " -%d", n);
+ else
+ fprint(2, " -*");
+ n = f->end2;
+ if(n >= 0)
+ fprint(2, ".%d", n);
+ else
+ fprint(2, ".*");
+ prefix = "";
+ }
+ if(f->flags & Bflag)
+ fprint(2, "%sb", prefix);
+ if(f->flags & Dflag)
+ fprint(2, "%sd", prefix);
+ if(f->flags & Fflag)
+ fprint(2, "%sf", prefix);
+ if(f->flags & Gflag)
+ fprint(2, "%sg", prefix);
+ if(f->flags & Iflag)
+ fprint(2, "%si", prefix);
+ if(f->flags & Mflag)
+ fprint(2, "%sM", prefix);
+ if(f->flags & Nflag)
+ fprint(2, "%sn", prefix);
+ if(f->flags & Rflag)
+ fprint(2, "%sr", prefix);
+ if(f->flags & Wflag)
+ fprint(2, "%sw", prefix);
+ }
+ if(args.cflag)
+ fprint(2, " -c");
+ if(args.uflag)
+ fprint(2, " -u");
+ if(args.ofile)
+ fprint(2, " -o %s", args.ofile);
+ if(args.mline != Nline)
+ fprint(2, " -l %ld", args.mline);
+ fprint(2, "\n");
+}
+
+void
+newfield(void)
+{
+ int n;
+ Field *f;
+
+ n = args.nfield + 1;
+ if(n >= Nfield) {
+ fprint(2, "sort: too many fields specified\n");
+ done("option");
+ }
+ args.nfield = n;
+ f = &args.field[n];
+ f->beg1 = -1;
+ f->beg2 = -1;
+ f->end1 = -1;
+ f->end2 = -1;
+}
+
+void
+doargs(int argc, char *argv[])
+{
+ int i, c, hadplus;
+ char *s, *p, *q;
+ Field *f;
+
+ hadplus = 0;
+ args.mline = Nline;
+ for(i=1; i<argc; i++) {
+ s = argv[i];
+ c = *s++;
+ if(c == '-') {
+ c = *s;
+ if(c == 0) /* forced end of arg marker */
+ break;
+ argv[i] = 0; /* clobber args processed */
+ if(c == '.' || (c >= '0' && c <= '9')) {
+ if(!hadplus)
+ newfield();
+ f = &args.field[args.nfield];
+ dofield(s, &f->end1, &f->end2, 0, 0);
+ hadplus = 0;
+ continue;
+ }
+
+ while(c = *s++)
+ switch(c) {
+ case '-': /* end of options */
+ i = argc;
+ continue;
+ case 'T': /* temp directory */
+ if(*s == 0) {
+ i++;
+ if(i < argc) {
+ args.tname = argv[i];
+ argv[i] = 0;
+ }
+ } else
+ args.tname = s;
+ s = strchr(s, 0);
+ break;
+ case 'o': /* output file */
+ if(*s == 0) {
+ i++;
+ if(i < argc) {
+ args.ofile = argv[i];
+ argv[i] = 0;
+ }
+ } else
+ args.ofile = s;
+ s = strchr(s, 0);
+ break;
+ case 'k': /* posix key (what were they thinking?) */
+ p = 0;
+ if(*s == 0) {
+ i++;
+ if(i < argc) {
+ p = argv[i];
+ argv[i] = 0;
+ }
+ } else
+ p = s;
+ s = strchr(s, 0);
+ if(p == 0)
+ break;
+
+ newfield();
+ q = strchr(p, ',');
+ if(q)
+ *q++ = 0;
+ f = &args.field[args.nfield];
+ dofield(p, &f->beg1, &f->beg2, 1, 1);
+ if(f->flags & Bflag) {
+ f->flags |= B1flag;
+ f->flags &= ~Bflag;
+ }
+ if(q) {
+ dofield(q, &f->end1, &f->end2, 1, 0);
+ if(f->end2 <= 0)
+ f->end1++;
+ }
+ hadplus = 0;
+ break;
+ case 't': /* tab character */
+ if(*s == 0) {
+ i++;
+ if(i < argc) {
+ chartorune(&args.tabchar, argv[i]);
+ argv[i] = 0;
+ }
+ } else
+ s += chartorune(&args.tabchar, s);
+ if(args.tabchar == '\n') {
+ fprint(2, "aw come on, rob\n");
+ done("rob");
+ }
+ break;
+ case 'c': /* check order */
+ args.cflag = 1;
+ break;
+ case 'u': /* unique */
+ args.uflag = 1;
+ break;
+ case 'v': /* debugging noise */
+ args.vflag = 1;
+ break;
+ case 'l':
+ if(*s == 0) {
+ i++;
+ if(i < argc) {
+ args.mline = atol(argv[i]);
+ argv[i] = 0;
+ }
+ } else
+ args.mline = atol(s);
+ s = strchr(s, 0);
+ break;
+
+ case 'M': /* month */
+ case 'b': /* skip blanks */
+ case 'd': /* directory order */
+ case 'f': /* fold case */
+ case 'g': /* floating numbers */
+ case 'i': /* ignore non-ascii */
+ case 'n': /* numbers */
+ case 'r': /* reverse */
+ case 'w': /* ignore white */
+ if(args.nfield > 0)
+ fprint(2, "sort: global field set after -k\n");
+ setfield(0, c);
+ break;
+ case 'm':
+ /* option m silently ignored but required by posix */
+ break;
+ default:
+ fprint(2, "sort: unknown option: -%C\n", c);
+ done("option");
+ }
+ continue;
+ }
+ if(c == '+') {
+ argv[i] = 0; /* clobber args processed */
+ c = *s;
+ if(c == '.' || (c >= '0' && c <= '9')) {
+ newfield();
+ f = &args.field[args.nfield];
+ dofield(s, &f->beg1, &f->beg2, 0, 0);
+ if(f->flags & Bflag) {
+ f->flags |= B1flag;
+ f->flags &= ~Bflag;
+ }
+ hadplus = 1;
+ continue;
+ }
+ fprint(2, "sort: unknown option: +%C\n", c);
+ done("option");
+ }
+ args.nfile++;
+ }
+
+ for(i=0; i<=args.nfield; i++) {
+ f = &args.field[i];
+
+ /*
+ * global options apply to fields that
+ * specify no options
+ */
+ if(f->flags == 0) {
+ f->flags = args.field[0].flags;
+ if(args.field[0].flags & Bflag)
+ f->flags |= B1flag;
+ }
+
+
+ /*
+ * build buildkey specification
+ */
+ switch(f->flags & ~(Bflag|B1flag)) {
+ default:
+ fprint(2, "sort: illegal combination of flags: %lx\n", f->flags);
+ done("option");
+ case 0:
+ f->dokey = dokey_;
+ break;
+ case Rflag:
+ f->dokey = dokey_r;
+ break;
+ case Gflag:
+ case Nflag:
+ case Gflag|Nflag:
+ case Gflag|Rflag:
+ case Nflag|Rflag:
+ case Gflag|Nflag|Rflag:
+ f->dokey = dokey_gn;
+ break;
+ case Mflag:
+ case Mflag|Rflag:
+ f->dokey = dokey_m;
+ makemapm(f);
+ break;
+ case Dflag:
+ case Dflag|Fflag:
+ case Dflag|Fflag|Iflag:
+ case Dflag|Fflag|Iflag|Rflag:
+ case Dflag|Fflag|Iflag|Rflag|Wflag:
+ case Dflag|Fflag|Iflag|Wflag:
+ case Dflag|Fflag|Rflag:
+ case Dflag|Fflag|Rflag|Wflag:
+ case Dflag|Fflag|Wflag:
+ case Dflag|Iflag:
+ case Dflag|Iflag|Rflag:
+ case Dflag|Iflag|Rflag|Wflag:
+ case Dflag|Iflag|Wflag:
+ case Dflag|Rflag:
+ case Dflag|Rflag|Wflag:
+ case Dflag|Wflag:
+ case Fflag:
+ case Fflag|Iflag:
+ case Fflag|Iflag|Rflag:
+ case Fflag|Iflag|Rflag|Wflag:
+ case Fflag|Iflag|Wflag:
+ case Fflag|Rflag:
+ case Fflag|Rflag|Wflag:
+ case Fflag|Wflag:
+ case Iflag:
+ case Iflag|Rflag:
+ case Iflag|Rflag|Wflag:
+ case Iflag|Wflag:
+ case Wflag:
+ f->dokey = dokey_dfi;
+ makemapd(f);
+ break;
+ }
+ }
+
+ /*
+ * random spot checks
+ */
+ if(args.nfile > 1 && args.cflag) {
+ fprint(2, "sort: -c can have at most one input file\n");
+ done("option");
+ }
+ return;
+}
+
+uchar*
+skip(uchar *l, int n1, int n2, int bflag, int endfield)
+{
+ int i, c, tc;
+ Rune r;
+
+ if(endfield && n1 < 0)
+ return 0;
+
+ c = *l++;
+ tc = args.tabchar;
+ if(tc) {
+ if(tc < Runeself) {
+ for(i=n1; i>0; i--) {
+ while(c != tc) {
+ if(c == '\n')
+ return 0;
+ c = *l++;
+ }
+ if(!(endfield && i == 1))
+ c = *l++;
+ }
+ } else {
+ l--;
+ l += chartorune(&r, (char*)l);
+ for(i=n1; i>0; i--) {
+ while(r != tc) {
+ if(r == '\n')
+ return 0;
+ l += chartorune(&r, (char*)l);
+ }
+ if(!(endfield && i == 1))
+ l += chartorune(&r, (char*)l);
+ }
+ c = r;
+ }
+ } else {
+ for(i=n1; i>0; i--) {
+ while(c == ' ' || c == '\t')
+ c = *l++;
+ while(c != ' ' && c != '\t') {
+ if(c == '\n')
+ return 0;
+ c = *l++;
+ }
+ }
+ }
+
+ if(bflag)
+ while(c == ' ' || c == '\t')
+ c = *l++;
+
+ l--;
+ for(i=n2; i>0; i--) {
+ c = *l;
+ if(c < Runeself) {
+ if(c == '\n')
+ return 0;
+ l++;
+ continue;
+ }
+ l += chartorune(&r, (char*)l);
+ }
+ return l;
+}
+
+void
+dokey_gn(Key *k, uchar *lp, uchar *lpe, Field *f)
+{
+ uchar *kp;
+ int c, cl, dp;
+ int state, nzero, exp, expsign, rflag;
+
+ cl = k->klen + 3;
+ kp = k->key + cl; /* skip place for sign, exponent[2] */
+
+ nzero = 0; /* number of trailing zeros */
+ exp = 0; /* value of the exponent */
+ expsign = 0; /* sign of the exponent */
+ dp = 0x4040; /* location of decimal point */
+ rflag = f->flags&Rflag; /* xor of rflag and - sign */
+ state = NSstart;
+
+ for(;; lp++) {
+ if(lp >= lpe)
+ break;
+ c = *lp;
+
+ if(c == ' ' || c == '\t') {
+ switch(state) {
+ case NSstart:
+ case NSsign:
+ continue;
+ }
+ break;
+ }
+ if(c == '+' || c == '-') {
+ switch(state) {
+ case NSstart:
+ state = NSsign;
+ if(c == '-')
+ rflag = !rflag;
+ continue;
+ case NSexp:
+ state = NSexpsign;
+ if(c == '-')
+ expsign = 1;
+ continue;
+ }
+ break;
+ }
+ if(c == '0') {
+ switch(state) {
+ case NSdigit:
+ if(rflag)
+ c = ~c;
+ *kp++ = c;
+ cl++;
+ nzero++;
+ dp++;
+ state = NSdigit;
+ continue;
+ case NSfract:
+ if(rflag)
+ c = ~c;
+ *kp++ = c;
+ cl++;
+ nzero++;
+ state = NSfract;
+ continue;
+ case NSstart:
+ case NSsign:
+ case NSzero:
+ state = NSzero;
+ continue;
+ case NSzerofract:
+ case NSpoint:
+ dp--;
+ state = NSzerofract;
+ continue;
+ case NSexpsign:
+ case NSexp:
+ case NSexpdigit:
+ exp = exp*10 + (c - '0');
+ state = NSexpdigit;
+ continue;
+ }
+ break;
+ }
+ if(c >= '1' && c <= '9') {
+ switch(state) {
+ case NSzero:
+ case NSstart:
+ case NSsign:
+ case NSdigit:
+ if(rflag)
+ c = ~c;
+ *kp++ = c;
+ cl++;
+ nzero = 0;
+ dp++;
+ state = NSdigit;
+ continue;
+ case NSzerofract:
+ case NSpoint:
+ case NSfract:
+ if(rflag)
+ c = ~c;
+ *kp++ = c;
+ cl++;
+ nzero = 0;
+ state = NSfract;
+ continue;
+ case NSexpsign:
+ case NSexp:
+ case NSexpdigit:
+ exp = exp*10 + (c - '0');
+ state = NSexpdigit;
+ continue;
+ }
+ break;
+ }
+ if(c == '.') {
+ switch(state) {
+ case NSstart:
+ case NSsign:
+ state = NSpoint;
+ continue;
+ case NSzero:
+ state = NSzerofract;
+ continue;
+ case NSdigit:
+ state = NSfract;
+ continue;
+ }
+ break;
+ }
+ if((f->flags & Gflag) && (c == 'e' || c == 'E')) {
+ switch(state) {
+ case NSdigit:
+ case NSfract:
+ state = NSexp;
+ continue;
+ }
+ break;
+ }
+ break;
+ }
+
+ switch(state) {
+ /*
+ * result is zero
+ */
+ case NSstart:
+ case NSsign:
+ case NSzero:
+ case NSzerofract:
+ case NSpoint:
+ kp = k->key + k->klen;
+ k->klen += 2;
+ kp[0] = 0x20; /* between + and - */
+ kp[1] = 0;
+ return;
+ /*
+ * result has exponent
+ */
+ case NSexpsign:
+ case NSexp:
+ case NSexpdigit:
+ if(expsign)
+ exp = -exp;
+ dp += exp;
+
+ /*
+ * result is fixed point number
+ */
+ case NSdigit:
+ case NSfract:
+ kp -= nzero;
+ cl -= nzero;
+ break;
+ }
+
+ /*
+ * end of number
+ */
+ c = 0;
+ if(rflag)
+ c = ~c;
+ *kp = c;
+
+ /*
+ * sign and exponent
+ */
+ c = 0x30;
+ if(rflag) {
+ c = 0x10;
+ dp = ~dp;
+ }
+ kp = k->key + k->klen;
+ kp[0] = c;
+ kp[1] = (dp >> 8);
+ kp[2] = dp;
+ k->klen = cl+1;
+}
+
+void
+dokey_m(Key *k, uchar *lp, uchar *lpe, Field *f)
+{
+ uchar *kp;
+ Rune r, place[3];
+ int c, cl, pc;
+ int rflag;
+
+ rflag = f->flags&Rflag;
+ pc = 0;
+
+ cl = k->klen;
+ kp = k->key + cl;
+
+ for(;;) {
+ /*
+ * get the character
+ */
+ if(lp >= lpe)
+ break;
+ c = *lp;
+ if(c >= Runeself) {
+ lp += chartorune(&r, (char*)lp);
+ c = r;
+ } else
+ lp++;
+
+ if(c < nelem(f->mapto)) {
+ c = f->mapto[c];
+ if(c == 0)
+ continue;
+ }
+ place[pc++] = c;
+ if(pc < 3)
+ continue;
+ for(c=11; c>=0; c--)
+ if(memcmp(month[c], place, sizeof(place)) == 0)
+ break;
+ c += 10;
+ if(rflag)
+ c = ~c;
+ *kp++ = c;
+ cl++;
+ break;
+ }
+
+ c = 0;
+ if(rflag)
+ c = ~c;
+ *kp = c;
+ k->klen = cl+1;
+}
+
+void
+dokey_dfi(Key *k, uchar *lp, uchar *lpe, Field *f)
+{
+ uchar *kp;
+ Rune r;
+ int c, cl, n, rflag;
+
+ cl = k->klen;
+ kp = k->key + cl;
+ rflag = f->flags & Rflag;
+
+ for(;;) {
+ /*
+ * get the character
+ */
+ if(lp >= lpe)
+ break;
+ c = *lp;
+ if(c >= Runeself) {
+ lp += chartorune(&r, (char*)lp);
+ c = r;
+ } else
+ lp++;
+
+ /*
+ * do the various mappings.
+ * the common case is handled
+ * completely by the table.
+ */
+ if(c != 0 && c < Runeself) {
+ c = f->mapto[c];
+ if(c) {
+ *kp++ = c;
+ cl++;
+ }
+ continue;
+ }
+
+ /*
+ * for characters out of range,
+ * the table does not do Rflag.
+ * ignore is based on mapto[255]
+ */
+ if(c != 0 && c < nelem(f->mapto)) {
+ c = f->mapto[c];
+ if(c == 0)
+ continue;
+ } else
+ if(f->mapto[nelem(f->mapto)-1] == 0)
+ continue;
+
+ /*
+ * put it in the key
+ */
+ r = c;
+ n = runetochar((char*)kp, &r);
+ kp += n;
+ cl += n;
+ if(rflag)
+ while(n > 0) {
+ kp[-n] = ~kp[-n];
+ n--;
+ }
+ }
+
+ /*
+ * end of key
+ */
+ k->klen = cl+1;
+ if(rflag) {
+ *kp = ~0;
+ return;
+ }
+ *kp = 0;
+}
+
+void
+dokey_r(Key *k, uchar *lp, uchar *lpe, Field *f)
+{
+ int cl, n;
+ uchar *kp;
+
+ USED(f);
+ n = lpe - lp;
+ if(n < 0)
+ n = 0;
+ cl = k->klen;
+ kp = k->key + cl;
+ k->klen = cl+n+1;
+
+ lpe -= 3;
+ while(lp < lpe) {
+ kp[0] = ~lp[0];
+ kp[1] = ~lp[1];
+ kp[2] = ~lp[2];
+ kp[3] = ~lp[3];
+ kp += 4;
+ lp += 4;
+ }
+
+ lpe += 3;
+ while(lp < lpe)
+ *kp++ = ~*lp++;
+ *kp = ~0;
+}
+
+void
+dokey_(Key *k, uchar *lp, uchar *lpe, Field *f)
+{
+ int n, cl;
+ uchar *kp;
+
+ USED(f);
+ n = lpe - lp;
+ if(n < 0)
+ n = 0;
+ cl = k->klen;
+ kp = k->key + cl;
+ k->klen = cl+n+1;
+ memmove(kp, lp, n);
+ kp[n] = 0;
+}
+
+void
+buildkey(Line *l)
+{
+ Key *k;
+ uchar *lp, *lpe;
+ int ll, kl, cl, i, n;
+ Field *f;
+
+ ll = l->llen - 1;
+ kl = 0; /* allocated length */
+ cl = 0; /* current length */
+ k = 0;
+
+ for(i=1; i<=args.nfield; i++) {
+ f = &args.field[i];
+ lp = skip(l->line, f->beg1, f->beg2, f->flags&B1flag, 0);
+ if(lp == 0)
+ lp = l->line + ll;
+ lpe = skip(l->line, f->end1, f->end2, f->flags&Bflag, 1);
+ if(lpe == 0)
+ lpe = l->line + ll;
+ n = (lpe - lp) + 1;
+ if(n <= 0)
+ n = 1;
+ if(cl+(n+4) > kl) {
+ kl = cl+(n+4);
+ k = realloc(k, sizeof(Key) +
+ (kl-1)*sizeof(k->key[0]));
+ if(k == 0)
+ nomem();
+ }
+ k->klen = cl;
+ (*f->dokey)(k, lp, lpe, f);
+ cl = k->klen;
+ }
+
+ /*
+ * global comparisons
+ */
+ if(!(args.uflag && cl > 0)) {
+ f = &args.field[0];
+ if(cl+(ll+4) > kl) {
+ kl = cl+(ll+4);
+ k = realloc(k, sizeof(Key) +
+ (kl-1)*sizeof(k->key[0]));
+ if(k == 0)
+ nomem();
+ }
+ k->klen = cl;
+ (*f->dokey)(k, l->line, l->line+ll, f);
+ cl = k->klen;
+ }
+
+ l->key = k;
+ k->klen = cl;
+
+ if(args.vflag) {
+ write(2, l->line, l->llen);
+ for(i=0; i<k->klen; i++) {
+ fprint(2, " %.2x", k->key[i]);
+ if(k->key[i] == 0x00 || k->key[i] == 0xff)
+ fprint(2, "\n");
+ }
+ }
+}
+
+void
+makemapm(Field *f)
+{
+ int i, c;
+
+ for(i=0; i<nelem(f->mapto); i++) {
+ c = 1;
+ if(i == ' ' || i == '\t')
+ c = 0;
+ if(i >= 'a' && i <= 'z')
+ c = i + ('A' - 'a');
+ if(i >= 'A' && i <= 'Z')
+ c = i;
+ f->mapto[i] = c;
+ if(args.vflag) {
+ if((i & 15) == 0)
+ fprint(2, " ");
+ fprint(2, " %.2x", c);
+ if((i & 15) == 15)
+ fprint(2, "\n");
+ }
+ }
+}
+
+void
+makemapd(Field *f)
+{
+ int i, j, c;
+
+ for(i=0; i<nelem(f->mapto); i++) {
+ c = i;
+ if(f->flags & Iflag)
+ if(c < 040 || c > 0176)
+ c = -1;
+ if((f->flags & Wflag) && c >= 0)
+ if(c == ' ' || c == '\t')
+ c = -1;
+ if((f->flags & Dflag) && c >= 0)
+ if(!(c == ' ' || c == '\t' ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9'))) {
+ for(j=0; latinmap[j]; j+=3)
+ if(c == latinmap[j+0] ||
+ c == latinmap[j+1])
+ break;
+ if(latinmap[j] == 0)
+ c = -1;
+ }
+ if((f->flags & Fflag) && c >= 0) {
+ if(c >= 'a' && c <= 'z')
+ c += 'A' - 'a';
+ for(j=0; latinmap[j]; j+=3)
+ if(c == latinmap[j+0] ||
+ c == latinmap[j+1]) {
+ c = latinmap[j+2];
+ break;
+ }
+ }
+ if((f->flags & Rflag) && c >= 0 && i > 0 && i < Runeself)
+ c = ~c & 0xff;
+ if(c < 0)
+ c = 0;
+ f->mapto[i] = c;
+ if(args.vflag) {
+ if((i & 15) == 0)
+ fprint(2, " ");
+ fprint(2, " %.2x", c);
+ if((i & 15) == 15)
+ fprint(2, "\n");
+ }
+ }
+}
+
+int latinmap[] =
+{
+/* lcase ucase fold */
+ 0xe0, 0xc0, 0x41, /* L'à', L'À', L'A', */
+ 0xe1, 0xc1, 0x41, /* L'á', L'Á', L'A', */
+ 0xe2, 0xc2, 0x41, /* L'â', L'Â', L'A', */
+ 0xe4, 0xc4, 0x41, /* L'ä', L'Ä', L'A', */
+ 0xe3, 0xc3, 0x41, /* L'ã', L'Ã', L'A', */
+ 0xe5, 0xc5, 0x41, /* L'å', L'Å', L'A', */
+ 0xe8, 0xc8, 0x45, /* L'è', L'È', L'E', */
+ 0xe9, 0xc9, 0x45, /* L'é', L'É', L'E', */
+ 0xea, 0xca, 0x45, /* L'ê', L'Ê', L'E', */
+ 0xeb, 0xcb, 0x45, /* L'ë', L'Ë', L'E', */
+ 0xec, 0xcc, 0x49, /* L'ì', L'Ì', L'I', */
+ 0xed, 0xcd, 0x49, /* L'í', L'Í', L'I', */
+ 0xee, 0xce, 0x49, /* L'î', L'Î', L'I', */
+ 0xef, 0xcf, 0x49, /* L'ï', L'Ï', L'I', */
+ 0xf2, 0xd2, 0x4f, /* L'ò', L'Ò', L'O', */
+ 0xf3, 0xd3, 0x4f, /* L'ó', L'Ó', L'O', */
+ 0xf4, 0xd4, 0x4f, /* L'ô', L'Ô', L'O', */
+ 0xf6, 0xd6, 0x4f, /* L'ö', L'Ö', L'O', */
+ 0xf5, 0xd5, 0x4f, /* L'õ', L'Õ', L'O', */
+ 0xf8, 0xd8, 0x4f, /* L'ø', L'Ø', L'O', */
+ 0xf9, 0xd9, 0x55, /* L'ù', L'Ù', L'U', */
+ 0xfa, 0xda, 0x55, /* L'ú', L'Ú', L'U', */
+ 0xfb, 0xdb, 0x55, /* L'û', L'Û', L'U', */
+ 0xfc, 0xdc, 0x55, /* L'ü', L'Ü', L'U', */
+ 0xe6, 0xc6, 0x41, /* L'æ', L'Æ', L'A', */
+ 0xf0, 0xd0, 0x44, /* L'ð', L'Ð', L'D', */
+ 0xf1, 0xd1, 0x4e, /* L'ñ', L'Ñ', L'N', */
+ 0xfd, 0xdd, 0x59, /* L'ý', L'Ý', L'Y', */
+ 0xe7, 0xc7, 0x43, /* L'ç', L'Ç', L'C', */
+ 0,
+};
+
+Rune LJAN[] = { 'J', 'A', 'N', 0 };
+Rune LFEB[] = { 'F', 'E', 'B', 0 };
+Rune LMAR[] = { 'M', 'A', 'R', 0 };
+Rune LAPR[] = { 'A', 'P', 'R', 0 };
+Rune LMAY[] = { 'M', 'A', 'Y', 0 };
+Rune LJUN[] = { 'J', 'U', 'N', 0 };
+Rune LJUL[] = { 'J', 'U', 'L', 0 };
+Rune LAUG[] = { 'A', 'U', 'G', 0 };
+Rune LSEP[] = { 'S', 'E', 'P', 0 };
+Rune LOCT[] = { 'O', 'C', 'T', 0 };
+Rune LNOV[] = { 'N', 'O', 'V', 0 };
+Rune LDEC[] = { 'D', 'E', 'C', 0 };
+
+Rune* month[12] =
+{
+ LJAN,
+ LFEB,
+ LMAR,
+ LAPR,
+ LMAY,
+ LJUN,
+ LJUL,
+ LAUG,
+ LSEP,
+ LOCT,
+ LNOV,
+ LDEC,
+};
+
+/************** radix sort ***********/
+
+enum
+{
+ Threshold = 14,
+};
+
+void rsort4(Key***, ulong, int);
+void bsort4(Key***, ulong, int);
+
+void
+sort4(void *a, ulong n)
+{
+ if(n > Threshold)
+ rsort4((Key***)a, n, 0);
+ else
+ bsort4((Key***)a, n, 0);
+}
+
+void
+rsort4(Key ***a, ulong n, int b)
+{
+ Key ***ea, ***t, ***u, **t1, **u1, *k;
+ Key ***part[257];
+ static long count[257];
+ long clist[257+257], *cp, *cp1;
+ int c, lowc, higc;
+
+ /*
+ * pass 1 over all keys,
+ * count the number of each key[b].
+ * find low count and high count.
+ */
+ lowc = 256;
+ higc = 0;
+ ea = a+n;
+ for(t=a; t<ea; t++) {
+ k = **t;
+ n = k->klen;
+ if(b >= n) {
+ count[256]++;
+ continue;
+ }
+ c = k->key[b];
+ n = count[c]++;
+ if(n == 0) {
+ if(c < lowc)
+ lowc = c;
+ if(c > higc)
+ higc = c;
+ }
+ }
+
+ /*
+ * pass 2 over all counts,
+ * put partition pointers in part[c].
+ * save compacted indexes and counts
+ * in clist[].
+ */
+ t = a;
+ n = count[256];
+ clist[0] = n;
+ part[256] = t;
+ t += n;
+
+ cp1 = clist+1;
+ cp = count+lowc;
+ for(c=lowc; c<=higc; c++,cp++) {
+ n = *cp;
+ if(n) {
+ cp1[0] = n;
+ cp1[1] = c;
+ cp1 += 2;
+ part[c] = t;
+ t += n;
+ }
+ }
+ *cp1 = 0;
+
+ /*
+ * pass 3 over all counts.
+ * chase lowest pointer in each partition
+ * around a permutation until it comes
+ * back and is stored where it started.
+ * static array, count[], should be
+ * reduced to zero entries except maybe
+ * count[256].
+ */
+ for(cp1=clist+1; cp1[0]; cp1+=2) {
+ c = cp1[1];
+ cp = count+c;
+ while(*cp) {
+ t1 = *part[c];
+ for(;;) {
+ k = *t1;
+ n = 256;
+ if(b < k->klen)
+ n = k->key[b];
+ u = part[n]++;
+ count[n]--;
+ u1 = *u;
+ *u = t1;
+ if(n == c)
+ break;
+ t1 = u1;
+ }
+ }
+ }
+
+ /*
+ * pass 4 over all partitions.
+ * call recursively.
+ */
+ b++;
+ t = a + clist[0];
+ count[256] = 0;
+ for(cp1=clist+1; n=cp1[0]; cp1+=2) {
+ if(n > Threshold)
+ rsort4(t, n, b);
+ else
+ if(n > 1)
+ bsort4(t, n, b);
+ t += n;
+ }
+}
+
+/*
+ * bubble sort to pick up
+ * the pieces.
+ */
+void
+bsort4(Key ***a, ulong n, int b)
+{
+ Key ***i, ***j, ***k, ***l, **t;
+ Key *ka, *kb;
+ int n1, n2;
+
+ l = a+n;
+ j = a;
+
+loop:
+ i = j;
+ j++;
+ if(j >= l)
+ return;
+
+ ka = **i;
+ kb = **j;
+ n1 = ka->klen - b;
+ n2 = kb->klen - b;
+ if(n1 > n2)
+ n1 = n2;
+ if(n1 <= 0)
+ goto loop;
+ n2 = ka->key[b] - kb->key[b];
+ if(n2 == 0)
+ n2 = memcmp(ka->key+b, kb->key+b, n1);
+ if(n2 <= 0)
+ goto loop;
+
+ for(;;) {
+ k = i+1;
+
+ t = *k;
+ *k = *i;
+ *i = t;
+
+ if(i <= a)
+ goto loop;
+
+ i--;
+ ka = **i;
+ kb = *t;
+ n1 = ka->klen - b;
+ n2 = kb->klen - b;
+ if(n1 > n2)
+ n1 = n2;
+ if(n1 <= 0)
+ goto loop;
+ n2 = ka->key[b] - kb->key[b];
+ if(n2 == 0)
+ n2 = memcmp(ka->key+b, kb->key+b, n1);
+ if(n2 <= 0)
+ goto loop;
+ }
+}
diff --git a/tee/Makefile b/tee/Makefile
@@ -0,0 +1,36 @@
+# tee - tee unix port from plan9
+# Depends on ../lib9
+
+include ../config.mk
+
+TARG = tee
+
+OFILES = tee.o
+
+MANFILES = tee.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
diff --git a/tee/tee.1 b/tee/tee.1
@@ -0,0 +1,28 @@
+.TH TEE 1
+.SH NAME
+tee \- pipe fitting
+.SH SYNOPSIS
+.B tee
+[
+.B -i
+]
+[
+.B -a
+]
+.I files
+.SH DESCRIPTION
+.I Tee
+transcribes the standard input to the standard
+output and makes copies in the
+.IR files .
+The options are
+.TP
+.B -i
+Ignore interrupts.
+.TP
+.B -a
+Append the output to the
+.I files
+rather than rewriting them.
+.SH SOURCE
+.B \*9/src/cmd/tee.c
diff --git a/tee/tee.c b/tee/tee.c
@@ -0,0 +1,75 @@
+/*
+ * tee-- pipe fitting
+ */
+
+#include <u.h>
+#include <libc.h>
+
+int uflag;
+int aflag;
+int openf[100];
+
+char in[8192];
+
+int intignore(void*, char*);
+
+void
+main(int argc, char **argv)
+{
+ int i;
+ int r, n;
+
+ ARGBEGIN {
+ case 'a':
+ aflag++;
+ break;
+
+ case 'i':
+ atnotify(intignore, 1);
+ break;
+
+ case 'u':
+ uflag++;
+ /* uflag is ignored and undocumented; it's a relic from Unix */
+ break;
+
+ default:
+ fprint(2, "usage: tee [-ai] [file ...]\n");
+ exits("usage");
+ } ARGEND
+
+ USED(argc);
+ n = 0;
+ while(*argv) {
+ if(aflag) {
+ openf[n] = open(argv[0], OWRITE);
+ if(openf[n] < 0)
+ openf[n] = create(argv[0], OWRITE, 0666);
+ seek(openf[n], 0L, 2);
+ } else
+ openf[n] = create(argv[0], OWRITE, 0666);
+ if(openf[n] < 0) {
+ fprint(2, "tee: cannot open %s: %r\n", argv[0]);
+ } else
+ n++;
+ argv++;
+ }
+ openf[n++] = 1;
+
+ for(;;) {
+ r = read(0, in, sizeof in);
+ if(r <= 0)
+ exits(nil);
+ for(i=0; i<n; i++)
+ write(openf[i], in, r);
+ }
+}
+
+int
+intignore(void *a, char *msg)
+{
+ USED(a);
+ if(strcmp(msg, "interrupt") == 0)
+ return 1;
+ return 0;
+}
diff --git a/test/Makefile b/test/Makefile
@@ -0,0 +1,36 @@
+# test - test unix port from plan9
+# Depends on ../lib9
+
+include ../config.mk
+
+TARG = test
+
+OFILES = test.o
+
+MANFILES = test.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
diff --git a/test/test.1 b/test/test.1
@@ -0,0 +1,211 @@
+.TH TEST 1
+.SH NAME
+test \- set status according to condition
+.SH SYNOPSIS
+.B test
+.I expr
+.SH DESCRIPTION
+.I Test
+evaluates the expression
+.IR expr .
+If the value is true the exit status is null; otherwise the
+exit status is non-null.
+If there are no arguments the exit status is non-null.
+.PP
+The following primitives are used to construct
+.IR expr .
+.TP "\w'\fIn1 \fL-eq \fIn2\fLXX'u"
+.BI -r " file"
+True if the file exists (is accessible) and is readable.
+.PD0
+.TP
+.BI -w " file"
+True if the file exists and is writable.
+.TP
+.BI -x " file"
+True if the file exists and has execute permission.
+.TP
+.BI -e " file
+True if the file exists.
+.TP
+.BI -f " file"
+True if the file exists and is a plain file.
+.TP
+.BI -d " file"
+True if the file exists and is a directory.
+.TP
+.BI -s " file"
+True if the file exists and has a size greater than zero.
+.TP
+.BI -t " fildes
+True if the open file whose file descriptor number is
+.I fildes
+(1 by default)
+is the same file as
+.BR /dev/cons .
+.TP
+.BI -A " file"
+True if the file exists and is append-only.
+.TP
+.BI -L " file"
+True if the file exists and is exclusive-use.
+.TP
+.BI -T "file"
+True if the file exists and is temporary.
+.TP
+.IB s1 " = " s2
+True
+if the strings
+.I s1
+and
+.I s2
+are identical.
+.TP
+.IB s1 " != " s2
+True
+if the strings
+.I s1
+and
+.I s2
+are not identical.
+.TP
+s1
+True if
+.I s1
+is not the null string.
+(Deprecated.)
+.TP
+.BI -n " s1"
+True if the length of string
+.I s1
+is non-zero.
+.TP
+.BI -z " s1"
+True if the length of string
+.I s1
+is zero.
+.TP
+.IB n1 " -eq " n2
+True if the integers
+.I n1
+and
+.I n2
+are arithmetically equal.
+Any of the comparisons
+.BR -ne ,
+.BR -gt ,
+.BR -ge ,
+.BR -lt ,
+or
+.BR -le
+may be used in place of
+.BR -eq .
+The (nonstandard) construct
+.BI -l " string\f1,
+meaning the length of
+.IR string ,
+may be used in place of an integer.
+.TP
+.IB a " -nt " b
+True if file
+.I a
+is newer than (modified after) file
+.IR b .
+.TP
+.IB a " -ot " b
+True if file
+.I a
+is older than (modified before) file
+.IR b .
+.TP
+.IB f " -older " t
+True if file
+.I f
+is older than (modified before) time
+.IR t .
+If
+.I t
+is a integer followed by the letters
+.BR y (years),
+.BR M (months),
+.BR d (days),
+.BR h (hours),
+.BR m (minutes),
+or
+.BR s (seconds),
+it represents current time minus the specified time.
+If there is no letter, it represents seconds since
+epoch.
+You can also concatenate mixed units. For example,
+.B 3d12h
+means three days and twelve hours ago.
+.PD
+.PP
+These primaries may be combined with the
+following operators:
+.TP "\w'\fL( \fIexpr\fL )XX'u"
+.B !
+unary negation operator
+.PD0
+.TP
+.B -o
+binary
+.I or
+operator
+.TP
+.B -a
+binary
+.I and
+operator; higher precedence than
+.BR -o
+.TP
+.BI "( " expr " )"
+parentheses for grouping.
+.PD
+.PP
+The primitives
+.BR -b ,
+.BR -u ,
+.BR -g ,
+and
+.BR -s
+return false; they are recognized for compatibility with POSIX.
+.PP
+Notice that all the operators and flags are separate
+arguments to
+.IR test .
+Notice also that parentheses and equal signs are meaningful
+to
+.I rc
+and must be enclosed in quotes.
+.SH EXAMPLES
+.I Test
+is a dubious way to check for specific character strings:
+it uses a process to do what an
+.IR rc (1)
+match or switch statement can do.
+The first example is not only inefficient but wrong, because
+.I test
+understands the purported string
+.B \&"-c"
+as an option.
+.IP
+.EX
+if (test $1 '=' "-c") echo OK # wrong!
+.EE
+.LP
+A better way is
+.IP
+.EX
+if (~ $1 -c) echo OK
+.EE
+.PP
+Test whether
+.L abc
+is in the current directory.
+.IP
+.B test -f abc -o -d abc
+.SH SOURCE
+.B \*9/src/cmd/test.c
+.SH "SEE ALSO"
+.IR rc (1)
diff --git a/test/test.c b/test/test.c
@@ -0,0 +1,292 @@
+/*
+ * POSIX standard
+ * test expression
+ * [ expression ]
+ *
+ * Plan 9 additions:
+ * -A file exists and is append-only
+ * -L file exists and is exclusive-use
+ */
+
+#include <u.h>
+#include <libc.h>
+#define EQ(a,b) ((tmp=a)==0?0:(strcmp(tmp,b)==0))
+
+extern int isatty(int); /* <unistd.h> */
+
+int ap;
+int ac;
+char **av;
+char *tmp;
+
+void synbad(char *, char *);
+int fsizep(char *);
+int isdir(char *);
+int isreg(char *);
+int isint(char *, int *);
+int hasmode(char *, ulong);
+int tio(char *, int);
+int e(void), e1(void), e2(void), e3(void);
+
+void
+main(int argc, char *argv[])
+{
+
+ ac = argc; av = argv; ap = 1;
+ if(EQ(argv[0],"[")) {
+ if(!EQ(argv[--ac],"]"))
+ synbad("] missing","");
+ }
+ argv[ac] = 0;
+ if (ac<=1) exits("usage");
+ exits(e()?0:"false");
+}
+
+char *
+nxtarg(int mt)
+{
+ if(ap>=ac){
+ if(mt){
+ ap++;
+ return(0);
+ }
+ synbad("argument expected","");
+ }
+ return(av[ap++]);
+}
+
+int
+nxtintarg(int *pans)
+{
+ if(ap<ac && isint(av[ap], pans)){
+ ap++;
+ return 1;
+ }
+ return 0;
+}
+
+int
+e(void) {
+ int p1;
+
+ p1 = e1();
+ if (EQ(nxtarg(1), "-o")) return(p1 || e());
+ ap--;
+ return(p1);
+}
+
+int
+e1(void) {
+ int p1;
+
+ p1 = e2();
+ if (EQ(nxtarg(1), "-a")) return (p1 && e1());
+ ap--;
+ return(p1);
+}
+
+int
+e2(void) {
+ if (EQ(nxtarg(0), "!"))
+ return(!e2());
+ ap--;
+ return(e3());
+}
+
+int
+e3(void) {
+ int p1;
+ char *a;
+ char *p2;
+ int int1, int2;
+
+ a = nxtarg(0);
+ if(EQ(a, "(")) {
+ p1 = e();
+ if(!EQ(nxtarg(0), ")")) synbad(") expected","");
+ return(p1);
+ }
+
+ if(EQ(a, "-A"))
+ return(hasmode(nxtarg(0), DMAPPEND));
+
+ if(EQ(a, "-L"))
+ return(hasmode(nxtarg(0), DMEXCL));
+
+ if(EQ(a, "-f"))
+ return(isreg(nxtarg(0)));
+
+ if(EQ(a, "-d"))
+ return(isdir(nxtarg(0)));
+
+ if(EQ(a, "-r"))
+ return(tio(nxtarg(0), 4));
+
+ if(EQ(a, "-w"))
+ return(tio(nxtarg(0), 2));
+
+ if(EQ(a, "-x"))
+ return(tio(nxtarg(0), 1));
+
+ if(EQ(a, "-e"))
+ return(tio(nxtarg(0), 0));
+
+ if(EQ(a, "-c"))
+ return(0);
+
+ if(EQ(a, "-b"))
+ return(0);
+
+ if(EQ(a, "-u"))
+ return(0);
+
+ if(EQ(a, "-g"))
+ return(0);
+
+ if(EQ(a, "-s"))
+ return(fsizep(nxtarg(0)));
+
+ if(EQ(a, "-t"))
+ if(ap>=ac || !nxtintarg(&int1))
+ return(isatty(1));
+ else
+ return(isatty(int1));
+
+ if(EQ(a, "-n"))
+ return(!EQ(nxtarg(0), ""));
+ if(EQ(a, "-z"))
+ return(EQ(nxtarg(0), ""));
+
+ p2 = nxtarg(1);
+ if (p2==0)
+ return(!EQ(a,""));
+ if(EQ(p2, "="))
+ return(EQ(nxtarg(0), a));
+
+ if(EQ(p2, "!="))
+ return(!EQ(nxtarg(0), a));
+
+ if(!isint(a, &int1))
+ return(!EQ(a,""));
+
+ if(nxtintarg(&int2)){
+ if(EQ(p2, "-eq"))
+ return(int1==int2);
+ if(EQ(p2, "-ne"))
+ return(int1!=int2);
+ if(EQ(p2, "-gt"))
+ return(int1>int2);
+ if(EQ(p2, "-lt"))
+ return(int1<int2);
+ if(EQ(p2, "-ge"))
+ return(int1>=int2);
+ if(EQ(p2, "-le"))
+ return(int1<=int2);
+ }
+
+ synbad("unknown operator ",p2);
+ return 0; /* to shut ken up */
+}
+
+int
+tio(char *a, int f)
+{
+ return access (a, f) >= 0;
+}
+
+/* copy to local memory; clear names for safety */
+int
+localstat(char *f, Dir *dir)
+{
+ Dir *d;
+
+ d = dirstat(f);
+ if(d == 0)
+ return(-1);
+ *dir = *d;
+ dir->name = 0;
+ dir->uid = 0;
+ dir->gid = 0;
+ dir->muid = 0;
+ return 0;
+}
+
+/* copy to local memory; clear names for safety */
+int
+localfstat(int f, Dir *dir)
+{
+ Dir *d;
+
+ d = dirfstat(f);
+ if(d == 0)
+ return(-1);
+ *dir = *d;
+ dir->name = 0;
+ dir->uid = 0;
+ dir->gid = 0;
+ dir->muid = 0;
+ return 0;
+}
+
+int
+hasmode(char *f, ulong m)
+{
+ Dir dir;
+
+ if(localstat(f,&dir)<0)
+ return(0);
+ return(dir.mode&m);
+}
+
+int
+isdir(char *f)
+{
+ Dir dir;
+
+ if(localstat(f,&dir)<0)
+ return(0);
+ return(dir.mode&DMDIR);
+}
+
+int
+isreg(char *f)
+{
+ Dir dir;
+
+ if(localstat(f,&dir)<0)
+ return(0);
+ return(!(dir.mode&DMDIR));
+}
+
+int
+fsizep(char *f)
+{
+ Dir dir;
+
+ if(localstat(f,&dir)<0)
+ return(0);
+ return(dir.length>0);
+}
+
+void
+synbad(char *s1, char *s2)
+{
+ int len;
+
+ write(2, "test: ", 6);
+ if ((len = strlen(s1)) != 0)
+ write(2, s1, len);
+ if ((len = strlen(s2)) != 0)
+ write(2, s2, len);
+ write(2, "\n", 1);
+ exits("bad syntax");
+}
+
+int
+isint(char *s, int *pans)
+{
+ char *ep;
+
+ *pans = strtol(s, &ep, 0);
+ return (*ep == 0);
+}
diff --git a/touch/Makefile b/touch/Makefile
@@ -0,0 +1,36 @@
+# touch - touch unix port from plan9
+# Depends on ../lib9
+
+include ../config.mk
+
+TARG = touch
+
+OFILES = touch.o
+
+MANFILES = touch.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
diff --git a/touch/touch.1 b/touch/touch.1
@@ -0,0 +1,35 @@
+.TH TOUCH 1
+.SH NAME
+touch \- set modification date of a file
+.SH SYNOPSIS
+.B touch
+[
+.B -c
+]
+[
+.B -t
+.I time
+]
+.I file ...
+.SH DESCRIPTION
+.I Touch
+attempts to set the modification time of the
+.I files
+to
+.I time
+(by default, the current time).
+If a
+.I file
+does not exist,
+it will be created unless option
+.B -c
+is present.
+.SH SOURCE
+.B \*9/src/cmd/touch.c
+.SH SEE ALSO
+.IR ls (1),
+.IR stat (3),
+.IR chmod (1)
+.SH BUGS
+.I Touch
+will not touch directories.
diff --git a/touch/touch.c b/touch/touch.c
@@ -0,0 +1,62 @@
+#include <u.h>
+#include <libc.h>
+
+int touch(int, char *);
+ulong now;
+
+void
+usage(void)
+{
+ fprint(2, "usage: touch [-c] [-t time] files\n");
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ int nocreate = 0;
+ int status = 0;
+
+ now = time(0);
+ ARGBEGIN{
+ case 't':
+ now = strtoul(EARGF(usage()), 0, 0);
+ break;
+ case 'c':
+ nocreate = 1;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(!*argv)
+ usage();
+ while(*argv)
+ status += touch(nocreate, *argv++);
+ if(status)
+ exits("touch");
+ exits(0);
+}
+
+int
+touch(int nocreate, char *name)
+{
+ Dir stbuff;
+ int fd;
+
+ nulldir(&stbuff);
+ stbuff.mtime = now;
+ if(dirwstat(name, &stbuff) >= 0)
+ return 0;
+ if(nocreate){
+ fprint(2, "touch: %s: cannot wstat: %r\n", name);
+ return 1;
+ }
+ if ((fd = create(name, OREAD, 0666)) < 0) {
+ fprint(2, "touch: %s: cannot create: %r\n", name);
+ return 1;
+ }
+ dirfwstat(fd, &stbuff);
+ close(fd);
+ return 0;
+}
diff --git a/tr/Makefile b/tr/Makefile
@@ -0,0 +1,36 @@
+# tr - tr unix port from plan9
+# Depends on ../lib9
+
+include ../config.mk
+
+TARG = tr
+
+OFILES = tr.o
+
+MANFILES = tr.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
diff --git a/tr/tr.1 b/tr/tr.1
@@ -0,0 +1,97 @@
+.TH TR 1
+.SH NAME
+tr \- translate characters
+.SH SYNOPSIS
+.B tr
+[
+.B -cds
+]
+[
+.I string1
+[
+.I string2
+]
+]
+.SH DESCRIPTION
+.I Tr
+copies the standard input to the standard output with
+substitution or deletion of selected characters (runes).
+Input characters found in
+.I string1
+are mapped into the corresponding characters of
+.IR string2 .
+When
+.I string2
+is short it is padded to the length of
+.I string1
+by duplicating its last character.
+Any combination of the options
+.B -cds
+may be used:
+.TP
+.B -c
+Complement
+.IR string1 :
+replace it with a lexicographically ordered
+list of all other characters.
+.TP
+.B -d
+Delete from input all characters in
+.IR string1 .
+.TP
+.B -s
+Squeeze repeated output characters that occur in
+.I string2
+to single characters.
+.PP
+In either string a noninitial sequence
+.BI - x\f1,
+where
+.I x
+is any character (possibly quoted), stands for
+a range of characters:
+a possibly empty sequence of codes running from
+the successor of the previous code up through
+the code for
+.IR x .
+The character
+.L \e
+followed by 1, 2 or 3 octal digits stands for the
+character whose
+16-bit
+value is given by those digits.
+The character sequence
+.L \ex
+followed by 1, 2, 3, or 4 hexadecimal digits stands
+for the character whose
+16-bit value is given by those digits.
+A
+.L \e
+followed by any other character stands
+for that character.
+.SH EXAMPLES
+Replace all upper-case
+.SM ASCII
+letters by lower-case.
+.IP
+.EX
+tr A-Z a-z <mixed >lower
+.EE
+.PP
+Create a list of all
+the words in
+.L file1
+one per line in
+.LR file2 ,
+where a word is taken to be a maximal string of alphabetics.
+.I String2
+is given as a quoted newline.
+.IP
+.EX
+tr -cs A-Za-z '
+\&' <file1 >file2
+.EE
+.SH SOURCE
+.B \*9/src/cmd/tr.c
+.SH "SEE ALSO"
+.IR sed (1)
diff --git a/tr/tr.c b/tr/tr.c
@@ -0,0 +1,356 @@
+#include <u.h>
+#include <libc.h>
+
+typedef struct PCB /* Control block controlling specification parse */
+{
+ char *base; /* start of specification */
+ char *current; /* current parse point */
+ long last; /* last Rune returned */
+ long final; /* final Rune in a span */
+} Pcb;
+
+uchar bits[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
+
+#define SETBIT(a, c) ((a)[(c)/8] |= bits[(c)&07])
+#define CLEARBIT(a,c) ((a)[(c)/8] &= ~bits[(c)&07])
+#define BITSET(a,c) ((a)[(c)/8] & bits[(c)&07])
+
+#define MAXRUNE 0xFFFF
+
+uchar f[(MAXRUNE+1)/8];
+uchar t[(MAXRUNE+1)/8];
+char wbuf[4096];
+char *wptr;
+
+Pcb pfrom, pto;
+
+int cflag;
+int dflag;
+int sflag;
+
+void complement(void);
+void delete(void);
+void squeeze(void);
+void translit(void);
+void error(char*);
+long canon(Pcb*);
+char *getrune(char*, Rune*);
+void Pinit(Pcb*, char*);
+void Prewind(Pcb *p);
+int readrune(int, long*);
+void wflush(int);
+void writerune(int, Rune);
+
+void
+main(int argc, char **argv)
+{
+ ARGBEGIN{
+ case 's': sflag++; break;
+ case 'd': dflag++; break;
+ case 'c': cflag++; break;
+ default: error("bad option");
+ }ARGEND
+ if(argc>0)
+ Pinit(&pfrom, argv[0]);
+ if(argc>1)
+ Pinit(&pto, argv[1]);
+ if(argc>2)
+ error("arg count");
+ if(dflag) {
+ if ((sflag && argc != 2) || (!sflag && argc != 1))
+ error("arg count");
+ delete();
+ } else {
+ if (argc != 2)
+ error("arg count");
+ if (cflag)
+ complement();
+ else translit();
+ }
+ exits(0);
+}
+
+void
+delete(void)
+{
+ long c, last;
+
+ if (cflag) {
+ memset((char *) f, 0xff, sizeof f);
+ while ((c = canon(&pfrom)) >= 0)
+ CLEARBIT(f, c);
+ } else {
+ while ((c = canon(&pfrom)) >= 0)
+ SETBIT(f, c);
+ }
+ if (sflag) {
+ while ((c = canon(&pto)) >= 0)
+ SETBIT(t, c);
+ }
+
+ last = 0x10000;
+ while (readrune(0, &c) > 0) {
+ if(!BITSET(f, c) && (c != last || !BITSET(t,c))) {
+ last = c;
+ writerune(1, (Rune) c);
+ }
+ }
+ wflush(1);
+}
+
+void
+complement(void)
+{
+ Rune *p;
+ int i;
+ long from, to, lastc, high;
+
+ lastc = 0;
+ high = 0;
+ while ((from = canon(&pfrom)) >= 0) {
+ if (from > high) high = from;
+ SETBIT(f, from);
+ }
+ while ((to = canon(&pto)) > 0) {
+ if (to > high) high = to;
+ SETBIT(t,to);
+ }
+ Prewind(&pto);
+ if ((p = (Rune *) malloc((high+1)*sizeof(Rune))) == 0)
+ error("can't allocate memory");
+ for (i = 0; i <= high; i++){
+ if (!BITSET(f,i)) {
+ if ((to = canon(&pto)) < 0)
+ to = lastc;
+ else lastc = to;
+ p[i] = to;
+ }
+ else p[i] = i;
+ }
+ if (sflag){
+ lastc = 0x10000;
+ while (readrune(0, &from) > 0) {
+ if (from > high)
+ from = to;
+ else
+ from = p[from];
+ if (from != lastc || !BITSET(t,from)) {
+ lastc = from;
+ writerune(1, (Rune) from);
+ }
+ }
+
+ } else {
+ while (readrune(0, &from) > 0){
+ if (from > high)
+ from = to;
+ else
+ from = p[from];
+ writerune(1, (Rune) from);
+ }
+ }
+ wflush(1);
+}
+
+void
+translit(void)
+{
+ Rune *p;
+ int i;
+ long from, to, lastc, high;
+
+ lastc = 0;
+ high = 0;
+ while ((from = canon(&pfrom)) >= 0)
+ if (from > high) high = from;
+ Prewind(&pfrom);
+ if ((p = (Rune *) malloc((high+1)*sizeof(Rune))) == 0)
+ error("can't allocate memory");
+ for (i = 0; i <= high; i++)
+ p[i] = i;
+ while ((from = canon(&pfrom)) >= 0) {
+ if ((to = canon(&pto)) < 0)
+ to = lastc;
+ else lastc = to;
+ if (BITSET(f,from) && p[from] != to)
+ error("ambiguous translation");
+ SETBIT(f,from);
+ p[from] = to;
+ SETBIT(t,to);
+ }
+ while ((to = canon(&pto)) >= 0) {
+ SETBIT(t,to);
+ }
+ if (sflag){
+ lastc = 0x10000;
+ while (readrune(0, &from) > 0) {
+ if (from <= high)
+ from = p[from];
+ if (from != lastc || !BITSET(t,from)) {
+ lastc = from;
+ writerune(1, (Rune) from);
+ }
+ }
+
+ } else {
+ while (readrune(0, &from) > 0) {
+ if (from <= high)
+ from = p[from];
+ writerune(1, (Rune) from);
+ }
+ }
+ wflush(1);
+}
+
+int
+readrune(int fd, long *rp)
+{
+ Rune r;
+ int j;
+ static int i, n;
+ static char buf[4096];
+
+ j = i;
+ for (;;) {
+ if (i >= n) {
+ wflush(1);
+ if (j != i)
+ memcpy(buf, buf+j, n-j);
+ i = n-j;
+ n = read(fd, &buf[i], sizeof(buf)-i);
+ if (n < 0)
+ error("read error");
+ if (n == 0)
+ return 0;
+ j = 0;
+ n += i;
+ }
+ i++;
+ if (fullrune(&buf[j], i-j))
+ break;
+ }
+ chartorune(&r, &buf[j]);
+ *rp = r;
+ return 1;
+}
+
+void
+writerune(int fd, Rune r)
+{
+ char buf[UTFmax];
+ int n;
+
+ if (!wptr)
+ wptr = wbuf;
+ n = runetochar(buf, (Rune*)&r);
+ if (wptr+n >= wbuf+sizeof(wbuf))
+ wflush(fd);
+ memcpy(wptr, buf, n);
+ wptr += n;
+}
+
+void
+wflush(int fd)
+{
+ if (wptr && wptr > wbuf)
+ if (write(fd, wbuf, wptr-wbuf) != wptr-wbuf)
+ error("write error");
+ wptr = wbuf;
+}
+
+char *
+getrune(char *s, Rune *rp)
+{
+ Rune r;
+ char *save;
+ int i, n;
+
+ s += chartorune(rp, s);
+ if((r = *rp) == '\\' && *s){
+ n = 0;
+ if (*s == 'x') {
+ s++;
+ for (i = 0; i < 4; i++) {
+ save = s;
+ s += chartorune(&r, s);
+ if ('0' <= r && r <= '9')
+ n = 16*n + r - '0';
+ else if ('a' <= r && r <= 'f')
+ n = 16*n + r - 'a' + 10;
+ else if ('A' <= r && r <= 'F')
+ n = 16*n + r - 'A' + 10;
+ else {
+ if (i == 0)
+ *rp = 'x';
+ else *rp = n;
+ return save;
+ }
+ }
+ } else {
+ for(i = 0; i < 3; i++) {
+ save = s;
+ s += chartorune(&r, s);
+ if('0' <= r && r <= '7')
+ n = 8*n + r - '0';
+ else {
+ if (i == 0)
+ {
+ *rp = r;
+ return s;
+ }
+ *rp = n;
+ return save;
+ }
+ }
+ if(n > 0377)
+ error("char>0377");
+ }
+ *rp = n;
+ }
+ return s;
+}
+
+long
+canon(Pcb *p)
+{
+ Rune r;
+
+ if (p->final >= 0) {
+ if (p->last < p->final)
+ return ++p->last;
+ p->final = -1;
+ }
+ if (*p->current == '\0')
+ return -1;
+ if(*p->current == '-' && p->last >= 0 && p->current[1]){
+ p->current = getrune(p->current+1, &r);
+ if (r < p->last)
+ error ("Invalid range specification");
+ if (r > p->last) {
+ p->final = r;
+ return ++p->last;
+ }
+ }
+ p->current = getrune(p->current, &r);
+ p->last = r;
+ return p->last;
+}
+
+void
+Pinit(Pcb *p, char *cp)
+{
+ p->current = p->base = cp;
+ p->last = p->final = -1;
+}
+void
+Prewind(Pcb *p)
+{
+ p->current = p->base;
+ p->last = p->final = -1;
+}
+void
+error(char *s)
+{
+ fprint(2, "%s: %s\n", argv0, s);
+ exits(s);
+}
diff --git a/uniq/Makefile b/uniq/Makefile
@@ -0,0 +1,36 @@
+# uniq - uniq unix port from plan9
+# Depends on ../lib9
+
+include ../config.mk
+
+TARG = uniq
+
+OFILES = uniq.o
+
+MANFILES = uniq.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
diff --git a/uniq/uniq.1 b/uniq/uniq.1
@@ -0,0 +1,59 @@
+.TH UNIQ 1
+.SH NAME
+uniq \- report repeated lines in a file
+.SH SYNOPSIS
+.B uniq
+[
+.B -udc
+[
+.BI +- num
+]
+]
+[
+.I file
+]
+.SH DESCRIPTION
+.I Uniq
+copies the input
+.IR file ,
+or the standard input, to the
+standard output, comparing adjacent lines.
+In the normal case, the second and succeeding copies
+of repeated lines are
+removed.
+Repeated lines must be adjacent
+in order to be found.
+.TP
+.B -u
+Print unique lines.
+.TP
+.B -d
+Print (one copy of) duplicated lines.
+.TP
+.B -c
+Prefix a repetition count and a tab to each output line.
+Implies
+.B -u
+and
+.BR -d .
+.TP
+.BI - num
+The first
+.IR num
+fields
+together with any blanks before each are ignored.
+A field is defined as a string of non-space, non-tab characters
+separated by tabs and spaces from its neighbors.
+.TP
+.BI + num
+The first
+.IR num
+characters are ignored.
+Fields are skipped before characters.
+.SH SOURCE
+.B \*9/src/cmd/uniq.c
+.SH "SEE ALSO"
+.IR sort (1)
+.SH BUGS
+Field selection and comparison should be compatible with
+.IR sort (1).
diff --git a/uniq/uniq.c b/uniq/uniq.c
@@ -0,0 +1,169 @@
+/*
+ * Deal with duplicated lines in a file
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+
+#define SIZE 8000
+
+int fields = 0;
+int letters = 0;
+int linec = 0;
+char mode;
+int uniq;
+char *b1, *b2;
+long bsize;
+Biobuf fin;
+Biobuf fout;
+
+int gline(char *buf);
+void pline(char *buf);
+int equal(char *b1, char *b2);
+char* skip(char *s);
+
+void
+main(int argc, char *argv[])
+{
+ int f;
+
+ bsize = SIZE;
+ b1 = malloc(bsize);
+ b2 = malloc(bsize);
+ f = 0;
+ while(argc > 1) {
+ if(*argv[1] == '-') {
+ if(isdigit((uchar)argv[1][1]))
+ fields = atoi(&argv[1][1]);
+ else
+ mode = argv[1][1];
+ argc--;
+ argv++;
+ continue;
+ }
+ if(*argv[1] == '+') {
+ letters = atoi(&argv[1][1]);
+ argc--;
+ argv++;
+ continue;
+ }
+ f = open(argv[1], 0);
+ if(f < 0) {
+ fprint(2, "cannot open %s\n", argv[1]);
+ exits("open");
+ }
+ break;
+ }
+ if(argc > 2) {
+ fprint(2, "unexpected argument %s\n", argv[2]);
+ exits("arg");
+ }
+ Binit(&fin, f, OREAD);
+ Binit(&fout, 1, OWRITE);
+
+ if(gline(b1))
+ exits(0);
+ for(;;) {
+ linec++;
+ if(gline(b2)) {
+ pline(b1);
+ exits(0);
+ }
+ if(!equal(b1, b2)) {
+ pline(b1);
+ linec = 0;
+ do {
+ linec++;
+ if(gline(b1)) {
+ pline(b2);
+ exits(0);
+ }
+ } while(equal(b2, b1));
+ pline(b2);
+ linec = 0;
+ }
+ }
+}
+
+int
+gline(char *buf)
+{
+ char *p;
+
+ p = Brdline(&fin, '\n');
+ if(p == 0)
+ return 1;
+ if(fin.rdline >= bsize-1) {
+ fprint(2, "line too long\n");
+ exits("too long");
+ }
+ memmove(buf, p, fin.rdline);
+ buf[fin.rdline-1] = 0;
+ return 0;
+}
+
+void
+pline(char *buf)
+{
+
+ switch(mode) {
+
+ case 'u':
+ if(uniq) {
+ uniq = 0;
+ return;
+ }
+ break;
+
+ case 'd':
+ if(uniq)
+ break;
+ return;
+
+ case 'c':
+ Bprint(&fout, "%4d ", linec);
+ }
+ uniq = 0;
+ Bprint(&fout, "%s\n", buf);
+}
+
+int
+equal(char *b1, char *b2)
+{
+ char c;
+
+ if(fields || letters) {
+ b1 = skip(b1);
+ b2 = skip(b2);
+ }
+ for(;;) {
+ c = *b1++;
+ if(c != *b2++) {
+ if(c == 0 && mode == 's')
+ return 1;
+ return 0;
+ }
+ if(c == 0) {
+ uniq++;
+ return 1;
+ }
+ }
+}
+
+char*
+skip(char *s)
+{
+ int nf, nl;
+
+ nf = nl = 0;
+ while(nf++ < fields) {
+ while(*s == ' ' || *s == '\t')
+ s++;
+ while(!(*s == ' ' || *s == '\t' || *s == 0) )
+ s++;
+ }
+ while(nl++ < letters && *s != 0)
+ s++;
+ return s;
+}
diff --git a/yacc/9yacc b/yacc/9yacc
@@ -0,0 +1,4 @@
+#!/bin/sh
+PLAN9="`pwd`/.."
+export PLAN9
+../yacc/yacc $*
diff --git a/yacc/Makefile b/yacc/Makefile
@@ -0,0 +1,27 @@
+# yacc - yacc unix port from plan9
+# Depends on ../lib9
+
+include ../config.mk
+
+TARG = yacc
+
+OFILES = yacc.o
+
+MANFILES = yacc.1
+
+all: ${TARG}
+
+.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
+
+install:
+
+uninstall:
diff --git a/yacc/yacc.1 b/yacc/yacc.1
@@ -0,0 +1,176 @@
+.TH YACC 1
+.SH NAME
+yacc \- yet another compiler-compiler
+.SH SYNOPSIS
+.B yacc
+[
+.I option ...
+]
+.I grammar
+.SH DESCRIPTION
+.I Yacc
+converts a context-free grammar and translation code
+into a set of
+tables for an LR(1) parser and translator.
+The grammar may be ambiguous;
+specified precedence rules are used to break ambiguities.
+.PP
+The output file,
+.BR y.tab.c ,
+must be compiled by the C compiler
+to produce a program
+.LR yyparse .
+This program must be loaded with a lexical analyzer function,
+.B yylex(void)
+(often generated by
+.IR lex (1)),
+with a
+.B main(int argc, char *argv[])
+program, and with an error handling routine,
+.BR yyerror(char*) .
+.PP
+The options are
+.TP "\w'\fL-o \fIoutput\fLXX'u"
+.BI -o " output
+Direct output to the specified file instead of
+.BR y.tab.c .
+.TP
+.BI -D n
+Create file
+.BR y.debug ,
+containing diagnostic messages.
+To incorporate them in the parser, compile it with preprocessor symbol
+.B yydebug
+defined.
+The amount of
+diagnostic output from the parser is regulated by
+value
+.IR n .
+The value 0 reports errors; 1 reports reductions;
+higher values (up to 4) include more information about
+state transitions.
+.TP
+.B -v
+Create file
+.BR y.output ,
+containing a description of the parsing tables and of
+conflicts arising from ambiguities in the grammar.
+.TP
+.B -d
+Create file
+.BR y.tab.h ,
+containing
+.B #define
+statements that associate
+.IR yacc -assigned
+`token codes' with user-declared `token names'.
+Include it in source files other than
+.B y.tab.c
+to give access to the token codes.
+.TP
+.BI -s " stem
+Change the prefix
+.L y
+of the file names
+.BR y.tab.c ,
+.BR y.tab.h ,
+.BR y.debug ,
+and
+.B y.output
+to
+.IR stem .
+.TP
+.B -S
+Write a parser that uses
+Stdio
+instead of the
+.B print
+routines in libc.
+.TP
+.BI -l
+Disable #line directives in the generated parser.
+.TP
+.BI -a
+Generate a parser that takes an argument of type Yyarg
+and passes this argument to each invocation of the lexer
+function, yylex. Yyarg contains per-instance state
+and a single user-visible member, arg, of type void*.
+.PP
+The specification of
+.I yacc
+itself is essentially the same as the UNIX version
+described in the references mentioned below.
+Besides the
+.B -D
+option, the main relevant differences are:
+.IP
+The interface to the C environment is by default through
+.B <libc.h>
+rather than
+.BR <stdio.h> ;
+the
+.B -S
+option reverses this.
+.IP
+The parser accepts
+.SM UTF
+input text (see
+.IR utf (7)),
+which has a couple of effects.
+First, the return value of
+.B yylex()
+no longer fits in a
+.BR short ;
+second, the starting value for non-terminals is now 0xE000 rather than 257.
+.IP
+The generated parser can be recursive: actions can call
+.IR yyparse ,
+for example to implement a sort of
+.B #include
+statement in an interpreter.
+.IP
+Finally, some undocumented inner workings of the parser have been
+changed, which may affect programs that know too much about its structure.
+.SH FILES
+.TF y.debug.xxxxx
+.TP
+.B y.output
+.TP
+.B y.tab.c
+.TP
+.B y.tab.h
+.TP
+.B y.debug
+.TP
+.B y.tmp.*
+temporary file
+.TP
+.B y.acts.*
+temporary file
+.TP
+.B \*9/lib/yaccpar
+parser prototype
+.TP
+.B \*9/lib/yaccpars
+parser prototype using stdio
+.SH SOURCE
+.B \*9/src/cmd/yacc.c
+.SH "SEE ALSO"
+.IR lex (1)
+.br
+S. C. Johnson and R. Sethi,
+``Yacc: A parser generator'',
+.I
+Unix Research System Programmer's Manual,
+Tenth Edition, Volume 2
+.br
+B. W. Kernighan and Rob Pike,
+.I
+The UNIX Programming Environment,
+Prentice Hall, 1984
+.SH BUGS
+The parser may not have full information when it writes to
+.B y.debug
+so that the names of the tokens returned by
+.L yylex
+may be missing.
diff --git a/yacc/yacc.c b/yacc/yacc.c
@@ -0,0 +1,2980 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+
+#define Bungetrune Bungetc /* ok for now. */
+
+/*
+ * all these are 32 bit
+ */
+#define TBITSET ((32+NTERMS)/32) /* BOTCH?? +31 */
+#define BIT(a,i) ((a)[(i)>>5] & (1<<((i)&037)))
+#define SETBIT(a,i) ((a)[(i)>>5] |= (1<<((i)&037)))
+#define NWORDS(n) (((n)+32)/32)
+
+char *PARSER = "#9/yacc/yaccpar";
+char *PARSERS = "#9/yacc/yaccpars";
+
+#define TEMPNAME "y.tmp.XXXXXX"
+#define ACTNAME "y.acts.XXXXXX"
+#define OFILE "tab.c"
+#define FILEU "output"
+#define FILED "tab.h"
+#define FILEDEBUG "debug"
+
+enum
+{
+/*
+ * the following are adjustable
+ * according to memory size
+ */
+ ACTSIZE = 40000,
+ MEMSIZE = 40000,
+ NSTATES = 2000,
+ NTERMS = 511,
+ NPROD = 1600,
+ NNONTERM = 600,
+ TEMPSIZE = 2000,
+ CNAMSZ = 10000,
+ LSETSIZE = 2400,
+ WSETSIZE = 350,
+
+ NAMESIZE = 50,
+ NTYPES = 63,
+ ISIZE = 400,
+
+ PRIVATE = 0xE000, /* unicode private use */
+
+ /* relationships which must hold:
+ TBITSET ints must hold NTERMS+1 bits...
+ WSETSIZE >= NNONTERM
+ LSETSIZE >= NNONTERM
+ TEMPSIZE >= NTERMS + NNONTERM + 1
+ TEMPSIZE >= NSTATES
+ */
+
+ NTBASE = 010000,
+ ERRCODE = 8190,
+ ACCEPTCODE = 8191,
+
+ NOASC = 0, /* no assoc. */
+ LASC = 1, /* left assoc. */
+ RASC = 2, /* right assoc. */
+ BASC = 3, /* binary assoc. */
+
+ /* flags for state generation */
+
+ DONE = 0,
+ MUSTDO = 1,
+ MUSTLOOKAHEAD = 2,
+
+ /* flags for a rule having an action, and being reduced */
+
+ ACTFLAG = 04,
+ REDFLAG = 010,
+
+ /* output parser flags */
+ YYFLAG1 = -1000,
+
+ /* parse tokens */
+ IDENTIFIER = PRIVATE,
+ MARK,
+ TERM,
+ LEFT,
+ RIGHT,
+ BINARY,
+ PREC,
+ LCURLY,
+ IDENTCOLON,
+ NUMBER,
+ START,
+ TYPEDEF,
+ TYPENAME,
+ UNION,
+
+ ENDFILE = 0,
+
+ EMPTY = 1,
+ WHOKNOWS = 0,
+ OK = 1,
+ NOMORE = -1000,
+};
+
+ /* macros for getting associativity and precedence levels */
+
+#define ASSOC(i) ((i)&03)
+#define PLEVEL(i) (((i)>>4)&077)
+#define TYPE(i) (((i)>>10)&077)
+
+ /* macros for setting associativity and precedence levels */
+
+#define SETASC(i,j) i |= j
+#define SETPLEV(i,j) i |= (j<<4)
+#define SETTYPE(i,j) i |= (j<<10)
+
+ /* looping macros */
+
+#define TLOOP(i) for(i=1; i<=ntokens; i++)
+#define NTLOOP(i) for(i=0; i<=nnonter; i++)
+#define PLOOP(s,i) for(i=s; i<nprod; i++)
+#define SLOOP(i) for(i=0; i<nstate; i++)
+#define WSBUMP(x) x++
+#define WSLOOP(s,j) for(j=s; j<cwp; j++)
+#define ITMLOOP(i,p,q) for(q=pstate[i+1], p=pstate[i]; p<q; p++)
+#define SETLOOP(i) for(i=0; i<tbitset; i++)
+
+ /* command to clobber tempfiles after use */
+
+#define ZAPFILE(x) if(x) remove(x)
+
+ /* I/O descriptors */
+
+Biobuf* faction; /* file for saving actions */
+Biobuf* fdefine; /* file for #defines */
+Biobuf* fdebug; /* y.debug for strings for debugging */
+Biobuf* ftable; /* y.tab.c file */
+Biobuf* ftemp; /* tempfile to pass 2 */
+Biobuf* finput; /* input file */
+Biobuf* foutput; /* y.output file */
+
+ /* communication variables between various I/O routines */
+
+char* infile; /* input file name */
+int numbval; /* value of an input number */
+char tokname[NAMESIZE+4]; /* input token name, slop for runes and 0 */
+
+ /* structure declarations */
+
+typedef
+struct
+{
+ int lset[TBITSET];
+} Lkset;
+
+typedef
+struct
+{
+ int* pitem;
+ Lkset* look;
+} Item;
+
+typedef
+struct
+{
+ char* name;
+ int value;
+} Symb;
+
+typedef
+struct
+{
+ int* pitem;
+ int flag;
+ Lkset ws;
+} Wset;
+
+ /* storage of names */
+
+char cnames[CNAMSZ]; /* place where token and nonterminal names are stored */
+int cnamsz = CNAMSZ; /* size of cnames */
+char* cnamp = cnames; /* place where next name is to be put in */
+int ndefout = 4; /* number of defined symbols output */
+char* tempname;
+char* actname;
+char ttempname[] = TEMPNAME;
+char tactname[] = ACTNAME;
+char* parser;
+char* yydebug;
+int yyarg;
+int yyline = 1;
+
+ /* storage of types */
+int ntypes; /* number of types defined */
+char* typeset[NTYPES]; /* pointers to type tags */
+
+ /* token information */
+
+int ntokens = 0 ; /* number of tokens */
+Symb tokset[NTERMS];
+int toklev[NTERMS]; /* vector with the precedence of the terminals */
+
+ /* nonterminal information */
+
+int nnonter = -1; /* the number of nonterminals */
+Symb nontrst[NNONTERM];
+int start; /* start symbol */
+
+ /* assigned token type values */
+int extval = 0;
+
+char* ytabc = OFILE; /* name of y.tab.c */
+
+ /* grammar rule information */
+
+int mem0[MEMSIZE] ; /* production storage */
+int* mem = mem0;
+int nprod = 1; /* number of productions */
+int* prdptr[NPROD]; /* pointers to descriptions of productions */
+int levprd[NPROD]; /* precedence levels for the productions */
+int rlines[NPROD]; /* line number for this rule */
+
+ /* state information */
+
+int nstate = 0; /* number of states */
+Item* pstate[NSTATES+2]; /* pointers to the descriptions of the states */
+int tystate[NSTATES]; /* contains type information about the states */
+int defact[NSTATES]; /* the default actions of states */
+int tstates[NTERMS]; /* states generated by terminal gotos */
+int ntstates[NNONTERM]; /* states generated by nonterminal gotos */
+int mstates[NSTATES]; /* chain of overflows of term/nonterm generation lists */
+int lastred; /* the number of the last reduction of a state */
+
+ /* lookahead set information */
+
+Lkset lkst[LSETSIZE];
+int nolook; /* flag to turn off lookahead computations */
+int tbitset; /* size of lookahead sets */
+int nlset = 0; /* next lookahead set index */
+int nolook = 0; /* flag to suppress lookahead computations */
+Lkset clset; /* temporary storage for lookahead computations */
+
+ /* working set information */
+
+Wset wsets[WSETSIZE];
+Wset* cwp;
+
+ /* storage for action table */
+
+int amem[ACTSIZE]; /* action table storage */
+int* memp = amem; /* next free action table position */
+int indgo[NSTATES]; /* index to the stored goto table */
+
+ /* temporary vector, indexable by states, terms, or ntokens */
+
+int temp1[TEMPSIZE]; /* temporary storage, indexed by terms + ntokens or states */
+int lineno = 1; /* current input line number */
+int fatfl = 1; /* if on, error is fatal */
+int nerrors = 0; /* number of errors */
+
+ /* statistics collection variables */
+
+int zzgoent;
+int zzgobest;
+int zzacent;
+int zzexcp;
+int zzclose;
+int zzrrconf;
+int zzsrconf;
+
+int* ggreed = lkst[0].lset;
+int* pgo = wsets[0].ws.lset;
+int* yypgo = &nontrst[0].value;
+
+int maxspr = 0; /* maximum spread of any entry */
+int maxoff = 0; /* maximum offset into a array */
+int* pmem = mem0;
+int* maxa;
+int nxdb = 0;
+int adb = 0;
+
+
+ /* storage for information about the nonterminals */
+
+int** pres[NNONTERM+2]; /* vector of pointers to productions yielding each nonterminal */
+Lkset* pfirst[NNONTERM+2]; /* vector of pointers to first sets for each nonterminal */
+int pempty[NNONTERM+1]; /* vector of nonterminals nontrivially deriving e */
+
+ /* random stuff picked out from between functions */
+
+int indebug = 0;
+Wset* zzcwp = wsets;
+int zzgoent = 0;
+int zzgobest = 0;
+int zzacent = 0;
+int zzexcp = 0;
+int zzclose = 0;
+int zzsrconf = 0;
+int* zzmemsz = mem0;
+int zzrrconf = 0;
+int pidebug = 0; /* debugging flag for putitem */
+int gsdebug = 0;
+int cldebug = 0; /* debugging flag for closure */
+int pkdebug = 0;
+int g2debug = 0;
+
+struct
+{
+ char* name;
+ long value;
+} resrv[] =
+{
+ "binary", BINARY,
+ "left", LEFT,
+ "nonassoc", BINARY,
+ "prec", PREC,
+ "right", RIGHT,
+ "start", START,
+ "term", TERM,
+ "token", TERM,
+ "type", TYPEDEF,
+ "union", UNION,
+ 0,
+};
+
+ /* define functions */
+
+void main(int, char**);
+void others(void);
+char* chcopy(char*, char*);
+char* writem(int*);
+char* symnam(int);
+void summary(void);
+void error(char*, ...);
+void aryfil(int*, int, int);
+int setunion(int*, int*);
+void prlook(Lkset*);
+void cpres(void);
+void cpfir(void);
+int state(int);
+void putitem(int*, Lkset*);
+void cempty(void);
+void stagen(void);
+void closure(int);
+Lkset* flset(Lkset*);
+void cleantmp(void);
+void intr(void);
+void setup(int, char**);
+void finact(void);
+int defin(int, char*);
+void defout(int);
+char* cstash(char*);
+long gettok(void);
+int fdtype(int);
+int chfind(int, char*);
+void cpyunion(void);
+void cpycode(void);
+int skipcom(void);
+void cpyact(int);
+void openup(char*, int, int, int, char*);
+void output(void);
+int apack(int*, int);
+void go2out(void);
+void go2gen(int);
+void precftn(int, int, int);
+void wract(int);
+void wrstate(int);
+void warray(char*, int*, int);
+void hideprod(void);
+void callopt(void);
+void gin(int);
+void stin(int);
+int nxti(void);
+void osummary(void);
+void aoutput(void);
+void arout(char*, int*, int);
+int gtnm(void);
+
+void
+main(int argc, char *argv[])
+{
+ PARSER = unsharp(PARSER);
+ PARSERS = unsharp(PARSERS);
+ parser = PARSER;
+
+ setup(argc, argv); /* initialize and read productions */
+ tbitset = NWORDS(ntokens);
+ cpres(); /* make table of which productions yield a given nonterminal */
+ cempty(); /* make a table of which nonterminals can match the empty string */
+ cpfir(); /* make a table of firsts of nonterminals */
+ stagen(); /* generate the states */
+ output(); /* write the states and the tables */
+ go2out();
+ hideprod();
+ summary();
+ callopt();
+ others();
+ exits(0);
+}
+
+/*
+ * put out other arrays, copy the parsers
+ */
+void
+others(void)
+{
+ int c, i, j;
+
+ finput = Bopen(parser, OREAD);
+ if(finput == 0)
+ error("cannot open parser %s: %r", parser);
+ warray("yyr1", levprd, nprod);
+ aryfil(temp1, nprod, 0);
+ PLOOP(1, i)
+ temp1[i] = prdptr[i+1]-prdptr[i]-2;
+ warray("yyr2", temp1, nprod);
+
+ aryfil(temp1, nstate, -1000);
+ TLOOP(i)
+ for(j=tstates[i]; j!=0; j=mstates[j])
+ temp1[j] = i;
+ NTLOOP(i)
+ for(j=ntstates[i]; j!=0; j=mstates[j])
+ temp1[j] = -i;
+ warray("yychk", temp1, nstate);
+ warray("yydef", defact, nstate);
+
+ /* put out token translation tables */
+ /* table 1 has 0-256 */
+ aryfil(temp1, 256, 0);
+ c = 0;
+ TLOOP(i) {
+ j = tokset[i].value;
+ if(j >= 0 && j < 256) {
+ if(temp1[j]) {
+ print("yacc bug -- cant have 2 different Ts with same value\n");
+ print(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name);
+ nerrors++;
+ }
+ temp1[j] = i;
+ if(j > c)
+ c = j;
+ }
+ }
+ warray("yytok1", temp1, c+1);
+
+ /* table 2 has PRIVATE-PRIVATE+256 */
+ aryfil(temp1, 256, 0);
+ c = 0;
+ TLOOP(i) {
+ j = tokset[i].value - PRIVATE;
+ if(j >= 0 && j < 256) {
+ if(temp1[j]) {
+ print("yacc bug -- cant have 2 different Ts with same value\n");
+ print(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name);
+ nerrors++;
+ }
+ temp1[j] = i;
+ if(j > c)
+ c = j;
+ }
+ }
+ warray("yytok2", temp1, c+1);
+
+ /* table 3 has everything else */
+ Bprint(ftable, "static\tconst\tlong yytok3[] =\n{\n");
+ c = 0;
+ TLOOP(i) {
+ j = tokset[i].value;
+ if(j >= 0 && j < 256)
+ continue;
+ if(j >= PRIVATE && j < 256+PRIVATE)
+ continue;
+
+ Bprint(ftable, "%4d,%4d,", j, i);
+ c++;
+ if(c%5 == 0)
+ Bprint(ftable, "\n");
+ }
+ Bprint(ftable, "%4d\n};\n", 0);
+
+ /* copy parser text */
+ while((c=Bgetrune(finput)) != Beof) {
+ if(c == '$') {
+ if((c = Bgetrune(finput)) != 'A')
+ Bputrune(ftable, '$');
+ else { /* copy actions */
+ faction = Bopen(actname, OREAD);
+ if(faction == 0)
+ error("cannot reopen action tempfile");
+ while((c=Bgetrune(faction)) != Beof)
+ Bputrune(ftable, c);
+ Bterm(faction);
+ ZAPFILE(actname);
+ c = Bgetrune(finput);
+ }
+ }
+ Bputrune(ftable, c);
+ }
+ Bterm(ftable);
+}
+
+/*
+ * copies string q into p, returning next free char ptr
+ */
+char*
+chcopy(char* p, char* q)
+{
+ int c;
+
+ while(c = *q) {
+ if(c == '"')
+ *p++ = '\\';
+ *p++ = c;
+ q++;
+ }
+ *p = 0;
+ return p;
+}
+
+/*
+ * creates output string for item pointed to by pp
+ */
+char*
+writem(int *pp)
+{
+ int i,*p;
+ static char sarr[ISIZE];
+ char* q;
+
+ for(p=pp; *p>0; p++)
+ ;
+ p = prdptr[-*p];
+ q = chcopy(sarr, nontrst[*p-NTBASE].name);
+ q = chcopy(q, ": ");
+ for(;;) {
+ *q = ' ';
+ p++;
+ if(p == pp)
+ *q = '.';
+ q++;
+ *q = '\0';
+ i = *p;
+ if(i <= 0)
+ break;
+ q = chcopy(q, symnam(i));
+ if(q > &sarr[ISIZE-30])
+ error("item too big");
+ }
+
+ /* an item calling for a reduction */
+ i = *pp;
+ if(i < 0 ) {
+ q = chcopy(q, " (");
+ sprint(q, "%d)", -i);
+ }
+ return sarr;
+}
+
+/*
+ * return a pointer to the name of symbol i
+ */
+char*
+symnam(int i)
+{
+ char* cp;
+
+ cp = (i >= NTBASE)? nontrst[i-NTBASE].name: tokset[i].name;
+ if(*cp == ' ')
+ cp++;
+ return cp;
+}
+
+/*
+ * output the summary on y.output
+ */
+void
+summary(void)
+{
+
+ if(foutput != 0) {
+ Bprint(foutput, "\n%d/%d terminals, %d/%d nonterminals\n",
+ ntokens, NTERMS, nnonter, NNONTERM);
+ Bprint(foutput, "%d/%d grammar rules, %d/%d states\n",
+ nprod, NPROD, nstate, NSTATES);
+ Bprint(foutput, "%d shift/reduce, %d reduce/reduce conflicts reported\n",
+ zzsrconf, zzrrconf);
+ Bprint(foutput, "%d/%d working sets used\n",
+ (int)(zzcwp-wsets), WSETSIZE);
+ Bprint(foutput, "memory: states,etc. %d/%d, parser %d/%d\n",
+ (int)(zzmemsz-mem0), MEMSIZE, (int)(memp-amem), ACTSIZE);
+ Bprint(foutput, "%d/%d distinct lookahead sets\n", nlset, LSETSIZE);
+ Bprint(foutput, "%d extra closures\n", zzclose - 2*nstate);
+ Bprint(foutput, "%d shift entries, %d exceptions\n", zzacent, zzexcp);
+ Bprint(foutput, "%d goto entries\n", zzgoent);
+ Bprint(foutput, "%d entries saved by goto default\n", zzgobest);
+ }
+ if(zzsrconf != 0 || zzrrconf != 0) {
+ print("\nconflicts: ");
+ if(zzsrconf)
+ print("%d shift/reduce", zzsrconf);
+ if(zzsrconf && zzrrconf)
+ print(", ");
+ if(zzrrconf)
+ print("%d reduce/reduce", zzrrconf);
+ print("\n");
+ }
+ if(ftemp != 0) {
+ Bterm(ftemp);
+ ftemp = 0;
+ }
+ if(fdefine != 0) {
+ Bterm(fdefine);
+ fdefine = 0;
+ }
+}
+
+/*
+ * write out error comment -- NEEDS WORK
+ */
+void
+error(char *s, ...)
+{
+ va_list arg;
+
+ nerrors++;
+ fprint(2, "\n fatal error:");
+ va_start(arg, s);
+ vfprint(2, s, arg);
+ va_end(arg);
+ fprint(2, ", %s:%d\n", infile, lineno);
+ if(!fatfl)
+ return;
+ summary();
+ cleantmp();
+ exits("error");
+}
+
+/*
+ * set elements 0 through n-1 to c
+ */
+void
+aryfil(int *v, int n, int c)
+{
+ int i;
+
+ for(i=0; i<n; i++)
+ v[i] = c;
+}
+
+/*
+ * set a to the union of a and b
+ * return 1 if b is not a subset of a, 0 otherwise
+ */
+int
+setunion(int *a, int *b)
+{
+ int i, x, sub;
+
+ sub = 0;
+ SETLOOP(i) {
+ x = *a;
+ *a |= *b;
+ if(*a != x)
+ sub = 1;
+ a++;
+ b++;
+ }
+ return sub;
+}
+
+void
+prlook(Lkset* p)
+{
+ int j, *pp;
+
+ pp = p->lset;
+ if(pp == 0)
+ Bprint(foutput, "\tNULL");
+ else {
+ Bprint(foutput, " { ");
+ TLOOP(j)
+ if(BIT(pp,j))
+ Bprint(foutput, "%s ", symnam(j));
+ Bprint(foutput, "}");
+ }
+}
+
+/*
+ * compute an array with the beginnings of productions yielding given nonterminals
+ * The array pres points to these lists
+ * the array pyield has the lists: the total size is only NPROD+1
+ */
+void
+cpres(void)
+{
+ int c, j, i, **pmem;
+ static int *pyield[NPROD];
+
+ pmem = pyield;
+ NTLOOP(i) {
+ c = i+NTBASE;
+ pres[i] = pmem;
+ fatfl = 0; /* make undefined symbols nonfatal */
+ PLOOP(0, j)
+ if(*prdptr[j] == c)
+ *pmem++ = prdptr[j]+1;
+ if(pres[i] == pmem)
+ error("nonterminal %s not defined!", nontrst[i].name);
+ }
+ pres[i] = pmem;
+ fatfl = 1;
+ if(nerrors) {
+ summary();
+ cleantmp();
+ exits("error");
+ }
+ if(pmem != &pyield[nprod])
+ error("internal Yacc error: pyield %d", pmem-&pyield[nprod]);
+}
+
+/*
+ * compute an array with the first of nonterminals
+ */
+void
+cpfir(void)
+{
+ int *p, **s, i, **t, ch, changes;
+
+ zzcwp = &wsets[nnonter];
+ NTLOOP(i) {
+ aryfil(wsets[i].ws.lset, tbitset, 0);
+ t = pres[i+1];
+ /* initially fill the sets */
+ for(s=pres[i]; s<t; ++s)
+ for(p = *s; (ch = *p) > 0; ++p) {
+ if(ch < NTBASE) {
+ SETBIT(wsets[i].ws.lset, ch);
+ break;
+ }
+ if(!pempty[ch-NTBASE])
+ break;
+ }
+ }
+
+ /* now, reflect transitivity */
+ changes = 1;
+ while(changes) {
+ changes = 0;
+ NTLOOP(i) {
+ t = pres[i+1];
+ for(s = pres[i]; s < t; ++s)
+ for(p = *s; (ch = (*p-NTBASE)) >= 0; ++p) {
+ changes |= setunion(wsets[i].ws.lset, wsets[ch].ws.lset);
+ if(!pempty[ch])
+ break;
+ }
+ }
+ }
+
+ NTLOOP(i)
+ pfirst[i] = flset(&wsets[i].ws);
+ if(!indebug)
+ return;
+ if(foutput != 0)
+ NTLOOP(i) {
+ Bprint(foutput, "\n%s: ", nontrst[i].name);
+ prlook(pfirst[i]);
+ Bprint(foutput, " %d\n", pempty[i]);
+ }
+}
+
+/*
+ * sorts last state,and sees if it equals earlier ones. returns state number
+ */
+int
+state(int c)
+{
+ Item *p1, *p2, *k, *l, *q1, *q2;
+ int size1, size2, i;
+
+ p1 = pstate[nstate];
+ p2 = pstate[nstate+1];
+ if(p1 == p2)
+ return 0; /* null state */
+ /* sort the items */
+ for(k = p2-1; k > p1; k--) /* make k the biggest */
+ for(l = k-1; l >= p1; --l)
+ if(l->pitem > k->pitem) {
+ int *s;
+ Lkset *ss;
+
+ s = k->pitem;
+ k->pitem = l->pitem;
+ l->pitem = s;
+ ss = k->look;
+ k->look = l->look;
+ l->look = ss;
+ }
+ size1 = p2 - p1; /* size of state */
+
+ for(i = (c>=NTBASE)? ntstates[c-NTBASE]: tstates[c]; i != 0; i = mstates[i]) {
+ /* get ith state */
+ q1 = pstate[i];
+ q2 = pstate[i+1];
+ size2 = q2 - q1;
+ if(size1 != size2)
+ continue;
+ k = p1;
+ for(l = q1; l < q2; l++) {
+ if(l->pitem != k->pitem)
+ break;
+ k++;
+ }
+ if(l != q2)
+ continue;
+ /* found it */
+ pstate[nstate+1] = pstate[nstate]; /* delete last state */
+ /* fix up lookaheads */
+ if(nolook)
+ return i;
+ for(l = q1, k = p1; l < q2; ++l, ++k ) {
+ int s;
+
+ SETLOOP(s)
+ clset.lset[s] = l->look->lset[s];
+ if(setunion(clset.lset, k->look->lset)) {
+ tystate[i] = MUSTDO;
+ /* register the new set */
+ l->look = flset( &clset );
+ }
+ }
+ return i;
+ }
+ /* state is new */
+ if(nolook)
+ error("yacc state/nolook error");
+ pstate[nstate+2] = p2;
+ if(nstate+1 >= NSTATES)
+ error("too many states");
+ if(c >= NTBASE) {
+ mstates[nstate] = ntstates[c-NTBASE];
+ ntstates[c-NTBASE] = nstate;
+ } else {
+ mstates[nstate] = tstates[c];
+ tstates[c] = nstate;
+ }
+ tystate[nstate] = MUSTDO;
+ return nstate++;
+}
+
+void
+putitem(int *ptr, Lkset *lptr)
+{
+ Item *j;
+
+ if(pidebug && foutput != 0)
+ Bprint(foutput, "putitem(%s), state %d\n", writem(ptr), nstate);
+ j = pstate[nstate+1];
+ j->pitem = ptr;
+ if(!nolook)
+ j->look = flset(lptr);
+ pstate[nstate+1] = ++j;
+ if((int*)j > zzmemsz) {
+ zzmemsz = (int*)j;
+ if(zzmemsz >= &mem0[MEMSIZE])
+ error("out of state space");
+ }
+}
+
+/*
+ * mark nonterminals which derive the empty string
+ * also, look for nonterminals which don't derive any token strings
+ */
+void
+cempty(void)
+{
+
+ int i, *p;
+
+ /* first, use the array pempty to detect productions that can never be reduced */
+ /* set pempty to WHONOWS */
+ aryfil(pempty, nnonter+1, WHOKNOWS);
+
+ /* now, look at productions, marking nonterminals which derive something */
+more:
+ PLOOP(0, i) {
+ if(pempty[*prdptr[i] - NTBASE])
+ continue;
+ for(p = prdptr[i]+1; *p >= 0; ++p)
+ if(*p >= NTBASE && pempty[*p-NTBASE] == WHOKNOWS)
+ break;
+ /* production can be derived */
+ if(*p < 0) {
+ pempty[*prdptr[i]-NTBASE] = OK;
+ goto more;
+ }
+ }
+
+ /* now, look at the nonterminals, to see if they are all OK */
+ NTLOOP(i) {
+ /* the added production rises or falls as the start symbol ... */
+ if(i == 0)
+ continue;
+ if(pempty[i] != OK) {
+ fatfl = 0;
+ error("nonterminal %s never derives any token string", nontrst[i].name);
+ }
+ }
+
+ if(nerrors) {
+ summary();
+ cleantmp();
+ exits("error");
+ }
+
+ /* now, compute the pempty array, to see which nonterminals derive the empty string */
+ /* set pempty to WHOKNOWS */
+ aryfil( pempty, nnonter+1, WHOKNOWS);
+
+ /* loop as long as we keep finding empty nonterminals */
+
+again:
+ PLOOP(1, i) {
+ /* not known to be empty */
+ if(pempty[*prdptr[i]-NTBASE] == WHOKNOWS) {
+ for(p = prdptr[i]+1; *p >= NTBASE && pempty[*p-NTBASE] == EMPTY ; ++p)
+ ;
+ /* we have a nontrivially empty nonterminal */
+ if(*p < 0) {
+ pempty[*prdptr[i]-NTBASE] = EMPTY;
+ /* got one ... try for another */
+ goto again;
+ }
+ }
+ }
+}
+
+/*
+ * generate the states
+ */
+void
+stagen(void)
+{
+
+ int c, i, j, more;
+ Wset *p, *q;
+
+ /* initialize */
+ nstate = 0;
+
+ /* THIS IS FUNNY from the standpoint of portability
+ * it represents the magic moment when the mem0 array, which has
+ * been holding the productions, starts to hold item pointers, of a
+ * different type...
+ * someday, alloc should be used to allocate all this stuff... for now, we
+ * accept that if pointers don't fit in integers, there is a problem...
+ */
+
+ pstate[0] = pstate[1] = (Item*)mem;
+ aryfil(clset.lset, tbitset, 0);
+ putitem(prdptr[0]+1, &clset);
+ tystate[0] = MUSTDO;
+ nstate = 1;
+ pstate[2] = pstate[1];
+
+ aryfil(amem, ACTSIZE, 0);
+
+ /* now, the main state generation loop */
+ for(more=1; more;) {
+ more = 0;
+ SLOOP(i) {
+ if(tystate[i] != MUSTDO)
+ continue;
+ tystate[i] = DONE;
+ aryfil(temp1, nnonter+1, 0);
+ /* take state i, close it, and do gotos */
+ closure(i);
+ /* generate goto's */
+ WSLOOP(wsets, p) {
+ if(p->flag)
+ continue;
+ p->flag = 1;
+ c = *(p->pitem);
+ if(c <= 1) {
+ if(pstate[i+1]-pstate[i] <= p-wsets)
+ tystate[i] = MUSTLOOKAHEAD;
+ continue;
+ }
+ /* do a goto on c */
+ WSLOOP(p, q)
+ /* this item contributes to the goto */
+ if(c == *(q->pitem)) {
+ putitem(q->pitem+1, &q->ws);
+ q->flag = 1;
+ }
+ if(c < NTBASE)
+ state(c); /* register new state */
+ else
+ temp1[c-NTBASE] = state(c);
+ }
+ if(gsdebug && foutput != 0) {
+ Bprint(foutput, "%d: ", i);
+ NTLOOP(j)
+ if(temp1[j])
+ Bprint(foutput, "%s %d, ",
+ nontrst[j].name, temp1[j]);
+ Bprint(foutput, "\n");
+ }
+ indgo[i] = apack(&temp1[1], nnonter-1) - 1;
+ /* do some more */
+ more = 1;
+ }
+ }
+}
+
+/*
+ * generate the closure of state i
+ */
+void
+closure(int i)
+{
+
+ Wset *u, *v;
+ Item *p, *q;
+ int c, ch, work, k, *pi, **s, **t;
+
+ zzclose++;
+
+ /* first, copy kernel of state i to wsets */
+ cwp = wsets;
+ ITMLOOP(i, p, q) {
+ cwp->pitem = p->pitem;
+ cwp->flag = 1; /* this item must get closed */
+ SETLOOP(k)
+ cwp->ws.lset[k] = p->look->lset[k];
+ WSBUMP(cwp);
+ }
+
+ /* now, go through the loop, closing each item */
+ work = 1;
+ while(work) {
+ work = 0;
+ WSLOOP(wsets, u) {
+ if(u->flag == 0)
+ continue;
+ /* dot is before c */
+ c = *(u->pitem);
+ if(c < NTBASE) {
+ u->flag = 0;
+ /* only interesting case is where . is before nonterminal */
+ continue;
+ }
+
+ /* compute the lookahead */
+ aryfil(clset.lset, tbitset, 0);
+
+ /* find items involving c */
+ WSLOOP(u, v)
+ if(v->flag == 1 && *(pi=v->pitem) == c) {
+ v->flag = 0;
+ if(nolook)
+ continue;
+ while((ch = *++pi) > 0) {
+ /* terminal symbol */
+ if(ch < NTBASE) {
+ SETBIT(clset.lset, ch);
+ break;
+ }
+ /* nonterminal symbol */
+ setunion(clset.lset, pfirst[ch-NTBASE]->lset);
+ if(!pempty[ch-NTBASE])
+ break;
+ }
+ if(ch <= 0)
+ setunion(clset.lset, v->ws.lset);
+ }
+
+ /*
+ * now loop over productions derived from c
+ * c is now nonterminal number
+ */
+ c -= NTBASE;
+ t = pres[c+1];
+ for(s = pres[c]; s < t; ++s) {
+ /*
+ * put these items into the closure
+ * is the item there
+ */
+ WSLOOP(wsets, v)
+ /* yes, it is there */
+ if(v->pitem == *s) {
+ if(nolook)
+ goto nexts;
+ if(setunion(v->ws.lset, clset.lset))
+ v->flag = work = 1;
+ goto nexts;
+ }
+
+ /* not there; make a new entry */
+ if(cwp-wsets+1 >= WSETSIZE)
+ error( "working set overflow");
+ cwp->pitem = *s;
+ cwp->flag = 1;
+ if(!nolook) {
+ work = 1;
+ SETLOOP(k) cwp->ws.lset[k] = clset.lset[k];
+ }
+ WSBUMP(cwp);
+
+ nexts:;
+ }
+ }
+ }
+
+ /* have computed closure; flags are reset; return */
+ if(cwp > zzcwp)
+ zzcwp = cwp;
+ if(cldebug && foutput != 0) {
+ Bprint(foutput, "\nState %d, nolook = %d\n", i, nolook);
+ WSLOOP(wsets, u) {
+ if(u->flag)
+ Bprint(foutput, "flag set!\n");
+ u->flag = 0;
+ Bprint(foutput, "\t%s", writem(u->pitem));
+ prlook(&u->ws);
+ Bprint(foutput, "\n");
+ }
+ }
+}
+
+/*
+ * decide if the lookahead set pointed to by p is known
+ * return pointer to a perminent location for the set
+ */
+Lkset*
+flset(Lkset *p)
+{
+ Lkset *q;
+ int *u, *v, *w, j;
+
+ for(q = &lkst[nlset]; q-- > lkst;) {
+ u = p->lset;
+ v = q->lset;
+ w = &v[tbitset];
+ while(v < w)
+ if(*u++ != *v++)
+ goto more;
+ /* we have matched */
+ return q;
+ more:;
+ }
+ /* add a new one */
+ q = &lkst[nlset++];
+ if(nlset >= LSETSIZE)
+ error("too many lookahead sets");
+ SETLOOP(j)
+ q->lset[j] = p->lset[j];
+ return q;
+}
+
+void
+cleantmp(void)
+{
+ ZAPFILE(actname);
+ ZAPFILE(tempname);
+}
+
+void
+intr(void)
+{
+ cleantmp();
+ exits("interrupted");
+}
+
+void
+setup(int argc, char *argv[])
+{
+ long c, t;
+ int i, j, fd, lev, ty, ytab, *p;
+ int vflag, dflag, stem;
+ char actnm[8], *stemc, *s, dirbuf[128];
+ Biobuf *fout;
+
+ ytab = 0;
+ vflag = 0;
+ dflag = 0;
+ stem = 0;
+ stemc = "y";
+ foutput = 0;
+ fdefine = 0;
+ fdebug = 0;
+ ARGBEGIN{
+ case 'v':
+ case 'V':
+ vflag++;
+ break;
+ case 'D':
+ yydebug = ARGF();
+ break;
+ case 'a':
+ yyarg = 1;
+ break;
+ case 'd':
+ dflag++;
+ break;
+ case 'l':
+ yyline = 0;
+ break;
+ case 'o':
+ ytab++;
+ ytabc = ARGF();
+ break;
+ case 's':
+ stem++;
+ stemc = ARGF();
+ break;
+ case 'S':
+ parser = PARSERS;
+ break;
+ default:
+ error("illegal option: %c", ARGC());
+ }ARGEND
+ openup(stemc, dflag, vflag, ytab, ytabc);
+ fout = dflag?fdefine:ftable;
+ if(yyarg){
+ Bprint(fdefine, "#define\tYYARG\t1\n\n");
+ }
+ if((fd = mkstemp(ttempname)) >= 0){
+ tempname = ttempname;
+ ftemp = Bfdopen(fd, OWRITE);
+ }
+ if((fd = mkstemp(tactname)) >= 0){
+ actname = tactname;
+ faction = Bfdopen(fd, OWRITE);
+ }
+ if(ftemp == 0 || faction == 0)
+ error("cannot open temp file");
+ if(argc < 1)
+ error("no input file");
+ infile = argv[0];
+ if(infile[0] != '/' && getwd(dirbuf, sizeof dirbuf)!=nil){
+ i = strlen(infile)+1+strlen(dirbuf)+1+10;
+ s = malloc(i);
+ if(s != nil){
+ snprint(s, i, "%s/%s", dirbuf, infile);
+ cleanname(s);
+ infile = s;
+ }
+ }
+ finput = Bopen(infile, OREAD);
+ if(finput == 0)
+ error("cannot open '%s'", argv[0]);
+ cnamp = cnames;
+
+ defin(0, "$end");
+ extval = PRIVATE; /* tokens start in unicode 'private use' */
+ defin(0, "error");
+ defin(1, "$accept");
+ defin(0, "$unk");
+ mem = mem0;
+ i = 0;
+
+ for(t = gettok(); t != MARK && t != ENDFILE;)
+ switch(t) {
+ case ';':
+ t = gettok();
+ break;
+
+ case START:
+ if(gettok() != IDENTIFIER)
+ error("bad %%start construction");
+ start = chfind(1, tokname);
+ t = gettok();
+ continue;
+
+ case TYPEDEF:
+ if(gettok() != TYPENAME)
+ error("bad syntax in %%type");
+ ty = numbval;
+ for(;;) {
+ t = gettok();
+ switch(t) {
+ case IDENTIFIER:
+ if((t=chfind(1, tokname)) < NTBASE) {
+ j = TYPE(toklev[t]);
+ if(j != 0 && j != ty)
+ error("type redeclaration of token %s",
+ tokset[t].name);
+ else
+ SETTYPE(toklev[t], ty);
+ } else {
+ j = nontrst[t-NTBASE].value;
+ if(j != 0 && j != ty)
+ error("type redeclaration of nonterminal %s",
+ nontrst[t-NTBASE].name );
+ else
+ nontrst[t-NTBASE].value = ty;
+ }
+ case ',':
+ continue;
+ case ';':
+ t = gettok();
+ default:
+ break;
+ }
+ break;
+ }
+ continue;
+
+ case UNION:
+ /* copy the union declaration to the output */
+ cpyunion();
+ t = gettok();
+ continue;
+
+ case LEFT:
+ case BINARY:
+ case RIGHT:
+ i++;
+
+ case TERM:
+ /* nonzero means new prec. and assoc. */
+ lev = t-TERM;
+ ty = 0;
+
+ /* get identifiers so defined */
+ t = gettok();
+
+ /* there is a type defined */
+ if(t == TYPENAME) {
+ ty = numbval;
+ t = gettok();
+ }
+ for(;;) {
+ switch(t) {
+ case ',':
+ t = gettok();
+ continue;
+
+ case ';':
+ break;
+
+ case IDENTIFIER:
+ j = chfind(0, tokname);
+ if(j >= NTBASE)
+ error("%s defined earlier as nonterminal", tokname);
+ if(lev) {
+ if(ASSOC(toklev[j]))
+ error("redeclaration of precedence of %s", tokname);
+ SETASC(toklev[j], lev);
+ SETPLEV(toklev[j], i);
+ }
+ if(ty) {
+ if(TYPE(toklev[j]))
+ error("redeclaration of type of %s", tokname);
+ SETTYPE(toklev[j],ty);
+ }
+ t = gettok();
+ if(t == NUMBER) {
+ tokset[j].value = numbval;
+ if(j < ndefout && j > 3)
+ error("please define type number of %s earlier",
+ tokset[j].name);
+ t = gettok();
+ }
+ continue;
+ }
+ break;
+ }
+ continue;
+
+ case LCURLY:
+ defout(0);
+ cpycode();
+ t = gettok();
+ continue;
+
+ default:
+ error("syntax error");
+ }
+ if(t == ENDFILE)
+ error("unexpected EOF before %%");
+
+ /* t is MARK */
+ if(!yyarg)
+ Bprint(ftable, "extern int yyerrflag;\n");
+ Bprint(ftable, "#ifndef YYMAXDEPTH\n");
+ Bprint(ftable, "#define YYMAXDEPTH 150\n");
+ Bprint(ftable, "#endif\n" );
+ if(!ntypes) {
+ Bprint(ftable, "#ifndef YYSTYPE\n");
+ Bprint(ftable, "#define YYSTYPE int\n");
+ Bprint(ftable, "#endif\n");
+ }
+ if(!yyarg){
+ Bprint(ftable, "YYSTYPE yylval;\n");
+ Bprint(ftable, "YYSTYPE yyval;\n");
+ }else{
+ if(dflag)
+ Bprint(ftable, "#include \"%s.%s\"\n\n", stemc, FILED);
+ Bprint(fout, "struct Yyarg {\n");
+ Bprint(fout, "\tint\tyynerrs;\n");
+ Bprint(fout, "\tint\tyyerrflag;\n");
+ Bprint(fout, "\tvoid*\targ;\n");
+ Bprint(fout, "\tYYSTYPE\tyyval;\n");
+ Bprint(fout, "\tYYSTYPE\tyylval;\n");
+ Bprint(fout, "};\n\n");
+ }
+ prdptr[0] = mem;
+
+ /* added production */
+ *mem++ = NTBASE;
+
+ /* if start is 0, we will overwrite with the lhs of the first rule */
+ *mem++ = start;
+ *mem++ = 1;
+ *mem++ = 0;
+ prdptr[1] = mem;
+ while((t=gettok()) == LCURLY)
+ cpycode();
+ if(t != IDENTCOLON)
+ error("bad syntax on first rule");
+
+ if(!start)
+ prdptr[0][1] = chfind(1, tokname);
+
+ /* read rules */
+ while(t != MARK && t != ENDFILE) {
+ /* process a rule */
+ rlines[nprod] = lineno;
+ if(t == '|')
+ *mem++ = *prdptr[nprod-1];
+ else
+ if(t == IDENTCOLON) {
+ *mem = chfind(1, tokname);
+ if(*mem < NTBASE)
+ error("token illegal on LHS of grammar rule");
+ mem++;
+ } else
+ error("illegal rule: missing semicolon or | ?");
+ /* read rule body */
+ t = gettok();
+
+ more_rule:
+ while(t == IDENTIFIER) {
+ *mem = chfind(1, tokname);
+ if(*mem < NTBASE)
+ levprd[nprod] = toklev[*mem];
+ mem++;
+ t = gettok();
+ }
+ if(t == PREC) {
+ if(gettok() != IDENTIFIER)
+ error("illegal %%prec syntax");
+ j = chfind(2, tokname);
+ if(j >= NTBASE)
+ error("nonterminal %s illegal after %%prec",
+ nontrst[j-NTBASE].name);
+ levprd[nprod] = toklev[j];
+ t = gettok();
+ }
+ if(t == '=') {
+ levprd[nprod] |= ACTFLAG;
+ Bprint(faction, "\ncase %d:", nprod);
+ cpyact(mem-prdptr[nprod]-1);
+ Bprint(faction, " break;");
+ if((t=gettok()) == IDENTIFIER) {
+
+ /* action within rule... */
+ sprint(actnm, "$$%d", nprod);
+
+ /* make it a nonterminal */
+ j = chfind(1, actnm);
+
+ /*
+ * the current rule will become rule number nprod+1
+ * move the contents down, and make room for the null
+ */
+ for(p = mem; p >= prdptr[nprod]; --p)
+ p[2] = *p;
+ mem += 2;
+
+ /* enter null production for action */
+ p = prdptr[nprod];
+ *p++ = j;
+ *p++ = -nprod;
+
+ /* update the production information */
+ levprd[nprod+1] = levprd[nprod] & ~ACTFLAG;
+ levprd[nprod] = ACTFLAG;
+ if(++nprod >= NPROD)
+ error("more than %d rules", NPROD);
+ prdptr[nprod] = p;
+
+ /* make the action appear in the original rule */
+ *mem++ = j;
+
+ /* get some more of the rule */
+ goto more_rule;
+ }
+ }
+
+ while(t == ';')
+ t = gettok();
+ *mem++ = -nprod;
+
+ /* check that default action is reasonable */
+ if(ntypes && !(levprd[nprod]&ACTFLAG) && nontrst[*prdptr[nprod]-NTBASE].value) {
+
+ /* no explicit action, LHS has value */
+ int tempty;
+
+ tempty = prdptr[nprod][1];
+ if(tempty < 0)
+ error("must return a value, since LHS has a type");
+ else
+ if(tempty >= NTBASE)
+ tempty = nontrst[tempty-NTBASE].value;
+ else
+ tempty = TYPE(toklev[tempty]);
+ if(tempty != nontrst[*prdptr[nprod]-NTBASE].value)
+ error("default action causes potential type clash");
+ }
+ nprod++;
+ if(nprod >= NPROD)
+ error("more than %d rules", NPROD);
+ prdptr[nprod] = mem;
+ levprd[nprod] = 0;
+ }
+
+ /* end of all rules */
+ defout(1);
+
+ finact();
+ if(t == MARK) {
+ Bprint(ftable, "\n");
+ if(yyline)
+ Bprint(ftable, "#line\t%d\t\"%s\"\n", lineno, infile);
+ while((c=Bgetrune(finput)) != Beof)
+ Bputrune(ftable, c);
+ }
+ Bterm(finput);
+}
+
+/*
+ * finish action routine
+ */
+void
+finact(void)
+{
+
+ Bterm(faction);
+ Bprint(ftable, "#define YYEOFCODE %d\n", 1);
+ Bprint(ftable, "#define YYERRCODE %d\n", 2);
+}
+
+/*
+ * define s to be a terminal if t=0
+ * or a nonterminal if t=1
+ */
+int
+defin(int nt, char *s)
+{
+ int val;
+ Rune rune;
+
+ val = 0;
+ if(nt) {
+ nnonter++;
+ if(nnonter >= NNONTERM)
+ error("too many nonterminals, limit %d",NNONTERM);
+ nontrst[nnonter].name = cstash(s);
+ return NTBASE + nnonter;
+ }
+
+ /* must be a token */
+ ntokens++;
+ if(ntokens >= NTERMS)
+ error("too many terminals, limit %d", NTERMS);
+ tokset[ntokens].name = cstash(s);
+
+ /* establish value for token */
+ /* single character literal */
+ if(s[0] == ' ') {
+ val = chartorune(&rune, &s[1]);
+ if(s[val+1] == 0) {
+ val = rune;
+ goto out;
+ }
+ }
+
+ /* escape sequence */
+ if(s[0] == ' ' && s[1] == '\\') {
+ if(s[3] == 0) {
+ /* single character escape sequence */
+ switch(s[2]) {
+ case 'n': val = '\n'; break;
+ case 'r': val = '\r'; break;
+ case 'b': val = '\b'; break;
+ case 't': val = '\t'; break;
+ case 'f': val = '\f'; break;
+ case '\'': val = '\''; break;
+ case '"': val = '"'; break;
+ case '\\': val = '\\'; break;
+ default: error("invalid escape");
+ }
+ goto out;
+ }
+
+ /* \nnn sequence */
+ if(s[2] >= '0' && s[2] <= '7') {
+ if(s[3] < '0' ||
+ s[3] > '7' ||
+ s[4] < '0' ||
+ s[4] > '7' ||
+ s[5] != 0)
+ error("illegal \\nnn construction");
+ val = 64*s[2] + 8*s[3] + s[4] - 73*'0';
+ if(val == 0)
+ error("'\\000' is illegal");
+ goto out;
+ }
+ error("unknown escape");
+ }
+ val = extval++;
+
+out:
+ tokset[ntokens].value = val;
+ toklev[ntokens] = 0;
+ return ntokens;
+}
+
+/*
+ * write out the defines (at the end of the declaration section)
+ */
+void
+defout(int last)
+{
+ int i, c;
+ char sar[NAMESIZE+10];
+
+ for(i=ndefout; i<=ntokens; i++) {
+ /* non-literals */
+ c = tokset[i].name[0];
+ if(c != ' ' && c != '$') {
+ Bprint(ftable, "#define %s %d\n",
+ tokset[i].name, tokset[i].value);
+ if(fdefine)
+ Bprint(fdefine, "#define\t%s\t%d\n",
+ tokset[i].name, tokset[i].value);
+ }
+ }
+ ndefout = ntokens+1;
+ if(last && fdebug) {
+ Bprint(fdebug, "static char* yytoknames[] =\n{\n");
+ TLOOP(i) {
+ if(tokset[i].name) {
+ chcopy(sar, tokset[i].name);
+ Bprint(fdebug, "\t\"%s\",\n", sar);
+ continue;
+ }
+ Bprint(fdebug, "\t0,\n");
+ }
+ Bprint(fdebug, "};\n");
+ }
+}
+
+char*
+cstash(char *s)
+{
+ char *temp;
+
+ temp = cnamp;
+ do {
+ if(cnamp >= &cnames[cnamsz])
+ error("too many characters in id's and literals");
+ else
+ *cnamp++ = *s;
+ } while(*s++);
+ return temp;
+}
+
+long
+gettok(void)
+{
+ long c;
+ Rune rune;
+ int i, base, match, reserve;
+ static int peekline;
+
+begin:
+ reserve = 0;
+ lineno += peekline;
+ peekline = 0;
+ c = Bgetrune(finput);
+ while(c == ' ' || c == '\n' || c == '\t' || c == '\f') {
+ if(c == '\n')
+ lineno++;
+ c = Bgetrune(finput);
+ }
+
+ /* skip comment */
+ if(c == '/') {
+ lineno += skipcom();
+ goto begin;
+ }
+ switch(c) {
+ case Beof:
+ return ENDFILE;
+
+ case '{':
+ Bungetrune(finput);
+ return '=';
+
+ case '<':
+ /* get, and look up, a type name (union member name) */
+ i = 0;
+ while((c=Bgetrune(finput)) != '>' && c >= 0 && c != '\n') {
+ rune = c;
+ c = runetochar(&tokname[i], &rune);
+ if(i < NAMESIZE)
+ i += c;
+ }
+ if(c != '>')
+ error("unterminated < ... > clause");
+ tokname[i] = 0;
+ for(i=1; i<=ntypes; i++)
+ if(!strcmp(typeset[i], tokname)) {
+ numbval = i;
+ return TYPENAME;
+ }
+ ntypes++;
+ numbval = ntypes;
+ typeset[numbval] = cstash(tokname);
+ return TYPENAME;
+
+ case '"':
+ case '\'':
+ match = c;
+ tokname[0] = ' ';
+ i = 1;
+ for(;;) {
+ c = Bgetrune(finput);
+ if(c == '\n' || c <= 0)
+ error("illegal or missing ' or \"" );
+ if(c == '\\') {
+ tokname[i] = '\\';
+ if(i < NAMESIZE)
+ i++;
+ c = Bgetrune(finput);
+ } else
+ if(c == match)
+ break;
+ rune = c;
+ c = runetochar(&tokname[i], &rune);
+ if(i < NAMESIZE)
+ i += c;
+ }
+ break;
+
+ case '%':
+ case '\\':
+ switch(c = Bgetrune(finput)) {
+ case '0': return TERM;
+ case '<': return LEFT;
+ case '2': return BINARY;
+ case '>': return RIGHT;
+ case '%':
+ case '\\': return MARK;
+ case '=': return PREC;
+ case '{': return LCURLY;
+ default: reserve = 1;
+ }
+
+ default:
+ /* number */
+ if(isdigit(c)) {
+ numbval = c-'0';
+ base = (c=='0')? 8: 10;
+ for(c = Bgetrune(finput); isdigit(c); c = Bgetrune(finput))
+ numbval = numbval*base + (c-'0');
+ Bungetrune(finput);
+ return NUMBER;
+ }
+ if(islower(c) || isupper(c) || c=='_' || c=='.' || c=='$') {
+ i = 0;
+ while(islower(c) || isupper(c) || isdigit(c) ||
+ c == '-' || c=='_' || c=='.' || c=='$') {
+ if(reserve && isupper(c))
+ c += 'a'-'A';
+ rune = c;
+ c = runetochar(&tokname[i], &rune);
+ if(i < NAMESIZE)
+ i += c;
+ c = Bgetrune(finput);
+ }
+ } else
+ return c;
+ Bungetrune(finput);
+ }
+ tokname[i] = 0;
+
+ /* find a reserved word */
+ if(reserve) {
+ for(c=0; resrv[c].name; c++)
+ if(strcmp(tokname, resrv[c].name) == 0)
+ return resrv[c].value;
+ error("invalid escape, or illegal reserved word: %s", tokname);
+ }
+
+ /* look ahead to distinguish IDENTIFIER from IDENTCOLON */
+ c = Bgetrune(finput);
+ while(c == ' ' || c == '\t'|| c == '\n' || c == '\f' || c == '/') {
+ if(c == '\n')
+ peekline++;
+ /* look for comments */
+ if(c == '/')
+ peekline += skipcom();
+ c = Bgetrune(finput);
+ }
+ if(c == ':')
+ return IDENTCOLON;
+ Bungetrune(finput);
+ return IDENTIFIER;
+}
+
+/*
+ * determine the type of a symbol
+ */
+int
+fdtype(int t)
+{
+ int v;
+
+ if(t >= NTBASE)
+ v = nontrst[t-NTBASE].value;
+ else
+ v = TYPE(toklev[t]);
+ if(v <= 0)
+ error("must specify type for %s", (t>=NTBASE)?
+ nontrst[t-NTBASE].name: tokset[t].name);
+ return v;
+}
+
+int
+chfind(int t, char *s)
+{
+ int i;
+
+ if(s[0] == ' ')
+ t = 0;
+ TLOOP(i)
+ if(!strcmp(s, tokset[i].name))
+ return i;
+ NTLOOP(i)
+ if(!strcmp(s, nontrst[i].name))
+ return NTBASE+i;
+
+ /* cannot find name */
+ if(t > 1)
+ error("%s should have been defined earlier", s);
+ return defin(t, s);
+}
+
+/*
+ * copy the union declaration to the output, and the define file if present
+ */
+void
+cpyunion(void)
+{
+ long c;
+ int level;
+
+ Bprint(ftable, "\n");
+ if(yyline)
+ Bprint(ftable, "#line\t%d\t\"%s\"\n", lineno, infile);
+ Bprint(ftable, "typedef union ");
+ if(fdefine != 0)
+ Bprint(fdefine, "\ntypedef union ");
+
+ level = 0;
+ for(;;) {
+ if((c=Bgetrune(finput)) == Beof)
+ error("EOF encountered while processing %%union");
+ Bputrune(ftable, c);
+ if(fdefine != 0)
+ Bputrune(fdefine, c);
+ switch(c) {
+ case '\n':
+ lineno++;
+ break;
+ case '{':
+ level++;
+ break;
+ case '}':
+ level--;
+
+ /* we are finished copying */
+ if(level == 0) {
+ Bprint(ftable, " YYSTYPE;\n");
+ if(fdefine != 0){
+ Bprint(fdefine, "\tYYSTYPE;\n");
+ if(!yyarg)
+ Bprint(fdefine, "extern\tYYSTYPE\tyylval;\n");
+ }
+ return;
+ }
+ }
+ }
+}
+
+/*
+ * copies code between \{ and \}
+ */
+void
+cpycode(void)
+{
+ long c;
+
+ c = Bgetrune(finput);
+ if(c == '\n') {
+ c = Bgetrune(finput);
+ lineno++;
+ }
+ Bprint(ftable, "\n");
+ if(yyline)
+ Bprint(ftable, "#line\t%d\t\"%s\"\n", lineno, infile);
+ while(c != Beof) {
+ if(c == '\\') {
+ if((c=Bgetrune(finput)) == '}')
+ return;
+ Bputc(ftable, '\\');
+ }
+ if(c == '%') {
+ if((c=Bgetrune(finput)) == '}')
+ return;
+ Bputc(ftable, '%');
+ }
+ Bputrune(ftable, c);
+ if(c == '\n')
+ lineno++;
+ c = Bgetrune(finput);
+ }
+ error("eof before %%}");
+}
+
+/*
+ * skip over comments
+ * skipcom is called after reading a '/'
+ */
+int
+skipcom(void)
+{
+ long c;
+ int i;
+
+ /* i is the number of lines skipped */
+ i = 0;
+ if(Bgetrune(finput) != '*')
+ error("illegal comment");
+ c = Bgetrune(finput);
+ while(c != Beof) {
+ while(c == '*')
+ if((c=Bgetrune(finput)) == '/')
+ return i;
+ if(c == '\n')
+ i++;
+ c = Bgetrune(finput);
+ }
+ error("EOF inside comment");
+ return 0;
+}
+
+/*
+ * copy C action to the next ; or closing }
+ */
+void
+cpyact(int offset)
+{
+ long c;
+ int brac, match, j, s, fnd, tok;
+
+ Bprint(faction, "\n");
+ if(yyline)
+ Bprint(faction, "#line\t%d\t\"%s\"\n", lineno, infile);
+ brac = 0;
+
+loop:
+ c = Bgetrune(finput);
+swt:
+ switch(c) {
+ case ';':
+ if(brac == 0) {
+ Bputrune(faction, c);
+ return;
+ }
+ goto lcopy;
+
+ case '{':
+ brac++;
+ goto lcopy;
+
+ case '$':
+ s = 1;
+ tok = -1;
+ c = Bgetrune(finput);
+
+ /* type description */
+ if(c == '<') {
+ Bungetrune(finput);
+ if(gettok() != TYPENAME)
+ error("bad syntax on $<ident> clause");
+ tok = numbval;
+ c = Bgetrune(finput);
+ }
+ if(c == '$') {
+ Bprint(faction, "yyval");
+
+ /* put out the proper tag... */
+ if(ntypes) {
+ if(tok < 0)
+ tok = fdtype(*prdptr[nprod]);
+ Bprint(faction, ".%s", typeset[tok]);
+ }
+ goto loop;
+ }
+ if(c == '-') {
+ s = -s;
+ c = Bgetrune(finput);
+ }
+ if(isdigit(c)) {
+ j = 0;
+ while(isdigit(c)) {
+ j = j*10 + (c-'0');
+ c = Bgetrune(finput);
+ }
+ Bungetrune(finput);
+ j = j*s - offset;
+ if(j > 0)
+ error("Illegal use of $%d", j+offset);
+
+ dollar:
+ Bprint(faction, "yypt[-%d].yyv", -j);
+
+ /* put out the proper tag */
+ if(ntypes) {
+ if(j+offset <= 0 && tok < 0)
+ error("must specify type of $%d", j+offset);
+ if(tok < 0)
+ tok = fdtype(prdptr[nprod][j+offset]);
+ Bprint(faction, ".%s", typeset[tok]);
+ }
+ goto loop;
+ }
+ if(isupper(c) || islower(c) || c == '_' || c == '.') {
+ int tok; /* tok used oustide for type info */
+
+ /* look for $name */
+ Bungetrune(finput);
+ if(gettok() != IDENTIFIER)
+ error("$ must be followed by an identifier");
+ tok = chfind(2, tokname);
+ if((c = Bgetrune(finput)) != '#') {
+ Bungetrune(finput);
+ fnd = -1;
+ } else
+ if(gettok() != NUMBER) {
+ error("# must be followed by number");
+ fnd = -1;
+ } else
+ fnd = numbval;
+ for(j=1; j<=offset; ++j)
+ if(tok == prdptr[nprod][j]) {
+ if(--fnd <= 0) {
+ j -= offset;
+ goto dollar;
+ }
+ }
+ error("$name or $name#number not found");
+ }
+ Bputc(faction, '$');
+ if(s < 0 )
+ Bputc(faction, '-');
+ goto swt;
+
+ case '}':
+ brac--;
+ if(brac)
+ goto lcopy;
+ Bputrune(faction, c);
+ return;
+
+ case '/':
+ /* look for comments */
+ Bputrune(faction, c);
+ c = Bgetrune(finput);
+ if(c != '*')
+ goto swt;
+
+ /* it really is a comment */
+ Bputrune(faction, c);
+ c = Bgetrune(finput);
+ while(c >= 0) {
+ while(c == '*') {
+ Bputrune(faction, c);
+ if((c=Bgetrune(finput)) == '/')
+ goto lcopy;
+ }
+ Bputrune(faction, c);
+ if(c == '\n')
+ lineno++;
+ c = Bgetrune(finput);
+ }
+ error("EOF inside comment");
+
+ case '\'':
+ /* character constant */
+ match = '\'';
+ goto string;
+
+ case '"':
+ /* character string */
+ match = '"';
+
+ string:
+ Bputrune(faction, c);
+ while(c = Bgetrune(finput)) {
+ if(c == '\\') {
+ Bputrune(faction, c);
+ c = Bgetrune(finput);
+ if(c == '\n')
+ lineno++;
+ } else
+ if(c == match)
+ goto lcopy;
+ if(c == '\n')
+ error("newline in string or char. const.");
+ Bputrune(faction, c);
+ }
+ error("EOF in string or character constant");
+
+ case Beof:
+ error("action does not terminate");
+
+ case '\n':
+ lineno++;
+ goto lcopy;
+ }
+
+lcopy:
+ Bputrune(faction, c);
+ goto loop;
+}
+
+void
+openup(char *stem, int dflag, int vflag, int ytab, char *ytabc)
+{
+ char buf[256];
+
+ if(vflag) {
+ sprint(buf, "%s.%s", stem, FILEU);
+ foutput = Bopen(buf, OWRITE);
+ if(foutput == 0)
+ error("cannot open %s", buf);
+ }
+ if(yydebug) {
+ sprint(buf, "%s.%s", stem, FILEDEBUG);
+ if((fdebug = Bopen(buf, OWRITE)) == 0)
+ error("can't open %s", buf);
+ }
+ if(dflag) {
+ sprint(buf, "%s.%s", stem, FILED);
+ fdefine = Bopen(buf, OWRITE);
+ if(fdefine == 0)
+ error("can't create %s", buf);
+ }
+ if(ytab == 0)
+ sprint(buf, "%s.%s", stem, OFILE);
+ else
+ strcpy(buf, ytabc);
+ ftable = Bopen(buf, OWRITE);
+ if(ftable == 0)
+ error("cannot open table file %s", buf);
+}
+
+/*
+ * print the output for the states
+ */
+void
+output(void)
+{
+ int i, k, c;
+ Wset *u, *v;
+
+ Bprint(ftable, "static\tconst\tshort yyexca[] =\n{");
+ if(fdebug)
+ Bprint(fdebug, "static\tconst\tchar* yystates[] =\n{\n");
+
+ /* output the stuff for state i */
+ SLOOP(i) {
+ nolook = tystate[i]!=MUSTLOOKAHEAD;
+ closure(i);
+
+ /* output actions */
+ nolook = 1;
+ aryfil(temp1, ntokens+nnonter+1, 0);
+ WSLOOP(wsets, u) {
+ c = *(u->pitem);
+ if(c > 1 && c < NTBASE && temp1[c] == 0) {
+ WSLOOP(u, v)
+ if(c == *(v->pitem))
+ putitem(v->pitem+1, (Lkset*)0);
+ temp1[c] = state(c);
+ } else
+ if(c > NTBASE && temp1[(c -= NTBASE) + ntokens] == 0)
+ temp1[c+ntokens] = amem[indgo[i]+c];
+ }
+ if(i == 1)
+ temp1[1] = ACCEPTCODE;
+
+ /* now, we have the shifts; look at the reductions */
+ lastred = 0;
+ WSLOOP(wsets, u) {
+ c = *u->pitem;
+
+ /* reduction */
+ if(c <= 0) {
+ lastred = -c;
+ TLOOP(k)
+ if(BIT(u->ws.lset, k)) {
+ if(temp1[k] == 0)
+ temp1[k] = c;
+ else
+ if(temp1[k] < 0) { /* reduce/reduce conflict */
+ if(foutput)
+ Bprint(foutput,
+ "\n%d: reduce/reduce conflict"
+ " (red'ns %d and %d ) on %s",
+ i, -temp1[k], lastred,
+ symnam(k));
+ if(-temp1[k] > lastred)
+ temp1[k] = -lastred;
+ zzrrconf++;
+ } else
+ /* potential shift/reduce conflict */
+ precftn( lastred, k, i );
+ }
+ }
+ }
+ wract(i);
+ }
+
+ if(fdebug)
+ Bprint(fdebug, "};\n");
+ Bprint(ftable, "};\n");
+ Bprint(ftable, "#define YYNPROD %d\n", nprod);
+ Bprint(ftable, "#define YYPRIVATE %d\n", PRIVATE);
+ if(yydebug)
+ Bprint(ftable, "#define yydebug %s\n", yydebug);
+}
+
+/*
+ * pack state i from temp1 into amem
+ */
+int
+apack(int *p, int n)
+{
+ int *pp, *qq, *rr, off, *q, *r;
+
+ /* we don't need to worry about checking because
+ * we will only look at entries known to be there...
+ * eliminate leading and trailing 0's
+ */
+
+ q = p+n;
+ for(pp = p, off = 0; *pp == 0 && pp <= q; ++pp, --off)
+ ;
+ /* no actions */
+ if(pp > q)
+ return 0;
+ p = pp;
+
+ /* now, find a place for the elements from p to q, inclusive */
+ r = &amem[ACTSIZE-1];
+ for(rr = amem; rr <= r; rr++, off++) {
+ for(qq = rr, pp = p; pp <= q; pp++, qq++)
+ if(*pp != 0)
+ if(*pp != *qq && *qq != 0)
+ goto nextk;
+
+ /* we have found an acceptable k */
+ if(pkdebug && foutput != 0)
+ Bprint(foutput, "off = %d, k = %d\n", off, (int)(rr-amem));
+ for(qq = rr, pp = p; pp <= q; pp++, qq++)
+ if(*pp) {
+ if(qq > r)
+ error("action table overflow");
+ if(qq > memp)
+ memp = qq;
+ *qq = *pp;
+ }
+ if(pkdebug && foutput != 0)
+ for(pp = amem; pp <= memp; pp += 10) {
+ Bprint(foutput, "\t");
+ for(qq = pp; qq <= pp+9; qq++)
+ Bprint(foutput, "%d ", *qq);
+ Bprint(foutput, "\n");
+ }
+ return(off);
+ nextk:;
+ }
+ error("no space in action table");
+ return 0;
+}
+
+/*
+ * output the gotos for the nontermninals
+ */
+void
+go2out(void)
+{
+ int i, j, k, best, count, cbest, times;
+
+ /* mark begining of gotos */
+ Bprint(ftemp, "$\n");
+ for(i = 1; i <= nnonter; i++) {
+ go2gen(i);
+
+ /* find the best one to make default */
+ best = -1;
+ times = 0;
+
+ /* is j the most frequent */
+ for(j = 0; j <= nstate; j++) {
+ if(tystate[j] == 0)
+ continue;
+ if(tystate[j] == best)
+ continue;
+
+ /* is tystate[j] the most frequent */
+ count = 0;
+ cbest = tystate[j];
+ for(k = j; k <= nstate; k++)
+ if(tystate[k] == cbest)
+ count++;
+ if(count > times) {
+ best = cbest;
+ times = count;
+ }
+ }
+
+ /* best is now the default entry */
+ zzgobest += times-1;
+ for(j = 0; j <= nstate; j++)
+ if(tystate[j] != 0 && tystate[j] != best) {
+ Bprint(ftemp, "%d,%d,", j, tystate[j]);
+ zzgoent++;
+ }
+
+ /* now, the default */
+ if(best == -1)
+ best = 0;
+ zzgoent++;
+ Bprint(ftemp, "%d\n", best);
+ }
+}
+
+/*
+ * output the gotos for nonterminal c
+ */
+void
+go2gen(int c)
+{
+ int i, work, cc;
+ Item *p, *q;
+
+
+ /* first, find nonterminals with gotos on c */
+ aryfil(temp1, nnonter+1, 0);
+ temp1[c] = 1;
+ work = 1;
+ while(work) {
+ work = 0;
+ PLOOP(0, i)
+
+ /* cc is a nonterminal */
+ if((cc=prdptr[i][1]-NTBASE) >= 0)
+ /* cc has a goto on c */
+ if(temp1[cc] != 0) {
+
+ /* thus, the left side of production i does too */
+ cc = *prdptr[i]-NTBASE;
+ if(temp1[cc] == 0) {
+ work = 1;
+ temp1[cc] = 1;
+ }
+ }
+ }
+
+ /* now, we have temp1[c] = 1 if a goto on c in closure of cc */
+ if(g2debug && foutput != 0) {
+ Bprint(foutput, "%s: gotos on ", nontrst[c].name);
+ NTLOOP(i)
+ if(temp1[i])
+ Bprint(foutput, "%s ", nontrst[i].name);
+ Bprint(foutput, "\n");
+ }
+
+ /* now, go through and put gotos into tystate */
+ aryfil(tystate, nstate, 0);
+ SLOOP(i)
+ ITMLOOP(i, p, q)
+ if((cc = *p->pitem) >= NTBASE)
+ /* goto on c is possible */
+ if(temp1[cc-NTBASE]) {
+ tystate[i] = amem[indgo[i]+c];
+ break;
+ }
+}
+
+/*
+ * decide a shift/reduce conflict by precedence.
+ * r is a rule number, t a token number
+ * the conflict is in state s
+ * temp1[t] is changed to reflect the action
+ */
+void
+precftn(int r, int t, int s)
+{
+ int lp, lt, action;
+
+ lp = levprd[r];
+ lt = toklev[t];
+ if(PLEVEL(lt) == 0 || PLEVEL(lp) == 0) {
+
+ /* conflict */
+ if(foutput != 0)
+ Bprint(foutput,
+ "\n%d: shift/reduce conflict (shift %d(%d), red'n %d(%d)) on %s",
+ s, temp1[t], PLEVEL(lt), r, PLEVEL(lp), symnam(t));
+ zzsrconf++;
+ return;
+ }
+ if(PLEVEL(lt) == PLEVEL(lp))
+ action = ASSOC(lt);
+ else
+ if(PLEVEL(lt) > PLEVEL(lp))
+ action = RASC; /* shift */
+ else
+ action = LASC; /* reduce */
+ switch(action) {
+ case BASC: /* error action */
+ temp1[t] = ERRCODE;
+ break;
+ case LASC: /* reduce */
+ temp1[t] = -r;
+ break;
+ }
+}
+
+/*
+ * output state i
+ * temp1 has the actions, lastred the default
+ */
+void
+wract(int i)
+{
+ int p, p0, p1, ntimes, tred, count, j, flag;
+
+ /* find the best choice for lastred */
+ lastred = 0;
+ ntimes = 0;
+ TLOOP(j) {
+ if(temp1[j] >= 0)
+ continue;
+ if(temp1[j]+lastred == 0)
+ continue;
+ /* count the number of appearances of temp1[j] */
+ count = 0;
+ tred = -temp1[j];
+ levprd[tred] |= REDFLAG;
+ TLOOP(p)
+ if(temp1[p]+tred == 0)
+ count++;
+ if(count > ntimes) {
+ lastred = tred;
+ ntimes = count;
+ }
+ }
+
+ /*
+ * for error recovery, arrange that, if there is a shift on the
+ * error recovery token, `error', that the default be the error action
+ */
+ if(temp1[2] > 0)
+ lastred = 0;
+
+ /* clear out entries in temp1 which equal lastred */
+ TLOOP(p)
+ if(temp1[p]+lastred == 0)
+ temp1[p] = 0;
+
+ wrstate(i);
+ defact[i] = lastred;
+ flag = 0;
+ TLOOP(p0)
+ if((p1=temp1[p0]) != 0) {
+ if(p1 < 0) {
+ p1 = -p1;
+ goto exc;
+ }
+ if(p1 == ACCEPTCODE) {
+ p1 = -1;
+ goto exc;
+ }
+ if(p1 == ERRCODE) {
+ p1 = 0;
+ exc:
+ if(flag++ == 0)
+ Bprint(ftable, "-1, %d,\n", i);
+ Bprint(ftable, "\t%d, %d,\n", p0, p1);
+ zzexcp++;
+ continue;
+ }
+ Bprint(ftemp, "%d,%d,", p0, p1);
+ zzacent++;
+ }
+ if(flag) {
+ defact[i] = -2;
+ Bprint(ftable, "\t-2, %d,\n", lastred);
+ }
+ Bprint(ftemp, "\n");
+}
+
+/*
+ * writes state i
+ */
+void
+wrstate(int i)
+{
+ int j0, j1;
+ Item *pp, *qq;
+ Wset *u;
+
+ if(fdebug) {
+ if(lastred) {
+ Bprint(fdebug, " 0, /*%d*/\n", i);
+ } else {
+ Bprint(fdebug, " \"");
+ ITMLOOP(i, pp, qq)
+ Bprint(fdebug, "%s\\n", writem(pp->pitem));
+ if(tystate[i] == MUSTLOOKAHEAD)
+ WSLOOP(wsets + (pstate[i+1] - pstate[i]), u)
+ if(*u->pitem < 0)
+ Bprint(fdebug, "%s\\n", writem(u->pitem));
+ Bprint(fdebug, "\", /*%d*/\n", i);
+ }
+ }
+ if(foutput == 0)
+ return;
+ Bprint(foutput, "\nstate %d\n", i);
+ ITMLOOP(i, pp, qq)
+ Bprint(foutput, "\t%s\n", writem(pp->pitem));
+ if(tystate[i] == MUSTLOOKAHEAD)
+ /* print out empty productions in closure */
+ WSLOOP(wsets+(pstate[i+1]-pstate[i]), u)
+ if(*u->pitem < 0)
+ Bprint(foutput, "\t%s\n", writem(u->pitem));
+
+ /* check for state equal to another */
+ TLOOP(j0)
+ if((j1=temp1[j0]) != 0) {
+ Bprint(foutput, "\n\t%s ", symnam(j0));
+ /* shift, error, or accept */
+ if(j1 > 0) {
+ if(j1 == ACCEPTCODE)
+ Bprint(foutput, "accept");
+ else
+ if(j1 == ERRCODE)
+ Bprint(foutput, "error");
+ else
+ Bprint(foutput, "shift %d", j1);
+ } else
+ Bprint(foutput, "reduce %d (src line %d)", -j1, rlines[-j1]);
+ }
+
+ /* output the final production */
+ if(lastred)
+ Bprint(foutput, "\n\t. reduce %d (src line %d)\n\n",
+ lastred, rlines[lastred]);
+ else
+ Bprint(foutput, "\n\t. error\n\n");
+
+ /* now, output nonterminal actions */
+ j1 = ntokens;
+ for(j0 = 1; j0 <= nnonter; j0++) {
+ j1++;
+ if(temp1[j1])
+ Bprint(foutput, "\t%s goto %d\n", symnam(j0+NTBASE), temp1[j1]);
+ }
+}
+
+void
+warray(char *s, int *v, int n)
+{
+ int i;
+
+ Bprint(ftable, "static\tconst\tshort %s[] =\n{", s);
+ for(i=0;;) {
+ if(i%10 == 0)
+ Bprint(ftable, "\n");
+ Bprint(ftable, "%4d", v[i]);
+ i++;
+ if(i >= n) {
+ Bprint(ftable, "\n};\n");
+ break;
+ }
+ Bprint(ftable, ",");
+ }
+}
+
+/*
+ * in order to free up the mem and amem arrays for the optimizer,
+ * and still be able to output yyr1, etc., after the sizes of
+ * the action array is known, we hide the nonterminals
+ * derived by productions in levprd.
+ */
+
+void
+hideprod(void)
+{
+ int i, j;
+
+ j = 0;
+ levprd[0] = 0;
+ PLOOP(1, i) {
+ if(!(levprd[i] & REDFLAG)) {
+ j++;
+ if(foutput != 0)
+ Bprint(foutput, "Rule not reduced: %s\n", writem(prdptr[i]));
+ }
+ levprd[i] = *prdptr[i] - NTBASE;
+ }
+ if(j)
+ print("%d rules never reduced\n", j);
+}
+
+void
+callopt(void)
+{
+ int i, *p, j, k, *q;
+
+ /* read the arrays from tempfile and set parameters */
+ finput = Bopen(tempname, OREAD);
+ if(finput == 0)
+ error("optimizer cannot open tempfile");
+
+ pgo[0] = 0;
+ temp1[0] = 0;
+ nstate = 0;
+ nnonter = 0;
+ for(;;) {
+ switch(gtnm()) {
+ case '\n':
+ nstate++;
+ pmem--;
+ temp1[nstate] = pmem - mem0;
+ case ',':
+ continue;
+ case '$':
+ break;
+ default:
+ error("bad tempfile %s", tempname);
+ }
+ break;
+ }
+
+ pmem--;
+ temp1[nstate] = yypgo[0] = pmem - mem0;
+ for(;;) {
+ switch(gtnm()) {
+ case '\n':
+ nnonter++;
+ yypgo[nnonter] = pmem-mem0;
+ case ',':
+ continue;
+ case -1:
+ break;
+ default:
+ error("bad tempfile");
+ }
+ break;
+ }
+ pmem--;
+ yypgo[nnonter--] = pmem - mem0;
+ for(i = 0; i < nstate; i++) {
+ k = 32000;
+ j = 0;
+ q = mem0 + temp1[i+1];
+ for(p = mem0 + temp1[i]; p < q ; p += 2) {
+ if(*p > j)
+ j = *p;
+ if(*p < k)
+ k = *p;
+ }
+ /* nontrivial situation */
+ if(k <= j) {
+ /* j is now the range */
+/* j -= k; *//* call scj */
+ if(k > maxoff)
+ maxoff = k;
+ }
+ tystate[i] = (temp1[i+1]-temp1[i]) + 2*j;
+ if(j > maxspr)
+ maxspr = j;
+ }
+
+ /* initialize ggreed table */
+ for(i = 1; i <= nnonter; i++) {
+ ggreed[i] = 1;
+ j = 0;
+
+ /* minimum entry index is always 0 */
+ q = mem0 + yypgo[i+1] - 1;
+ for(p = mem0+yypgo[i]; p < q ; p += 2) {
+ ggreed[i] += 2;
+ if(*p > j)
+ j = *p;
+ }
+ ggreed[i] = ggreed[i] + 2*j;
+ if(j > maxoff)
+ maxoff = j;
+ }
+
+ /* now, prepare to put the shift actions into the amem array */
+ for(i = 0; i < ACTSIZE; i++)
+ amem[i] = 0;
+ maxa = amem;
+ for(i = 0; i < nstate; i++) {
+ if(tystate[i] == 0 && adb > 1)
+ Bprint(ftable, "State %d: null\n", i);
+ indgo[i] = YYFLAG1;
+ }
+ while((i = nxti()) != NOMORE)
+ if(i >= 0)
+ stin(i);
+ else
+ gin(-i);
+
+ /* print amem array */
+ if(adb > 2 )
+ for(p = amem; p <= maxa; p += 10) {
+ Bprint(ftable, "%4d ", (int)(p-amem));
+ for(i = 0; i < 10; ++i)
+ Bprint(ftable, "%4d ", p[i]);
+ Bprint(ftable, "\n");
+ }
+
+ /* write out the output appropriate to the language */
+ aoutput();
+ osummary();
+ ZAPFILE(tempname);
+}
+
+void
+gin(int i)
+{
+ int *p, *r, *s, *q1, *q2;
+
+ /* enter gotos on nonterminal i into array amem */
+ ggreed[i] = 0;
+
+ q2 = mem0+ yypgo[i+1] - 1;
+ q1 = mem0 + yypgo[i];
+
+ /* now, find amem place for it */
+ for(p = amem; p < &amem[ACTSIZE]; p++) {
+ if(*p)
+ continue;
+ for(r = q1; r < q2; r += 2) {
+ s = p + *r + 1;
+ if(*s)
+ goto nextgp;
+ if(s > maxa)
+ if((maxa = s) > &amem[ACTSIZE])
+ error("a array overflow");
+ }
+ /* we have found amem spot */
+ *p = *q2;
+ if(p > maxa)
+ if((maxa = p) > &amem[ACTSIZE])
+ error("a array overflow");
+ for(r = q1; r < q2; r += 2) {
+ s = p + *r + 1;
+ *s = r[1];
+ }
+ pgo[i] = p-amem;
+ if(adb > 1)
+ Bprint(ftable, "Nonterminal %d, entry at %d\n", i, pgo[i]);
+ return;
+
+ nextgp:;
+ }
+ error("cannot place goto %d\n", i);
+}
+
+void
+stin(int i)
+{
+ int *r, *s, n, flag, j, *q1, *q2;
+
+ tystate[i] = 0;
+
+ /* enter state i into the amem array */
+ q2 = mem0+temp1[i+1];
+ q1 = mem0+temp1[i];
+ /* find an acceptable place */
+ for(n = -maxoff; n < ACTSIZE; n++) {
+ flag = 0;
+ for(r = q1; r < q2; r += 2) {
+ if((s = *r + n + amem) < amem)
+ goto nextn;
+ if(*s == 0)
+ flag++;
+ else
+ if(*s != r[1])
+ goto nextn;
+ }
+
+ /* check that the position equals another only if the states are identical */
+ for(j=0; j<nstate; j++) {
+ if(indgo[j] == n) {
+
+ /* we have some disagreement */
+ if(flag)
+ goto nextn;
+ if(temp1[j+1]+temp1[i] == temp1[j]+temp1[i+1]) {
+
+ /* states are equal */
+ indgo[i] = n;
+ if(adb > 1)
+ Bprint(ftable,
+ "State %d: entry at %d equals state %d\n",
+ i, n, j);
+ return;
+ }
+
+ /* we have some disagreement */
+ goto nextn;
+ }
+ }
+
+ for(r = q1; r < q2; r += 2) {
+ if((s = *r+n+amem) >= &amem[ACTSIZE])
+ error("out of space in optimizer a array");
+ if(s > maxa)
+ maxa = s;
+ if(*s != 0 && *s != r[1])
+ error("clobber of a array, pos'n %d, by %d", s-amem, r[1]);
+ *s = r[1];
+ }
+ indgo[i] = n;
+ if(adb > 1)
+ Bprint(ftable, "State %d: entry at %d\n", i, indgo[i]);
+ return;
+ nextn:;
+ }
+ error("Error; failure to place state %d\n", i);
+}
+
+/*
+ * finds the next i
+ */
+int
+nxti(void)
+{
+ int i, max, maxi;
+
+ max = 0;
+ maxi = 0;
+ for(i = 1; i <= nnonter; i++)
+ if(ggreed[i] >= max) {
+ max = ggreed[i];
+ maxi = -i;
+ }
+ for(i = 0; i < nstate; ++i)
+ if(tystate[i] >= max) {
+ max = tystate[i];
+ maxi = i;
+ }
+ if(nxdb)
+ Bprint(ftable, "nxti = %d, max = %d\n", maxi, max);
+ if(max == 0)
+ return NOMORE;
+ return maxi;
+}
+
+/*
+ * write summary
+ */
+void
+osummary(void)
+{
+
+ int i, *p;
+
+ if(foutput == 0)
+ return;
+ i = 0;
+ for(p = maxa; p >= amem; p--)
+ if(*p == 0)
+ i++;
+
+ Bprint(foutput, "Optimizer space used: input %d/%d, output %d/%d\n",
+ (int)(pmem-mem0+1), MEMSIZE, (int)(maxa-amem+1), ACTSIZE);
+ Bprint(foutput, "%d table entries, %d zero\n", (int)(maxa-amem+1), i);
+ Bprint(foutput, "maximum spread: %d, maximum offset: %d\n", maxspr, maxoff);
+}
+
+/*
+ * this version is for C
+ * write out the optimized parser
+ */
+void
+aoutput(void)
+{
+ Bprint(ftable, "#define\tYYLAST\t%d\n", (int)(maxa-amem+1));
+ arout("yyact", amem, (maxa-amem)+1);
+ arout("yypact", indgo, nstate);
+ arout("yypgo", pgo, nnonter+1);
+}
+
+void
+arout(char *s, int *v, int n)
+{
+ int i;
+
+ Bprint(ftable, "static\tconst\tshort %s[] =\n{", s);
+ for(i = 0; i < n;) {
+ if(i%10 == 0)
+ Bprint(ftable, "\n");
+ Bprint(ftable, "%4d", v[i]);
+ i++;
+ if(i == n)
+ Bprint(ftable, "\n};\n");
+ else
+ Bprint(ftable, ",");
+ }
+}
+
+/*
+ * read and convert an integer from the standard input
+ * return the terminating character
+ * blanks, tabs, and newlines are ignored
+ */
+int
+gtnm(void)
+{
+ int sign, val, c;
+
+ sign = 0;
+ val = 0;
+ while((c=Bgetrune(finput)) != Beof) {
+ if(isdigit(c)) {
+ val = val*10 + c-'0';
+ continue;
+ }
+ if(c == '-') {
+ sign = 1;
+ continue;
+ }
+ break;
+ }
+ if(sign)
+ val = -val;
+ *pmem++ = val;
+ if(pmem >= &mem0[MEMSIZE])
+ error("out of space");
+ return c;
+}
diff --git a/yacc/yaccpar b/yacc/yaccpar
@@ -0,0 +1,276 @@
+#define YYFLAG -1000
+#define YYERROR goto yyerrlab
+#define YYACCEPT return(0)
+#define YYABORT return(1)
+#define yyclearin yychar = -1
+#define yyerrok yyerrflag = 0
+
+#ifdef yydebug
+#include "y.debug"
+#else
+#define yydebug 0
+static const char* yytoknames[1]; /* for debugging */
+static const char* yystates[1]; /* for debugging */
+#endif
+
+/* parser for yacc output */
+#ifdef YYARG
+#define yynerrs yyarg->yynerrs
+#define yyerrflag yyarg->yyerrflag
+#define yyval yyarg->yyval
+#define yylval yyarg->yylval
+#else
+int yynerrs = 0; /* number of errors */
+int yyerrflag = 0; /* error recovery flag */
+#endif
+
+extern int fprint(int, char*, ...);
+extern int sprint(char*, char*, ...);
+
+static const char*
+yytokname(int yyc)
+{
+ static char x[10];
+
+ if(yyc > 0 && yyc <= sizeof(yytoknames)/sizeof(yytoknames[0]))
+ if(yytoknames[yyc-1])
+ return yytoknames[yyc-1];
+ sprint(x, "<%d>", yyc);
+ return x;
+}
+
+static const char*
+yystatname(int yys)
+{
+ static char x[10];
+
+ if(yys >= 0 && yys < sizeof(yystates)/sizeof(yystates[0]))
+ if(yystates[yys])
+ return yystates[yys];
+ sprint(x, "<%d>\n", yys);
+ return x;
+}
+
+static long
+#ifdef YYARG
+yylex1(struct Yyarg *yyarg)
+#else
+yylex1(void)
+#endif
+{
+ long yychar;
+ const long *t3p;
+ int c;
+
+#ifdef YYARG
+ yychar = yylex(yyarg);
+#else
+ yychar = yylex();
+#endif
+ if(yychar <= 0) {
+ c = yytok1[0];
+ goto out;
+ }
+ if(yychar < sizeof(yytok1)/sizeof(yytok1[0])) {
+ c = yytok1[yychar];
+ goto out;
+ }
+ if(yychar >= YYPRIVATE)
+ if(yychar < YYPRIVATE+sizeof(yytok2)/sizeof(yytok2[0])) {
+ c = yytok2[yychar-YYPRIVATE];
+ goto out;
+ }
+ for(t3p=yytok3;; t3p+=2) {
+ c = t3p[0];
+ if(c == yychar) {
+ c = t3p[1];
+ goto out;
+ }
+ if(c == 0)
+ break;
+ }
+ c = 0;
+
+out:
+ if(c == 0)
+ c = yytok2[1]; /* unknown char */
+ if(yydebug >= 3)
+ fprint(2, "lex %.4lux %s\n", yychar, yytokname(c));
+ return c;
+}
+
+int
+#ifdef YYARG
+yyparse(struct Yyarg *yyarg)
+#else
+yyparse(void)
+#endif
+{
+ struct
+ {
+ YYSTYPE yyv;
+ int yys;
+ } yys[YYMAXDEPTH], *yyp, *yypt;
+ const short *yyxi;
+ int yyj, yym, yystate, yyn, yyg;
+ long yychar;
+#ifndef YYARG
+ YYSTYPE save1, save2;
+ int save3, save4;
+
+ save1 = yylval;
+ save2 = yyval;
+ save3 = yynerrs;
+ save4 = yyerrflag;
+#endif
+
+ yystate = 0;
+ yychar = -1;
+ yynerrs = 0;
+ yyerrflag = 0;
+ yyp = &yys[-1];
+ goto yystack;
+
+ret0:
+ yyn = 0;
+ goto ret;
+
+ret1:
+ yyn = 1;
+ goto ret;
+
+ret:
+#ifndef YYARG
+ yylval = save1;
+ yyval = save2;
+ yynerrs = save3;
+ yyerrflag = save4;
+#endif
+ return yyn;
+
+yystack:
+ /* put a state and value onto the stack */
+ if(yydebug >= 4)
+ fprint(2, "char %s in %s", yytokname(yychar), yystatname(yystate));
+
+ yyp++;
+ if(yyp >= &yys[YYMAXDEPTH]) {
+ yyerror("yacc stack overflow");
+ goto ret1;
+ }
+ yyp->yys = yystate;
+ yyp->yyv = yyval;
+
+yynewstate:
+ yyn = yypact[yystate];
+ if(yyn <= YYFLAG)
+ goto yydefault; /* simple state */
+ if(yychar < 0)
+#ifdef YYARG
+ yychar = yylex1(yyarg);
+#else
+ yychar = yylex1();
+#endif
+ yyn += yychar;
+ if(yyn < 0 || yyn >= YYLAST)
+ goto yydefault;
+ yyn = yyact[yyn];
+ if(yychk[yyn] == yychar) { /* valid shift */
+ yychar = -1;
+ yyval = yylval;
+ yystate = yyn;
+ if(yyerrflag > 0)
+ yyerrflag--;
+ goto yystack;
+ }
+
+yydefault:
+ /* default state action */
+ yyn = yydef[yystate];
+ if(yyn == -2) {
+ if(yychar < 0)
+#ifdef YYARG
+ yychar = yylex1(yyarg);
+#else
+ yychar = yylex1();
+#endif
+
+ /* look through exception table */
+ for(yyxi=yyexca;; yyxi+=2)
+ if(yyxi[0] == -1 && yyxi[1] == yystate)
+ break;
+ for(yyxi += 2;; yyxi += 2) {
+ yyn = yyxi[0];
+ if(yyn < 0 || yyn == yychar)
+ break;
+ }
+ yyn = yyxi[1];
+ if(yyn < 0)
+ goto ret0;
+ }
+ if(yyn == 0) {
+ /* error ... attempt to resume parsing */
+ switch(yyerrflag) {
+ case 0: /* brand new error */
+ yyerror("syntax error");
+ if(yydebug >= 1) {
+ fprint(2, "%s", yystatname(yystate));
+ fprint(2, "saw %s\n", yytokname(yychar));
+ }
+ goto yyerrlab;
+ yyerrlab:
+ yynerrs++;
+
+ case 1:
+ case 2: /* incompletely recovered error ... try again */
+ yyerrflag = 3;
+
+ /* find a state where "error" is a legal shift action */
+ while(yyp >= yys) {
+ yyn = yypact[yyp->yys] + YYERRCODE;
+ if(yyn >= 0 && yyn < YYLAST) {
+ yystate = yyact[yyn]; /* simulate a shift of "error" */
+ if(yychk[yystate] == YYERRCODE)
+ goto yystack;
+ }
+
+ /* the current yyp has no shift onn "error", pop stack */
+ if(yydebug >= 2)
+ fprint(2, "error recovery pops state %d, uncovers %d\n",
+ yyp->yys, (yyp-1)->yys );
+ yyp--;
+ }
+ /* there is no state on the stack with an error shift ... abort */
+ goto ret1;
+
+ case 3: /* no shift yet; clobber input char */
+ if(yydebug >= 2)
+ fprint(2, "error recovery discards %s\n", yytokname(yychar));
+ if(yychar == YYEOFCODE)
+ goto ret1;
+ yychar = -1;
+ goto yynewstate; /* try again in the same state */
+ }
+ }
+
+ /* reduction by production yyn */
+ if(yydebug >= 2)
+ fprint(2, "reduce %d in:\n\t%s", yyn, yystatname(yystate));
+
+ yypt = yyp;
+ yyp -= yyr2[yyn];
+ yyval = (yyp+1)->yyv;
+ yym = yyn;
+
+ /* consult goto table to find next state */
+ yyn = yyr1[yyn];
+ yyg = yypgo[yyn];
+ yyj = yyg + yyp->yys + 1;
+
+ if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn)
+ yystate = yyact[yyg];
+ switch(yym) {
+ $A
+ }
+ goto yystack; /* stack new state and value */
+}
diff --git a/yacc/yaccpars b/yacc/yaccpars
@@ -0,0 +1,273 @@
+#define YYFLAG -1000
+#define YYERROR goto yyerrlab
+#define YYACCEPT return(0)
+#define YYABORT return(1)
+#define yyclearin yychar = -1
+#define yyerrok yyerrflag = 0
+
+#ifdef yydebug
+#include "y.debug"
+#else
+#define yydebug 0
+static const char* yytoknames[1]; /* for debugging */
+static const char* yystates[1]; /* for debugging */
+#endif
+
+/* parser for yacc output */
+#ifdef YYARG
+#define yynerrs yyarg->yynerrs
+#define yyerrflag yyarg->yyerrflag
+#define yyval yyarg->yyval
+#define yylval yyarg->yylval
+#else
+int yynerrs = 0; /* number of errors */
+int yyerrflag = 0; /* error recovery flag */
+#endif
+
+static const char*
+yytokname(int yyc)
+{
+ static char x[10];
+
+ if(yyc > 0 && yyc <= sizeof(yytoknames)/sizeof(yytoknames[0]))
+ if(yytoknames[yyc-1])
+ return yytoknames[yyc-1];
+ sprintf(x, "<%d>", yyc);
+ return x;
+}
+
+static const char*
+yystatname(int yys)
+{
+ static char x[10];
+
+ if(yys >= 0 && yys < sizeof(yystates)/sizeof(yystates[0]))
+ if(yystates[yys])
+ return yystates[yys];
+ sprintf(x, "<%d>\n", yys);
+ return x;
+}
+
+static long
+#ifdef YYARG
+yylex1(struct Yyarg *yyarg)
+#else
+yylex1(void)
+#endif
+{
+ long yychar;
+ const long *t3p;
+ int c;
+
+#ifdef YYARG
+ yychar = yylex(yyarg);
+#else
+ yychar = yylex();
+#endif
+ if(yychar <= 0) {
+ c = yytok1[0];
+ goto out;
+ }
+ if(yychar < sizeof(yytok1)/sizeof(yytok1[0])) {
+ c = yytok1[yychar];
+ goto out;
+ }
+ if(yychar >= YYPRIVATE)
+ if(yychar < YYPRIVATE+sizeof(yytok2)/sizeof(yytok2[0])) {
+ c = yytok2[yychar-YYPRIVATE];
+ goto out;
+ }
+ for(t3p=yytok3;; t3p+=2) {
+ c = t3p[0];
+ if(c == yychar) {
+ c = t3p[1];
+ goto out;
+ }
+ if(c == 0)
+ break;
+ }
+ c = 0;
+
+out:
+ if(c == 0)
+ c = yytok2[1]; /* unknown char */
+ if(yydebug >= 3)
+ printf("lex %.4lX %s\n", yychar, yytokname(c));
+ return c;
+}
+
+int
+#ifdef YYARG
+yyparse(struct Yyarg *yyarg)
+#else
+yyparse(void)
+#endif
+{
+ struct
+ {
+ YYSTYPE yyv;
+ int yys;
+ } yys[YYMAXDEPTH], *yyp, *yypt;
+ const short *yyxi;
+ int yyj, yym, yystate, yyn, yyg;
+ long yychar;
+#ifndef YYARG
+ YYSTYPE save1, save2;
+ int save3, save4;
+
+ save1 = yylval;
+ save2 = yyval;
+ save3 = yynerrs;
+ save4 = yyerrflag;
+#endif
+
+ yystate = 0;
+ yychar = -1;
+ yynerrs = 0;
+ yyerrflag = 0;
+ yyp = &yys[-1];
+ goto yystack;
+
+ret0:
+ yyn = 0;
+ goto ret;
+
+ret1:
+ yyn = 1;
+ goto ret;
+
+ret:
+#ifndef YYARG
+ yylval = save1;
+ yyval = save2;
+ yynerrs = save3;
+ yyerrflag = save4;
+#endif
+ return yyn;
+
+yystack:
+ /* put a state and value onto the stack */
+ if(yydebug >= 4)
+ printf("char %s in %s", yytokname(yychar), yystatname(yystate));
+
+ yyp++;
+ if(yyp >= &yys[YYMAXDEPTH]) {
+ yyerror("yacc stack overflow");
+ goto ret1;
+ }
+ yyp->yys = yystate;
+ yyp->yyv = yyval;
+
+yynewstate:
+ yyn = yypact[yystate];
+ if(yyn <= YYFLAG)
+ goto yydefault; /* simple state */
+ if(yychar < 0)
+#ifdef YYARG
+ yychar = yylex1(yyarg);
+#else
+ yychar = yylex1();
+#endif
+ yyn += yychar;
+ if(yyn < 0 || yyn >= YYLAST)
+ goto yydefault;
+ yyn = yyact[yyn];
+ if(yychk[yyn] == yychar) { /* valid shift */
+ yychar = -1;
+ yyval = yylval;
+ yystate = yyn;
+ if(yyerrflag > 0)
+ yyerrflag--;
+ goto yystack;
+ }
+
+yydefault:
+ /* default state action */
+ yyn = yydef[yystate];
+ if(yyn == -2) {
+ if(yychar < 0)
+#ifdef YYARG
+ yychar = yylex1(yyarg);
+#else
+ yychar = yylex1();
+#endif
+
+ /* look through exception table */
+ for(yyxi=yyexca;; yyxi+=2)
+ if(yyxi[0] == -1 && yyxi[1] == yystate)
+ break;
+ for(yyxi += 2;; yyxi += 2) {
+ yyn = yyxi[0];
+ if(yyn < 0 || yyn == yychar)
+ break;
+ }
+ yyn = yyxi[1];
+ if(yyn < 0)
+ goto ret0;
+ }
+ if(yyn == 0) {
+ /* error ... attempt to resume parsing */
+ switch(yyerrflag) {
+ case 0: /* brand new error */
+ yyerror("syntax error");
+ if(yydebug >= 1) {
+ printf("%s", yystatname(yystate));
+ printf("saw %s\n", yytokname(yychar));
+ }
+ goto yyerrlab;
+ yyerrlab:
+ yynerrs++;
+
+ case 1:
+ case 2: /* incompletely recovered error ... try again */
+ yyerrflag = 3;
+
+ /* find a state where "error" is a legal shift action */
+ while(yyp >= yys) {
+ yyn = yypact[yyp->yys] + YYERRCODE;
+ if(yyn >= 0 && yyn < YYLAST) {
+ yystate = yyact[yyn]; /* simulate a shift of "error" */
+ if(yychk[yystate] == YYERRCODE)
+ goto yystack;
+ }
+
+ /* the current yyp has no shift onn "error", pop stack */
+ if(yydebug >= 2)
+ printf("error recovery pops state %d, uncovers %d\n",
+ yyp->yys, (yyp-1)->yys );
+ yyp--;
+ }
+ /* there is no state on the stack with an error shift ... abort */
+ goto ret1;
+
+ case 3: /* no shift yet; clobber input char */
+ if(yydebug >= YYEOFCODE)
+ printf("error recovery discards %s\n", yytokname(yychar));
+ if(yychar == YYEOFCODE)
+ goto ret1;
+ yychar = -1;
+ goto yynewstate; /* try again in the same state */
+ }
+ }
+
+ /* reduction by production yyn */
+ if(yydebug >= 2)
+ printf("reduce %d in:\n\t%s", yyn, yystatname(yystate));
+
+ yypt = yyp;
+ yyp -= yyr2[yyn];
+ yyval = (yyp+1)->yyv;
+ yym = yyn;
+
+ /* consult goto table to find next state */
+ yyn = yyr1[yyn];
+ yyg = yypgo[yyn];
+ yyj = yyg + yyp->yys + 1;
+
+ if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn)
+ yystate = yyact[yyg];
+ switch(yym) {
+ $A
+ }
+ goto yystack; /* stack new state and value */
+}