commit e70375948b9e9afebde07fb294abf5f039f19d44
parent 7dce804fe658181cb8cbb102a525ca34b4fb0006
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:
15 files changed, 3114 insertions(+), 9 deletions(-)
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:
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
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:
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.
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);
+}
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}
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
diff --git a/lib9/sec/os.h b/lib9/sec/os.h
@@ -0,0 +1,2 @@
+#include <u.h>
+#include <libc.h>
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;
+ }
+}
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;
+ }
+}
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;
+}
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:
diff --git a/sha1sum/sha1sum.1 b/sha1sum/sha1sum.1
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);
+}
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