added sha1sum and ed, enabled factor and primes, increases version to 6, remaining work is syncing with most recent code and applying some patches to rc - 9base - revived minimalist port of Plan 9 userland to Unix
  HTML git clone git://git.suckless.org/9base
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit e70375948b9e9afebde07fb294abf5f039f19d44
   DIR parent 7dce804fe658181cb8cbb102a525ca34b4fb0006
  HTML Author: anselm@garbe.us <unknown>
       Date:   Thu, 27 May 2010 10:26:43 +0100
       
       added sha1sum and ed, enabled factor and primes, increases version to 6, remaining work is syncing with most recent code and applying some patches to rc
       Diffstat:
         M Makefile                            |       8 +++-----
         M config.mk                           |       2 +-
         A ed/Makefile                         |      10 ++++++++++
         A ed/ed.1                             |     683 +++++++++++++++++++++++++++++++
         A ed/ed.c                             |    1616 +++++++++++++++++++++++++++++++
         M lib9/Makefile                       |       9 ++++++++-
         A lib9/sec/libsec.h                   |     366 +++++++++++++++++++++++++++++++
         A lib9/sec/os.h                       |       2 ++
         A lib9/sec/sha1.c                     |     127 +++++++++++++++++++++++++++++++
         A lib9/sec/sha1block.c                |     187 +++++++++++++++++++++++++++++++
         A lib9/sec/sha1pickle.c               |      38 +++++++++++++++++++++++++++++++
         A sha1sum/Makefile                    |      10 ++++++++++
         A sha1sum/sha1sum.1                   |       0 
         A sha1sum/sha1sum.c                   |      61 +++++++++++++++++++++++++++++++
         M std.mk                              |       4 ++--
       
       15 files changed, 3114 insertions(+), 9 deletions(-)
       ---
   DIR diff --git a/Makefile b/Makefile
       @@ -2,11 +2,9 @@
        
        include config.mk
        
       -SUBDIRS  = lib9 yacc awk basename bc cal cat cleanname date dc du echo \
       -           fortune freq getflags grep hoc ls mk mkdir mtime rc read \
       -           sed seq sleep sort tail tee test touch tr troff uniq
       -
       -# factor primes
       +SUBDIRS  = lib9 yacc awk basename bc cal cat cleanname date dc du echo ed \
       +           factor fortune freq getflags grep hoc ls mk mkdir mtime primes rc read \
       +           sha1sum sed seq sleep sort tail tee test touch tr troff uniq
        
        all:
                @echo 9base build options:
   DIR diff --git a/config.mk b/config.mk
       @@ -4,7 +4,7 @@
        PREFIX      = /usr/local/plan9
        MANPREFIX   = ${PREFIX}/share/man
        
       -VERSION     = 5
       +VERSION     = 6
        OBJTYPE     = 386
        #OBJTYPE     = arm
        #OBJTYPE     = x86_64
   DIR diff --git a/ed/Makefile b/ed/Makefile
       @@ -0,0 +1,10 @@
       +# ed - ed unix port from plan9
       +# Depends on ../lib9
       +
       +TARG      = ed
       +
       +include ../std.mk
       +
       +pre-uninstall:
       +
       +post-install:
   DIR diff --git a/ed/ed.1 b/ed/ed.1
       @@ -0,0 +1,683 @@
       +.TH ED 1
       +.SH NAME
       +ed \- text editor
       +.SH SYNOPSIS
       +.B ed
       +[
       +.B -
       +]
       +[
       +.B -o
       +]
       +[
       +.I file
       +]
       +.SH DESCRIPTION
       +.I Ed
       +is a venerable text editor.
       +.PP
       +If a
       +.I file
       +argument is given,
       +.I ed
       +simulates an
       +.L e
       +command (see below) on that file:
       +it is read into
       +.I ed's
       +buffer so that it can be edited.
       +The options are
       +.TP
       +.B -
       +Suppress the printing
       +of character counts by
       +.LR e ,
       +.LR r ,
       +and
       +.L w
       +commands and of the confirming 
       +.L !
       +by
       +.L !
       +commands.
       +.TP
       +.B -o
       +(for output piping)
       +Write all output to the standard error file except writing by
       +.L w
       +commands.
       +If no 
       +.I file
       +is given, make
       +.B /dev/stdout
       +the remembered file; see the
       +.L e
       +command below.
       +.PP
       +.I Ed
       +operates on a `buffer', a copy of the file it is editing;
       +changes made
       +in the buffer have no effect on the file until a
       +.L w
       +(write)
       +command is given.
       +The copy of the text being edited resides
       +in a temporary file called the 
       +.IR buffer .
       +.PP
       +Commands to
       +.I ed
       +have a simple and regular structure: zero, one, or
       +two
       +.I addresses
       +followed by a single character
       +.IR command ,
       +possibly
       +followed by parameters to the command.
       +These addresses specify one or more lines in the buffer.
       +Missing addresses are supplied by default.
       +.PP
       +In general, only one command may appear on a line.
       +Certain commands allow the 
       +addition of text to the buffer.
       +While
       +.I ed
       +is accepting text, it is said
       +to be in
       +.I  "input mode."
       +In this mode, no commands are recognized;
       +all input is merely collected.
       +Input mode is left by typing a period 
       +.L .
       +alone at the
       +beginning of a line.
       +.PP
       +.I Ed
       +supports the 
       +.I "regular expression"
       +notation described in
       +.IR regexp (7).
       +Regular expressions are used in addresses to specify
       +lines and in one command
       +(see
       +.I s
       +below)
       +to specify a portion of a line which is to be replaced.
       +If it is desired to use one of
       +the regular expression metacharacters as an ordinary
       +character, that character may be preceded by
       +.RB ` \e '.
       +This also applies to the character bounding the regular
       +expression (often 
       +.LR / )
       +and to
       +.L \e
       +itself.
       +.PP
       +To understand addressing in
       +.I ed
       +it is necessary to know that at any time there is a
       +.I "current line."
       +Generally, the current line is
       +the last line affected by a command; however,
       +the exact effect on the current line
       +is discussed under the description of
       +each command.
       +Addresses are constructed as follows.
       +.TP
       +1.
       +The character
       +.LR . ,
       +customarily called `dot',
       +addresses the current line.
       +.TP
       +2.
       +The character 
       +.L $
       +addresses the last line of the buffer.
       +.TP
       +3.
       +A decimal number
       +.I n
       +addresses the
       +.IR n -th
       +line of the buffer.
       +.TP
       +4.
       +.BI \'x
       +addresses the line marked with the name
       +.IR x ,
       +which must be a lower-case letter.
       +Lines are marked with the
       +.L k
       +command.
       +.TP
       +5.
       +A regular expression enclosed in slashes (
       +.LR / )
       +addresses
       +the line found by searching forward from the current line
       +and stopping at the first line containing a
       +string that matches the regular expression.
       +If necessary the search wraps around to the beginning of the
       +buffer.
       +.TP
       +6.
       +A regular expression enclosed in queries 
       +.L ?
       +addresses
       +the line found by searching backward from the current line
       +and stopping at the first line containing
       +a string that matches the regular expression.
       +If necessary
       +the search wraps around to the end of the buffer.
       +.TP
       +7.
       +An address followed by a plus sign 
       +.L +
       +or a minus sign
       +.L -
       +followed by a decimal number specifies that address plus
       +(resp. minus) the indicated number of lines.
       +The plus sign may be omitted.
       +.TP
       +8.
       +An address followed by 
       +.L +
       +(or
       +.LR - )
       +followed by a
       +regular expression enclosed in slashes specifies the first
       +matching line following (or preceding) that address.
       +The search wraps around if necessary.
       +The 
       +.L +
       +may be omitted, so
       +.L 0/x/
       +addresses the
       +.I first
       +line in the buffer with an 
       +.LR x .
       +Enclosing the regular expression in 
       +.L ?
       +reverses the search direction.
       +.TP
       +9.
       +If an address begins with 
       +.L +
       +or
       +.L -
       +the addition or subtraction is taken with respect to the current line;
       +e.g.\&
       +.L -5
       +is understood to mean
       +.LR .-5 .
       +.TP
       +10.
       +If an address ends with 
       +.L +
       +or
       +.LR - ,
       +then 1 is added (resp. subtracted).
       +As a consequence of this rule and rule 9,
       +the address
       +.L -
       +refers to the line before the current line.
       +Moreover,
       +trailing
       +.L +
       +and
       +.L -
       +characters
       +have cumulative effect, so
       +.L --
       +refers to the current
       +line less 2.
       +.TP
       +11.
       +To maintain compatibility with earlier versions of the editor,
       +the character 
       +.L ^
       +in addresses is 
       +equivalent to
       +.LR - .
       +.PP
       +Commands may require zero, one, or two addresses.
       +Commands which require no addresses regard the presence
       +of an address as an error.
       +Commands which accept one or two addresses
       +assume default addresses when insufficient are given.
       +If more addresses are given than a command requires,
       +the last one or two (depending on what is accepted) are used.
       +.PP
       +Addresses are separated from each other typically by a comma
       +.LR , .
       +They may also be separated by a semicolon
       +.LR ; .
       +In this case the current line 
       +is set to
       +the previous address before the next address is interpreted.
       +If no address precedes a comma or semicolon, line 1 is assumed;
       +if no address follows, the last line of the buffer is assumed.
       +The second address of any two-address sequence
       +must correspond to a line following the line corresponding to the first address.
       +.PP
       +In the following list of
       +.I ed
       +commands, the default addresses
       +are shown in parentheses.
       +The parentheses are not part of
       +the address, but are used to show that the given addresses are
       +the default.
       +`Dot' means the current line.
       +.TP
       +.RB (\|\fL.\fP\|) \|a
       +.br
       +.ns
       +.TP
       +<text>
       +.br
       +.ns
       +.TP
       +.B .
       +Read the given text
       +and append it after the addressed line.
       +Dot is left
       +on the last line input, if there
       +were any, otherwise at the addressed line.
       +Address 
       +.L 0
       +is legal for this command; text is placed
       +at the beginning of the buffer.
       +.TP
       +.RB (\|\fL.,.\fP\|) \|b [ +- ][\fIpagesize\fP][ pln\fR]
       +Browse.
       +Print a `page', normally 20 lines.
       +The optional 
       +.L +
       +(default) or
       +.L -
       +specifies whether the next or previous
       +page is to be printed.
       +The optional
       +.I pagesize
       +is the number of lines in a page.
       +The optional
       +.LR p ,
       +.LR n ,
       +or 
       +.L l
       +causes printing in the specified format, initially
       +.LR p .
       +Pagesize and format are remembered between 
       +.L b
       +commands.
       +Dot is left at the last line displayed.
       +.TP
       +.RB (\|\fL.,.\fP\|) \|c
       +.br
       +.ns
       +.TP
       +<text>
       +.br
       +.ns
       +.TP
       +.B .
       +Change.
       +Delete the addressed lines, then accept input
       +text to replace these lines.
       +Dot is left at the last line input; if there were none,
       +it is left at the line preceding the deleted lines.
       +.TP
       +.RB (\|\fL.,.\fP\|) \|d
       +Delete the addressed lines from the buffer.
       +Dot is set to the line following the last line deleted, or to
       +the last line of the buffer if the deleted lines had no successor.
       +.TP
       +.BI e " filename"
       +Edit.
       +Delete the entire contents of the buffer;
       +then read the named file into the buffer.
       +Dot is set to the last line of the buffer.
       +The number of characters read is typed.
       +The file name is remembered for possible use in later
       +.LR e ,
       +.LR r ,
       +or
       +.L w
       +commands.
       +If
       +.I filename
       +is missing, the remembered name is used.
       +.TP
       +.BI E " filename"
       +Unconditional
       +.LR e ;
       +see
       +.RL ` q '
       +below.
       +.TP
       +.BI f " filename"
       +Print the currently remembered file name.
       +If
       +.I filename
       +is given,
       +the currently remembered file name is first changed to
       +.IR filename .
       +.TP
       +.RB (\|\fL1,$\fP\|) \|g/\fIregular\ expression\fP/\fIcommand\ list\fP
       +.PD 0
       +.TP
       +.RB (\|\fL1,$\fP\|) \|g/\fIregular\ expression\fP/
       +.TP
       +.RB (\|\fL1,$\fP\|) \|g/\fIregular\ expression\fP
       +.PD
       +Global.
       +First mark every line which matches
       +the given
       +.IR regular expression .
       +Then for every such line, execute the
       +.I command list
       +with dot initially set to that line.
       +A single command or the first of multiple commands
       +appears on the same line with the global command.
       +All lines of a multi-line list except the last line must end with
       +.LR \e .
       +The
       +.RB \&` \&. \&'
       +terminating input mode for an
       +.LR a ,
       +.LR i ,
       +.L c
       +command may be omitted if it would be on the
       +last line of the command list.
       +The commands
       +.L g
       +and
       +.L v
       +are not permitted in the command list.
       +Any character other than space or newline may
       +be used instead of 
       +.L /
       +to delimit the regular expression.
       +The second and third forms mean
       +.BI g/ regular\ expression /p \f1.
       +.TP
       +.RB (\| .\| ) \|i
       +.PD 0
       +.TP
       +<text>
       +.TP
       +.B .
       +Insert the given text before the addressed line.
       +Dot is left at the last line input, or, if there were none,
       +at the line before the addressed line.
       +This command differs from the
       +.I a
       +command only in the placement of the
       +text.
       +.PD
       +.TP
       +.RB (\| .,.+1 \|) \|j
       +Join the addressed lines into a single line;
       +intermediate newlines are deleted.
       +Dot is left at the resulting line.
       +.TP
       +.RB (\|\fL.\fP\|) \|k\fIx\fP
       +Mark the addressed line with name
       +.IR x ,
       +which must be a lower-case letter.
       +The address form
       +.BI \' x
       +then addresses this line.
       +.ne 2.5
       +.TP
       +.RB (\|\fL.,.\fP\|) \|l
       +List.
       +Print the addressed lines in an unambiguous way:
       +a tab is printed as
       +.LR \et ,
       +a backspace as
       +.LR \eb ,
       +backslashes as
       +.LR \e\e ,
       +and non-printing characters as
       +a backslash, an
       +.LR x ,
       +and four hexadecimal digits.
       +Long lines are folded,
       +with the second and subsequent sub-lines indented one tab stop.
       +If the last character in the line is a blank,
       +it is followed by
       +.LR \en .
       +An
       +.L l
       +may be appended, like
       +.LR p ,
       +to any non-I/O command.
       +.TP
       +.RB (\|\fL.,.\fP\|) \|m\fIa
       +Move.
       +Reposition the addressed lines after the line
       +addressed by
       +.IR a .
       +Dot is left at the last moved line.
       +.TP
       +.RB (\|\fL.,.\fP\|) \|n
       +Number.
       +Perform
       +.LR p ,
       +prefixing each line with its line number and a tab.
       +An
       +.L n
       +may be appended, like
       +.LR p ,
       +to any non-I/O command.
       +.TP
       +.RB (\|\fL.,.\fP\|) \|p
       +Print the addressed lines.
       +Dot is left at the last line printed.
       +A
       +.L p
       +appended to any non-I/O command causes the then current line
       +to be printed after the command is executed.
       +.TP
       +.RB (\|\fL.,.\fP\|) \|P
       +This command is a synonym for
       +.LR p .
       +.TP
       +.B q
       +Quit the editor.
       +No automatic write
       +of a file is done.
       +A
       +.L q
       +or
       +.L e
       +command is considered to be in error if the buffer has
       +been modified since the last
       +.LR w ,
       +.LR q ,
       +or
       +.L e
       +command.
       +.TP
       +.B Q
       +Quit unconditionally.
       +.TP
       +.RB ( $ )\|r\ \fIfilename\fP
       +Read in the given file after the addressed line.
       +If no
       +.I filename
       +is given, the remembered file name is used.
       +The file name is remembered if there were no
       +remembered file name already.
       +If the read is successful, the number of characters
       +read is printed.
       +Dot is left at the last line read from the file.
       +.TP
       +.RB (\|\fL.,.\fP\|) \|s\fIn\fP/\fIregular\ expression\fP/\fIreplacement\fP/
       +.PD 0
       +.TP
       +.RB (\|\fL.,.\fP\|) \|s\fIn\fP/\fIregular\ expression\fP/\fIreplacement\fP/g
       +.TP
       +.RB (\|\fL.,.\fP\|) \|s\fIn\fP/\fIregular\ expression\fP/\fIreplacement\fP
       +.PD
       +Substitute.
       +Search each addressed
       +line for an occurrence of the specified regular expression.
       +On each line in which
       +.I n
       +matches are found
       +.RI ( n
       +defaults to 1 if missing),
       +the
       +.IR n th
       +matched string is replaced by the replacement specified.
       +If the global replacement indicator 
       +.L g
       +appears after the command,
       +all subsequent matches on the line are also replaced.
       +It is an error for the substitution to fail on all addressed lines.
       +Any character other than space or newline
       +may be used instead of 
       +.L /
       +to delimit the regular expression
       +and the replacement.
       +Dot is left at the last line substituted.
       +The third form means
       +.BI s n / regular\ expression / replacement\fP/p\f1.
       +The second
       +.L /
       +may be omitted if the replacement is
       +empty.
       +.IP
       +An ampersand 
       +.L &
       +appearing in the replacement
       +is replaced by the string matching the regular expression.
       +The characters
       +.BI \e n\f1,
       +where
       +.I n
       +is a digit,
       +are replaced by the text matched by the
       +.IR n -th
       +regular subexpression
       +enclosed between
       +.L (
       +and
       +.LR ) .
       +When
       +nested parenthesized subexpressions
       +are present,
       +.I n
       +is determined by counting occurrences of
       +.L (
       +starting from the left.
       +.IP
       +A literal 
       +.LR & ,
       +.LR / ,
       +.L \e
       +or newline may be included in a replacement
       +by prefixing it with
       +.LR \e .
       +.TP
       +.RB (\|\fL.,.\fP\|) \|t\|\fIa
       +Transfer.
       +Copy the addressed lines 
       +after the line addressed by
       +.IR a .
       +Dot is left at the last line of the copy.
       +.TP
       +.RB (\|\fL.,.\fP\|) \|u
       +Undo.
       +Restore the preceding contents
       +of the first addressed line (sic), which must be the last line
       +in which a substitution was made (double sic).
       +.TP
       +.RB (\|\fL1,$\fP\|) \|v/\fIregular\ expression\fP/\fIcommand\ list\fP
       +This command is the same as the global command
       +.L g
       +except that the command list is executed with
       +dot initially set to every line
       +.I except
       +those
       +matching the regular expression.
       +.TP
       +.RB (\|\fL1,$\fP\|) \|w " \fIfilename\fP"
       +Write the addressed lines to
       +the given file.
       +If the file does not exist,
       +it is created with mode 666 (readable and writable by everyone).
       +If no
       +.I filename
       +is given, the remembered file name, if any, is used.
       +The file name is remembered if there were no 
       +remembered file name already.
       +Dot is unchanged.
       +If the write is successful, the number of characters written is
       +printed.
       +.TP
       +.RB (\|\fL1,$\fP\|) \|W " \fIfilename\fP"
       +Perform
       +.LR w ,
       +but append to, instead of overwriting, any existing file contents.
       +.TP
       +.RB ( $ ) \|=
       +Print the line number of the addressed line.
       +Dot is unchanged.
       +.TP
       +.BI ! shell\ command
       +Send the remainder of the line after the 
       +.L !
       +to
       +.IR rc (1)
       +to be interpreted as a command.
       +Dot is unchanged.
       +.TP
       +.RB (\| .+1 )\|<newline>
       +An address without a command is taken as a
       +.L p 
       +command.
       +A terminal
       +.L /
       +may be omitted from the address.
       +A blank line alone is equivalent to
       +.LR .+1p ;
       +it is useful
       +for stepping through text.
       +.PP
       +If an interrupt signal 
       +.SM (DEL)
       +is sent,
       +.I ed
       +prints a 
       +.L ?
       +and returns to its command level.
       +.PP
       +When reading a file,
       +.I ed
       +discards
       +.SM NUL
       +characters
       +and all characters after the last newline.
       +.SH FILES
       +.B /tmp/e*
       +.br
       +.B ed.hup
       +\ \ work is saved here if terminal hangs up
       +.SH SOURCE
       +.B \*9/src/cmd/ed.c
       +.SH "SEE ALSO"
       +.IR sam (1), 
       +.IR sed (1),
       +.IR regexp (7)
       +.SH DIAGNOSTICS
       +.BI ? name
       +for inaccessible file;
       +.L ?TMP
       +for temporary file overflow;
       +.L ?
       +for errors in commands or other overflows.
   DIR diff --git a/ed/ed.c b/ed/ed.c
       @@ -0,0 +1,1616 @@
       +/*
       + * Editor
       + */
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +#include <regexp.h>
       +
       +#undef EOF        /* stdio? */
       +
       +enum
       +{
       +        FNSIZE        = 128,                /* file name */
       +        LBSIZE        = 4096,                /* max line size */
       +        BLKSIZE        = 4096,                /* block size in temp file */
       +        NBLK        = 8191,                /* max size of temp file */
       +        ESIZE        = 256,                /* max size of reg exp */
       +        GBSIZE        = 256,                /* max size of global command */
       +        MAXSUB        = 9,                /* max number of sub reg exp */
       +        ESCFLG        = 0xFFFF,        /* escape Rune - user defined code */
       +        EOF        = -1
       +};
       +
       +void        (*oldhup)(int);
       +void        (*oldquit)(int);
       +int*        addr1;
       +int*        addr2;
       +int        anymarks;
       +int        col;
       +long        count;
       +int*        dol;
       +int*        dot;
       +int        fchange;
       +char        file[FNSIZE];
       +Rune        genbuf[LBSIZE];
       +int        given;
       +Rune*        globp;
       +int        iblock;
       +int        ichanged;
       +int        io;
       +Biobuf        iobuf;
       +int        lastc;
       +char        line[70];
       +Rune*        linebp;
       +Rune        linebuf[LBSIZE];
       +int        listf;
       +int        listn;
       +Rune*        loc1;
       +Rune*        loc2;
       +int        names[26];
       +int        nleft;
       +int        oblock;
       +int        oflag;
       +Reprog        *pattern;
       +int        peekc;
       +int        pflag;
       +int        rescuing;
       +Rune        rhsbuf[LBSIZE/sizeof(Rune)];
       +char        savedfile[FNSIZE];
       +jmp_buf        savej;
       +int        subnewa;
       +int        subolda;
       +Resub        subexp[MAXSUB];
       +char*        tfname;
       +int        tline;
       +int        waiting;
       +int        wrapp;
       +int*        zero;
       +
       +char        Q[]        = "";
       +char        T[]        = "TMP";
       +char        WRERR[]        = "WRITE ERROR";
       +int        bpagesize = 20;
       +char        hex[]        = "0123456789abcdef";
       +char*        linp        = line;
       +ulong        nlall = 128;
       +int        tfile        = -1;
       +int        vflag        = 1;
       +
       +void        add(int);
       +int*        address(void);
       +int        append(int(*)(void), int*);
       +void        browse(void);
       +void        callunix(void);
       +void        commands(void);
       +void        compile(int);
       +int        compsub(void);
       +void        dosub(void);
       +void        error(char*);
       +int        match(int*);
       +void        exfile(int);
       +void        filename(int);
       +Rune*        getblock(int, int);
       +int        getchr(void);
       +int        getcopy(void);
       +int        getfile(void);
       +Rune*        getline(int);
       +int        getnum(void);
       +int        getsub(void);
       +int        gettty(void);
       +void        global(int);
       +void        init(void);
       +void        join(void);
       +void        move(int);
       +void        newline(void);
       +void        nonzero(void);
       +void        notifyf(void*, char*);
       +Rune*        place(Rune*, Rune*, Rune*);
       +void        printcom(void);
       +void        putchr(int);
       +void        putd(void);
       +void        putfile(void);
       +int        putline(void);
       +void        putshst(Rune*);
       +void        putst(char*);
       +void        quit(void);
       +void        rdelete(int*, int*);
       +void        regerror(char *);
       +void        reverse(int*, int*);
       +void        setnoaddr(void);
       +void        setwide(void);
       +void        squeeze(int);
       +void        substitute(int);
       +
       +Rune La[] = { 'a', 0 };
       +Rune Lr[] = { 'r', 0 };
       +
       +char tmp[] = "/var/tmp/eXXXXX";
       +
       +void
       +main(int argc, char *argv[])
       +{
       +        char *p1, *p2;
       +
       +        notify(notifyf);
       +        ARGBEGIN {
       +        case 'o':
       +                oflag = 1;
       +                vflag = 0;
       +                break;
       +        } ARGEND
       +
       +        USED(argc);
       +        if(*argv && (strcmp(*argv, "-") == 0)) {
       +                argv++;
       +                vflag = 0;
       +        }
       +        if(oflag) {
       +                p1 = "/dev/stdout";
       +                p2 = savedfile;
       +                while(*p2++ = *p1++)
       +                        ;
       +                globp = La;
       +        } else
       +        if(*argv) {
       +                p1 = *argv;
       +                p2 = savedfile;
       +                while(*p2++ = *p1++)
       +                        if(p2 >= &savedfile[sizeof(savedfile)])
       +                                p2--;
       +                globp = Lr;
       +        }
       +        zero = malloc((nlall+5)*sizeof(int*));
       +        tfname = mktemp(tmp);
       +        init();
       +        setjmp(savej);
       +        commands();
       +        quit();
       +}
       +
       +void
       +commands(void)
       +{
       +        int *a1, c, temp;
       +        char lastsep;
       +        Dir *d;
       +
       +        for(;;) {
       +                if(pflag) {
       +                        pflag = 0;
       +                        addr1 = addr2 = dot;
       +                        printcom();
       +                }
       +                c = '\n';
       +                for(addr1 = 0;;) {
       +                        lastsep = c;
       +                        a1 = address();
       +                        c = getchr();
       +                        if(c != ',' && c != ';')
       +                                break;
       +                        if(lastsep == ',')
       +                                error(Q);
       +                        if(a1 == 0) {
       +                                a1 = zero+1;
       +                                if(a1 > dol)
       +                                        a1--;
       +                        }
       +                        addr1 = a1;
       +                        if(c == ';')
       +                                dot = a1;
       +                }
       +                if(lastsep != '\n' && a1 == 0)
       +                        a1 = dol;
       +                if((addr2=a1) == 0) {
       +                        given = 0;
       +                        addr2 = dot;        
       +                } else
       +                        given = 1;
       +                if(addr1 == 0)
       +                        addr1 = addr2;
       +                switch(c) {
       +
       +                case 'a':
       +                        add(0);
       +                        continue;
       +
       +                case 'b':
       +                        nonzero();
       +                        browse();
       +                        continue;
       +
       +                case 'c':
       +                        nonzero();
       +                        newline();
       +                        rdelete(addr1, addr2);
       +                        append(gettty, addr1-1);
       +                        continue;
       +
       +                case 'd':
       +                        nonzero();
       +                        newline();
       +                        rdelete(addr1, addr2);
       +                        continue;
       +
       +                case 'E':
       +                        fchange = 0;
       +                        c = 'e';
       +                case 'e':
       +                        setnoaddr();
       +                        if(vflag && fchange) {
       +                                fchange = 0;
       +                                error(Q);
       +                        }
       +                        filename(c);
       +                        init();
       +                        addr2 = zero;
       +                        goto caseread;
       +
       +                case 'f':
       +                        setnoaddr();
       +                        filename(c);
       +                        putst(savedfile);
       +                        continue;
       +
       +                case 'g':
       +                        global(1);
       +                        continue;
       +
       +                case 'i':
       +                        add(-1);
       +                        continue;
       +
       +
       +                case 'j':
       +                        if(!given)
       +                                addr2++;
       +                        newline();
       +                        join();
       +                        continue;
       +
       +                case 'k':
       +                        nonzero();
       +                        c = getchr();
       +                        if(c < 'a' || c > 'z')
       +                                error(Q);
       +                        newline();
       +                        names[c-'a'] = *addr2 & ~01;
       +                        anymarks |= 01;
       +                        continue;
       +
       +                case 'm':
       +                        move(0);
       +                        continue;
       +
       +                case 'n':
       +                        listn++;
       +                        newline();
       +                        printcom();
       +                        continue;
       +
       +                case '\n':
       +                        if(a1==0) {
       +                                a1 = dot+1;
       +                                addr2 = a1;
       +                                addr1 = a1;
       +                        }
       +                        if(lastsep==';')
       +                                addr1 = a1;
       +                        printcom();
       +                        continue;
       +
       +                case 'l':
       +                        listf++;
       +                case 'p':
       +                case 'P':
       +                        newline();
       +                        printcom();
       +                        continue;
       +
       +                case 'Q':
       +                        fchange = 0;
       +                case 'q':
       +                        setnoaddr();
       +                        newline();
       +                        quit();
       +
       +                case 'r':
       +                        filename(c);
       +                caseread:
       +                        if((io=open(file, OREAD)) < 0) {
       +                                lastc = '\n';
       +                                error(file);
       +                        }
       +                        if((d = dirfstat(io)) != nil){
       +                                if(d->mode & DMAPPEND)
       +                                        print("warning: %s is append only\n", file);
       +                                free(d);
       +                        }
       +                        Binit(&iobuf, io, OREAD);
       +                        setwide();
       +                        squeeze(0);
       +                        c = zero != dol;
       +                        append(getfile, addr2);
       +                        exfile(OREAD);
       +
       +                        fchange = c;
       +                        continue;
       +
       +                case 's':
       +                        nonzero();
       +                        substitute(globp != 0);
       +                        continue;
       +
       +                case 't':
       +                        move(1);
       +                        continue;
       +
       +                case 'u':
       +                        nonzero();
       +                        newline();
       +                        if((*addr2&~01) != subnewa)
       +                                error(Q);
       +                        *addr2 = subolda;
       +                        dot = addr2;
       +                        continue;
       +
       +                case 'v':
       +                        global(0);
       +                        continue;
       +
       +                case 'W':
       +                        wrapp++;
       +                case 'w':
       +                        setwide();
       +                        squeeze(dol>zero);
       +                        temp = getchr();
       +                        if(temp != 'q' && temp != 'Q') {
       +                                peekc = temp;
       +                                temp = 0;
       +                        }
       +                        filename(c);
       +                        if(!wrapp ||
       +                          ((io = open(file, OWRITE)) == -1) ||
       +                          ((seek(io, 0L, 2)) == -1))
       +                                if((io = create(file, OWRITE, 0666)) < 0)
       +                                        error(file);
       +                        Binit(&iobuf, io, OWRITE);
       +                        wrapp = 0;
       +                        if(dol > zero)
       +                                putfile();
       +                        exfile(OWRITE);
       +                        if(addr1<=zero+1 && addr2==dol)
       +                                fchange = 0;
       +                        if(temp == 'Q')
       +                                fchange = 0;
       +                        if(temp)
       +                                quit();
       +                        continue;
       +
       +                case '=':
       +                        setwide();
       +                        squeeze(0);
       +                        newline();
       +                        count = addr2 - zero;
       +                        putd();
       +                        putchr('\n');
       +                        continue;
       +
       +                case '!':
       +                        callunix();
       +                        continue;
       +
       +                case EOF:
       +                        return;
       +
       +                }
       +                error(Q);
       +        }
       +}
       +
       +void
       +printcom(void)
       +{
       +        int *a1;
       +
       +        nonzero();
       +        a1 = addr1;
       +        do {
       +                if(listn) {
       +                        count = a1-zero;
       +                        putd();
       +                        putchr('\t');
       +                }
       +                putshst(getline(*a1++));
       +        } while(a1 <= addr2);
       +        dot = addr2;
       +        listf = 0;
       +        listn = 0;
       +        pflag = 0;
       +}
       +
       +int*
       +address(void)
       +{
       +        int sign, *a, opcnt, nextopand, *b, c;
       +
       +        nextopand = -1;
       +        sign = 1;
       +        opcnt = 0;
       +        a = dot;
       +        do {
       +                do {
       +                        c = getchr();
       +                } while(c == ' ' || c == '\t');
       +                if(c >= '0' && c <= '9') {
       +                        peekc = c;
       +                        if(!opcnt)
       +                                a = zero;
       +                        a += sign*getnum();
       +                } else
       +                switch(c) {
       +                case '$':
       +                        a = dol;
       +                case '.':
       +                        if(opcnt)
       +                                error(Q);
       +                        break;
       +                case '\'':
       +                        c = getchr();
       +                        if(opcnt || c < 'a' || c > 'z')
       +                                error(Q);
       +                        a = zero;
       +                        do {
       +                                a++;
       +                        } while(a <= dol && names[c-'a'] != (*a & ~01));
       +                        break;
       +                case '?':
       +                        sign = -sign;
       +                case '/':
       +                        compile(c);
       +                        b = a;
       +                        for(;;) {
       +                                a += sign;
       +                                if(a <= zero)
       +                                        a = dol;
       +                                if(a > dol)
       +                                        a = zero;
       +                                if(match(a))
       +                                        break;
       +                                if(a == b)
       +                                        error(Q);
       +                        }
       +                        break;
       +                default:
       +                        if(nextopand == opcnt) {
       +                                a += sign;
       +                                if(a < zero || dol < a)
       +                                        continue;       /* error(Q); */
       +                        }
       +                        if(c != '+' && c != '-' && c != '^') {
       +                                peekc = c;
       +                                if(opcnt == 0)
       +                                        a = 0;
       +                                return a;
       +                        }
       +                        sign = 1;
       +                        if(c != '+')
       +                                sign = -sign;
       +                        nextopand = ++opcnt;
       +                        continue;
       +                }
       +                sign = 1;
       +                opcnt++;
       +        } while(zero <= a && a <= dol);
       +        error(Q);
       +        return 0;
       +}
       +
       +int
       +getnum(void)
       +{
       +        int r, c;
       +
       +        r = 0;
       +        for(;;) {
       +                c = getchr();
       +                if(c < '0' || c > '9')
       +                        break;
       +                r = r*10 + (c-'0');
       +        }
       +        peekc = c;
       +        return r;
       +}
       +
       +void
       +setwide(void)
       +{
       +        if(!given) {
       +                addr1 = zero + (dol>zero);
       +                addr2 = dol;
       +        }
       +}
       +
       +void
       +setnoaddr(void)
       +{
       +        if(given)
       +                error(Q);
       +}
       +
       +void
       +nonzero(void)
       +{
       +        squeeze(1);
       +}
       +
       +void
       +squeeze(int i)
       +{
       +        if(addr1 < zero+i || addr2 > dol || addr1 > addr2)
       +                error(Q);
       +}
       +
       +void
       +newline(void)
       +{
       +        int c;
       +
       +        c = getchr();
       +        if(c == '\n' || c == EOF)
       +                return;
       +        if(c == 'p' || c == 'l' || c == 'n') {
       +                pflag++;
       +                if(c == 'l')
       +                        listf++;
       +                else
       +                if(c == 'n')
       +                        listn++;
       +                c = getchr();
       +                if(c == '\n')
       +                        return;
       +        }
       +        error(Q);
       +}
       +
       +void
       +filename(int comm)
       +{
       +        char *p1, *p2;
       +        Rune rune;
       +        int c;
       +
       +        count = 0;
       +        c = getchr();
       +        if(c == '\n' || c == EOF) {
       +                p1 = savedfile;
       +                if(*p1 == 0 && comm != 'f')
       +                        error(Q);
       +                p2 = file;
       +                while(*p2++ = *p1++)
       +                        ;
       +                return;
       +        }
       +        if(c != ' ')
       +                error(Q);
       +        while((c=getchr()) == ' ')
       +                ;
       +        if(c == '\n')
       +                error(Q);
       +        p1 = file;
       +        do {
       +                if(p1 >= &file[sizeof(file)-6] || c == ' ' || c == EOF)
       +                        error(Q);
       +                rune = c;
       +                p1 += runetochar(p1, &rune);
       +        } while((c=getchr()) != '\n');
       +        *p1 = 0;
       +        if(savedfile[0] == 0 || comm == 'e' || comm == 'f') {
       +                p1 = savedfile;
       +                p2 = file;
       +                while(*p1++ = *p2++)
       +                        ;
       +        }
       +}
       +
       +void
       +exfile(int om)
       +{
       +
       +        if(om == OWRITE)
       +                if(Bflush(&iobuf) < 0)
       +                        error(Q);
       +        close(io);
       +        io = -1;
       +        if(vflag) {
       +                putd();
       +                putchr('\n');
       +        }
       +}
       +
       +void
       +error1(char *s)
       +{
       +        int c;
       +
       +        wrapp = 0;
       +        listf = 0;
       +        listn = 0;
       +        count = 0;
       +        seek(0, 0, 2);
       +        pflag = 0;
       +        if(globp)
       +                lastc = '\n';
       +        globp = 0;
       +        peekc = lastc;
       +        if(lastc)
       +                for(;;) {
       +                        c = getchr();
       +                        if(c == '\n' || c == EOF)
       +                                break;
       +                }
       +        if(io > 0) {
       +                close(io);
       +                io = -1;
       +        }
       +        putchr('?');
       +        putst(s);
       +}
       +
       +void
       +error(char *s)
       +{
       +        error1(s);
       +        longjmp(savej, 1);
       +}
       +
       +void
       +rescue(void)
       +{
       +        rescuing = 1;
       +        if(dol > zero) {
       +                addr1 = zero+1;
       +                addr2 = dol;
       +                io = create("ed.hup", OWRITE, 0666);
       +                if(io > 0){
       +                        Binit(&iobuf, io, OWRITE);
       +                        putfile();
       +                }
       +        }
       +        fchange = 0;
       +        quit();
       +}
       +
       +void
       +notifyf(void *a, char *s)
       +{
       +        if(strcmp(s, "interrupt") == 0){
       +                if(rescuing || waiting)
       +                        noted(NCONT);
       +                putchr('\n');
       +                lastc = '\n';
       +                error1(Q);
       +                notejmp(a, savej, 0);
       +        }
       +        if(strcmp(s, "hangup") == 0 || strcmp(s, "kill") == 0){
       +                if(rescuing)
       +                        noted(NDFLT);
       +                rescue();
       +        }
       +        if(strstr(s, "child"))
       +                noted(NCONT);
       +        fprint(2, "ed: note: %s\n", s);
       +        abort();
       +}
       +
       +int
       +getchr(void)
       +{
       +        char s[UTFmax];
       +        int i;
       +        Rune r;
       +
       +        if(lastc = peekc) {
       +                peekc = 0;
       +                return lastc;
       +        }
       +        if(globp) {
       +                if((lastc=*globp++) != 0)
       +                        return lastc;
       +                globp = 0;
       +                return EOF;
       +        }
       +        for(i=0;;) {
       +                if(read(0, s+i, 1) <= 0)
       +                        return lastc = EOF;
       +                i++;
       +                if(fullrune(s, i))
       +                        break;
       +                
       +        }
       +        chartorune(&r, s);
       +        lastc = r;
       +        return lastc;
       +}
       +
       +int
       +gety(void)
       +{
       +        int c;
       +        Rune *gf, *p;
       +
       +        p = linebuf;
       +        gf = globp;
       +        for(;;) {
       +                c = getchr();
       +                if(c == '\n') {
       +                        *p = 0;
       +                        return 0;
       +                }
       +                if(c == EOF) {
       +                        if(gf)
       +                                peekc = c;
       +                        return c;
       +                }
       +                if(c == 0)
       +                        continue;
       +                *p++ = c;
       +                if(p >= &linebuf[LBSIZE-2])
       +                        error(Q);
       +        }
       +}
       +
       +int
       +gettty(void)
       +{
       +        int rc;
       +
       +        rc = gety();
       +        if(rc)
       +                return rc;
       +        if(linebuf[0] == '.' && linebuf[1] == 0)
       +                return EOF;
       +        return 0;
       +}
       +
       +int
       +getfile(void)
       +{
       +        int c;
       +        Rune *lp;
       +
       +        lp = linebuf;
       +        do {
       +                c = Bgetrune(&iobuf);
       +                if(c < 0) {
       +                        if(lp > linebuf) {
       +                                putst("'\\n' appended");
       +                                c = '\n';
       +                        } else
       +                                return EOF;
       +                }
       +                if(lp >= &linebuf[LBSIZE]) {
       +                        lastc = '\n';
       +                        error(Q);
       +                }
       +                *lp++ = c;
       +                count++;
       +        } while(c != '\n');
       +        lp[-1] = 0;
       +        return 0;
       +}
       +
       +void
       +putfile(void)
       +{
       +        int *a1;
       +        Rune *lp;
       +        long c;
       +
       +        a1 = addr1;
       +        do {
       +                lp = getline(*a1++);
       +                for(;;) {
       +                        count++;
       +                        c = *lp++;
       +                        if(c == 0) {
       +                                if(Bputrune(&iobuf, '\n') < 0)
       +                                        error(Q);
       +                                break;
       +                        }
       +                        if(Bputrune(&iobuf, c) < 0)
       +                                error(Q);
       +                }
       +        } while(a1 <= addr2);
       +        if(Bflush(&iobuf) < 0)
       +                error(Q);
       +}
       +
       +int
       +append(int (*f)(void), int *a)
       +{
       +        int *a1, *a2, *rdot, nline, d;
       +
       +        nline = 0;
       +        dot = a;
       +        while((*f)() == 0) {
       +                if((dol-zero) >= nlall) {
       +                        nlall += 512;
       +                        a1 = realloc(zero, (nlall+50)*sizeof(int*));
       +                        if(a1 == 0) {
       +                                error("MEM?");
       +                                rescue();
       +                        }
       +                        /* relocate pointers; avoid wraparound if sizeof(int) < sizeof(int*) */
       +                        d = addr1 - zero;
       +                        addr1 = a1 + d;
       +                        d = addr2 - zero;
       +                        addr2 = a1 + d;
       +                        d = dol - zero;
       +                        dol = a1 + d;
       +                        d = dot - zero;
       +                        dot = a1 + d;
       +                        zero = a1;
       +                }
       +                d = putline();
       +                nline++;
       +                a1 = ++dol;
       +                a2 = a1+1;
       +                rdot = ++dot;
       +                while(a1 > rdot)
       +                        *--a2 = *--a1;
       +                *rdot = d;
       +        }
       +        return nline;
       +}
       +
       +void
       +add(int i)
       +{
       +        if(i && (given || dol > zero)) {
       +                addr1--;
       +                addr2--;
       +        }
       +        squeeze(0);
       +        newline();
       +        append(gettty, addr2);
       +}
       +
       +void
       +browse(void)
       +{
       +        int forward, n;
       +        static int bformat, bnum; /* 0 */
       +
       +        forward = 1;
       +        peekc = getchr();
       +        if(peekc != '\n'){
       +                if(peekc == '-' || peekc == '+') {
       +                        if(peekc == '-')
       +                                forward = 0;
       +                        getchr();
       +                }
       +                n = getnum();
       +                if(n > 0)
       +                        bpagesize = n;
       +        }
       +        newline();
       +        if(pflag) {
       +                bformat = listf;
       +                bnum = listn;
       +        } else {
       +                listf = bformat;
       +                listn = bnum;
       +        }
       +        if(forward) {
       +                addr1 = addr2;
       +                addr2 += bpagesize;
       +                if(addr2 > dol)
       +                        addr2 = dol;
       +        } else {
       +                addr1 = addr2-bpagesize;
       +                if(addr1 <= zero)
       +                        addr1 = zero+1;
       +        }
       +        printcom();
       +}
       +
       +void
       +callunix(void)
       +{
       +        int c, pid;
       +        Rune rune;
       +        char buf[512];
       +        char *p;
       +
       +        setnoaddr();
       +        p = buf;
       +        while((c=getchr()) != EOF && c != '\n')
       +                if(p < &buf[sizeof(buf) - 6]) {
       +                        rune = c;
       +                        p += runetochar(p, &rune);
       +                }
       +        *p = 0;
       +        pid = fork();
       +        if(pid == 0) {
       +                execlp("rc", "rc", "-c", buf, (char*)0);
       +                sysfatal("exec failed: %r");
       +                exits("execl failed");
       +        }
       +        waiting = 1;
       +        while(waitpid() != pid)
       +                ;
       +        waiting = 0;
       +        if(vflag)
       +                putst("!");
       +}
       +
       +void
       +quit(void)
       +{
       +        if(vflag && fchange && dol!=zero) {
       +                fchange = 0;
       +                error(Q);
       +        }
       +        remove(tfname);
       +        exits(0);
       +}
       +
       +void
       +onquit(int sig)
       +{
       +        USED(sig);
       +        quit();
       +}
       +
       +void
       +rdelete(int *ad1, int *ad2)
       +{
       +        int *a1, *a2, *a3;
       +
       +        a1 = ad1;
       +        a2 = ad2+1;
       +        a3 = dol;
       +        dol -= a2 - a1;
       +        do {
       +                *a1++ = *a2++;
       +        } while(a2 <= a3);
       +        a1 = ad1;
       +        if(a1 > dol)
       +                a1 = dol;
       +        dot = a1;
       +        fchange = 1;
       +}
       +
       +void
       +gdelete(void)
       +{
       +        int *a1, *a2, *a3;
       +
       +        a3 = dol;
       +        for(a1=zero; (*a1&01)==0; a1++)
       +                if(a1>=a3)
       +                        return;
       +        for(a2=a1+1; a2<=a3;) {
       +                if(*a2 & 01) {
       +                        a2++;
       +                        dot = a1;
       +                } else
       +                        *a1++ = *a2++;
       +        }
       +        dol = a1-1;
       +        if(dot > dol)
       +                dot = dol;
       +        fchange = 1;
       +}
       +
       +Rune*
       +getline(int tl)
       +{
       +        Rune *lp, *bp;
       +        int nl;
       +
       +        lp = linebuf;
       +        bp = getblock(tl, OREAD);
       +        nl = nleft;
       +        tl &= ~((BLKSIZE/sizeof(Rune)) - 1);
       +        while(*lp++ = *bp++) {
       +                nl -= sizeof(Rune);
       +                if(nl == 0) {
       +                        bp = getblock(tl += BLKSIZE/sizeof(Rune), OREAD);
       +                        nl = nleft;
       +                }
       +        }
       +        return linebuf;
       +}
       +
       +int
       +putline(void)
       +{
       +        Rune *lp, *bp;
       +        int nl, tl;
       +
       +        fchange = 1;
       +        lp = linebuf;
       +        tl = tline;
       +        bp = getblock(tl, OWRITE);
       +        nl = nleft;
       +        tl &= ~((BLKSIZE/sizeof(Rune))-1);
       +        while(*bp = *lp++) {
       +                if(*bp++ == '\n') {
       +                        bp[-1] = 0;
       +                        linebp = lp;
       +                        break;
       +                }
       +                nl -= sizeof(Rune);
       +                if(nl == 0) {
       +                        tl += BLKSIZE/sizeof(Rune);
       +                        bp = getblock(tl, OWRITE);
       +                        nl = nleft;
       +                }
       +        }
       +        nl = tline;
       +        tline += ((lp-linebuf) + 03) & 077776;
       +        return nl;
       +}
       +
       +void
       +blkio(int b, uchar *buf, int isread)
       +{
       +        int n;
       +
       +        seek(tfile, b*BLKSIZE, 0);
       +        if(isread)
       +                n = read(tfile, buf, BLKSIZE);
       +        else
       +                n = write(tfile, buf, BLKSIZE);
       +        if(n != BLKSIZE)
       +                error(T);
       +}
       +
       +Rune*
       +getblock(int atl, int iof)
       +{
       +        int bno, off;
       +        
       +        static uchar ibuff[BLKSIZE];
       +        static uchar obuff[BLKSIZE];
       +
       +        bno = atl / (BLKSIZE/sizeof(Rune));
       +        off = (atl*sizeof(Rune)) & (BLKSIZE-1) & ~03;
       +        if(bno >= NBLK) {
       +                lastc = '\n';
       +                error(T);
       +        }
       +        nleft = BLKSIZE - off;
       +        if(bno == iblock) {
       +                ichanged |= iof;
       +                return (Rune*)(ibuff+off);
       +        }
       +        if(bno == oblock)
       +                return (Rune*)(obuff+off);
       +        if(iof == OREAD) {
       +                if(ichanged)
       +                        blkio(iblock, ibuff, 0);
       +                ichanged = 0;
       +                iblock = bno;
       +                blkio(bno, ibuff, 1);
       +                return (Rune*)(ibuff+off);
       +        }
       +        if(oblock >= 0)
       +                blkio(oblock, obuff, 0);
       +        oblock = bno;
       +        return (Rune*)(obuff+off);
       +}
       +
       +void
       +init(void)
       +{
       +        int *markp;
       +
       +        close(tfile);
       +        tline = 2;
       +        for(markp = names; markp < &names[26]; )
       +                *markp++ = 0;
       +        subnewa = 0;
       +        anymarks = 0;
       +        iblock = -1;
       +        oblock = -1;
       +        ichanged = 0;
       +        if((tfile = create(tfname, ORDWR, 0600)) < 0){
       +                error1(T);
       +                exits(0);
       +        }
       +        dot = dol = zero;
       +}
       +
       +void
       +global(int k)
       +{
       +        Rune *gp, globuf[GBSIZE];
       +        int c, *a1;
       +
       +        if(globp)
       +                error(Q);
       +        setwide();
       +        squeeze(dol > zero);
       +        c = getchr();
       +        if(c == '\n')
       +                error(Q);
       +        compile(c);
       +        gp = globuf;
       +        while((c=getchr()) != '\n') {
       +                if(c == EOF)
       +                        error(Q);
       +                if(c == '\\') {
       +                        c = getchr();
       +                        if(c != '\n')
       +                                *gp++ = '\\';
       +                }
       +                *gp++ = c;
       +                if(gp >= &globuf[GBSIZE-2])
       +                        error(Q);
       +        }
       +        if(gp == globuf)
       +                *gp++ = 'p';
       +        *gp++ = '\n';
       +        *gp = 0;
       +        for(a1=zero; a1<=dol; a1++) {
       +                *a1 &= ~01;
       +                if(a1 >= addr1 && a1 <= addr2 && match(a1) == k)
       +                        *a1 |= 01;
       +        }
       +
       +        /*
       +         * Special case: g/.../d (avoid n^2 algorithm)
       +         */
       +        if(globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == 0) {
       +                gdelete();
       +                return;
       +        }
       +        for(a1=zero; a1<=dol; a1++) {
       +                if(*a1 & 01) {
       +                        *a1 &= ~01;
       +                        dot = a1;
       +                        globp = globuf;
       +                        commands();
       +                        a1 = zero;
       +                }
       +        }
       +}
       +
       +void
       +join(void)
       +{
       +        Rune *gp, *lp;
       +        int *a1;
       +
       +        nonzero();
       +        gp = genbuf;
       +        for(a1=addr1; a1<=addr2; a1++) {
       +                lp = getline(*a1);
       +                while(*gp = *lp++)
       +                        if(gp++ >= &genbuf[LBSIZE-2])
       +                                error(Q);
       +        }
       +        lp = linebuf;
       +        gp = genbuf;
       +        while(*lp++ = *gp++)
       +                ;
       +        *addr1 = putline();
       +        if(addr1 < addr2)
       +                rdelete(addr1+1, addr2);
       +        dot = addr1;
       +}
       +
       +void
       +substitute(int inglob)
       +{
       +        int *mp, *a1, nl, gsubf, n;
       +
       +        n = getnum();        /* OK even if n==0 */
       +        gsubf = compsub();
       +        for(a1 = addr1; a1 <= addr2; a1++) {
       +                if(match(a1)){
       +                        int *ozero;
       +                        int m = n;
       +
       +                        do {
       +                                int span = loc2-loc1;
       +
       +                                if(--m <= 0) {
       +                                        dosub();
       +                                        if(!gsubf)
       +                                                break;
       +                                        if(span == 0) {        /* null RE match */
       +                                                if(*loc2 == 0)
       +                                                        break;
       +                                                loc2++;
       +                                        }
       +                                }
       +                        } while(match(0));
       +                        if(m <= 0) {
       +                                inglob |= 01;
       +                                subnewa = putline();
       +                                *a1 &= ~01;
       +                                if(anymarks) {
       +                                        for(mp=names; mp<&names[26]; mp++)
       +                                                if(*mp == *a1)
       +                                                        *mp = subnewa;
       +                                }
       +                                subolda = *a1;
       +                                *a1 = subnewa;
       +                                ozero = zero;
       +                                nl = append(getsub, a1);
       +                                addr2 += nl;
       +                                nl += zero-ozero;
       +                                a1 += nl;
       +                        }
       +                }
       +        }
       +        if(inglob == 0)
       +                error(Q);
       +}
       +
       +int
       +compsub(void)
       +{
       +        int seof, c;
       +        Rune *p;
       +
       +        seof = getchr();
       +        if(seof == '\n' || seof == ' ')
       +                error(Q);
       +        compile(seof);
       +        p = rhsbuf;
       +        for(;;) {
       +                c = getchr();
       +                if(c == '\\') {
       +                        c = getchr();
       +                        *p++ = ESCFLG;
       +                        if(p >= &rhsbuf[LBSIZE/sizeof(Rune)])
       +                                error(Q);
       +                } else
       +                if(c == '\n' && (!globp || !globp[0])) {
       +                        peekc = c;
       +                        pflag++;
       +                        break;
       +                } else
       +                if(c == seof)
       +                        break;
       +                *p++ = c;
       +                if(p >= &rhsbuf[LBSIZE/sizeof(Rune)])
       +                        error(Q);
       +        }
       +        *p = 0;
       +        peekc = getchr();
       +        if(peekc == 'g') {
       +                peekc = 0;
       +                newline();
       +                return 1;
       +        }
       +        newline();
       +        return 0;
       +}
       +
       +int
       +getsub(void)
       +{
       +        Rune *p1, *p2;
       +
       +        p1 = linebuf;
       +        if((p2 = linebp) == 0)
       +                return EOF;
       +        while(*p1++ = *p2++)
       +                ;
       +        linebp = 0;
       +        return 0;
       +}
       +
       +void
       +dosub(void)
       +{
       +        Rune *lp, *sp, *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 == ESCFLG && (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;
       +                        }
       +                        error(Q);
       +                }
       +                *sp++ = c;
       +                if(sp >= &genbuf[LBSIZE])
       +                        error(Q);
       +        }
       +        lp = loc2;
       +        loc2 = sp - genbuf + linebuf;
       +        while(*sp++ = *lp++)
       +                if(sp >= &genbuf[LBSIZE])
       +                        error(Q);
       +        lp = linebuf;
       +        sp = genbuf;
       +        while(*lp++ = *sp++)
       +                ;
       +}
       +
       +Rune*
       +place(Rune *sp, Rune *l1, Rune *l2)
       +{
       +
       +        while(l1 < l2) {
       +                *sp++ = *l1++;
       +                if(sp >= &genbuf[LBSIZE])
       +                        error(Q);
       +        }
       +        return sp;
       +}
       +
       +void
       +move(int cflag)
       +{
       +        int *adt, *ad1, *ad2;
       +
       +        nonzero();
       +        if((adt = address())==0)        /* address() guarantees addr is in range */
       +                error(Q);
       +        newline();
       +        if(cflag) {
       +                int *ozero, delta;
       +                ad1 = dol;
       +                ozero = zero;
       +                append(getcopy, ad1++);
       +                ad2 = dol;
       +                delta = zero - ozero;
       +                ad1 += delta;
       +                adt += delta;
       +        } else {
       +                ad2 = addr2;
       +                for(ad1 = addr1; ad1 <= ad2;)
       +                        *ad1++ &= ~01;
       +                ad1 = addr1;
       +        }
       +        ad2++;
       +        if(adt<ad1) {
       +                dot = adt + (ad2-ad1);
       +                if((++adt)==ad1)
       +                        return;
       +                reverse(adt, ad1);
       +                reverse(ad1, ad2);
       +                reverse(adt, ad2);
       +        } else
       +        if(adt >= ad2) {
       +                dot = adt++;
       +                reverse(ad1, ad2);
       +                reverse(ad2, adt);
       +                reverse(ad1, adt);
       +        } else
       +                error(Q);
       +        fchange = 1;
       +}
       +
       +void
       +reverse(int *a1, int *a2)
       +{
       +        int t;
       +
       +        for(;;) {
       +                t = *--a2;
       +                if(a2 <= a1)
       +                        return;
       +                *a2 = *a1;
       +                *a1++ = t;
       +        }
       +}
       +
       +int
       +getcopy(void)
       +{
       +        if(addr1 > addr2)
       +                return EOF;
       +        getline(*addr1++);
       +        return 0;
       +}
       +
       +void
       +compile(int eof)
       +{
       +        Rune c;
       +        char *ep;
       +        char expbuf[ESIZE];
       +
       +        if((c = getchr()) == '\n') {
       +                peekc = c;
       +                c = eof;
       +        }
       +        if(c == eof) {
       +                if(!pattern)
       +                        error(Q);
       +                return;
       +        }
       +        if(pattern) {
       +                free(pattern);
       +                pattern = 0;
       +        }
       +        ep = expbuf;
       +        do {
       +                if(c == '\\') {
       +                        if(ep >= expbuf+sizeof(expbuf)) {
       +                                error(Q);
       +                                return;
       +                        }
       +                        ep += runetochar(ep, &c);
       +                        if((c = getchr()) == '\n') {
       +                                error(Q);
       +                                return;
       +                        }
       +                }
       +                if(ep >= expbuf+sizeof(expbuf)) {
       +                        error(Q);
       +                        return;
       +                }
       +                ep += runetochar(ep, &c);
       +        } while((c = getchr()) != eof && c != '\n');
       +        if(c == '\n')
       +                peekc = c;
       +        *ep = 0;
       +        pattern = regcomp(expbuf);
       +}
       +
       +int
       +match(int *addr)
       +{
       +        if(!pattern)
       +                return 0;
       +        if(addr){
       +                if(addr == zero)
       +                        return 0;
       +                subexp[0].s.rsp = getline(*addr);
       +        } else
       +                subexp[0].s.rsp = loc2;
       +        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;
       +        
       +}
       +
       +void
       +putd(void)
       +{
       +        int r;
       +
       +        r = count%10;
       +        count /= 10;
       +        if(count)
       +                putd();
       +        putchr(r + '0');
       +}
       +
       +void
       +putst(char *sp)
       +{
       +        Rune r;
       +
       +        col = 0;
       +        for(;;) {
       +                sp += chartorune(&r, sp);
       +                if(r == 0)
       +                        break;
       +                putchr(r);
       +        }
       +        putchr('\n');
       +}
       +
       +void
       +putshst(Rune *sp)
       +{
       +        col = 0;
       +        while(*sp)
       +                putchr(*sp++);
       +        putchr('\n');
       +}
       +
       +void
       +putchr(int ac)
       +{
       +        char *lp;
       +        int c;
       +        Rune rune;
       +
       +        lp = linp;
       +        c = ac;
       +        if(listf) {
       +                if(c == '\n') {
       +                        if(linp != line && linp[-1] == ' ') {
       +                                *lp++ = '\\';
       +                                *lp++ = 'n';
       +                        }
       +                } else {
       +                        if(col > (72-6-2)) {
       +                                col = 8;
       +                                *lp++ = '\\';
       +                                *lp++ = '\n';
       +                                *lp++ = '\t';
       +                        }
       +                        col++;
       +                        if(c=='\b' || c=='\t' || c=='\\') {
       +                                *lp++ = '\\';
       +                                if(c == '\b')
       +                                        c = 'b';
       +                                else
       +                                if(c == '\t')
       +                                        c = 't';
       +                                col++;
       +                        } else
       +                        if(c<' ' || c>='\177') {
       +                                *lp++ = '\\';
       +                                *lp++ = 'x';
       +                                *lp++ =  hex[c>>12];
       +                                *lp++ =  hex[c>>8&0xF];
       +                                *lp++ =  hex[c>>4&0xF];
       +                                c     =  hex[c&0xF];
       +                                col += 5;
       +                        }
       +                }
       +        }
       +
       +        rune = c;
       +        lp += runetochar(lp, &rune);
       +
       +        if(c == '\n' || lp >= &line[sizeof(line)-5]) {
       +                linp = line;
       +                write(oflag? 2: 1, line, lp-line);
       +                return;
       +        }
       +        linp = lp;
       +}
       +
       +char*
       +mktemp(char *as)
       +{
       +        char *s;
       +        unsigned pid;
       +        int i;
       +
       +        pid = getpid();
       +        s = as;
       +        while(*s++)
       +                ;
       +        s--;
       +        while(*--s == 'X') {
       +                *s = pid % 10 + '0';
       +                pid /= 10;
       +        }
       +        s++;
       +        i = 'a';
       +        while(access(as, 0) != -1) {
       +                if(i == 'z')
       +                        return "/";
       +                *s = i++;
       +        }
       +        return as;
       +}
       +
       +void
       +regerror(char *s)
       +{
       +        USED(s);
       +        error(Q);
       +}
   DIR diff --git a/lib9/Makefile b/lib9/Makefile
       @@ -13,10 +13,16 @@ TARG=lib9
        #        convM2S.o
        #        convS2M.o
        
       +SECFILES=\
       +        sec/sha1block.o\
       +        sec/sha1.o\
       +        sec/sha1pickle.o\
       +
        NUM=\
                fmt/charstod.o\
                fmt/pow10.o\
        
       +
        FMTOFILES=\
                fmt/dofmt.o\
                fmt/fltfmt.o\
       @@ -193,6 +199,7 @@ LIB9OFILES=\
                zoneinfo.o\
        
        OFILES=\
       +        $(SECFILES)\
                $(FMTOFILES)\
                $(UTFOFILES)\
                $(BIOFILES)\
       @@ -212,7 +219,7 @@ ${LIB}: ${OFILES}
        
        .c.o:
                @echo CC $*.c
       -        @${CC} -o $*.o ${CFLAGS} -I${PREFIX}/include $*.c
       +        @${CC} -o $*.o ${CFLAGS} -Isec -I${PREFIX}/include $*.c
        
        clean:
                rm -f ${OFILES} ${LIB}
   DIR diff --git a/lib9/sec/libsec.h b/lib9/sec/libsec.h
       @@ -0,0 +1,366 @@
       +#ifndef _LIBSEC_H_
       +#define _LIBSEC_H_ 1
       +#if defined(__cplusplus)
       +extern "C" { 
       +#endif
       +/*
       +#pragma        lib        "libsec.a"
       +#pragma        src        "/sys/src/libsec"
       +*/
       +
       +AUTOLIB(sec)
       +
       +#ifndef _MPINT
       +typedef struct mpint mpint;
       +#endif
       +
       +/*******************************************************/
       +/* AES definitions */
       +/*******************************************************/
       +
       +enum
       +{
       +        AESbsize=        16,
       +        AESmaxkey=        32,
       +        AESmaxrounds=        14
       +};
       +
       +typedef struct AESstate AESstate;
       +struct AESstate
       +{
       +        ulong        setup;
       +        int        rounds;
       +        int        keybytes;
       +        uchar        key[AESmaxkey];                /* unexpanded key */
       +        u32int        ekey[4*(AESmaxrounds + 1)];        /* encryption key */
       +        u32int        dkey[4*(AESmaxrounds + 1)];        /* decryption key */
       +        uchar        ivec[AESbsize];        /* initialization vector */
       +};
       +
       +void        setupAESstate(AESstate *s, uchar key[], int keybytes, uchar *ivec);
       +void        aesCBCencrypt(uchar *p, int len, AESstate *s);
       +void        aesCBCdecrypt(uchar *p, int len, AESstate *s);
       +
       +/*******************************************************/
       +/* Blowfish Definitions */
       +/*******************************************************/
       +
       +enum
       +{
       +        BFbsize        = 8,
       +        BFrounds        = 16
       +};
       +
       +/* 16-round Blowfish */
       +typedef struct BFstate BFstate;
       +struct BFstate
       +{
       +        ulong        setup;
       +
       +        uchar        key[56];
       +        uchar        ivec[8];
       +
       +        u32int         pbox[BFrounds+2];
       +        u32int        sbox[1024];
       +};
       +
       +void        setupBFstate(BFstate *s, uchar key[], int keybytes, uchar *ivec);
       +void        bfCBCencrypt(uchar*, int, BFstate*);
       +void        bfCBCdecrypt(uchar*, int, BFstate*);
       +void        bfECBencrypt(uchar*, int, BFstate*);
       +void        bfECBdecrypt(uchar*, int, BFstate*);
       +
       +/*******************************************************/
       +/* DES definitions */
       +/*******************************************************/
       +
       +enum
       +{
       +        DESbsize=        8
       +};
       +
       +/* single des */
       +typedef struct DESstate DESstate;
       +struct DESstate
       +{
       +        ulong        setup;
       +        uchar        key[8];                /* unexpanded key */
       +        ulong        expanded[32];        /* expanded key */
       +        uchar        ivec[8];        /* initialization vector */
       +};
       +
       +void        setupDESstate(DESstate *s, uchar key[8], uchar *ivec);
       +void        des_key_setup(uchar[8], ulong[32]);
       +void        block_cipher(ulong*, uchar*, int);
       +void        desCBCencrypt(uchar*, int, DESstate*);
       +void        desCBCdecrypt(uchar*, int, DESstate*);
       +void        desECBencrypt(uchar*, int, DESstate*);
       +void        desECBdecrypt(uchar*, int, DESstate*);
       +
       +/* for backward compatibility with 7 byte DES key format */
       +void        des56to64(uchar *k56, uchar *k64);
       +void        des64to56(uchar *k64, uchar *k56);
       +void        key_setup(uchar[7], ulong[32]);
       +
       +/* triple des encrypt/decrypt orderings */
       +enum {
       +        DES3E=                0,
       +        DES3D=                1,
       +        DES3EEE=        0,
       +        DES3EDE=        2,
       +        DES3DED=        5,
       +        DES3DDD=        7
       +};
       +
       +typedef struct DES3state DES3state;
       +struct DES3state
       +{
       +        ulong        setup;
       +        uchar        key[3][8];                /* unexpanded key */
       +        ulong        expanded[3][32];        /* expanded key */
       +        uchar        ivec[8];                /* initialization vector */
       +};
       +
       +void        setupDES3state(DES3state *s, uchar key[3][8], uchar *ivec);
       +void        triple_block_cipher(ulong keys[3][32], uchar*, int);
       +void        des3CBCencrypt(uchar*, int, DES3state*);
       +void        des3CBCdecrypt(uchar*, int, DES3state*);
       +void        des3ECBencrypt(uchar*, int, DES3state*);
       +void        des3ECBdecrypt(uchar*, int, DES3state*);
       +
       +/*******************************************************/
       +/* digests */
       +/*******************************************************/
       +
       +enum
       +{
       +        SHA1dlen=        20,        /* SHA digest length */
       +        MD4dlen=        16,        /* MD4 digest length */
       +        MD5dlen=        16        /* MD5 digest length */
       +};
       +
       +typedef struct DigestState DigestState;
       +struct DigestState
       +{
       +        ulong len;
       +        u32int state[5];
       +        uchar buf[128];
       +        int blen;
       +        char malloced;
       +        char seeded;
       +};
       +typedef struct DigestState SHAstate;        /* obsolete name */
       +typedef struct DigestState SHA1state;
       +typedef struct DigestState MD5state;
       +typedef struct DigestState MD4state;
       +
       +DigestState* md4(uchar*, ulong, uchar*, DigestState*);
       +DigestState* md5(uchar*, ulong, uchar*, DigestState*);
       +DigestState* sha1(uchar*, ulong, uchar*, DigestState*);
       +DigestState* hmac_md5(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
       +DigestState* hmac_sha1(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
       +char* sha1pickle(SHA1state*);
       +SHA1state* sha1unpickle(char*);
       +
       +/*******************************************************/
       +/* random number generation */
       +/*******************************************************/
       +void        genrandom(uchar *buf, int nbytes);
       +void        prng(uchar *buf, int nbytes);
       +ulong        fastrand(void);
       +ulong        nfastrand(ulong);
       +
       +/*******************************************************/
       +/* primes */
       +/*******************************************************/
       +void        genprime(mpint *p, int n, int accuracy); /* generate an n bit probable prime */
       +void        gensafeprime(mpint *p, mpint *alpha, int n, int accuracy);        /* prime and generator */
       +void        genstrongprime(mpint *p, int n, int accuracy);        /* generate an n bit strong prime */
       +void        DSAprimes(mpint *q, mpint *p, uchar seed[SHA1dlen]);
       +int        probably_prime(mpint *n, int nrep);        /* miller-rabin test */
       +int        smallprimetest(mpint *p);                /* returns -1 if not prime, 0 otherwise */
       +
       +/*******************************************************/
       +/* rc4 */
       +/*******************************************************/
       +typedef struct RC4state RC4state;
       +struct RC4state
       +{
       +         uchar state[256];
       +         uchar x;
       +         uchar y;
       +};
       +
       +void        setupRC4state(RC4state*, uchar*, int);
       +void        rc4(RC4state*, uchar*, int);
       +void        rc4skip(RC4state*, int);
       +void        rc4back(RC4state*, int);
       +
       +/*******************************************************/
       +/* rsa */
       +/*******************************************************/
       +typedef struct RSApub RSApub;
       +typedef struct RSApriv RSApriv;
       +typedef struct PEMChain PEMChain;
       +
       +/* public/encryption key */
       +struct RSApub
       +{
       +        mpint        *n;        /* modulus */
       +        mpint        *ek;        /* exp (encryption key) */
       +};
       +
       +/* private/decryption key */
       +struct RSApriv
       +{
       +        RSApub        pub;
       +
       +        mpint        *dk;        /* exp (decryption key) */
       +
       +        /* precomputed values to help with chinese remainder theorem calc */
       +        mpint        *p;
       +        mpint        *q;
       +        mpint        *kp;        /* dk mod p-1 */
       +        mpint        *kq;        /* dk mod q-1 */
       +        mpint        *c2;        /* (inv p) mod q */
       +};
       +
       +struct PEMChain
       +{
       +        PEMChain *next;
       +        uchar *pem;
       +        int pemlen;
       +};
       +
       +RSApriv*        rsagen(int nlen, int elen, int rounds);
       +mpint*                rsaencrypt(RSApub *k, mpint *in, mpint *out);
       +mpint*                rsadecrypt(RSApriv *k, mpint *in, mpint *out);
       +RSApub*                rsapuballoc(void);
       +void                rsapubfree(RSApub*);
       +RSApriv*        rsaprivalloc(void);
       +void                rsaprivfree(RSApriv*);
       +RSApub*                rsaprivtopub(RSApriv*);
       +RSApub*                X509toRSApub(uchar*, int, char*, int);
       +RSApriv*        asn1toRSApriv(uchar*, int);
       +uchar*                decodepem(char *s, char *type, int *len, char**);
       +PEMChain*        decodepemchain(char *s, char *type);
       +uchar*                X509gen(RSApriv *priv, char *subj, ulong valid[2], int *certlen);
       +RSApriv*        rsafill(mpint *n, mpint *ek, mpint *dk, mpint *p, mpint *q);
       +uchar*        X509req(RSApriv *priv, char *subj, int *certlen);
       +
       +/*******************************************************/
       +/* elgamal */
       +/*******************************************************/
       +typedef struct EGpub EGpub;
       +typedef struct EGpriv EGpriv;
       +typedef struct EGsig EGsig;
       +
       +/* public/encryption key */
       +struct EGpub
       +{
       +        mpint        *p;        /* modulus */
       +        mpint        *alpha;        /* generator */
       +        mpint        *key;        /* (encryption key) alpha**secret mod p */
       +};
       +
       +/* private/decryption key */
       +struct EGpriv
       +{
       +        EGpub        pub;
       +        mpint        *secret; /* (decryption key) */
       +};
       +
       +/* signature */
       +struct EGsig
       +{
       +        mpint        *r, *s;
       +};
       +
       +EGpriv*                eggen(int nlen, int rounds);
       +mpint*                egencrypt(EGpub *k, mpint *in, mpint *out);
       +mpint*                egdecrypt(EGpriv *k, mpint *in, mpint *out);
       +EGsig*                egsign(EGpriv *k, mpint *m);
       +int                egverify(EGpub *k, EGsig *sig, mpint *m);
       +EGpub*                egpuballoc(void);
       +void                egpubfree(EGpub*);
       +EGpriv*                egprivalloc(void);
       +void                egprivfree(EGpriv*);
       +EGsig*                egsigalloc(void);
       +void                egsigfree(EGsig*);
       +EGpub*                egprivtopub(EGpriv*);
       +
       +/*******************************************************/
       +/* dsa */
       +/*******************************************************/
       +typedef struct DSApub DSApub;
       +typedef struct DSApriv DSApriv;
       +typedef struct DSAsig DSAsig;
       +
       +/* public/encryption key */
       +struct DSApub
       +{
       +        mpint        *p;        /* modulus */
       +        mpint        *q;        /* group order, q divides p-1 */
       +        mpint        *alpha;        /* group generator */
       +        mpint        *key;        /* (encryption key) alpha**secret mod p */
       +};
       +
       +/* private/decryption key */
       +struct DSApriv
       +{
       +        DSApub        pub;
       +        mpint        *secret; /* (decryption key) */
       +};
       +
       +/* signature */
       +struct DSAsig
       +{
       +        mpint        *r, *s;
       +};
       +
       +DSApriv*        dsagen(DSApub *opub);
       +DSAsig*                dsasign(DSApriv *k, mpint *m);
       +int                dsaverify(DSApub *k, DSAsig *sig, mpint *m);
       +DSApub*                dsapuballoc(void);
       +void                dsapubfree(DSApub*);
       +DSApriv*        dsaprivalloc(void);
       +void                dsaprivfree(DSApriv*);
       +DSAsig*                dsasigalloc(void);
       +void                dsasigfree(DSAsig*);
       +DSApub*                dsaprivtopub(DSApriv*);
       +DSApriv*        asn1toDSApriv(uchar*, int);
       +
       +/*******************************************************/
       +/* TLS */
       +/*******************************************************/
       +typedef struct Thumbprint{
       +        struct Thumbprint *next;
       +        uchar sha1[SHA1dlen];
       +} Thumbprint;
       +
       +typedef struct TLSconn{
       +        char dir[40];  /* connection directory */
       +        uchar *cert;   /* certificate (local on input, remote on output) */
       +        uchar *sessionID;
       +        int certlen, sessionIDlen;
       +        int (*trace)(char*fmt, ...);
       +        PEMChain *chain;
       +} TLSconn;
       +
       +/* tlshand.c */
       +extern int tlsClient(int fd, TLSconn *c);
       +extern int tlsServer(int fd, TLSconn *c);
       +
       +/* thumb.c */
       +extern Thumbprint* initThumbprints(char *ok, char *crl);
       +extern void freeThumbprints(Thumbprint *ok);
       +extern int okThumbprint(uchar *sha1, Thumbprint *ok);
       +
       +/* readcert.c */
       +extern uchar *readcert(char *filename, int *pcertlen);
       +PEMChain *readcertchain(char *filename);
       +
       +#if defined(__cplusplus)
       +}
       +#endif
       +#endif
   DIR diff --git a/lib9/sec/os.h b/lib9/sec/os.h
       @@ -0,0 +1,2 @@
       +#include <u.h>
       +#include <libc.h>
   DIR diff --git a/lib9/sec/sha1.c b/lib9/sec/sha1.c
       @@ -0,0 +1,127 @@
       +#include "os.h"
       +#include <libsec.h>
       +
       +static void encode(uchar*, u32int*, ulong);
       +
       +extern void _sha1block(uchar*, ulong, u32int*);
       +
       +/*
       + *  we require len to be a multiple of 64 for all but
       + *  the last call.  There must be room in the input buffer
       + *  to pad.
       + */
       +SHA1state*
       +sha1(uchar *p, ulong len, uchar *digest, SHA1state *s)
       +{
       +        uchar buf[128];
       +        u32int x[16];
       +        int i;
       +        uchar *e;
       +
       +        if(s == nil){
       +                s = malloc(sizeof(*s));
       +                if(s == nil)
       +                        return nil;
       +                memset(s, 0, sizeof(*s));
       +                s->malloced = 1;
       +        }
       +
       +        if(s->seeded == 0){
       +                /* seed the state, these constants would look nicer big-endian */
       +                s->state[0] = 0x67452301;
       +                s->state[1] = 0xefcdab89;
       +                s->state[2] = 0x98badcfe;
       +                s->state[3] = 0x10325476;
       +                s->state[4] = 0xc3d2e1f0;
       +                s->seeded = 1;
       +        }
       +
       +        /* fill out the partial 64 byte block from previous calls */
       +        if(s->blen){
       +                i = 64 - s->blen;
       +                if(len < i)
       +                        i = len;
       +                memmove(s->buf + s->blen, p, i);
       +                len -= i;
       +                s->blen += i;
       +                p += i;
       +                if(s->blen == 64){
       +                        _sha1block(s->buf, s->blen, s->state);
       +                        s->len += s->blen;
       +                        s->blen = 0;
       +                }
       +        }
       +
       +        /* do 64 byte blocks */
       +        i = len & ~0x3f;
       +        if(i){
       +                _sha1block(p, i, s->state);
       +                s->len += i;
       +                len -= i;
       +                p += i;
       +        }
       +
       +        /* save the left overs if not last call */
       +        if(digest == 0){
       +                if(len){
       +                        memmove(s->buf, p, len);
       +                        s->blen += len;
       +                }
       +                return s;
       +        }
       +
       +        /*
       +         *  this is the last time through, pad what's left with 0x80,
       +         *  0's, and the input count to create a multiple of 64 bytes
       +         */
       +        if(s->blen){
       +                p = s->buf;
       +                len = s->blen;
       +        } else {
       +                memmove(buf, p, len);
       +                p = buf;
       +        }
       +        s->len += len;
       +        e = p + len;
       +        if(len < 56)
       +                i = 56 - len;
       +        else
       +                i = 120 - len;
       +        memset(e, 0, i);
       +        *e = 0x80;
       +        len += i;
       +
       +        /* append the count */
       +        x[0] = s->len>>29;
       +        x[1] = s->len<<3;
       +        encode(p+len, x, 8);
       +
       +        /* digest the last part */
       +        _sha1block(p, len+8, s->state);
       +        s->len += len+8;
       +
       +        /* return result and free state */
       +        encode(digest, s->state, SHA1dlen);
       +        if(s->malloced == 1)
       +                free(s);
       +        return nil;
       +}
       +
       +/*
       + *        encodes input (ulong) into output (uchar). Assumes len is
       + *        a multiple of 4.
       + */
       +static void
       +encode(uchar *output, u32int *input, ulong len)
       +{
       +        u32int x;
       +        uchar *e;
       +
       +        for(e = output + len; output < e;) {
       +                x = *input++;
       +                *output++ = x >> 24;
       +                *output++ = x >> 16;
       +                *output++ = x >> 8;
       +                *output++ = x;
       +        }
       +}
   DIR diff --git a/lib9/sec/sha1block.c b/lib9/sec/sha1block.c
       @@ -0,0 +1,187 @@
       +#include "os.h"
       +
       +void
       +_sha1block(uchar *p, ulong len, u32int *s)
       +{
       +        u32int a, b, c, d, e, x;
       +        uchar *end;
       +        u32int *wp, *wend;
       +        u32int w[80];
       +
       +        /* at this point, we have a multiple of 64 bytes */
       +        for(end = p+len; p < end;){
       +                a = s[0];
       +                b = s[1];
       +                c = s[2];
       +                d = s[3];
       +                e = s[4];
       +
       +                wend = w + 15;
       +                for(wp = w; wp < wend; wp += 5){
       +                        wp[0] = (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
       +                        e += ((a<<5) | (a>>27)) + wp[0];
       +                        e += 0x5a827999 + (((c^d)&b)^d);
       +                        b = (b<<30)|(b>>2);
       +
       +                        wp[1] = (p[4]<<24) | (p[5]<<16) | (p[6]<<8) | p[7];
       +                        d += ((e<<5) | (e>>27)) + wp[1];
       +                        d += 0x5a827999 + (((b^c)&a)^c);
       +                        a = (a<<30)|(a>>2);
       +
       +                        wp[2] = (p[8]<<24) | (p[9]<<16) | (p[10]<<8) | p[11];
       +                        c += ((d<<5) | (d>>27)) + wp[2];
       +                        c += 0x5a827999 + (((a^b)&e)^b);
       +                        e = (e<<30)|(e>>2);
       +
       +                        wp[3] = (p[12]<<24) | (p[13]<<16) | (p[14]<<8) | p[15];
       +                        b += ((c<<5) | (c>>27)) + wp[3];
       +                        b += 0x5a827999 + (((e^a)&d)^a);
       +                        d = (d<<30)|(d>>2);
       +
       +                        wp[4] = (p[16]<<24) | (p[17]<<16) | (p[18]<<8) | p[19];
       +                        a += ((b<<5) | (b>>27)) + wp[4];
       +                        a += 0x5a827999 + (((d^e)&c)^e);
       +                        c = (c<<30)|(c>>2);
       +                        
       +                        p += 20;
       +                }
       +
       +                wp[0] = (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
       +                e += ((a<<5) | (a>>27)) + wp[0];
       +                e += 0x5a827999 + (((c^d)&b)^d);
       +                b = (b<<30)|(b>>2);
       +
       +                x = wp[-2] ^ wp[-7] ^ wp[-13] ^ wp[-15];
       +                wp[1] = (x<<1) | (x>>31);
       +                d += ((e<<5) | (e>>27)) + wp[1];
       +                d += 0x5a827999 + (((b^c)&a)^c);
       +                a = (a<<30)|(a>>2);
       +
       +                x = wp[-1] ^ wp[-6] ^ wp[-12] ^ wp[-14];
       +                wp[2] = (x<<1) | (x>>31);
       +                c += ((d<<5) | (d>>27)) + wp[2];
       +                c += 0x5a827999 + (((a^b)&e)^b);
       +                e = (e<<30)|(e>>2);
       +
       +                x = wp[0] ^ wp[-5] ^ wp[-11] ^ wp[-13];
       +                wp[3] = (x<<1) | (x>>31);
       +                b += ((c<<5) | (c>>27)) + wp[3];
       +                b += 0x5a827999 + (((e^a)&d)^a);
       +                d = (d<<30)|(d>>2);
       +
       +                x = wp[1] ^ wp[-4] ^ wp[-10] ^ wp[-12];
       +                wp[4] = (x<<1) | (x>>31);
       +                a += ((b<<5) | (b>>27)) + wp[4];
       +                a += 0x5a827999 + (((d^e)&c)^e);
       +                c = (c<<30)|(c>>2);
       +
       +                wp += 5;
       +                p += 4;
       +
       +                wend = w + 40;
       +                for(; wp < wend; wp += 5){
       +                        x = wp[-3] ^ wp[-8] ^ wp[-14] ^ wp[-16];
       +                        wp[0] = (x<<1) | (x>>31);
       +                        e += ((a<<5) | (a>>27)) + wp[0];
       +                        e += 0x6ed9eba1 + (b^c^d);
       +                        b = (b<<30)|(b>>2);
       +
       +                        x = wp[-2] ^ wp[-7] ^ wp[-13] ^ wp[-15];
       +                        wp[1] = (x<<1) | (x>>31);
       +                        d += ((e<<5) | (e>>27)) + wp[1];
       +                        d += 0x6ed9eba1 + (a^b^c);
       +                        a = (a<<30)|(a>>2);
       +
       +                        x = wp[-1] ^ wp[-6] ^ wp[-12] ^ wp[-14];
       +                        wp[2] = (x<<1) | (x>>31);
       +                        c += ((d<<5) | (d>>27)) + wp[2];
       +                        c += 0x6ed9eba1 + (e^a^b);
       +                        e = (e<<30)|(e>>2);
       +
       +                        x = wp[0] ^ wp[-5] ^ wp[-11] ^ wp[-13];
       +                        wp[3] = (x<<1) | (x>>31);
       +                        b += ((c<<5) | (c>>27)) + wp[3];
       +                        b += 0x6ed9eba1 + (d^e^a);
       +                        d = (d<<30)|(d>>2);
       +
       +                        x = wp[1] ^ wp[-4] ^ wp[-10] ^ wp[-12];
       +                        wp[4] = (x<<1) | (x>>31);
       +                        a += ((b<<5) | (b>>27)) + wp[4];
       +                        a += 0x6ed9eba1 + (c^d^e);
       +                        c = (c<<30)|(c>>2);
       +                }
       +
       +                wend = w + 60;
       +                for(; wp < wend; wp += 5){
       +                        x = wp[-3] ^ wp[-8] ^ wp[-14] ^ wp[-16];
       +                        wp[0] = (x<<1) | (x>>31);
       +                        e += ((a<<5) | (a>>27)) + wp[0];
       +                        e += 0x8f1bbcdc + ((b&c)|((b|c)&d));
       +                        b = (b<<30)|(b>>2);
       +
       +                        x = wp[-2] ^ wp[-7] ^ wp[-13] ^ wp[-15];
       +                        wp[1] = (x<<1) | (x>>31);
       +                        d += ((e<<5) | (e>>27)) + wp[1];
       +                        d += 0x8f1bbcdc + ((a&b)|((a|b)&c));
       +                        a = (a<<30)|(a>>2);
       +
       +                        x = wp[-1] ^ wp[-6] ^ wp[-12] ^ wp[-14];
       +                        wp[2] = (x<<1) | (x>>31);
       +                        c += ((d<<5) | (d>>27)) + wp[2];
       +                        c += 0x8f1bbcdc + ((e&a)|((e|a)&b));
       +                        e = (e<<30)|(e>>2);
       +
       +                        x = wp[0] ^ wp[-5] ^ wp[-11] ^ wp[-13];
       +                        wp[3] = (x<<1) | (x>>31);
       +                        b += ((c<<5) | (c>>27)) + wp[3];
       +                        b += 0x8f1bbcdc + ((d&e)|((d|e)&a));
       +                        d = (d<<30)|(d>>2);
       +
       +                        x = wp[1] ^ wp[-4] ^ wp[-10] ^ wp[-12];
       +                        wp[4] = (x<<1) | (x>>31);
       +                        a += ((b<<5) | (b>>27)) + wp[4];
       +                        a += 0x8f1bbcdc + ((c&d)|((c|d)&e));
       +                        c = (c<<30)|(c>>2);
       +                }
       +
       +                wend = w + 80;
       +                for(; wp < wend; wp += 5){
       +                        x = wp[-3] ^ wp[-8] ^ wp[-14] ^ wp[-16];
       +                        wp[0] = (x<<1) | (x>>31);
       +                        e += ((a<<5) | (a>>27)) + wp[0];
       +                        e += 0xca62c1d6 + (b^c^d);
       +                        b = (b<<30)|(b>>2);
       +
       +                        x = wp[-2] ^ wp[-7] ^ wp[-13] ^ wp[-15];
       +                        wp[1] = (x<<1) | (x>>31);
       +                        d += ((e<<5) | (e>>27)) + wp[1];
       +                        d += 0xca62c1d6 + (a^b^c);
       +                        a = (a<<30)|(a>>2);
       +
       +                        x = wp[-1] ^ wp[-6] ^ wp[-12] ^ wp[-14];
       +                        wp[2] = (x<<1) | (x>>31);
       +                        c += ((d<<5) | (d>>27)) + wp[2];
       +                        c += 0xca62c1d6 + (e^a^b);
       +                        e = (e<<30)|(e>>2);
       +
       +                        x = wp[0] ^ wp[-5] ^ wp[-11] ^ wp[-13];
       +                        wp[3] = (x<<1) | (x>>31);
       +                        b += ((c<<5) | (c>>27)) + wp[3];
       +                        b += 0xca62c1d6 + (d^e^a);
       +                        d = (d<<30)|(d>>2);
       +
       +                        x = wp[1] ^ wp[-4] ^ wp[-10] ^ wp[-12];
       +                        wp[4] = (x<<1) | (x>>31);
       +                        a += ((b<<5) | (b>>27)) + wp[4];
       +                        a += 0xca62c1d6 + (c^d^e);
       +                        c = (c<<30)|(c>>2);
       +                }
       +
       +                /* save state */
       +                s[0] += a;
       +                s[1] += b;
       +                s[2] += c;
       +                s[3] += d;
       +                s[4] += e;
       +        }
       +}
   DIR diff --git a/lib9/sec/sha1pickle.c b/lib9/sec/sha1pickle.c
       @@ -0,0 +1,38 @@
       +#include "os.h"
       +#include <libsec.h>
       +
       +char*
       +sha1pickle(SHA1state *s)
       +{
       +        char *p;
       +        int m, n;
       +
       +        m = 5*9+4*((s->blen+3)/3);
       +        p = malloc(m);
       +        if(p == nil)
       +                return p;
       +        n = sprint(p, "%8.8ux %8.8ux %8.8ux %8.8ux %8.8ux ",
       +                s->state[0], s->state[1], s->state[2],
       +                s->state[3], s->state[4]);
       +        enc64(p+n, m-n, s->buf, s->blen);
       +        return p;
       +}
       +
       +SHA1state*
       +sha1unpickle(char *p)
       +{
       +        SHA1state *s;
       +
       +        s = malloc(sizeof(*s));
       +        if(s == nil)
       +                return nil;
       +        s->state[0] = strtoul(p, &p, 16);
       +        s->state[1] = strtoul(p, &p, 16);
       +        s->state[2] = strtoul(p, &p, 16);
       +        s->state[3] = strtoul(p, &p, 16);
       +        s->state[4] = strtoul(p, &p, 16);
       +        s->blen = dec64(s->buf, sizeof(s->buf), p, strlen(p));
       +        s->malloced = 1;
       +        s->seeded = 1;
       +        return s;
       +}
   DIR diff --git a/sha1sum/Makefile b/sha1sum/Makefile
       @@ -0,0 +1,10 @@
       +# sha1sum - sha1sum unix port from plan9
       +# Depends on ../lib9
       +
       +TARG      = sha1sum
       +
       +include ../std.mk
       +
       +pre-uninstall:
       +
       +post-install:
   DIR diff --git a/sha1sum/sha1sum.1 b/sha1sum/sha1sum.1
   DIR diff --git a/sha1sum/sha1sum.c b/sha1sum/sha1sum.c
       @@ -0,0 +1,61 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +#include <libsec.h>
       +
       +static int
       +digestfmt(Fmt *fmt)
       +{
       +        char buf[SHA1dlen*2+1];
       +        uchar *p;
       +        int i;
       +
       +        p = va_arg(fmt->args, uchar*);
       +        for(i=0; i<SHA1dlen; i++)
       +                sprint(buf+2*i, "%.2ux", p[i]);
       +        return fmtstrcpy(fmt, buf);
       +}
       +
       +static void
       +sum(int fd, char *name)
       +{
       +        int n;
       +        uchar buf[8192], digest[SHA1dlen];
       +        DigestState *s;
       +
       +        s = sha1(nil, 0, nil, nil);
       +        while((n = read(fd, buf, sizeof buf)) > 0)
       +                sha1(buf, n, nil, s);
       +        sha1(nil, 0, digest, s);
       +        if(name == nil)
       +                print("%M\n", digest);
       +        else
       +                print("%M\t%s\n", digest, name);
       +}
       +
       +void
       +main(int argc, char *argv[])
       +{
       +        int i, fd;
       +
       +        ARGBEGIN{
       +        default:
       +                fprint(2, "usage: sha1sum [file...]\n");
       +                exits("usage");
       +        }ARGEND
       +
       +        fmtinstall('M', digestfmt);
       +
       +        if(argc == 0)
       +                sum(0, nil);
       +        else for(i = 0; i < argc; i++){
       +                fd = open(argv[i], OREAD);
       +                if(fd < 0){
       +                        fprint(2, "sha1sum: can't open %s: %r\n", argv[i]);
       +                        continue;
       +                }
       +                sum(fd, argv[i]);
       +                close(fd);
       +        }
       +        exits(nil);
       +}
   DIR diff --git a/std.mk b/std.mk
       @@ -25,11 +25,11 @@ uninstall: pre-uninstall
        
        .c.o:
                @echo CC $*.c
       -        @${CC} ${CFLAGS} -I../lib9 -I../lib9 $*.c
       +        @${CC} ${CFLAGS} -I../lib9 -I../lib9/sec $*.c
        
        clean:
                rm -f ${OFILES} ${TARG}
        
        ${TARG}: ${OFILES}
                @echo LD ${TARG}
       -        @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L../lib9 -l9
       +        @${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -L../lib9 -l9 -lm