aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/9c1
-rw-r--r--dist/deb.html2
-rw-r--r--dist/main.html6
-rw-r--r--dist/pkg/main2
-rw-r--r--dist/unix.html12
-rw-r--r--install.txt2
-rwxr-xr-xmac/9term.app/Contents/MacOS/9term24
-rw-r--r--man/man1/0intro.12
-rw-r--r--man/man1/rc.134
-rw-r--r--src/cmd/acme/cols.c8
-rwxr-xr-xsrc/cmd/rc/checkparse23
-rw-r--r--src/cmd/rc/code.c10
-rw-r--r--src/cmd/rc/exec.c4
-rw-r--r--src/cmd/rc/fns.h1
-rw-r--r--src/cmd/rc/io.c5
-rw-r--r--src/cmd/rc/io.h1
-rw-r--r--src/cmd/rc/lex.c35
-rw-r--r--src/cmd/rc/mkfile1
-rw-r--r--src/cmd/rc/parse.c552
-rw-r--r--src/cmd/rc/pcmd.c118
-rw-r--r--src/cmd/rc/simple.c1
-rw-r--r--src/cmd/rc/syn.y10
-rw-r--r--src/cmd/rc/test.rc93
-rw-r--r--unix/NOTICE.bio2
-rw-r--r--unix/NOTICE.fmt2
-rw-r--r--unix/NOTICE.mk2
-rw-r--r--unix/NOTICE.regexp2
-rw-r--r--unix/NOTICE.utf2
28 files changed, 891 insertions, 66 deletions
diff --git a/bin/9c b/bin/9c
index b07d0149..0f836d24 100755
--- a/bin/9c
+++ b/bin/9c
@@ -20,6 +20,7 @@ usegcc()
-Wno-format-truncation \
-fno-omit-frame-pointer \
-fsigned-char \
+ -fcommon \
"
# want to put -fno-optimize-sibling-calls here but
# that option only works with gcc3+ it seems
diff --git a/dist/deb.html b/dist/deb.html
index 840f6755..b0202085 100644
--- a/dist/deb.html
+++ b/dist/deb.html
@@ -20,7 +20,7 @@ Edit ./^$/,s/<Table/<table border=0 cellspacing=0 cellpadding=0 width=100%/g
<table border=0 cellspacing=0 cellpadding=0 width=100%><tr height=10><td></table>
<a href="../">Plan 9 from User Space (aka plan9port)</a>
is a port of many Plan 9 programs from their native
- <a href="http://plan9.bell-labs.com/plan9/">Plan 9</a>
+ <a href="http://9p.io/plan9/">Plan 9</a>
environment to Unix-like operating systems.
<table border=0 cellspacing=0 cellpadding=0 width=100%><tr height=20><td></table>
diff --git a/dist/main.html b/dist/main.html
index f0b51911..2cc944ed 100644
--- a/dist/main.html
+++ b/dist/main.html
@@ -41,7 +41,7 @@ Edit ./^$/,s/<Table/<table border=0 cellspacing=0 cellpadding=0 width=100%/g
<table border=0 cellspacing=0 cellpadding=0 width=100%><tr height=10><td></table>
Plan 9 from User Space (aka plan9port)
is a port of many Plan 9 programs from their native
- <a href="http://plan9.bell-labs.com/plan9/">Plan 9</a>
+ <a href="http://9p.io/plan9/">Plan 9</a>
environment to Unix-like operating systems.
<table border=0 cellspacing=0 cellpadding=0 width=100%><tr height=20><td></table>
@@ -89,7 +89,7 @@ Edit ./^$/,s/<Table/<table border=0 cellspacing=0 cellpadding=0 width=100%/g
<tr><td><td>
Most obviously, plan9port derives from
- <a href="http://plan9.bell-labs.com/plan9">Plan 9 from Bell Labs</a>
+ <a href="http://9p.io/plan9">Plan 9 from Bell Labs</a>
and would not exist without the work of the Plan 9 team over the
past many years.
@@ -173,7 +173,7 @@ Edit ./^$/,s/<Table/<table border=0 cellspacing=0 cellpadding=0 width=100%/g
</a>
of Plan 9 from User Space for Windows. Only for reference.
<br>
- <a href="http://plan9.bell-labs.com/plan9dist/ureg.html">
+ <a href="https://9p.io/plan9/addons.html">
Sean Quinlan's 9pm
</a> &#8722; a port of an earlier Plan 9 (including <a href="man/man1/sam.html"><i>sam</i>(1)</a>) to Windows
<br>
diff --git a/dist/pkg/main b/dist/pkg/main
index 52e5efda..c779a028 100644
--- a/dist/pkg/main
+++ b/dist/pkg/main
@@ -5,7 +5,7 @@ depends=plan9port-^(base bin devel dict dist faces font-asian \
desc='Plan 9 from User Space
Plan 9 is a distributed computing environment built at Bell Labs
starting in the late 1980s. The system can be obtained from Bell Labs
- at http://plan9.bell-labs.com/plan9 and runs on PCs and a variety
+ at http://9p.io/plan9 and runs on PCs and a variety
of other platforms. Plan 9 became a convenient platform for
experimenting with new ideas, applications, and services.
.
diff --git a/dist/unix.html b/dist/unix.html
index 0e8394c2..9948da2f 100644
--- a/dist/unix.html
+++ b/dist/unix.html
@@ -19,7 +19,7 @@ Edit ./^$/,s/<Table/<table border=0 cellspacing=0 cellpadding=0 width=100%/g
</center>
<table border=0 cellspacing=0 cellpadding=0 width=100%><tr height=10><td></table>
- These are ports of <a href="http://plan9.bell-labs.com/plan9">Plan 9</a>'s
+ These are ports of <a href="http://9p.io/plan9">Plan 9</a>'s
UTF-8, formatted print, buffered I/O, and regular expression
libraries, along with mk, a simple replacement for make.
<p>
@@ -82,7 +82,7 @@ Edit ./^$/,s/<Table/<table border=0 cellspacing=0 cellpadding=0 width=100%/g
<table border=0 cellspacing=0 cellpadding=0 width=100%><tr height=10><td></table>
<font size=-1>license: original <a href="license-bl.txt">Bell Labs MIT-like</a>
- or <a href="http://plan9.bell-labs.com/plan9dist/license.html">Lucent Public License</a></font>
+ or <a href="http://9p.io/plan9/license.html">Lucent Public License</a></font>
</table>
@@ -119,7 +119,7 @@ Edit ./^$/,s/<Table/<table border=0 cellspacing=0 cellpadding=0 width=100%/g
<table border=0 cellspacing=0 cellpadding=0 width=100%><tr height=10><td></table>
<font size=-1>License: original <a href="license-bl.txt">Bell Labs MIT-like</a>
- or <a href="http://plan9.bell-labs.com/plan9dist/license.html">Lucent Public License</a></font>
+ or <a href="http://9p.io/plan9/license.html">Lucent Public License</a></font>
</table>
@@ -146,7 +146,7 @@ Edit ./^$/,s/<Table/<table border=0 cellspacing=0 cellpadding=0 width=100%/g
<table border=0 cellspacing=0 cellpadding=0 width=100%><tr height=10><td></table>
<font size=-1>License: <a href="license-vn.txt">Vita Nuova MIT-like</a>
- or <a href="http://plan9.bell-labs.com/plan9dist/license.html">Lucent Public License</a></font>
+ or <a href="http://9p.io/plan9/license.html">Lucent Public License</a></font>
</table>
<table border=0 cellspacing=0 cellpadding=0 width=100%><tr height=20><td></table>
@@ -175,7 +175,7 @@ Edit ./^$/,s/<Table/<table border=0 cellspacing=0 cellpadding=0 width=100%/g
<table border=0 cellspacing=0 cellpadding=0 width=100%><tr height=10><td></table>
<font size=-1>License: original <a href="license-bl.txt">Bell Labs MIT-like</a>
- or <a href="http://plan9.bell-labs.com/plan9dist/license.html">Lucent Public License</a></font>
+ or <a href="http://9p.io/plan9/license.html">Lucent Public License</a></font>
</table>
@@ -200,7 +200,7 @@ Edit ./^$/,s/<Table/<table border=0 cellspacing=0 cellpadding=0 width=100%/g
<table border=0 cellspacing=0 cellpadding=0 width=100%><tr height=10><td></table>
<font size=-1>License: <a href="license-vn.txt">Vita Nuova MIT-like</a>
- or <a href="http://plan9.bell-labs.com/plan9dist/license.html">Lucent Public License</a></font>
+ or <a href="http://9p.io/plan9/license.html">Lucent Public License</a></font>
</table>
<table border=0 cellspacing=0 cellpadding=0 width=100%><tr height=20><td></table>
diff --git a/install.txt b/install.txt
index 2de7fa3a..6e0e9d3e 100644
--- a/install.txt
+++ b/install.txt
@@ -7,7 +7,7 @@
DESCRIPTION
Plan 9 is a distributed computing environment built at Bell
Labs starting in the late 1980s. The system can be obtained
- from Bell Labs at http://plan9.bell-labs.com/plan9 and runs
+ from Bell Labs at http://9p.io/plan9 and runs
on PCs and a variety of other platforms. Plan 9 became a
convenient platform for experimenting with new ideas, appli-
cations, and services.
diff --git a/mac/9term.app/Contents/MacOS/9term b/mac/9term.app/Contents/MacOS/9term
index e5ef2735..b5a46bc8 100755
--- a/mac/9term.app/Contents/MacOS/9term
+++ b/mac/9term.app/Contents/MacOS/9term
@@ -1,9 +1,29 @@
#!/bin/bash
-cd $HOME
-. ~/.bashrc
+fshell=$(finger $(whoami) | sed -n 's/.*Shell: //p' | sed 1q)
+SHELL=${fshell:-$SHELL}
PLAN9=${PLAN9:-/usr/local/plan9}
+cd $HOME
+case "$SHELL" in
+*/rc)
+ echo '
+ if(! ~ $PLAN9/bin $path)
+ path=($path $PLAN9/bin)
+ $PLAN9/bin/9term -l -W600x800 &
+ ' | $SHELL -l
+ exit 0
+ ;;
+*/bash)
+ . ~/.bash_profile
+ ;;
+*)
+ . ~/.profile
+ ;;
+esac
+
if ! [[ :$PATH: =~ :$PLAN9/bin: ]]
then
PATH=$PATH:$PLAN9/bin
fi
$PLAN9/bin/9term -l -W600x800 &
+exit 0
+
diff --git a/man/man1/0intro.1 b/man/man1/0intro.1
index f018fc8a..16134933 100644
--- a/man/man1/0intro.1
+++ b/man/man1/0intro.1
@@ -5,7 +5,7 @@ intro \- introduction to Plan 9 from User Space
Plan 9 is a distributed computing environment built
at Bell Labs starting in the late 1980s.
The system can be obtained from Bell Labs at
-.B http://plan9.bell-labs.com/plan9
+.B http://9p.io/plan9
and runs on PCs and a variety of other platforms.
Plan 9 became a convenient platform for experimenting
with new ideas, applications, and services.
diff --git a/man/man1/rc.1 b/man/man1/rc.1
index 7ea8998a..792cdc95 100644
--- a/man/man1/rc.1
+++ b/man/man1/rc.1
@@ -109,7 +109,7 @@ The simplest kind of argument is the unquoted word:
a sequence of one or more characters none of which is a blank, tab,
newline, or any of the following:
.EX
- # ; & | ^ $ = ` ' { } ( ) < >
+ # ; & | ^ $ ` ' { } ( ) < >
.EE
An unquoted word that contains any of the characters
.B *
@@ -290,28 +290,10 @@ then one operand must have one component, and the other must be non-empty,
and concatenation is distributive.
.PD
.SS Free Carets
-In most circumstances,
-.I rc
+.I Rc
will insert the
.B ^
operator automatically between words that are not separated by white space.
-Whenever one of
-.B $
-.B '
-.B `
-follows a quoted or unquoted word or an unquoted word follows a quoted word
-with no intervening blanks or tabs,
-a
-.B ^
-is inserted between the two.
-If an unquoted word immediately follows a
-.BR $
-and contains a character other than an alphanumeric, underscore,
-or
-.BR * ,
-a
-.B ^
-is inserted before the first such character.
Thus
.IP
.B cc -$flags $stem.c
@@ -367,7 +349,7 @@ or
.I Fd1
is a previously opened file descriptor and
.I fd0
-becomes a new copy (in the sense of
+becomes a new copy (in the sense of
.IR dup (3))
of it.
A file descriptor may be closed by writing
@@ -477,7 +459,7 @@ is executed.
The
.I command
is executed once for each
-.IR argument
+.IR argument
with that argument assigned to
.IR name .
If the argument list is omitted,
@@ -982,8 +964,6 @@ changes
.PP
Functions that use here documents don't work.
.PP
-Free carets don't get inserted next to keywords.
-.PP
The
.BI <{ command }
syntax depends on the underlying operating system
@@ -1009,3 +989,9 @@ and then
.B fstab
ensures causes FreeBSD to mount the file system
automatically at boot time.)
+.PP
+Some systems require
+.B \*9/bin/rc
+to be listed in
+.B /etc/shells
+before it can be used as a login shell.
diff --git a/src/cmd/acme/cols.c b/src/cmd/acme/cols.c
index 63c33ac1..63247a84 100644
--- a/src/cmd/acme/cols.c
+++ b/src/cmd/acme/cols.c
@@ -232,7 +232,7 @@ colmousebut(Column *c)
void
colresize(Column *c, Rectangle r)
{
- int i;
+ int i, old, new;
Rectangle r1, r2;
Window *w;
@@ -245,6 +245,8 @@ colresize(Column *c, Rectangle r)
r1.max.y += Border;
draw(screen, r1, display->black, nil, ZP);
r1.max.y = r.max.y;
+ new = Dy(r) - c->nw*(Border + font->height);
+ old = Dy(c->r) - c->nw*(Border + font->height);
for(i=0; i<c->nw; i++){
w = c->w[i];
w->maxlines = 0;
@@ -252,8 +254,8 @@ colresize(Column *c, Rectangle r)
r1.max.y = r.max.y;
else{
r1.max.y = r1.min.y;
- if(Dy(c->r) != 0){
- r1.max.y += (Dy(w->r)+Border)*Dy(r)/Dy(c->r);
+ if(new > 0 && old > 0 && Dy(w->r) > Border+font->height){
+ r1.max.y += (Dy(w->r)-Border-font->height)*new/old + Border + font->height;
}
}
r1.max.y = max(r1.max.y, r1.min.y + Border+font->height);
diff --git a/src/cmd/rc/checkparse b/src/cmd/rc/checkparse
new file mode 100755
index 00000000..0a1472b2
--- /dev/null
+++ b/src/cmd/rc/checkparse
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+aflag=false
+if [ "$1" = -a ]; then
+ aflag=true
+ shift
+fi
+
+files="$@"
+if [ $# = 0 ]; then
+ files=$(echo ./test.rc;
+ grep -l '^#!/usr/local/plan9/bin/rc' /usr/local/plan9/bin/{*,*/*} 2>/dev/null;
+ grep -R -l '^#!/bin/rc' $HOME/pub/plan9 | egrep -v 'plan9/(lib/(oui|pci)|sys/man|sys/lib/man|sys/lib/scsicodes)' 2>/dev/null)
+fi
+
+for i in $files
+do
+ if ! diff <(./o.rc -DY $i 2>&1) <(./o.rc -D $i 2>&1); then
+ echo '^^^' $i
+ ! $aflag && exit 1
+ fi
+done
+
diff --git a/src/cmd/rc/code.c b/src/cmd/rc/code.c
index eeaa3ed8..208476ae 100644
--- a/src/cmd/rc/code.c
+++ b/src/cmd/rc/code.c
@@ -41,6 +41,16 @@ stuffdot(int a)
int
compile(tree *t)
{
+ if(flag['D']) {
+ struct io *s;
+ s = openstr();
+ pfmt(s, "compile: %u\n", t);
+ write(2, s->strp, strlen(s->strp));
+ closeio(s);
+ if(eflagok) // made it out of rcmain - stop executing commands, just print them
+ t = nil;
+ }
+
ncode = 100;
codebuf = (code *)emalloc(ncode*sizeof codebuf[0]);
codep = 0;
diff --git a/src/cmd/rc/exec.c b/src/cmd/rc/exec.c
index 172acfa3..0320976a 100644
--- a/src/cmd/rc/exec.c
+++ b/src/cmd/rc/exec.c
@@ -139,7 +139,7 @@ main(int argc, char *argv[])
/* needed for rcmain later */
putenv("PLAN9", unsharp("#9"));
- argc = getflags(argc, argv, "SsrdiIlxepvVc:1m:1[command]", 1);
+ argc = getflags(argc, argv, "DSYsrdiIlxepvVc:1m:1[command]", 1);
if(argc==-1)
usage("[file [arg ...]]");
if(argv[0][0]=='-')
@@ -901,7 +901,7 @@ Xrdcmds(void)
promptstr="% ";
}
Noerror();
- if(yyparse()){
+ if((flag['Y'] ? yyparse : parse)()){
if(!p->iflag || p->eof && !Eintr()){
if(p->cmdfile)
efree(p->cmdfile);
diff --git a/src/cmd/rc/fns.h b/src/cmd/rc/fns.h
index c4d201dc..5721d617 100644
--- a/src/cmd/rc/fns.h
+++ b/src/cmd/rc/fns.h
@@ -65,3 +65,4 @@ int wordchr(int);
void yyerror(char*);
int yylex(void);
int yyparse(void);
+int parse(void);
diff --git a/src/cmd/rc/io.c b/src/cmd/rc/io.c
index bb8af4ab..c2e9d7b4 100644
--- a/src/cmd/rc/io.c
+++ b/src/cmd/rc/io.c
@@ -44,7 +44,10 @@ pfmt(io *f, char *fmt, ...)
pstr(f, va_arg(ap, char *));
break;
case 't':
- pcmd(f, va_arg(ap, struct tree *));
+ pcmd(f, va_arg(ap, tree *));
+ break;
+ case 'u':
+ pcmdu(f, va_arg(ap, tree *));
break;
case 'v':
pval(f, va_arg(ap, struct word *));
diff --git a/src/cmd/rc/io.h b/src/cmd/rc/io.h
index 21cc6b8e..6c75cc5b 100644
--- a/src/cmd/rc/io.h
+++ b/src/cmd/rc/io.h
@@ -25,6 +25,7 @@ void pquo(io*, char*);
void pwrd(io*, char*);
void pstr(io*, char*);
void pcmd(io*, tree*);
+void pcmdu(io*, tree*);
void pval(io*, word*);
void pfnc(io*, thread*);
void pfmt(io*, char*, ...);
diff --git a/src/cmd/rc/lex.c b/src/cmd/rc/lex.c
index 973ddee8..e4410c00 100644
--- a/src/cmd/rc/lex.c
+++ b/src/cmd/rc/lex.c
@@ -102,15 +102,17 @@ pprompt(void)
doprompt = 0;
}
-void
+int
skipwhite(void)
{
- int c;
+ int c, skipped;
+ skipped = 0;
for(;;){
c = nextc();
/* Why did this used to be if(!inquote && c=='#') ?? */
if(c=='#'){
incomm = 1;
+ skipped = 1;
for(;;){
c = nextc();
if(c=='\n' || c==EOF) {
@@ -120,9 +122,12 @@ skipwhite(void)
advance();
}
}
- if(c==' ' || c=='\t')
+ if(c==' ' || c=='\t') {
+ skipped = 1;
advance();
- else return;
+ }
+ else
+ return skipped;
}
}
@@ -188,7 +193,7 @@ yylex(void)
{
int c, d = nextc();
char *w = tok;
- struct tree *t;
+ tree *t;
yylval.tree = 0;
/*
* Embarassing sneakiness: if the last token read was a quoted or unquoted
@@ -197,11 +202,11 @@ yylex(void)
* if the next character is the first character of a simple or compound word,
* we insert a `^' before it.
*/
- if(lastword){
+ if(lastword && flag['Y']){
lastword = 0;
if(d=='('){
advance();
- strcpy(tok, "( [SUB]");
+ strcpy(tok, "(");
return SUB;
}
if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){
@@ -210,7 +215,8 @@ yylex(void)
}
}
inquote = 0;
- skipwhite();
+ if(skipwhite() && !flag['Y'])
+ return ' ';
switch(c = advance()){
case EOF:
lastdol = 0;
@@ -231,7 +237,8 @@ yylex(void)
case '&':
lastdol = 0;
if(nextis('&')){
- skipnl();
+ if(flag['Y'])
+ skipnl();
strcpy(tok, "&&");
return ANDAND;
}
@@ -240,7 +247,8 @@ yylex(void)
case '|':
lastdol = 0;
if(nextis(c)){
- skipnl();
+ if(flag['Y'])
+ skipnl();
strcpy(tok, "||");
return OROR;
}
@@ -329,8 +337,13 @@ yylex(void)
}
*w='\0';
yylval.tree = t;
- if(t->type==PIPE)
+ if(t->type==PIPE && flag['Y'])
skipnl();
+ if(t->type==REDIR) {
+ skipwhite();
+ if(nextc() == '{')
+ t->type = REDIRW;
+ }
return t->type;
case '\'':
lastdol = 0;
diff --git a/src/cmd/rc/mkfile b/src/cmd/rc/mkfile
index 79b407f8..c1e77aef 100644
--- a/src/cmd/rc/mkfile
+++ b/src/cmd/rc/mkfile
@@ -10,6 +10,7 @@ OFILES=\
here.$O\
io.$O\
lex.$O\
+ parse.$O\
pcmd.$O\
pfnc.$O\
simple.$O\
diff --git a/src/cmd/rc/parse.c b/src/cmd/rc/parse.c
new file mode 100644
index 00000000..dd102190
--- /dev/null
+++ b/src/cmd/rc/parse.c
@@ -0,0 +1,552 @@
+#include "rc.h"
+#include "io.h"
+#include "fns.h"
+
+static tree* body(int tok, int *ptok);
+static tree* brace(int tok);
+static tree* cmd(int tok, int *ptok);
+static tree* cmd2(int tok, int *ptok);
+static tree* cmd3(int tok, int *ptok);
+static tree* cmds(int tok, int *ptok, int nlok);
+static tree* epilog(int tok, int *ptok);
+static int iswordtok(int tok);
+static tree* line(int tok, int *ptok);
+static tree* paren(int tok);
+static tree* yyredir(int tok, int *ptok);
+static tree* yyword(int tok, int *ptok, int eqok);
+static tree* word1(int tok, int *ptok);
+static tree* words(int tok, int *ptok);
+
+static jmp_buf yyjmp;
+
+static int
+dropnl(int tok)
+{
+ while(tok == ' ' || tok == '\n')
+ tok = yylex();
+ return tok;
+}
+
+static int
+dropsp(int tok)
+{
+ while(tok == ' ')
+ tok = yylex();
+ return tok;
+}
+
+static void
+syntax(int tok)
+{
+ USED(tok);
+ yyerror("syntax error");
+ longjmp(yyjmp, 1);
+}
+
+int
+parse(void)
+{
+ tree *t;
+ int tok;
+
+ if(setjmp(yyjmp))
+ return 1;
+
+ // rc: { return 1;}
+ // | line '\n' {return !compile($1);}
+
+ tok = dropsp(yylex());
+ if(tok == EOF)
+ return 1;
+ t = line(tok, &tok);
+ if(tok != '\n')
+ yyerror("missing newline at end of line");
+ yylval.tree = t;
+ return !compile(t);
+}
+
+static tree*
+line(int tok, int *ptok)
+{
+ return cmds(tok, ptok, 0);
+}
+
+static tree*
+body(int tok, int *ptok)
+{
+ return cmds(tok, ptok, 1);
+}
+
+static tree*
+cmds(int tok, int *ptok, int nlok)
+{
+ tree *t, **last, *t2;
+
+ // line: cmd
+ // | cmdsa line {$$=tree2(';', $1, $2);}
+ // cmdsa: cmd ';'
+ // | cmd '&' {$$=tree1('&', $1);}
+
+ // body: cmd
+ // | cmdsan body {$$=tree2(';', $1, $2);}
+ // cmdsan: cmdsa
+ // | cmd '\n'
+
+ t = nil;
+ last = nil;
+ for(;;) {
+ t2 = cmd(tok, &tok);
+ if(tok == '&')
+ t2 = tree1('&', t2);
+ if(t2 != nil) {
+ // slot into list t
+ if(last == nil) {
+ t = t2;
+ last = &t;
+ } else {
+ *last = tree2(';', *last, t2);
+ last = &(*last)->child[1];
+ }
+ }
+ if(tok != ';' && tok != '&' && (!nlok || tok != '\n'))
+ break;
+ tok = yylex();
+ }
+ *ptok = tok;
+ return t;
+}
+
+static tree*
+brace(int tok)
+{
+ tree *t;
+
+ // brace: '{' body '}' {$$=tree1(BRACE, $2);}
+
+ tok = dropsp(tok);
+ if(tok != '{')
+ syntax(tok);
+ t = body(yylex(), &tok);
+ if(tok != '}')
+ syntax(tok);
+ return tree1(BRACE, t);
+}
+
+static tree*
+paren(int tok)
+{
+ tree *t;
+
+ // paren: '(' body ')' {$$=tree1(PCMD, $2);}
+
+ tok = dropsp(tok);
+ if(tok != '(')
+ syntax(tok);
+ t = body(yylex(), &tok);
+ if(tok != ')')
+ syntax(tok);
+ return tree1(PCMD, t);
+}
+
+static tree*
+epilog(int tok, int *ptok)
+{
+ tree *t, *r;
+
+ // epilog: {$$=0;}
+ // | redir epilog {$$=mung2($1, $1->child[0], $2);}
+
+ if(tok != REDIR && tok != DUP) {
+ *ptok = tok;
+ return nil;
+ }
+
+ r = yyredir(tok, &tok);
+ t = epilog(tok, &tok);
+ *ptok = tok;
+ return mung2(r, r->child[0], t);
+}
+
+static tree*
+yyredir(int tok, int *ptok)
+{
+ tree *r, *w;
+
+ // redir: REDIR word {$$=mung1($1, $1->rtype==HERE?heredoc($2):$2);}
+ // | DUP
+
+ switch(tok) {
+ default:
+ syntax(tok);
+ case DUP:
+ r = yylval.tree;
+ *ptok = dropsp(yylex());
+ break;
+ case REDIR:
+ r = yylval.tree;
+ w = yyword(yylex(), &tok, 1);
+ *ptok = dropsp(tok);
+ r = mung1(r, r->rtype==HERE?heredoc(w):w);
+ break;
+ }
+ return r;
+}
+
+static tree*
+cmd(int tok, int *ptok)
+{
+ int op;
+ tree *t1, *t2;
+
+ // | cmd ANDAND cmd {$$=tree2(ANDAND, $1, $3);}
+ // | cmd OROR cmd {$$=tree2(OROR, $1, $3);}
+
+ tok = dropsp(tok);
+ t1 = cmd2(tok, &tok);
+ while(tok == ANDAND || tok == OROR) {
+ op = tok;
+ t2 = cmd2(dropnl(yylex()), &tok);
+ t1 = tree2(op, t1, t2);
+ }
+ *ptok = tok;
+ return t1;
+}
+
+static tree*
+cmd2(int tok, int *ptok)
+{
+ tree *t1, *t2, *t3;
+
+ // | cmd PIPE cmd {$$=mung2($2, $1, $3);}
+ t1 = cmd3(tok, &tok);
+ while(tok == PIPE) {
+ t2 = yylval.tree;
+ t3 = cmd3(dropnl(yylex()), &tok);
+ t1 = mung2(t2, t1, t3);
+ }
+ *ptok = tok;
+ return t1;
+}
+
+static tree*
+cmd3(int tok, int *ptok)
+{
+ tree *t1, *t2, *t3, *t4;
+
+ tok = dropsp(tok);
+ switch(tok) {
+ case ';':
+ case '&':
+ case '\n':
+ *ptok = tok;
+ return nil;
+
+ case IF:
+ // | IF paren {skipnl();} cmd {$$=mung2($1, $2, $4);}
+ // | IF NOT {skipnl();} cmd {$$=mung1($2, $4);}
+ t1 = yylval.tree;
+ tok = dropsp(yylex());
+ if(tok == NOT) {
+ t1 = yylval.tree;
+ t2 = cmd(dropnl(yylex()), ptok);
+ return mung1(t1, t2);
+ }
+ t2 = paren(tok);
+ t3 = cmd(dropnl(yylex()), ptok);
+ return mung2(t1, t2, t3);
+
+ case FOR:
+ // | FOR '(' word IN words ')' {skipnl();} cmd
+ // {$$=mung3($1, $3, $5 ? $5 : tree1(PAREN, $5), $8);}
+ // | FOR '(' word ')' {skipnl();} cmd
+ // {$$=mung3($1, $3, (tree *)0, $6);}
+ t1 = yylval.tree;
+ tok = dropsp(yylex());
+ if(tok != '(')
+ syntax(tok);
+ t2 = yyword(yylex(), &tok, 1);
+ switch(tok) {
+ default:
+ syntax(tok);
+ case ')':
+ t3 = nil;
+ break;
+ case IN:
+ t3 = words(yylex(), &tok);
+ if(t3 == nil)
+ t3 = tree1(PAREN, nil);
+ if(tok != ')')
+ syntax(tok);
+ break;
+ }
+ t4 = cmd(dropnl(yylex()), ptok);
+ return mung3(t1, t2, t3, t4);
+
+ case WHILE:
+ // | WHILE paren {skipnl();} cmd
+ // {$$=mung2($1, $2, $4);}
+ t1 = yylval.tree;
+ t2 = paren(yylex());
+ t3 = cmd(dropnl(yylex()), ptok);
+ return mung2(t1, t2, t3);
+
+ case SWITCH:
+ // | SWITCH word {skipnl();} brace
+ // {$$=tree2(SWITCH, $2, $4);}
+ t1 = yyword(yylex(), &tok, 1);
+ tok = dropnl(tok); // doesn't work in yacc grammar but works here!
+ t2 = brace(tok);
+ *ptok = dropsp(yylex());
+ return tree2(SWITCH, t1, t2);
+ // Note: cmd: a && for(x) y && b is a && {for (x) {y && b}}.
+ return cmd(tok, ptok);
+
+ case FN:
+ // | FN words brace {$$=tree2(FN, $2, $3);}
+ // | FN words {$$=tree1(FN, $2);}
+ t1 = words(yylex(), &tok);
+ if(tok != '{') {
+ *ptok = tok;
+ return tree1(FN, t1);
+ }
+ t2 = brace(tok);
+ *ptok = dropsp(yylex());
+ return tree2(FN, t1, t2);
+
+ case TWIDDLE:
+ // | TWIDDLE word words {$$=mung2($1, $2, $3);}
+ t1 = yylval.tree;
+ t2 = yyword(yylex(), &tok, 1);
+ t3 = words(tok, ptok);
+ return mung2(t1, t2, t3);
+
+ case BANG:
+ case SUBSHELL:
+ // | BANG cmd {$$=mung1($1, $2);}
+ // | SUBSHELL cmd {$$=mung1($1, $2);}
+ // Note: cmd2: ! x | y is !{x | y} not {!x} | y.
+ t1 = yylval.tree;
+ return mung1(t1, cmd2(yylex(), ptok));
+
+ case REDIR:
+ case DUP:
+ // | redir cmd %prec BANG {$$=mung2($1, $1->child[0], $2);}
+ // Note: cmd2: {>x echo a | tr a-z A-Z} writes A to x.
+ t1 = yyredir(tok, &tok);
+ t2 = cmd2(tok, ptok);
+ return mung2(t1, t1->child[0], t2);
+
+ case '{':
+ // | brace epilog {$$=epimung($1, $2);}
+ t1 = brace(tok);
+ tok = dropsp(yylex());
+ t2 = epilog(tok, ptok);
+ return epimung(t1, t2);
+ }
+
+ if(!iswordtok(tok)) {
+ *ptok = tok;
+ return nil;
+ }
+
+ // cmd: ...
+ // | simple {$$=simplemung($1);}
+ // | assign cmd %prec BANG {$$=mung3($1, $1->child[0], $1->child[1], $2);}
+ // assign: first '=' word {$$=tree2('=', $1, $3);}
+ // Note: first is same as word except for disallowing all the leading keywords,
+ // but all those keywords have been picked off in the switch above.
+ // Except NOT, but disallowing that in yacc was likely a mistake anyway:
+ // there's no ambiguity in not=1 or not x y z.
+ t1 = yyword(tok, &tok, 0);
+ if(tok == '=') {
+ // assignment
+ // Note: cmd2: {x=1 true | echo $x} echoes 1.
+ t1 = tree2('=', t1, yyword(yylex(), &tok, 1));
+ t2 = cmd2(tok, ptok);
+ return mung3(t1, t1->child[0], t1->child[1], t2);
+ }
+
+ // simple: first
+ // | simple word {$$=tree2(ARGLIST, $1, $2);}
+ // | simple redir {$$=tree2(ARGLIST, $1, $2);}
+ for(;;) {
+ if(tok == REDIR || tok == DUP) {
+ t1 = tree2(ARGLIST, t1, yyredir(tok, &tok));
+ } else if(iswordtok(tok)) {
+ t1 = tree2(ARGLIST, t1, yyword(tok, &tok, 1));
+ } else {
+ break;
+ }
+ }
+ *ptok = tok;
+ return simplemung(t1);
+}
+
+static tree*
+words(int tok, int *ptok)
+{
+ tree *t;
+
+ // words: {$$=(tree*)0;}
+ // | words word {$$=tree2(WORDS, $1, $2);}
+
+ t = nil;
+ tok = dropsp(tok);
+ while(iswordtok(tok))
+ t = tree2(WORDS, t, yyword(tok, &tok, 1));
+ *ptok = tok;
+ return t;
+}
+
+static tree*
+yyword(int tok, int *ptok, int eqok)
+{
+ tree *t;
+
+ // word: keyword {lastword=1; $1->type=WORD;}
+ // | comword
+ // | word '^' word {$$=tree2('^', $1, $3);}
+ // comword: '$' word {$$=tree1('$', $2);}
+ // | '$' word SUB words ')' {$$=tree2(SUB, $2, $4);}
+ // | '"' word {$$=tree1('"', $2);}
+ // | COUNT word {$$=tree1(COUNT, $2);}
+ // | WORD
+ // | '`' brace {$$=tree1('`', $2);}
+ // | '(' words ')' {$$=tree1(PAREN, $2);}
+ // | REDIR brace {$$=mung1($1, $2); $$->type=PIPEFD;}
+ // keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN
+ //
+ // factored into:
+ //
+ // word: word1
+ // | word '^' word1
+ //
+ // word1: keyword | comword
+
+ t = word1(tok, &tok);
+ if(tok == '=' && !eqok)
+ goto out;
+ for(;;) {
+ if(iswordtok(tok)) {
+ // No free carats around parens.
+ if(t->type == PAREN || tok == '(')
+ syntax(tok);
+ t = tree2('^', t, word1(tok, &tok));
+ continue;
+ }
+ tok = dropsp(tok);
+ if(tok == '^') {
+ t = tree2('^', t, word1(yylex(), &tok));
+ continue;
+ }
+ break;
+ }
+out:
+ *ptok = dropsp(tok);
+ return t;
+}
+
+static tree*
+word1(int tok, int *ptok)
+{
+ tree *w, *sub, *t;
+
+ tok = dropsp(tok);
+ switch(tok) {
+ default:
+ syntax(tok);
+
+ case WORD:
+ case FOR:
+ case IN:
+ case WHILE:
+ case IF:
+ case NOT:
+ case TWIDDLE:
+ case BANG:
+ case SUBSHELL:
+ case SWITCH:
+ case FN:
+ // | WORD
+ // keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN
+ t = yylval.tree;
+ t->type = WORD;
+ *ptok = yylex();
+ return t;
+
+ case '=':
+ *ptok = yylex();
+ return token("=", WORD);
+
+ case '$':
+ // comword: '$' word1 {$$=tree1('$', $2);}
+ // | '$' word1 SUB words ')' {$$=tree2(SUB, $2, $4);}
+ w = word1(yylex(), &tok);
+ if(tok == '(') {
+ sub = words(yylex(), &tok);
+ if(tok != ')')
+ syntax(tok);
+ *ptok = yylex();
+ return tree2(SUB, w, sub);
+ }
+ *ptok = tok;
+ return tree1('$', w);
+
+ case '"':
+ // | '"' word1 {$$=tree1('"', $2);}
+ return tree1('"', word1(yylex(), ptok));
+
+ case COUNT:
+ // | COUNT word1 {$$=tree1(COUNT, $2);}
+ return tree1(COUNT, word1(yylex(), ptok));
+
+ case '`':
+ // | '`' brace {$$=tree1('`', $2);}
+ t = tree1('`', brace(yylex()));
+ *ptok = yylex();
+ return t;
+
+ case '(':
+ // | '(' words ')' {$$=tree1(PAREN, $2);}
+ t = tree1(PAREN, words(yylex(), &tok));
+ if(tok != ')')
+ syntax(tok);
+ *ptok = yylex();
+ return t;
+
+ case REDIRW:
+ // | REDIRW brace {$$=mung1($1, $2); $$->type=PIPEFD;}
+ t = yylval.tree;
+ t = mung1(t, brace(yylex()));
+ t->type = PIPEFD;
+ *ptok = yylex();
+ return t;
+ }
+}
+
+static int
+iswordtok(int tok)
+{
+ switch(tok) {
+ case FOR:
+ case IN:
+ case WHILE:
+ case IF:
+ case NOT:
+ case TWIDDLE:
+ case BANG:
+ case SUBSHELL:
+ case SWITCH:
+ case FN:
+ case '$':
+ case '"':
+ case COUNT:
+ case WORD:
+ case '`':
+ case '(':
+ case REDIRW:
+ case '=':
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/cmd/rc/pcmd.c b/src/cmd/rc/pcmd.c
index 8caf60a2..cae84737 100644
--- a/src/cmd/rc/pcmd.c
+++ b/src/cmd/rc/pcmd.c
@@ -145,3 +145,121 @@ pcmd(io *f, tree *t)
break;
}
}
+
+void
+pcmdu(io *f, tree *t) /* unambiguous */
+{
+ if(t==0) {
+ pfmt(f, "<nil>");
+ return;
+ }
+
+ switch(t->type){
+ default: pfmt(f, "(bad %d %p %p %p)", t->type, c0, c1, c2);
+ break;
+ case '$': pfmt(f, "($ %u)", c0);
+ break;
+ case '"': pfmt(f, "($\" %u)", c0);
+ break;
+ case '&': pfmt(f, "(& %u)", c0);
+ break;
+ case '^': pfmt(f, "(^ %u %u)", c0, c1);
+ break;
+ case '`': pfmt(f, "(` %u)", c0);
+ break;
+ case ANDAND: pfmt(f, "(&& %u %u)", c0, c1);
+ break;
+ case BANG: pfmt(f, "(! %u)", c0);
+ break;
+ case BRACE: pfmt(f, "(brace %u)", c0);
+ break;
+ case COUNT: pfmt(f, "($# %u)", c0);
+ break;
+ case FN: pfmt(f, "(fn %u %u)", c0, c1);
+ break;
+ case IF: pfmt(f, "(if %u %u)", c0, c1);
+ break;
+ case NOT: pfmt(f, "(if not %u)", c0);
+ break;
+ case OROR: pfmt(f, "(|| %u %u)", c0, c1);
+ break;
+ case PCMD:
+ case PAREN: pfmt(f, "(paren %u)", c0);
+ break;
+ case SUB: pfmt(f, "($sub %u %u)", c0, c1);
+ break;
+ case SIMPLE: pfmt(f, "(simple %u)", c0);
+ break;
+ case SUBSHELL: pfmt(f, "(@ %u)", c0);
+ break;
+ case SWITCH: pfmt(f, "(switch %u %u)", c0, c1);
+ break;
+ case TWIDDLE: pfmt(f, "(~ %u %u)", c0, c1);
+ break;
+ case WHILE: pfmt(f, "(while %u %u)", c0, c1);
+ break;
+ case ARGLIST:
+ pfmt(f, "(arglist %u %u)", c0, c1);
+ break;
+ case ';':
+ pfmt(f, "(; %u %u)", c0, c1);
+ break;
+ case WORDS:
+ pfmt(f, "(words %u %u)", c0, c1);
+ break;
+ case FOR:
+ pfmt(f, "(for %u %u %u)", c0, c1, c2);
+ break;
+ case WORD:
+ if(t->quoted)
+ pfmt(f, "%Q", t->str);
+ else pdeglob(f, t->str);
+ break;
+ case DUP:
+ if(t->rtype==DUPFD)
+ pfmt(f, "(>[%d=%d]", t->fd1, t->fd0); /* yes, fd1, then fd0; read lex.c */
+ else
+ pfmt(f, "(>[%d=]", t->fd0); /*)*/
+ pfmt(f, " %u)", c1);
+ break;
+ case PIPEFD:
+ case REDIR:
+ pfmt(f, "(");
+ switch(t->rtype){
+ case HERE:
+ pchr(f, '<');
+ case READ:
+ case RDWR:
+ pchr(f, '<');
+ if(t->rtype==RDWR)
+ pchr(f, '>');
+ if(t->fd0!=0)
+ pfmt(f, "[%d]", t->fd0);
+ break;
+ case APPEND:
+ pchr(f, '>');
+ case WRITE:
+ pchr(f, '>');
+ if(t->fd0!=1)
+ pfmt(f, "[%d]", t->fd0);
+ break;
+ }
+ if(t->rtype == HERE)
+ pfmt(f, "HERE %u)", c1);
+ else
+ pfmt(f, "%u %u)", c0, c1);
+ break;
+ case '=':
+ pfmt(f, "(%u=%u %u)", c0, c1, c2);
+ break;
+ case PIPE:
+ pfmt(f, "(|");
+ if(t->fd1==0){
+ if(t->fd0!=1)
+ pfmt(f, "[%d]", t->fd0);
+ }
+ else pfmt(f, "[%d=%d]", t->fd0, t->fd1);
+ pfmt(f, " %u %u", c0, c1);
+ break;
+ }
+}
diff --git a/src/cmd/rc/simple.c b/src/cmd/rc/simple.c
index d587227a..a7d78f6f 100644
--- a/src/cmd/rc/simple.c
+++ b/src/cmd/rc/simple.c
@@ -322,6 +322,7 @@ execdot(void)
static int first = 1;
char file[512];
word *path;
+
if(first){
dotcmds[0].i = 1;
dotcmds[1].f = Xmark;
diff --git a/src/cmd/rc/syn.y b/src/cmd/rc/syn.y
index c7de3531..5c98ef80 100644
--- a/src/cmd/rc/syn.y
+++ b/src/cmd/rc/syn.y
@@ -1,5 +1,5 @@
%term FOR IN WHILE IF NOT TWIDDLE BANG SUBSHELL SWITCH FN
-%term WORD REDIR DUP PIPE SUB
+%term WORD REDIR REDIRW DUP PIPE SUB
%term SIMPLE ARGLIST WORDS BRACE PAREN PCMD PIPEFD /* not used in syntax */
/* operator priorities -- lowest first */
%left IF WHILE FOR SWITCH ')' NOT
@@ -19,7 +19,7 @@
%type<tree> line paren brace body cmdsa cmdsan assign epilog redir
%type<tree> cmd simple first word comword keyword words
%type<tree> NOT FOR IN WHILE IF TWIDDLE BANG SUBSHELL SWITCH FN
-%type<tree> WORD REDIR DUP PIPE
+%type<tree> WORD REDIR REDIRW DUP PIPE
%%
rc: { return 1;}
| line '\n' {return !compile($1);}
@@ -45,7 +45,7 @@ cmd: {$$=0;}
| IF NOT {skipnl();} cmd {$$=mung1($2, $4);}
| FOR '(' word IN words ')' {skipnl();} cmd
/*
- * if ``words'' is nil, we need a tree element to distinguish between
+ * if ``words'' is nil, we need a tree element to distinguish between
* for(i in ) and for(i), the former being a loop over the empty set
* and the latter being the implicit argument loop. so if $5 is nil
* (the empty set), we represent it as "()". don't parenthesize non-nil
@@ -73,7 +73,7 @@ cmd: {$$=0;}
simple: first
| simple word {$$=tree2(ARGLIST, $1, $2);}
| simple redir {$$=tree2(ARGLIST, $1, $2);}
-first: comword
+first: comword
| first '^' word {$$=tree2('^', $1, $3);}
word: keyword {lastword=1; $1->type=WORD;}
| comword
@@ -85,7 +85,7 @@ comword: '$' word {$$=tree1('$', $2);}
| WORD
| '`' brace {$$=tree1('`', $2);}
| '(' words ')' {$$=tree1(PAREN, $2);}
-| REDIR brace {$$=mung1($1, $2); $$->type=PIPEFD;}
+| REDIRW brace {$$=mung1($1, $2); $$->type=PIPEFD;}
keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN
words: {$$=(struct tree*)0;}
| words word {$$=tree2(WORDS, $1, $2);}
diff --git a/src/cmd/rc/test.rc b/src/cmd/rc/test.rc
new file mode 100644
index 00000000..4a33d87c
--- /dev/null
+++ b/src/cmd/rc/test.rc
@@ -0,0 +1,93 @@
+# test for parser
+
+a
+a b
+a|b
+a | b
+{a; b; c}
+x=y a && b || c
+x=y a | b | c
+x=y for(i) a | b
+>x for(i) a | b
+>x a || b && c
+a >x || b && c
+a | for(i) b | c
+fn x {y; z} | b && c
+if (x) y
+if not z
+`{} >x >[1=2]y >[3=] z <w
+~ x y && z
+x | y && z
+x | y || z
+! x | y | z
+@ x | y | z
+x | ! y | z
+x | @y | z
+! x | if(y) z
+@ x | if(y) z
+x=1 y | if(z) w
+a'b' c
+a^'b'^ c
+a^'b' c
+$$x
+$x($y)
+$"x
+$#x
+$#$x
+-$x-
+`{a;b;c}
+<{a;b}
+x for in while if not ~ ! @ switch fn
+x not$y
+a;b;c
+if(x)
+y
+if(x)
+{
+y
+}
+if not
+z
+for(x)
+y
+for(x in y)
+z
+while(x)
+y
+# yacc doesn't accept a newline before the brace
+# even though the rule is written as if it would
+switch x {
+}
+switch (x) {
+}
+z
+x &&
+y
+x ||
+y
+x |
+y
+switch x {y} && z
+switch x {} | y
+
+OPTIONS=$OPTIONS' /axescount '^`{echo $1 | sed s/-a//}^' def'
+
+# bug in old printfont script - expected more free carats
+# OPTIONS=$OPTIONS' /axescount '`{echo $1 | sed s/-a//}' def'
+
+(x) = y
+x=y
+x = y
+
+# works now!
+# x y=z
+# x =y
+# x -flag=y
+
+>z x | y
+
+# rejected now, was like parens were spaces before.
+# echo Formatting Venti arenas and indices (this takes a while).
+
+
+# echo $STATLINE(1)^$STATLINE(3)' '$STATLINE(2)' '$STATLINE(4)' '$LSLINE(6)
diff --git a/unix/NOTICE.bio b/unix/NOTICE.bio
index 42bc739f..24bef790 100644
--- a/unix/NOTICE.bio
+++ b/unix/NOTICE.bio
@@ -30,5 +30,5 @@ THE SOFTWARE.
----
This software is also made available under the Lucent Public License
-version 1.02; see http://plan9.bell-labs.com/plan9dist/license.html
+version 1.02; see http://9p.io/plan9/license.html
diff --git a/unix/NOTICE.fmt b/unix/NOTICE.fmt
index 43f24ce6..e4024070 100644
--- a/unix/NOTICE.fmt
+++ b/unix/NOTICE.fmt
@@ -21,5 +21,5 @@ to Russ Cox <rsc@swtch.com>.
----
This software is also made available under the Lucent Public License
-version 1.02; see http://plan9.bell-labs.com/plan9dist/license.html
+version 1.02; see http://9p.io/plan9/license.html
diff --git a/unix/NOTICE.mk b/unix/NOTICE.mk
index 017fb80e..4fb98fb7 100644
--- a/unix/NOTICE.mk
+++ b/unix/NOTICE.mk
@@ -30,5 +30,5 @@ THE SOFTWARE.
----
This software is also made available under the Lucent Public License
-version 1.02; see http://plan9.bell-labs.com/plan9dist/license.html
+version 1.02; see http://9p.io/plan9/license.html
diff --git a/unix/NOTICE.regexp b/unix/NOTICE.regexp
index 02856cfc..686891c1 100644
--- a/unix/NOTICE.regexp
+++ b/unix/NOTICE.regexp
@@ -21,5 +21,5 @@ to Russ Cox <rsc@swtch.com>.
----
This software is also made available under the Lucent Public License
-version 1.02; see http://plan9.bell-labs.com/plan9dist/license.html
+version 1.02; see http://9p.io/plan9/license.html
diff --git a/unix/NOTICE.utf b/unix/NOTICE.utf
index 43f24ce6..e4024070 100644
--- a/unix/NOTICE.utf
+++ b/unix/NOTICE.utf
@@ -21,5 +21,5 @@ to Russ Cox <rsc@swtch.com>.
----
This software is also made available under the Lucent Public License
-version 1.02; see http://plan9.bell-labs.com/plan9dist/license.html
+version 1.02; see http://9p.io/plan9/license.html