mirror of
https://git.suckless.org/9base
synced 2025-09-01 21:03:48 -07:00
added sam
This commit is contained in:
37
sam/Makefile
Normal file
37
sam/Makefile
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# sam - sam shell unix port from plan9
|
||||||
|
# Depends on ../lib9
|
||||||
|
|
||||||
|
TARG = sam
|
||||||
|
OFILES= sam.o address.o buff.o cmd.o disk.o error.o file.o\
|
||||||
|
io.o list.o mesg.o moveto.o multi.o rasp.o regexp.o\
|
||||||
|
shell.o string.o sys.o unix.o util.o xec.o
|
||||||
|
MANFILES = sam.1
|
||||||
|
|
||||||
|
include ../config.mk
|
||||||
|
|
||||||
|
all: ${TARG}
|
||||||
|
@strip ${TARG}
|
||||||
|
@echo built ${TARG}
|
||||||
|
|
||||||
|
install: ${TARG}
|
||||||
|
@mkdir -p ${DESTDIR}${PREFIX}/bin
|
||||||
|
@cp -f ${TARG} ${DESTDIR}${PREFIX}/bin/
|
||||||
|
@chmod 755 ${DESTDIR}${PREFIX}/bin/${TARG}
|
||||||
|
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
|
||||||
|
@cp -f ${MANFILES} ${DESTDIR}${MANPREFIX}/man1
|
||||||
|
@chmod 444 ${DESTDIR}${MANPREFIX}/man1/${MANFILES}
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -f ${DESTDIR}${PREFIX}/bin/${TARG}
|
||||||
|
rm -f ${DESTDIR}${PREFIX}/man1/${MANFILES}
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
@echo CC $*.c
|
||||||
|
@${CC} ${CFLAGS} -I../lib9 -I${PREFIX}/include -I../lib9 $*.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f ${OFILES} ${TARG}
|
||||||
|
|
||||||
|
${TARG}: ${OFILES}
|
||||||
|
@echo LD ${TARG}
|
||||||
|
@${CC} ${LDFLAGS} -o ${TARG} ${OFILES} -lm -L${PREFIX}/lib -L../lib9 -l9
|
29
sam/README
Normal file
29
sam/README
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
This is sam (not including samterm) from the 4th edition of Plan 9,
|
||||||
|
with changes so that it can be compiled under unix.
|
||||||
|
(Tested on Solaris 7 and Debian 3.0r1.)
|
||||||
|
|
||||||
|
Some extra libraries are needed. First, fetch libutf-2.0 and libfmt-2.0
|
||||||
|
from
|
||||||
|
http://pdos.lcs.mit.edu/~rsc/software/
|
||||||
|
|
||||||
|
(Beware that in libfmt/fmt.c there is a line that says:
|
||||||
|
'u', __ifmt, /* in Plan 9, __flagfmt */
|
||||||
|
Thus, sam will have to fmtinstall the other thing. Other ported programs
|
||||||
|
may have to do the same. The fmt library should probably print messages
|
||||||
|
about bad format characters to stderr, since no one seems to check the
|
||||||
|
return codes.)
|
||||||
|
|
||||||
|
Compile and install those two libraries.
|
||||||
|
Set PREFIX in the Makefile to match, then compile sam.
|
||||||
|
|
||||||
|
Your C compiler will emit many complaints of the form:
|
||||||
|
sam.c:496: warning: passing arg 1 of `bufread' from incompatible pointer type
|
||||||
|
|
||||||
|
This is because the Plan 9 compiler has a slightly different (better,
|
||||||
|
ala Oberon) type system than ISO C. Popular compilers generate the right
|
||||||
|
code, so in an act of civil disobediance I changed just enough to get
|
||||||
|
it to compile, but left the type errors in. Now the next C standard can
|
||||||
|
adopt this extension, because at least one important C program uses it!
|
||||||
|
|
||||||
|
-- Scott Schwartz, 4 July 2003
|
||||||
|
|
40
sam/_libc.h
Normal file
40
sam/_libc.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#define __USE_UNIX98 // for pread/pwrite, supposedly
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "utf.h"
|
||||||
|
#include "fmt.h"
|
||||||
|
|
||||||
|
#define nil 0
|
||||||
|
#define dup dup2
|
||||||
|
#define exec execv
|
||||||
|
#define seek lseek
|
||||||
|
#define getwd getcwd
|
||||||
|
#define USED(a)
|
||||||
|
#define SET(a)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
OREAD = 0,
|
||||||
|
OWRITE = 1,
|
||||||
|
ORDWR = 2,
|
||||||
|
OCEXEC = 4,
|
||||||
|
ORCLOSE = 8
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ERRMAX = 255
|
||||||
|
};
|
||||||
|
|
||||||
|
void exits(const char *);
|
||||||
|
void _exits(const char *);
|
||||||
|
int notify (void(*f)(void *, char *));
|
||||||
|
int create(char *, int, int);
|
||||||
|
int errstr(char *, int);
|
240
sam/address.c
Normal file
240
sam/address.c
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
#include "parse.h"
|
||||||
|
|
||||||
|
Address addr;
|
||||||
|
String lastpat;
|
||||||
|
int patset;
|
||||||
|
File *menu;
|
||||||
|
|
||||||
|
File *matchfile(String*);
|
||||||
|
Address charaddr(Posn, Address, int);
|
||||||
|
|
||||||
|
Address
|
||||||
|
address(Addr *ap, Address a, int sign)
|
||||||
|
{
|
||||||
|
File *f = a.f;
|
||||||
|
Address a1, a2;
|
||||||
|
|
||||||
|
do{
|
||||||
|
switch(ap->type){
|
||||||
|
case 'l':
|
||||||
|
case '#':
|
||||||
|
a = (*(ap->type=='#'?charaddr:lineaddr))(ap->num, a, sign);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '.':
|
||||||
|
a = f->dot;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '$':
|
||||||
|
a.r.p1 = a.r.p2 = f->b.nc;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\'':
|
||||||
|
a.r = f->mark;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '?':
|
||||||
|
sign = -sign;
|
||||||
|
if(sign == 0)
|
||||||
|
sign = -1;
|
||||||
|
/* fall through */
|
||||||
|
case '/':
|
||||||
|
nextmatch(f, ap->are, sign>=0? a.r.p2 : a.r.p1, sign);
|
||||||
|
a.r = sel.p[0];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '"':
|
||||||
|
a = matchfile(ap->are)->dot;
|
||||||
|
f = a.f;
|
||||||
|
if(f->unread)
|
||||||
|
load(f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '*':
|
||||||
|
a.r.p1 = 0, a.r.p2 = f->b.nc;
|
||||||
|
return a;
|
||||||
|
|
||||||
|
case ',':
|
||||||
|
case ';':
|
||||||
|
if(ap->left)
|
||||||
|
a1 = address(ap->left, a, 0);
|
||||||
|
else
|
||||||
|
a1.f = a.f, a1.r.p1 = a1.r.p2 = 0;
|
||||||
|
if(ap->type == ';'){
|
||||||
|
f = a1.f;
|
||||||
|
a = a1;
|
||||||
|
f->dot = a1;
|
||||||
|
}
|
||||||
|
if(ap->next)
|
||||||
|
a2 = address(ap->next, a, 0);
|
||||||
|
else
|
||||||
|
a2.f = a.f, a2.r.p1 = a2.r.p2 = f->b.nc;
|
||||||
|
if(a1.f != a2.f)
|
||||||
|
error(Eorder);
|
||||||
|
a.f = a1.f, a.r.p1 = a1.r.p1, a.r.p2 = a2.r.p2;
|
||||||
|
if(a.r.p2 < a.r.p1)
|
||||||
|
error(Eorder);
|
||||||
|
return a;
|
||||||
|
|
||||||
|
case '+':
|
||||||
|
case '-':
|
||||||
|
sign = 1;
|
||||||
|
if(ap->type == '-')
|
||||||
|
sign = -1;
|
||||||
|
if(ap->next==0 || ap->next->type=='+' || ap->next->type=='-')
|
||||||
|
a = lineaddr(1L, a, sign);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
panic("address");
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}while(ap = ap->next); /* assign = */
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nextmatch(File *f, String *r, Posn p, int sign)
|
||||||
|
{
|
||||||
|
compile(r);
|
||||||
|
if(sign >= 0){
|
||||||
|
if(!execute(f, p, INFINITY))
|
||||||
|
error(Esearch);
|
||||||
|
if(sel.p[0].p1==sel.p[0].p2 && sel.p[0].p1==p){
|
||||||
|
if(++p>f->b.nc)
|
||||||
|
p = 0;
|
||||||
|
if(!execute(f, p, INFINITY))
|
||||||
|
panic("address");
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(!bexecute(f, p))
|
||||||
|
error(Esearch);
|
||||||
|
if(sel.p[0].p1==sel.p[0].p2 && sel.p[0].p2==p){
|
||||||
|
if(--p<0)
|
||||||
|
p = f->b.nc;
|
||||||
|
if(!bexecute(f, p))
|
||||||
|
panic("address");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File *
|
||||||
|
matchfile(String *r)
|
||||||
|
{
|
||||||
|
File *f;
|
||||||
|
File *match = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i<file.nused; i++){
|
||||||
|
f = file.filepptr[i];
|
||||||
|
if(f == cmd)
|
||||||
|
continue;
|
||||||
|
if(filematch(f, r)){
|
||||||
|
if(match)
|
||||||
|
error(Emanyfiles);
|
||||||
|
match = f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!match)
|
||||||
|
error(Efsearch);
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
filematch(File *f, String *r)
|
||||||
|
{
|
||||||
|
char *c, buf[STRSIZE+100];
|
||||||
|
String *t;
|
||||||
|
|
||||||
|
c = Strtoc(&f->name);
|
||||||
|
sprint(buf, "%c%c%c %s\n", " '"[f->mod],
|
||||||
|
"-+"[f->rasp!=0], " ."[f==curfile], c);
|
||||||
|
free(c);
|
||||||
|
t = tmpcstr(buf);
|
||||||
|
Strduplstr(&genstr, t);
|
||||||
|
freetmpstr(t);
|
||||||
|
/* A little dirty... */
|
||||||
|
if(menu == 0)
|
||||||
|
menu = fileopen();
|
||||||
|
bufreset(&menu->b);
|
||||||
|
bufinsert(&menu->b, 0, genstr.s, genstr.n);
|
||||||
|
compile(r);
|
||||||
|
return execute(menu, 0, menu->b.nc);
|
||||||
|
}
|
||||||
|
|
||||||
|
Address
|
||||||
|
charaddr(Posn l, Address addr, int sign)
|
||||||
|
{
|
||||||
|
if(sign == 0)
|
||||||
|
addr.r.p1 = addr.r.p2 = l;
|
||||||
|
else if(sign < 0)
|
||||||
|
addr.r.p2 = addr.r.p1-=l;
|
||||||
|
else if(sign > 0)
|
||||||
|
addr.r.p1 = addr.r.p2+=l;
|
||||||
|
if(addr.r.p1<0 || addr.r.p2>addr.f->b.nc)
|
||||||
|
error(Erange);
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Address
|
||||||
|
lineaddr(Posn l, Address addr, int sign)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
int c;
|
||||||
|
File *f = addr.f;
|
||||||
|
Address a;
|
||||||
|
Posn p;
|
||||||
|
|
||||||
|
a.f = f;
|
||||||
|
if(sign >= 0){
|
||||||
|
if(l == 0){
|
||||||
|
if(sign==0 || addr.r.p2==0){
|
||||||
|
a.r.p1 = a.r.p2 = 0;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
a.r.p1 = addr.r.p2;
|
||||||
|
p = addr.r.p2-1;
|
||||||
|
}else{
|
||||||
|
if(sign==0 || addr.r.p2==0){
|
||||||
|
p = (Posn)0;
|
||||||
|
n = 1;
|
||||||
|
}else{
|
||||||
|
p = addr.r.p2-1;
|
||||||
|
n = filereadc(f, p++)=='\n';
|
||||||
|
}
|
||||||
|
while(n < l){
|
||||||
|
if(p >= f->b.nc)
|
||||||
|
error(Erange);
|
||||||
|
if(filereadc(f, p++) == '\n')
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
a.r.p1 = p;
|
||||||
|
}
|
||||||
|
while(p < f->b.nc && filereadc(f, p++)!='\n')
|
||||||
|
;
|
||||||
|
a.r.p2 = p;
|
||||||
|
}else{
|
||||||
|
p = addr.r.p1;
|
||||||
|
if(l == 0)
|
||||||
|
a.r.p2 = addr.r.p1;
|
||||||
|
else{
|
||||||
|
for(n = 0; n<l; ){ /* always runs once */
|
||||||
|
if(p == 0){
|
||||||
|
if(++n != l)
|
||||||
|
error(Erange);
|
||||||
|
}else{
|
||||||
|
c = filereadc(f, p-1);
|
||||||
|
if(c != '\n' || ++n != l)
|
||||||
|
p--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a.r.p2 = p;
|
||||||
|
if(p > 0)
|
||||||
|
p--;
|
||||||
|
}
|
||||||
|
while(p > 0 && filereadc(f, p-1)!='\n') /* lines start after a newline */
|
||||||
|
p--;
|
||||||
|
a.r.p1 = p;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
302
sam/buff.c
Normal file
302
sam/buff.c
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
Slop = 100 /* room to grow with reallocation */
|
||||||
|
};
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
sizecache(Buffer *b, uint n)
|
||||||
|
{
|
||||||
|
if(n <= b->cmax)
|
||||||
|
return;
|
||||||
|
b->cmax = n+Slop;
|
||||||
|
b->c = runerealloc(b->c, b->cmax);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
addblock(Buffer *b, uint i, uint n)
|
||||||
|
{
|
||||||
|
if(i > b->nbl)
|
||||||
|
panic("internal error: addblock");
|
||||||
|
|
||||||
|
b->bl = realloc(b->bl, (b->nbl+1)*sizeof b->bl[0]);
|
||||||
|
if(i < b->nbl)
|
||||||
|
memmove(b->bl+i+1, b->bl+i, (b->nbl-i)*sizeof(Block*));
|
||||||
|
b->bl[i] = disknewblock(disk, n);
|
||||||
|
b->nbl++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
delblock(Buffer *b, uint i)
|
||||||
|
{
|
||||||
|
if(i >= b->nbl)
|
||||||
|
panic("internal error: delblock");
|
||||||
|
|
||||||
|
diskrelease(disk, b->bl[i]);
|
||||||
|
b->nbl--;
|
||||||
|
if(i < b->nbl)
|
||||||
|
memmove(b->bl+i, b->bl+i+1, (b->nbl-i)*sizeof(Block*));
|
||||||
|
b->bl = realloc(b->bl, b->nbl*sizeof b->bl[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Move cache so b->cq <= q0 < b->cq+b->cnc.
|
||||||
|
* If at very end, q0 will fall on end of cache block.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
flush(Buffer *b)
|
||||||
|
{
|
||||||
|
if(b->cdirty || b->cnc==0){
|
||||||
|
if(b->cnc == 0)
|
||||||
|
delblock(b, b->cbi);
|
||||||
|
else
|
||||||
|
diskwrite(disk, &b->bl[b->cbi], b->c, b->cnc);
|
||||||
|
b->cdirty = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
setcache(Buffer *b, uint q0)
|
||||||
|
{
|
||||||
|
Block **blp, *bl;
|
||||||
|
uint i, q;
|
||||||
|
|
||||||
|
if(q0 > b->nc)
|
||||||
|
panic("internal error: setcache");
|
||||||
|
/*
|
||||||
|
* flush and reload if q0 is not in cache.
|
||||||
|
*/
|
||||||
|
if(b->nc == 0 || (b->cq<=q0 && q0<b->cq+b->cnc))
|
||||||
|
return;
|
||||||
|
/*
|
||||||
|
* if q0 is at end of file and end of cache, continue to grow this block
|
||||||
|
*/
|
||||||
|
if(q0==b->nc && q0==b->cq+b->cnc && b->cnc<=Maxblock)
|
||||||
|
return;
|
||||||
|
flush(b);
|
||||||
|
/* find block */
|
||||||
|
if(q0 < b->cq){
|
||||||
|
q = 0;
|
||||||
|
i = 0;
|
||||||
|
}else{
|
||||||
|
q = b->cq;
|
||||||
|
i = b->cbi;
|
||||||
|
}
|
||||||
|
blp = &b->bl[i];
|
||||||
|
while(q+(*blp)->u.n <= q0 && q+(*blp)->u.n < b->nc){
|
||||||
|
q += (*blp)->u.n;
|
||||||
|
i++;
|
||||||
|
blp++;
|
||||||
|
if(i >= b->nbl)
|
||||||
|
panic("block not found");
|
||||||
|
}
|
||||||
|
bl = *blp;
|
||||||
|
/* remember position */
|
||||||
|
b->cbi = i;
|
||||||
|
b->cq = q;
|
||||||
|
sizecache(b, bl->u.n);
|
||||||
|
b->cnc = bl->u.n;
|
||||||
|
/*read block*/
|
||||||
|
diskread(disk, bl, b->c, b->cnc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bufinsert(Buffer *b, uint q0, Rune *s, uint n)
|
||||||
|
{
|
||||||
|
uint i, m, t, off;
|
||||||
|
|
||||||
|
if(q0 > b->nc)
|
||||||
|
panic("internal error: bufinsert");
|
||||||
|
|
||||||
|
while(n > 0){
|
||||||
|
setcache(b, q0);
|
||||||
|
off = q0-b->cq;
|
||||||
|
if(b->cnc+n <= Maxblock){
|
||||||
|
/* Everything fits in one block. */
|
||||||
|
t = b->cnc+n;
|
||||||
|
m = n;
|
||||||
|
if(b->bl == nil){ /* allocate */
|
||||||
|
if(b->cnc != 0)
|
||||||
|
panic("internal error: bufinsert1 cnc!=0");
|
||||||
|
addblock(b, 0, t);
|
||||||
|
b->cbi = 0;
|
||||||
|
}
|
||||||
|
sizecache(b, t);
|
||||||
|
runemove(b->c+off+m, b->c+off, b->cnc-off);
|
||||||
|
runemove(b->c+off, s, m);
|
||||||
|
b->cnc = t;
|
||||||
|
goto Tail;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* We must make a new block. If q0 is at
|
||||||
|
* the very beginning or end of this block,
|
||||||
|
* just make a new block and fill it.
|
||||||
|
*/
|
||||||
|
if(q0==b->cq || q0==b->cq+b->cnc){
|
||||||
|
if(b->cdirty)
|
||||||
|
flush(b);
|
||||||
|
m = min(n, Maxblock);
|
||||||
|
if(b->bl == nil){ /* allocate */
|
||||||
|
if(b->cnc != 0)
|
||||||
|
panic("internal error: bufinsert2 cnc!=0");
|
||||||
|
i = 0;
|
||||||
|
}else{
|
||||||
|
i = b->cbi;
|
||||||
|
if(q0 > b->cq)
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
addblock(b, i, m);
|
||||||
|
sizecache(b, m);
|
||||||
|
runemove(b->c, s, m);
|
||||||
|
b->cq = q0;
|
||||||
|
b->cbi = i;
|
||||||
|
b->cnc = m;
|
||||||
|
goto Tail;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Split the block; cut off the right side and
|
||||||
|
* let go of it.
|
||||||
|
*/
|
||||||
|
m = b->cnc-off;
|
||||||
|
if(m > 0){
|
||||||
|
i = b->cbi+1;
|
||||||
|
addblock(b, i, m);
|
||||||
|
diskwrite(disk, &b->bl[i], b->c+off, m);
|
||||||
|
b->cnc -= m;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Now at end of block. Take as much input
|
||||||
|
* as possible and tack it on end of block.
|
||||||
|
*/
|
||||||
|
m = min(n, Maxblock-b->cnc);
|
||||||
|
sizecache(b, b->cnc+m);
|
||||||
|
runemove(b->c+b->cnc, s, m);
|
||||||
|
b->cnc += m;
|
||||||
|
Tail:
|
||||||
|
b->nc += m;
|
||||||
|
q0 += m;
|
||||||
|
s += m;
|
||||||
|
n -= m;
|
||||||
|
b->cdirty = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bufdelete(Buffer *b, uint q0, uint q1)
|
||||||
|
{
|
||||||
|
uint m, n, off;
|
||||||
|
|
||||||
|
if(!(q0<=q1 && q0<=b->nc && q1<=b->nc))
|
||||||
|
panic("internal error: bufdelete");
|
||||||
|
while(q1 > q0){
|
||||||
|
setcache(b, q0);
|
||||||
|
off = q0-b->cq;
|
||||||
|
if(q1 > b->cq+b->cnc)
|
||||||
|
n = b->cnc - off;
|
||||||
|
else
|
||||||
|
n = q1-q0;
|
||||||
|
m = b->cnc - (off+n);
|
||||||
|
if(m > 0)
|
||||||
|
runemove(b->c+off, b->c+off+n, m);
|
||||||
|
b->cnc -= n;
|
||||||
|
b->cdirty = TRUE;
|
||||||
|
q1 -= n;
|
||||||
|
b->nc -= n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint
|
||||||
|
bufload(Buffer *b, uint q0, int fd, int *nulls)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
Rune *r;
|
||||||
|
int l, m, n, nb, nr;
|
||||||
|
uint q1;
|
||||||
|
|
||||||
|
if(q0 > b->nc)
|
||||||
|
panic("internal error: bufload");
|
||||||
|
p = malloc((Maxblock+UTFmax+1)*sizeof p[0]);
|
||||||
|
if(p == nil)
|
||||||
|
panic("bufload: malloc failed");
|
||||||
|
r = runemalloc(Maxblock);
|
||||||
|
m = 0;
|
||||||
|
n = 1;
|
||||||
|
q1 = q0;
|
||||||
|
/*
|
||||||
|
* At top of loop, may have m bytes left over from
|
||||||
|
* last pass, possibly representing a partial rune.
|
||||||
|
*/
|
||||||
|
while(n > 0){
|
||||||
|
n = read(fd, p+m, Maxblock);
|
||||||
|
if(n < 0){
|
||||||
|
error(Ebufload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m += n;
|
||||||
|
p[m] = 0;
|
||||||
|
l = m;
|
||||||
|
if(n > 0)
|
||||||
|
l -= UTFmax;
|
||||||
|
cvttorunes(p, l, r, &nb, &nr, nulls);
|
||||||
|
memmove(p, p+nb, m-nb);
|
||||||
|
m -= nb;
|
||||||
|
bufinsert(b, q1, r, nr);
|
||||||
|
q1 += nr;
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
free(r);
|
||||||
|
return q1-q0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bufread(Buffer *b, uint q0, Rune *s, uint n)
|
||||||
|
{
|
||||||
|
uint m;
|
||||||
|
|
||||||
|
if(!(q0<=b->nc && q0+n<=b->nc))
|
||||||
|
panic("bufread: internal error");
|
||||||
|
|
||||||
|
while(n > 0){
|
||||||
|
setcache(b, q0);
|
||||||
|
m = min(n, b->cnc-(q0-b->cq));
|
||||||
|
runemove(s, b->c+(q0-b->cq), m);
|
||||||
|
q0 += m;
|
||||||
|
s += m;
|
||||||
|
n -= m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bufreset(Buffer *b)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
b->nc = 0;
|
||||||
|
b->cnc = 0;
|
||||||
|
b->cq = 0;
|
||||||
|
b->cdirty = 0;
|
||||||
|
b->cbi = 0;
|
||||||
|
/* delete backwards to avoid n² behavior */
|
||||||
|
for(i=b->nbl-1; --i>=0; )
|
||||||
|
delblock(b, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bufclose(Buffer *b)
|
||||||
|
{
|
||||||
|
bufreset(b);
|
||||||
|
free(b->c);
|
||||||
|
b->c = nil;
|
||||||
|
b->cnc = 0;
|
||||||
|
free(b->bl);
|
||||||
|
b->bl = nil;
|
||||||
|
b->nbl = 0;
|
||||||
|
}
|
608
sam/cmd.c
Normal file
608
sam/cmd.c
Normal file
@@ -0,0 +1,608 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
#include "parse.h"
|
||||||
|
|
||||||
|
static char linex[]="\n";
|
||||||
|
static char wordx[]=" \t\n";
|
||||||
|
struct cmdtab cmdtab[]={
|
||||||
|
/* cmdc text regexp addr defcmd defaddr count token fn */
|
||||||
|
'\n', 0, 0, 0, 0, aDot, 0, 0, nl_cmd,
|
||||||
|
'a', 1, 0, 0, 0, aDot, 0, 0, a_cmd,
|
||||||
|
'b', 0, 0, 0, 0, aNo, 0, linex, b_cmd,
|
||||||
|
'B', 0, 0, 0, 0, aNo, 0, linex, b_cmd,
|
||||||
|
'c', 1, 0, 0, 0, aDot, 0, 0, c_cmd,
|
||||||
|
'd', 0, 0, 0, 0, aDot, 0, 0, d_cmd,
|
||||||
|
'D', 0, 0, 0, 0, aNo, 0, linex, D_cmd,
|
||||||
|
'e', 0, 0, 0, 0, aNo, 0, wordx, e_cmd,
|
||||||
|
'f', 0, 0, 0, 0, aNo, 0, wordx, f_cmd,
|
||||||
|
'g', 0, 1, 0, 'p', aDot, 0, 0, g_cmd,
|
||||||
|
'i', 1, 0, 0, 0, aDot, 0, 0, i_cmd,
|
||||||
|
'k', 0, 0, 0, 0, aDot, 0, 0, k_cmd,
|
||||||
|
'm', 0, 0, 1, 0, aDot, 0, 0, m_cmd,
|
||||||
|
'n', 0, 0, 0, 0, aNo, 0, 0, n_cmd,
|
||||||
|
'p', 0, 0, 0, 0, aDot, 0, 0, p_cmd,
|
||||||
|
'q', 0, 0, 0, 0, aNo, 0, 0, q_cmd,
|
||||||
|
'r', 0, 0, 0, 0, aDot, 0, wordx, e_cmd,
|
||||||
|
's', 0, 1, 0, 0, aDot, 1, 0, s_cmd,
|
||||||
|
't', 0, 0, 1, 0, aDot, 0, 0, m_cmd,
|
||||||
|
'u', 0, 0, 0, 0, aNo, 2, 0, u_cmd,
|
||||||
|
'v', 0, 1, 0, 'p', aDot, 0, 0, g_cmd,
|
||||||
|
'w', 0, 0, 0, 0, aAll, 0, wordx, w_cmd,
|
||||||
|
'x', 0, 1, 0, 'p', aDot, 0, 0, x_cmd,
|
||||||
|
'y', 0, 1, 0, 'p', aDot, 0, 0, x_cmd,
|
||||||
|
'X', 0, 1, 0, 'f', aNo, 0, 0, X_cmd,
|
||||||
|
'Y', 0, 1, 0, 'f', aNo, 0, 0, X_cmd,
|
||||||
|
'!', 0, 0, 0, 0, aNo, 0, linex, plan9_cmd,
|
||||||
|
'>', 0, 0, 0, 0, aDot, 0, linex, plan9_cmd,
|
||||||
|
'<', 0, 0, 0, 0, aDot, 0, linex, plan9_cmd,
|
||||||
|
'|', 0, 0, 0, 0, aDot, 0, linex, plan9_cmd,
|
||||||
|
'=', 0, 0, 0, 0, aDot, 0, linex, eq_cmd,
|
||||||
|
'c'|0x100,0, 0, 0, 0, aNo, 0, wordx, cd_cmd,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
};
|
||||||
|
Cmd *parsecmd(int);
|
||||||
|
Addr *compoundaddr(void);
|
||||||
|
Addr *simpleaddr(void);
|
||||||
|
void freecmd(void);
|
||||||
|
void okdelim(int);
|
||||||
|
|
||||||
|
Rune line[BLOCKSIZE];
|
||||||
|
Rune termline[BLOCKSIZE];
|
||||||
|
Rune *linep = line;
|
||||||
|
Rune *terminp = termline;
|
||||||
|
Rune *termoutp = termline;
|
||||||
|
|
||||||
|
List cmdlist = { 'p' };
|
||||||
|
List addrlist = { 'p' };
|
||||||
|
List relist = { 'p' };
|
||||||
|
List stringlist = { 'p' };
|
||||||
|
|
||||||
|
int eof;
|
||||||
|
|
||||||
|
void
|
||||||
|
resetcmd(void)
|
||||||
|
{
|
||||||
|
linep = line;
|
||||||
|
*linep = 0;
|
||||||
|
terminp = termoutp = termline;
|
||||||
|
freecmd();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
inputc(void)
|
||||||
|
{
|
||||||
|
int n, nbuf;
|
||||||
|
char buf[UTFmax];
|
||||||
|
Rune r;
|
||||||
|
|
||||||
|
Again:
|
||||||
|
nbuf = 0;
|
||||||
|
if(downloaded){
|
||||||
|
while(termoutp == terminp){
|
||||||
|
cmdupdate();
|
||||||
|
if(patset)
|
||||||
|
tellpat();
|
||||||
|
while(termlocked > 0){
|
||||||
|
outT0(Hunlock);
|
||||||
|
termlocked--;
|
||||||
|
}
|
||||||
|
if(rcv() == 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
r = *termoutp++;
|
||||||
|
if(termoutp == terminp)
|
||||||
|
terminp = termoutp = termline;
|
||||||
|
}else{
|
||||||
|
do{
|
||||||
|
n = read(0, buf+nbuf, 1);
|
||||||
|
if(n <= 0)
|
||||||
|
return -1;
|
||||||
|
nbuf += n;
|
||||||
|
}while(!fullrune(buf, nbuf));
|
||||||
|
chartorune(&r, buf);
|
||||||
|
}
|
||||||
|
if(r == 0){
|
||||||
|
warn(Wnulls);
|
||||||
|
goto Again;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
inputline(void)
|
||||||
|
{
|
||||||
|
int i, c, start;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Could set linep = line and i = 0 here and just
|
||||||
|
* error(Etoolong) below, but this way we keep
|
||||||
|
* old input buffer history around for a while.
|
||||||
|
* This is useful only for debugging.
|
||||||
|
*/
|
||||||
|
i = linep - line;
|
||||||
|
do{
|
||||||
|
if((c = inputc())<=0)
|
||||||
|
return -1;
|
||||||
|
if(i == nelem(line)-1){
|
||||||
|
if(linep == line)
|
||||||
|
error(Etoolong);
|
||||||
|
start = linep - line;
|
||||||
|
runemove(line, linep, i-start);
|
||||||
|
i -= start;
|
||||||
|
linep = line;
|
||||||
|
}
|
||||||
|
}while((line[i++]=c) != '\n');
|
||||||
|
line[i] = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
getch(void)
|
||||||
|
{
|
||||||
|
if(eof)
|
||||||
|
return -1;
|
||||||
|
if(*linep==0 && inputline()<0){
|
||||||
|
eof = TRUE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return *linep++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nextc(void)
|
||||||
|
{
|
||||||
|
if(*linep == 0)
|
||||||
|
return -1;
|
||||||
|
return *linep;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ungetch(void)
|
||||||
|
{
|
||||||
|
if(--linep < line)
|
||||||
|
panic("ungetch");
|
||||||
|
}
|
||||||
|
|
||||||
|
Posn
|
||||||
|
getnum(int signok)
|
||||||
|
{
|
||||||
|
Posn n=0;
|
||||||
|
int c, sign;
|
||||||
|
|
||||||
|
sign = 1;
|
||||||
|
if(signok>1 && nextc()=='-'){
|
||||||
|
sign = -1;
|
||||||
|
getch();
|
||||||
|
}
|
||||||
|
if((c=nextc())<'0' || '9'<c) /* no number defaults to 1 */
|
||||||
|
return sign;
|
||||||
|
while('0'<=(c=getch()) && c<='9')
|
||||||
|
n = n*10 + (c-'0');
|
||||||
|
ungetch();
|
||||||
|
return sign*n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
skipbl(void)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
do
|
||||||
|
c = getch();
|
||||||
|
while(c==' ' || c=='\t');
|
||||||
|
if(c >= 0)
|
||||||
|
ungetch();
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
termcommand(void)
|
||||||
|
{
|
||||||
|
Posn p;
|
||||||
|
|
||||||
|
for(p=cmdpt; p<cmd->b.nc; p++){
|
||||||
|
if(terminp >= &termline[BLOCKSIZE]){
|
||||||
|
cmdpt = cmd->b.nc;
|
||||||
|
error(Etoolong);
|
||||||
|
}
|
||||||
|
*terminp++ = filereadc(cmd, p);
|
||||||
|
}
|
||||||
|
cmdpt = cmd->b.nc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cmdloop(void)
|
||||||
|
{
|
||||||
|
Cmd *cmdp;
|
||||||
|
File *ocurfile;
|
||||||
|
int loaded;
|
||||||
|
|
||||||
|
for(;;){
|
||||||
|
if(!downloaded && curfile && curfile->unread)
|
||||||
|
load(curfile);
|
||||||
|
if((cmdp = parsecmd(0))==0){
|
||||||
|
if(downloaded){
|
||||||
|
rescue();
|
||||||
|
exits("eof");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ocurfile = curfile;
|
||||||
|
loaded = curfile && !curfile->unread;
|
||||||
|
if(cmdexec(curfile, cmdp) == 0)
|
||||||
|
break;
|
||||||
|
freecmd();
|
||||||
|
cmdupdate();
|
||||||
|
update();
|
||||||
|
if(downloaded && curfile &&
|
||||||
|
(ocurfile!=curfile || (!loaded && !curfile->unread)))
|
||||||
|
outTs(Hcurrent, curfile->tag);
|
||||||
|
/* don't allow type ahead on files that aren't bound */
|
||||||
|
if(downloaded && curfile && curfile->rasp == 0)
|
||||||
|
terminp = termoutp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cmd *
|
||||||
|
newcmd(void){
|
||||||
|
Cmd *p;
|
||||||
|
|
||||||
|
p = emalloc(sizeof(Cmd));
|
||||||
|
inslist(&cmdlist, cmdlist.nused, (long)p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
Addr*
|
||||||
|
newaddr(void)
|
||||||
|
{
|
||||||
|
Addr *p;
|
||||||
|
|
||||||
|
p = emalloc(sizeof(Addr));
|
||||||
|
inslist(&addrlist, addrlist.nused, (long)p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
String*
|
||||||
|
newre(void)
|
||||||
|
{
|
||||||
|
String *p;
|
||||||
|
|
||||||
|
p = emalloc(sizeof(String));
|
||||||
|
inslist(&relist, relist.nused, (long)p);
|
||||||
|
Strinit(p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
String*
|
||||||
|
newstring(void)
|
||||||
|
{
|
||||||
|
String *p;
|
||||||
|
|
||||||
|
p = emalloc(sizeof(String));
|
||||||
|
inslist(&stringlist, stringlist.nused, (long)p);
|
||||||
|
Strinit(p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
freecmd(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
while(cmdlist.nused > 0)
|
||||||
|
free(cmdlist.voidpptr[--cmdlist.nused]);
|
||||||
|
while(addrlist.nused > 0)
|
||||||
|
free(addrlist.voidpptr[--addrlist.nused]);
|
||||||
|
while(relist.nused > 0){
|
||||||
|
i = --relist.nused;
|
||||||
|
Strclose(relist.stringpptr[i]);
|
||||||
|
free(relist.stringpptr[i]);
|
||||||
|
}
|
||||||
|
while(stringlist.nused>0){
|
||||||
|
i = --stringlist.nused;
|
||||||
|
Strclose(stringlist.stringpptr[i]);
|
||||||
|
free(stringlist.stringpptr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
lookup(int c)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; cmdtab[i].cmdc; i++)
|
||||||
|
if(cmdtab[i].cmdc == c)
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
okdelim(int c)
|
||||||
|
{
|
||||||
|
if(c=='\\' || ('a'<=c && c<='z')
|
||||||
|
|| ('A'<=c && c<='Z') || ('0'<=c && c<='9'))
|
||||||
|
error_c(Edelim, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
atnl(void)
|
||||||
|
{
|
||||||
|
skipbl();
|
||||||
|
if(getch() != '\n')
|
||||||
|
error(Enewline);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
getrhs(String *s, int delim, int cmd)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
|
while((c = getch())>0 && c!=delim && c!='\n'){
|
||||||
|
if(c == '\\'){
|
||||||
|
if((c=getch()) <= 0)
|
||||||
|
error(Ebadrhs);
|
||||||
|
if(c == '\n'){
|
||||||
|
ungetch();
|
||||||
|
c='\\';
|
||||||
|
}else if(c == 'n')
|
||||||
|
c='\n';
|
||||||
|
else if(c!=delim && (cmd=='s' || c!='\\')) /* s does its own */
|
||||||
|
Straddc(s, '\\');
|
||||||
|
}
|
||||||
|
Straddc(s, c);
|
||||||
|
}
|
||||||
|
ungetch(); /* let client read whether delimeter, '\n' or whatever */
|
||||||
|
}
|
||||||
|
|
||||||
|
String *
|
||||||
|
collecttoken(char *end)
|
||||||
|
{
|
||||||
|
String *s = newstring();
|
||||||
|
int c;
|
||||||
|
|
||||||
|
while((c=nextc())==' ' || c=='\t')
|
||||||
|
Straddc(s, getch()); /* blanks significant for getname() */
|
||||||
|
while((c=getch())>0 && utfrune(end, c)==0)
|
||||||
|
Straddc(s, c);
|
||||||
|
Straddc(s, 0);
|
||||||
|
if(c != '\n')
|
||||||
|
atnl();
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
String *
|
||||||
|
collecttext(void)
|
||||||
|
{
|
||||||
|
String *s = newstring();
|
||||||
|
int begline, i, c, delim;
|
||||||
|
|
||||||
|
if(skipbl()=='\n'){
|
||||||
|
getch();
|
||||||
|
i = 0;
|
||||||
|
do{
|
||||||
|
begline = i;
|
||||||
|
while((c = getch())>0 && c!='\n')
|
||||||
|
i++, Straddc(s, c);
|
||||||
|
i++, Straddc(s, '\n');
|
||||||
|
if(c < 0)
|
||||||
|
goto Return;
|
||||||
|
}while(s->s[begline]!='.' || s->s[begline+1]!='\n');
|
||||||
|
Strdelete(s, s->n-2, s->n);
|
||||||
|
}else{
|
||||||
|
okdelim(delim = getch());
|
||||||
|
getrhs(s, delim, 'a');
|
||||||
|
if(nextc()==delim)
|
||||||
|
getch();
|
||||||
|
atnl();
|
||||||
|
}
|
||||||
|
Return:
|
||||||
|
Straddc(s, 0); /* JUST FOR CMDPRINT() */
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cmd *
|
||||||
|
parsecmd(int nest)
|
||||||
|
{
|
||||||
|
int i, c;
|
||||||
|
struct cmdtab *ct;
|
||||||
|
Cmd *cp, *ncp;
|
||||||
|
Cmd cmd;
|
||||||
|
|
||||||
|
cmd.next = cmd.ccmd = 0;
|
||||||
|
cmd.re = 0;
|
||||||
|
cmd.flag = cmd.num = 0;
|
||||||
|
cmd.addr = compoundaddr();
|
||||||
|
if(skipbl() == -1)
|
||||||
|
return 0;
|
||||||
|
if((c=getch())==-1)
|
||||||
|
return 0;
|
||||||
|
cmd.cmdc = c;
|
||||||
|
if(cmd.cmdc=='c' && nextc()=='d'){ /* sleazy two-character case */
|
||||||
|
getch(); /* the 'd' */
|
||||||
|
cmd.cmdc='c'|0x100;
|
||||||
|
}
|
||||||
|
i = lookup(cmd.cmdc);
|
||||||
|
if(i >= 0){
|
||||||
|
if(cmd.cmdc == '\n')
|
||||||
|
goto Return; /* let nl_cmd work it all out */
|
||||||
|
ct = &cmdtab[i];
|
||||||
|
if(ct->defaddr==aNo && cmd.addr)
|
||||||
|
error(Enoaddr);
|
||||||
|
if(ct->count)
|
||||||
|
cmd.num = getnum(ct->count);
|
||||||
|
if(ct->regexp){
|
||||||
|
/* x without pattern -> .*\n, indicated by cmd.re==0 */
|
||||||
|
/* X without pattern is all files */
|
||||||
|
if((ct->cmdc!='x' && ct->cmdc!='X') ||
|
||||||
|
((c = nextc())!=' ' && c!='\t' && c!='\n')){
|
||||||
|
skipbl();
|
||||||
|
if((c = getch())=='\n' || c<0)
|
||||||
|
error(Enopattern);
|
||||||
|
okdelim(c);
|
||||||
|
cmd.re = getregexp(c);
|
||||||
|
if(ct->cmdc == 's'){
|
||||||
|
cmd.ctext = newstring();
|
||||||
|
getrhs(cmd.ctext, c, 's');
|
||||||
|
if(nextc() == c){
|
||||||
|
getch();
|
||||||
|
if(nextc() == 'g')
|
||||||
|
cmd.flag = getch();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(ct->addr && (cmd.caddr=simpleaddr())==0)
|
||||||
|
error(Eaddress);
|
||||||
|
if(ct->defcmd){
|
||||||
|
if(skipbl() == '\n'){
|
||||||
|
getch();
|
||||||
|
cmd.ccmd = newcmd();
|
||||||
|
cmd.ccmd->cmdc = ct->defcmd;
|
||||||
|
}else if((cmd.ccmd = parsecmd(nest))==0)
|
||||||
|
panic("defcmd");
|
||||||
|
}else if(ct->text)
|
||||||
|
cmd.ctext = collecttext();
|
||||||
|
else if(ct->token)
|
||||||
|
cmd.ctext = collecttoken(ct->token);
|
||||||
|
else
|
||||||
|
atnl();
|
||||||
|
}else
|
||||||
|
switch(cmd.cmdc){
|
||||||
|
case '{':
|
||||||
|
cp = 0;
|
||||||
|
do{
|
||||||
|
if(skipbl()=='\n')
|
||||||
|
getch();
|
||||||
|
ncp = parsecmd(nest+1);
|
||||||
|
if(cp)
|
||||||
|
cp->next = ncp;
|
||||||
|
else
|
||||||
|
cmd.ccmd = ncp;
|
||||||
|
}while(cp = ncp);
|
||||||
|
break;
|
||||||
|
case '}':
|
||||||
|
atnl();
|
||||||
|
if(nest==0)
|
||||||
|
error(Enolbrace);
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
error_c(Eunk, cmd.cmdc);
|
||||||
|
}
|
||||||
|
Return:
|
||||||
|
cp = newcmd();
|
||||||
|
*cp = cmd;
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
String* /* BUGGERED */
|
||||||
|
getregexp(int delim)
|
||||||
|
{
|
||||||
|
String *r = newre();
|
||||||
|
int c;
|
||||||
|
|
||||||
|
for(Strzero(&genstr); ; Straddc(&genstr, c))
|
||||||
|
if((c = getch())=='\\'){
|
||||||
|
if(nextc()==delim)
|
||||||
|
c = getch();
|
||||||
|
else if(nextc()=='\\'){
|
||||||
|
Straddc(&genstr, c);
|
||||||
|
c = getch();
|
||||||
|
}
|
||||||
|
}else if(c==delim || c=='\n')
|
||||||
|
break;
|
||||||
|
if(c!=delim && c)
|
||||||
|
ungetch();
|
||||||
|
if(genstr.n > 0){
|
||||||
|
patset = TRUE;
|
||||||
|
Strduplstr(&lastpat, &genstr);
|
||||||
|
Straddc(&lastpat, '\0');
|
||||||
|
}
|
||||||
|
if(lastpat.n <= 1)
|
||||||
|
error(Epattern);
|
||||||
|
Strduplstr(r, &lastpat);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
Addr *
|
||||||
|
simpleaddr(void)
|
||||||
|
{
|
||||||
|
Addr addr;
|
||||||
|
Addr *ap, *nap;
|
||||||
|
|
||||||
|
addr.next = 0;
|
||||||
|
addr.left = 0;
|
||||||
|
addr.num = 0;
|
||||||
|
switch(skipbl()){
|
||||||
|
case '#':
|
||||||
|
addr.type = getch();
|
||||||
|
addr.num = getnum(1);
|
||||||
|
break;
|
||||||
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
|
addr.num = getnum(1);
|
||||||
|
addr.type='l';
|
||||||
|
break;
|
||||||
|
case '/': case '?': case '"':
|
||||||
|
addr.are = getregexp(addr.type = getch());
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
case '$':
|
||||||
|
case '+':
|
||||||
|
case '-':
|
||||||
|
case '\'':
|
||||||
|
addr.type = getch();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(addr.next = simpleaddr())
|
||||||
|
switch(addr.next->type){
|
||||||
|
case '.':
|
||||||
|
case '$':
|
||||||
|
case '\'':
|
||||||
|
if(addr.type!='"')
|
||||||
|
case '"':
|
||||||
|
error(Eaddress);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
case '#':
|
||||||
|
if(addr.type=='"')
|
||||||
|
break;
|
||||||
|
/* fall through */
|
||||||
|
case '/':
|
||||||
|
case '?':
|
||||||
|
if(addr.type!='+' && addr.type!='-'){
|
||||||
|
/* insert the missing '+' */
|
||||||
|
nap = newaddr();
|
||||||
|
nap->type='+';
|
||||||
|
nap->next = addr.next;
|
||||||
|
addr.next = nap;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
case '-':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
panic("simpleaddr");
|
||||||
|
}
|
||||||
|
ap = newaddr();
|
||||||
|
*ap = addr;
|
||||||
|
return ap;
|
||||||
|
}
|
||||||
|
|
||||||
|
Addr *
|
||||||
|
compoundaddr(void)
|
||||||
|
{
|
||||||
|
Addr addr;
|
||||||
|
Addr *ap, *next;
|
||||||
|
|
||||||
|
addr.left = simpleaddr();
|
||||||
|
if((addr.type = skipbl())!=',' && addr.type!=';')
|
||||||
|
return addr.left;
|
||||||
|
getch();
|
||||||
|
next = addr.next = compoundaddr();
|
||||||
|
if(next && (next->type==',' || next->type==';') && next->left==0)
|
||||||
|
error(Eaddress);
|
||||||
|
ap = newaddr();
|
||||||
|
*ap = addr;
|
||||||
|
return ap;
|
||||||
|
}
|
122
sam/disk.c
Normal file
122
sam/disk.c
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
|
||||||
|
static Block *blist;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static int
|
||||||
|
tempdisk(void)
|
||||||
|
{
|
||||||
|
char buf[128];
|
||||||
|
int i, fd;
|
||||||
|
|
||||||
|
snprint(buf, sizeof buf, "/tmp/X%d.%.4ssam", getpid(), getuser());
|
||||||
|
for(i='A'; i<='Z'; i++){
|
||||||
|
buf[5] = i;
|
||||||
|
if(access(buf, AEXIST) == 0)
|
||||||
|
continue;
|
||||||
|
fd = create(buf, ORDWR|ORCLOSE|OCEXEC, 0600);
|
||||||
|
if(fd >= 0)
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
extern int tempdisk(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Disk*
|
||||||
|
diskinit(void)
|
||||||
|
{
|
||||||
|
Disk *d;
|
||||||
|
|
||||||
|
d = emalloc(sizeof(Disk));
|
||||||
|
d->fd = tempdisk();
|
||||||
|
if(d->fd < 0){
|
||||||
|
fprint(2, "sam: can't create temp file: %r\n");
|
||||||
|
exits("diskinit");
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
uint
|
||||||
|
ntosize(uint n, uint *ip)
|
||||||
|
{
|
||||||
|
uint size;
|
||||||
|
|
||||||
|
if(n > Maxblock)
|
||||||
|
panic("internal error: ntosize");
|
||||||
|
size = n;
|
||||||
|
if(size & (Blockincr-1))
|
||||||
|
size += Blockincr - (size & (Blockincr-1));
|
||||||
|
/* last bucket holds blocks of exactly Maxblock */
|
||||||
|
if(ip)
|
||||||
|
*ip = size/Blockincr;
|
||||||
|
return size * sizeof(Rune);
|
||||||
|
}
|
||||||
|
|
||||||
|
Block*
|
||||||
|
disknewblock(Disk *d, uint n)
|
||||||
|
{
|
||||||
|
uint i, j, size;
|
||||||
|
Block *b;
|
||||||
|
|
||||||
|
size = ntosize(n, &i);
|
||||||
|
b = d->free[i];
|
||||||
|
if(b)
|
||||||
|
d->free[i] = b->u.next;
|
||||||
|
else{
|
||||||
|
/* allocate in chunks to reduce malloc overhead */
|
||||||
|
if(blist == nil){
|
||||||
|
blist = emalloc(100*sizeof(Block));
|
||||||
|
for(j=0; j<100-1; j++)
|
||||||
|
blist[j].u.next = &blist[j+1];
|
||||||
|
}
|
||||||
|
b = blist;
|
||||||
|
blist = b->u.next;
|
||||||
|
b->addr = d->addr;
|
||||||
|
d->addr += size;
|
||||||
|
}
|
||||||
|
b->u.n = n;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
diskrelease(Disk *d, Block *b)
|
||||||
|
{
|
||||||
|
uint i;
|
||||||
|
|
||||||
|
ntosize(b->u.n, &i);
|
||||||
|
b->u.next = d->free[i];
|
||||||
|
d->free[i] = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
diskwrite(Disk *d, Block **bp, Rune *r, uint n)
|
||||||
|
{
|
||||||
|
int size, nsize;
|
||||||
|
Block *b;
|
||||||
|
|
||||||
|
b = *bp;
|
||||||
|
size = ntosize(b->u.n, nil);
|
||||||
|
nsize = ntosize(n, nil);
|
||||||
|
if(size != nsize){
|
||||||
|
diskrelease(d, b);
|
||||||
|
b = disknewblock(d, n);
|
||||||
|
*bp = b;
|
||||||
|
}
|
||||||
|
if(pwrite(d->fd, r, n*sizeof(Rune), b->addr) != n*sizeof(Rune))
|
||||||
|
panic("write error to temp file");
|
||||||
|
b->u.n = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
diskread(Disk *d, Block *b, Rune *r, uint n)
|
||||||
|
{
|
||||||
|
if(n > b->u.n)
|
||||||
|
panic("internal error: diskread");
|
||||||
|
|
||||||
|
ntosize(b->u.n, nil); /* called only for sanity check on Maxblock */
|
||||||
|
if(pread(d->fd, r, n*sizeof(Rune), b->addr) != n*sizeof(Rune))
|
||||||
|
panic("read error from temp file");
|
||||||
|
}
|
39
sam/err
Normal file
39
sam/err
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
address.c: In function `filematch':
|
||||||
|
address.c:159: warning: passing arg 1 of `bufreset' from incompatible pointer type
|
||||||
|
address.c:160: warning: passing arg 1 of `bufinsert' from incompatible pointer type
|
||||||
|
file.c: In function `mergeextend':
|
||||||
|
file.c:117: warning: passing arg 1 of `bufread' from incompatible pointer type
|
||||||
|
file.c: In function `fileinsert':
|
||||||
|
file.c:275: warning: passing arg 1 of `bufinsert' from incompatible pointer type
|
||||||
|
file.c: In function `filedelete':
|
||||||
|
file.c:301: warning: passing arg 1 of `bufdelete' from incompatible pointer type
|
||||||
|
file.c: In function `fileundelete':
|
||||||
|
file.c:324: warning: passing arg 1 of `bufread' from incompatible pointer type
|
||||||
|
file.c: In function `filereadc':
|
||||||
|
file.c:339: warning: passing arg 1 of `bufread' from incompatible pointer type
|
||||||
|
file.c: In function `fileload':
|
||||||
|
file.c:405: warning: passing arg 1 of `bufload' from incompatible pointer type
|
||||||
|
file.c: In function `fileundo':
|
||||||
|
file.c:528: warning: passing arg 1 of `bufdelete' from incompatible pointer type
|
||||||
|
file.c:546: warning: passing arg 1 of `bufinsert' from incompatible pointer type
|
||||||
|
file.c: In function `fileclose':
|
||||||
|
file.c:604: warning: passing arg 1 of `bufclose' from incompatible pointer type
|
||||||
|
io.c: In function `readio':
|
||||||
|
io.c:90: warning: passing arg 1 of `bufload' from incompatible pointer type
|
||||||
|
io.c: In function `writeio':
|
||||||
|
io.c:152: warning: passing arg 1 of `bufread' from incompatible pointer type
|
||||||
|
mesg.c: In function `inmesg':
|
||||||
|
mesg.c:248: warning: passing arg 1 of `bufread' from incompatible pointer type
|
||||||
|
mesg.c: In function `snarf':
|
||||||
|
mesg.c:568: warning: passing arg 1 of `bufread' from incompatible pointer type
|
||||||
|
mesg.c: In function `setgenstr':
|
||||||
|
mesg.c:612: warning: passing arg 1 of `bufread' from incompatible pointer type
|
||||||
|
sam.c: In function `readcmd':
|
||||||
|
sam.c:496: warning: passing arg 1 of `bufread' from incompatible pointer type
|
||||||
|
sam.c: In function `copy':
|
||||||
|
sam.c:676: warning: passing arg 1 of `bufread' from incompatible pointer type
|
||||||
|
xec.c: In function `s_cmd':
|
||||||
|
xec.c:234: warning: passing arg 1 of `bufread' from incompatible pointer type
|
||||||
|
xec.c:243: warning: passing arg 1 of `bufread' from incompatible pointer type
|
||||||
|
xec.c: In function `display':
|
||||||
|
xec.c:401: warning: passing arg 1 of `bufread' from incompatible pointer type
|
144
sam/error.c
Normal file
144
sam/error.c
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
|
||||||
|
static char *emsg[]={
|
||||||
|
/* error_s */
|
||||||
|
"can't open",
|
||||||
|
"can't create",
|
||||||
|
"not in menu:",
|
||||||
|
"changes to",
|
||||||
|
"I/O error:",
|
||||||
|
"can't write while changing:",
|
||||||
|
/* error_c */
|
||||||
|
"unknown command",
|
||||||
|
"no operand for",
|
||||||
|
"bad delimiter",
|
||||||
|
/* error */
|
||||||
|
"can't fork",
|
||||||
|
"interrupt",
|
||||||
|
"address",
|
||||||
|
"search",
|
||||||
|
"pattern",
|
||||||
|
"newline expected",
|
||||||
|
"blank expected",
|
||||||
|
"pattern expected",
|
||||||
|
"can't nest X or Y",
|
||||||
|
"unmatched `}'",
|
||||||
|
"command takes no address",
|
||||||
|
"addresses overlap",
|
||||||
|
"substitution",
|
||||||
|
"& match too long",
|
||||||
|
"bad \\ in rhs",
|
||||||
|
"address range",
|
||||||
|
"changes not in sequence",
|
||||||
|
"addresses out of order",
|
||||||
|
"no file name",
|
||||||
|
"unmatched `('",
|
||||||
|
"unmatched `)'",
|
||||||
|
"malformed `[]'",
|
||||||
|
"malformed regexp",
|
||||||
|
"reg. exp. list overflow",
|
||||||
|
"plan 9 command",
|
||||||
|
"can't pipe",
|
||||||
|
"no current file",
|
||||||
|
"string too long",
|
||||||
|
"changed files",
|
||||||
|
"empty string",
|
||||||
|
"file search",
|
||||||
|
"non-unique match for \"\"",
|
||||||
|
"tag match too long",
|
||||||
|
"too many subexpressions",
|
||||||
|
"temporary file too large",
|
||||||
|
"file is append-only",
|
||||||
|
"no destination for plumb message",
|
||||||
|
"internal read error in buffer load"
|
||||||
|
};
|
||||||
|
static char *wmsg[]={
|
||||||
|
/* warn_s */
|
||||||
|
"duplicate file name",
|
||||||
|
"no such file",
|
||||||
|
"write might change good version of",
|
||||||
|
/* warn_S */
|
||||||
|
"files might be aliased",
|
||||||
|
/* warn */
|
||||||
|
"null characters elided",
|
||||||
|
"can't run pwd",
|
||||||
|
"last char not newline",
|
||||||
|
"exit status not 0"
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
error(Err s)
|
||||||
|
{
|
||||||
|
char buf[512];
|
||||||
|
|
||||||
|
sprint(buf, "?%s", emsg[s]);
|
||||||
|
hiccough(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
error_s(Err s, char *a)
|
||||||
|
{
|
||||||
|
char buf[512];
|
||||||
|
|
||||||
|
sprint(buf, "?%s \"%s\"", emsg[s], a);
|
||||||
|
hiccough(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
error_r(Err s, char *a)
|
||||||
|
{
|
||||||
|
char buf[512];
|
||||||
|
|
||||||
|
sprint(buf, "?%s \"%s\": %r", emsg[s], a);
|
||||||
|
hiccough(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
error_c(Err s, int c)
|
||||||
|
{
|
||||||
|
char buf[512];
|
||||||
|
|
||||||
|
sprint(buf, "?%s `%C'", emsg[s], c);
|
||||||
|
hiccough(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
warn(Warn s)
|
||||||
|
{
|
||||||
|
dprint("?warning: %s\n", wmsg[s]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
warn_S(Warn s, String *a)
|
||||||
|
{
|
||||||
|
print_s(wmsg[s], a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
warn_SS(Warn s, String *a, String *b)
|
||||||
|
{
|
||||||
|
print_ss(wmsg[s], a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
warn_s(Warn s, char *a)
|
||||||
|
{
|
||||||
|
dprint("?warning: %s `%s'\n", wmsg[s], a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
termwrite(char *s)
|
||||||
|
{
|
||||||
|
String *p;
|
||||||
|
|
||||||
|
if(downloaded){
|
||||||
|
p = tmpcstr(s);
|
||||||
|
if(cmd)
|
||||||
|
loginsert(cmd, cmdpt, p->s, p->n);
|
||||||
|
else
|
||||||
|
Strinsert(&cmdstr, p, cmdstr.n);
|
||||||
|
cmdptadv += p->n;
|
||||||
|
free(p);
|
||||||
|
}else
|
||||||
|
Write(2, s, strlen(s));
|
||||||
|
}
|
65
sam/errors.h
Normal file
65
sam/errors.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
typedef enum Err{
|
||||||
|
/* error_s */
|
||||||
|
Eopen,
|
||||||
|
Ecreate,
|
||||||
|
Emenu,
|
||||||
|
Emodified,
|
||||||
|
Eio,
|
||||||
|
Ewseq,
|
||||||
|
/* error_c */
|
||||||
|
Eunk,
|
||||||
|
Emissop,
|
||||||
|
Edelim,
|
||||||
|
/* error */
|
||||||
|
Efork,
|
||||||
|
Eintr,
|
||||||
|
Eaddress,
|
||||||
|
Esearch,
|
||||||
|
Epattern,
|
||||||
|
Enewline,
|
||||||
|
Eblank,
|
||||||
|
Enopattern,
|
||||||
|
EnestXY,
|
||||||
|
Enolbrace,
|
||||||
|
Enoaddr,
|
||||||
|
Eoverlap,
|
||||||
|
Enosub,
|
||||||
|
Elongrhs,
|
||||||
|
Ebadrhs,
|
||||||
|
Erange,
|
||||||
|
Esequence,
|
||||||
|
Eorder,
|
||||||
|
Enoname,
|
||||||
|
Eleftpar,
|
||||||
|
Erightpar,
|
||||||
|
Ebadclass,
|
||||||
|
Ebadregexp,
|
||||||
|
Eoverflow,
|
||||||
|
Enocmd,
|
||||||
|
Epipe,
|
||||||
|
Enofile,
|
||||||
|
Etoolong,
|
||||||
|
Echanges,
|
||||||
|
Eempty,
|
||||||
|
Efsearch,
|
||||||
|
Emanyfiles,
|
||||||
|
Elongtag,
|
||||||
|
Esubexp,
|
||||||
|
Etmpovfl,
|
||||||
|
Eappend,
|
||||||
|
Ecantplumb,
|
||||||
|
Ebufload
|
||||||
|
}Err;
|
||||||
|
typedef enum Warn{
|
||||||
|
/* warn_s */
|
||||||
|
Wdupname,
|
||||||
|
Wfile,
|
||||||
|
Wdate,
|
||||||
|
/* warn_ss */
|
||||||
|
Wdupfile,
|
||||||
|
/* warn */
|
||||||
|
Wnulls,
|
||||||
|
Wpwd,
|
||||||
|
Wnotnewline,
|
||||||
|
Wbadstatus
|
||||||
|
}Warn;
|
610
sam/file.c
Normal file
610
sam/file.c
Normal file
@@ -0,0 +1,610 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Structure of Undo list:
|
||||||
|
* The Undo structure follows any associated data, so the list
|
||||||
|
* can be read backwards: read the structure, then read whatever
|
||||||
|
* data is associated (insert string, file name) and precedes it.
|
||||||
|
* The structure includes the previous value of the modify bit
|
||||||
|
* and a sequence number; successive Undo structures with the
|
||||||
|
* same sequence number represent simultaneous changes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct Undo Undo;
|
||||||
|
typedef struct Merge Merge;
|
||||||
|
|
||||||
|
struct Undo
|
||||||
|
{
|
||||||
|
short type; /* Delete, Insert, Filename, Dot, Mark */
|
||||||
|
short mod; /* modify bit */
|
||||||
|
uint seq; /* sequence number */
|
||||||
|
uint p0; /* location of change (unused in f) */
|
||||||
|
uint n; /* # runes in string or file name */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Merge
|
||||||
|
{
|
||||||
|
File *f;
|
||||||
|
uint seq; /* of logged change */
|
||||||
|
uint p0; /* location of change (unused in f) */
|
||||||
|
uint n; /* # runes to delete */
|
||||||
|
uint nbuf; /* # runes to insert */
|
||||||
|
Rune buf[RBUFSIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
Maxmerge = 50,
|
||||||
|
Undosize = sizeof(Undo)/sizeof(Rune)
|
||||||
|
};
|
||||||
|
|
||||||
|
static Merge merge;
|
||||||
|
|
||||||
|
File*
|
||||||
|
fileopen(void)
|
||||||
|
{
|
||||||
|
File *f;
|
||||||
|
|
||||||
|
f = emalloc(sizeof(File));
|
||||||
|
f->dot.f = f;
|
||||||
|
f->ndot.f = f;
|
||||||
|
f->seq = 0;
|
||||||
|
f->mod = FALSE;
|
||||||
|
f->unread = TRUE;
|
||||||
|
Strinit0(&f->name);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
fileisdirty(File *f)
|
||||||
|
{
|
||||||
|
return f->seq != f->cleanseq;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wrinsert(Buffer *delta, int seq, int mod, uint p0, Rune *s, uint ns)
|
||||||
|
{
|
||||||
|
Undo u;
|
||||||
|
|
||||||
|
u.type = Insert;
|
||||||
|
u.mod = mod;
|
||||||
|
u.seq = seq;
|
||||||
|
u.p0 = p0;
|
||||||
|
u.n = ns;
|
||||||
|
bufinsert(delta, delta->nc, s, ns);
|
||||||
|
bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wrdelete(Buffer *delta, int seq, int mod, uint p0, uint p1)
|
||||||
|
{
|
||||||
|
Undo u;
|
||||||
|
|
||||||
|
u.type = Delete;
|
||||||
|
u.mod = mod;
|
||||||
|
u.seq = seq;
|
||||||
|
u.p0 = p0;
|
||||||
|
u.n = p1 - p0;
|
||||||
|
bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
flushmerge(void)
|
||||||
|
{
|
||||||
|
File *f;
|
||||||
|
|
||||||
|
f = merge.f;
|
||||||
|
if(f == nil)
|
||||||
|
return;
|
||||||
|
if(merge.seq != f->seq)
|
||||||
|
panic("flushmerge seq mismatch");
|
||||||
|
if(merge.n != 0)
|
||||||
|
wrdelete(&f->epsilon, f->seq, TRUE, merge.p0, merge.p0+merge.n);
|
||||||
|
if(merge.nbuf != 0)
|
||||||
|
wrinsert(&f->epsilon, f->seq, TRUE, merge.p0+merge.n, merge.buf, merge.nbuf);
|
||||||
|
merge.f = nil;
|
||||||
|
merge.n = 0;
|
||||||
|
merge.nbuf = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mergeextend(File *f, uint p0)
|
||||||
|
{
|
||||||
|
uint mp0n;
|
||||||
|
|
||||||
|
mp0n = merge.p0+merge.n;
|
||||||
|
if(mp0n != p0){
|
||||||
|
bufread(&f->b, mp0n, merge.buf+merge.nbuf, p0-mp0n);
|
||||||
|
merge.nbuf += p0-mp0n;
|
||||||
|
merge.n = p0-merge.p0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* like fileundelete, but get the data from arguments
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
loginsert(File *f, uint p0, Rune *s, uint ns)
|
||||||
|
{
|
||||||
|
if(f->rescuing)
|
||||||
|
return;
|
||||||
|
if(ns == 0)
|
||||||
|
return;
|
||||||
|
if(ns<0 || ns>STRSIZE)
|
||||||
|
panic("loginsert");
|
||||||
|
if(f->seq < seq)
|
||||||
|
filemark(f);
|
||||||
|
if(p0 < f->hiposn)
|
||||||
|
error(Esequence);
|
||||||
|
|
||||||
|
if(merge.f != f
|
||||||
|
|| p0-(merge.p0+merge.n)>Maxmerge /* too far */
|
||||||
|
|| merge.nbuf+((p0+ns)-(merge.p0+merge.n))>=RBUFSIZE) /* too long */
|
||||||
|
flushmerge();
|
||||||
|
|
||||||
|
if(ns>=RBUFSIZE){
|
||||||
|
if(!(merge.n == 0 && merge.nbuf == 0 && merge.f == nil))
|
||||||
|
panic("loginsert bad merge state");
|
||||||
|
wrinsert(&f->epsilon, f->seq, TRUE, p0, s, ns);
|
||||||
|
}else{
|
||||||
|
if(merge.f != f){
|
||||||
|
merge.f = f;
|
||||||
|
merge.p0 = p0;
|
||||||
|
merge.seq = f->seq;
|
||||||
|
}
|
||||||
|
mergeextend(f, p0);
|
||||||
|
|
||||||
|
/* append string to merge */
|
||||||
|
runemove(merge.buf+merge.nbuf, s, ns);
|
||||||
|
merge.nbuf += ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
f->hiposn = p0;
|
||||||
|
if(!f->unread && !f->mod)
|
||||||
|
state(f, Dirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
logdelete(File *f, uint p0, uint p1)
|
||||||
|
{
|
||||||
|
if(f->rescuing)
|
||||||
|
return;
|
||||||
|
if(p0 == p1)
|
||||||
|
return;
|
||||||
|
if(f->seq < seq)
|
||||||
|
filemark(f);
|
||||||
|
if(p0 < f->hiposn)
|
||||||
|
error(Esequence);
|
||||||
|
|
||||||
|
if(merge.f != f
|
||||||
|
|| p0-(merge.p0+merge.n)>Maxmerge /* too far */
|
||||||
|
|| merge.nbuf+(p0-(merge.p0+merge.n))>=RBUFSIZE){ /* too long */
|
||||||
|
flushmerge();
|
||||||
|
merge.f = f;
|
||||||
|
merge.p0 = p0;
|
||||||
|
merge.seq = f->seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeextend(f, p0);
|
||||||
|
|
||||||
|
/* add to deletion */
|
||||||
|
merge.n = p1-merge.p0;
|
||||||
|
|
||||||
|
f->hiposn = p1;
|
||||||
|
if(!f->unread && !f->mod)
|
||||||
|
state(f, Dirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* like fileunsetname, but get the data from arguments
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
logsetname(File *f, String *s)
|
||||||
|
{
|
||||||
|
Undo u;
|
||||||
|
Buffer *delta;
|
||||||
|
|
||||||
|
if(f->rescuing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(f->unread){ /* This is setting initial file name */
|
||||||
|
filesetname(f, s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(f->seq < seq)
|
||||||
|
filemark(f);
|
||||||
|
|
||||||
|
/* undo a file name change by restoring old name */
|
||||||
|
delta = &f->epsilon;
|
||||||
|
u.type = Filename;
|
||||||
|
u.mod = TRUE;
|
||||||
|
u.seq = f->seq;
|
||||||
|
u.p0 = 0; /* unused */
|
||||||
|
u.n = s->n;
|
||||||
|
if(s->n)
|
||||||
|
bufinsert(delta, delta->nc, s->s, s->n);
|
||||||
|
bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
|
||||||
|
if(!f->unread && !f->mod)
|
||||||
|
state(f, Dirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef NOTEXT
|
||||||
|
File*
|
||||||
|
fileaddtext(File *f, Text *t)
|
||||||
|
{
|
||||||
|
if(f == nil){
|
||||||
|
f = emalloc(sizeof(File));
|
||||||
|
f->unread = TRUE;
|
||||||
|
}
|
||||||
|
f->text = realloc(f->text, (f->ntext+1)*sizeof(Text*));
|
||||||
|
f->text[f->ntext++] = t;
|
||||||
|
f->curtext = t;
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
filedeltext(File *f, Text *t)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; i<f->ntext; i++)
|
||||||
|
if(f->text[i] == t)
|
||||||
|
goto Found;
|
||||||
|
panic("can't find text in filedeltext");
|
||||||
|
|
||||||
|
Found:
|
||||||
|
f->ntext--;
|
||||||
|
if(f->ntext == 0){
|
||||||
|
fileclose(f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memmove(f->text+i, f->text+i+1, (f->ntext-i)*sizeof(Text*));
|
||||||
|
if(f->curtext == t)
|
||||||
|
f->curtext = f->text[0];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
fileuninsert(File *f, Buffer *delta, uint p0, uint ns)
|
||||||
|
{
|
||||||
|
Undo u;
|
||||||
|
|
||||||
|
/* undo an insertion by deleting */
|
||||||
|
u.type = Delete;
|
||||||
|
u.mod = f->mod;
|
||||||
|
u.seq = f->seq;
|
||||||
|
u.p0 = p0;
|
||||||
|
u.n = ns;
|
||||||
|
bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fileundelete(File *f, Buffer *delta, uint p0, uint p1)
|
||||||
|
{
|
||||||
|
Undo u;
|
||||||
|
Rune *buf;
|
||||||
|
uint i, n;
|
||||||
|
|
||||||
|
/* undo a deletion by inserting */
|
||||||
|
u.type = Insert;
|
||||||
|
u.mod = f->mod;
|
||||||
|
u.seq = f->seq;
|
||||||
|
u.p0 = p0;
|
||||||
|
u.n = p1-p0;
|
||||||
|
buf = fbufalloc();
|
||||||
|
for(i=p0; i<p1; i+=n){
|
||||||
|
n = p1 - i;
|
||||||
|
if(n > RBUFSIZE)
|
||||||
|
n = RBUFSIZE;
|
||||||
|
bufread(&f->b, i, buf, n);
|
||||||
|
bufinsert(delta, delta->nc, buf, n);
|
||||||
|
}
|
||||||
|
fbuffree(buf);
|
||||||
|
bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
filereadc(File *f, uint q)
|
||||||
|
{
|
||||||
|
Rune r;
|
||||||
|
|
||||||
|
if(q >= f->b.nc)
|
||||||
|
return -1;
|
||||||
|
bufread(&f->b, q, &r, 1);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
filesetname(File *f, String *s)
|
||||||
|
{
|
||||||
|
if(!f->unread) /* This is setting initial file name */
|
||||||
|
fileunsetname(f, &f->delta);
|
||||||
|
Strduplstr(&f->name, s);
|
||||||
|
sortname(f);
|
||||||
|
f->unread = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fileunsetname(File *f, Buffer *delta)
|
||||||
|
{
|
||||||
|
String s;
|
||||||
|
Undo u;
|
||||||
|
|
||||||
|
/* undo a file name change by restoring old name */
|
||||||
|
u.type = Filename;
|
||||||
|
u.mod = f->mod;
|
||||||
|
u.seq = f->seq;
|
||||||
|
u.p0 = 0; /* unused */
|
||||||
|
Strinit(&s);
|
||||||
|
Strduplstr(&s, &f->name);
|
||||||
|
fullname(&s);
|
||||||
|
u.n = s.n;
|
||||||
|
if(s.n)
|
||||||
|
bufinsert(delta, delta->nc, s.s, s.n);
|
||||||
|
bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
|
||||||
|
Strclose(&s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fileunsetdot(File *f, Buffer *delta, Range dot)
|
||||||
|
{
|
||||||
|
Undo u;
|
||||||
|
|
||||||
|
u.type = Dot;
|
||||||
|
u.mod = f->mod;
|
||||||
|
u.seq = f->seq;
|
||||||
|
u.p0 = dot.p1;
|
||||||
|
u.n = dot.p2 - dot.p1;
|
||||||
|
bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fileunsetmark(File *f, Buffer *delta, Range mark)
|
||||||
|
{
|
||||||
|
Undo u;
|
||||||
|
|
||||||
|
u.type = Mark;
|
||||||
|
u.mod = f->mod;
|
||||||
|
u.seq = f->seq;
|
||||||
|
u.p0 = mark.p1;
|
||||||
|
u.n = mark.p2 - mark.p1;
|
||||||
|
bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint
|
||||||
|
fileload(File *f, uint p0, int fd, int *nulls)
|
||||||
|
{
|
||||||
|
if(f->seq > 0)
|
||||||
|
panic("undo in file.load unimplemented");
|
||||||
|
return bufload(&f->b, p0, fd, nulls);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
fileupdate(File *f, int notrans, int toterm)
|
||||||
|
{
|
||||||
|
uint p1, p2;
|
||||||
|
int mod;
|
||||||
|
|
||||||
|
if(f->rescuing)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
flushmerge();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fix the modification bit
|
||||||
|
* subtle point: don't save it away in the log.
|
||||||
|
*
|
||||||
|
* if another change is made, the correct f->mod
|
||||||
|
* state is saved in the undo log by filemark
|
||||||
|
* when setting the dot and mark.
|
||||||
|
*
|
||||||
|
* if the change is undone, the correct state is
|
||||||
|
* saved from f in the fileun... routines.
|
||||||
|
*/
|
||||||
|
mod = f->mod;
|
||||||
|
f->mod = f->prevmod;
|
||||||
|
if(f == cmd)
|
||||||
|
notrans = TRUE;
|
||||||
|
else{
|
||||||
|
fileunsetdot(f, &f->delta, f->prevdot);
|
||||||
|
fileunsetmark(f, &f->delta, f->prevmark);
|
||||||
|
}
|
||||||
|
f->dot = f->ndot;
|
||||||
|
fileundo(f, FALSE, !notrans, &p1, &p2, toterm);
|
||||||
|
f->mod = mod;
|
||||||
|
|
||||||
|
if(f->delta.nc == 0)
|
||||||
|
f->seq = 0;
|
||||||
|
|
||||||
|
if(f == cmd)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if(f->mod){
|
||||||
|
f->closeok = 0;
|
||||||
|
quitok = 0;
|
||||||
|
}else
|
||||||
|
f->closeok = 1;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
long
|
||||||
|
prevseq(Buffer *b)
|
||||||
|
{
|
||||||
|
Undo u;
|
||||||
|
uint up;
|
||||||
|
|
||||||
|
up = b->nc;
|
||||||
|
if(up == 0)
|
||||||
|
return 0;
|
||||||
|
up -= Undosize;
|
||||||
|
bufread(b, up, (Rune*)&u, Undosize);
|
||||||
|
return u.seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
long
|
||||||
|
undoseq(File *f, int isundo)
|
||||||
|
{
|
||||||
|
if(isundo)
|
||||||
|
return f->seq;
|
||||||
|
|
||||||
|
return prevseq(&f->epsilon);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fileundo(File *f, int isundo, int canredo, uint *q0p, uint *q1p, int flag)
|
||||||
|
{
|
||||||
|
Undo u;
|
||||||
|
Rune *buf;
|
||||||
|
uint i, n, up;
|
||||||
|
uint stop;
|
||||||
|
Buffer *delta, *epsilon;
|
||||||
|
|
||||||
|
if(isundo){
|
||||||
|
/* undo; reverse delta onto epsilon, seq decreases */
|
||||||
|
delta = &f->delta;
|
||||||
|
epsilon = &f->epsilon;
|
||||||
|
stop = f->seq;
|
||||||
|
}else{
|
||||||
|
/* redo; reverse epsilon onto delta, seq increases */
|
||||||
|
delta = &f->epsilon;
|
||||||
|
epsilon = &f->delta;
|
||||||
|
stop = 0; /* don't know yet */
|
||||||
|
}
|
||||||
|
|
||||||
|
raspstart(f);
|
||||||
|
while(delta->nc > 0){
|
||||||
|
/* rasp and buffer are in sync; sync with wire if needed */
|
||||||
|
if(needoutflush())
|
||||||
|
raspflush(f);
|
||||||
|
up = delta->nc-Undosize;
|
||||||
|
bufread(delta, up, (Rune*)&u, Undosize);
|
||||||
|
if(isundo){
|
||||||
|
if(u.seq < stop){
|
||||||
|
f->seq = u.seq;
|
||||||
|
raspdone(f, flag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(stop == 0)
|
||||||
|
stop = u.seq;
|
||||||
|
if(u.seq > stop){
|
||||||
|
raspdone(f, flag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch(u.type){
|
||||||
|
default:
|
||||||
|
panic("undo unknown u.type");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Delete:
|
||||||
|
f->seq = u.seq;
|
||||||
|
if(canredo)
|
||||||
|
fileundelete(f, epsilon, u.p0, u.p0+u.n);
|
||||||
|
f->mod = u.mod;
|
||||||
|
bufdelete(&f->b, u.p0, u.p0+u.n);
|
||||||
|
raspdelete(f, u.p0, u.p0+u.n, flag);
|
||||||
|
*q0p = u.p0;
|
||||||
|
*q1p = u.p0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Insert:
|
||||||
|
f->seq = u.seq;
|
||||||
|
if(canredo)
|
||||||
|
fileuninsert(f, epsilon, u.p0, u.n);
|
||||||
|
f->mod = u.mod;
|
||||||
|
up -= u.n;
|
||||||
|
buf = fbufalloc();
|
||||||
|
for(i=0; i<u.n; i+=n){
|
||||||
|
n = u.n - i;
|
||||||
|
if(n > RBUFSIZE)
|
||||||
|
n = RBUFSIZE;
|
||||||
|
bufread(delta, up+i, buf, n);
|
||||||
|
bufinsert(&f->b, u.p0+i, buf, n);
|
||||||
|
raspinsert(f, u.p0+i, buf, n, flag);
|
||||||
|
}
|
||||||
|
fbuffree(buf);
|
||||||
|
*q0p = u.p0;
|
||||||
|
*q1p = u.p0+u.n;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Filename:
|
||||||
|
f->seq = u.seq;
|
||||||
|
if(canredo)
|
||||||
|
fileunsetname(f, epsilon);
|
||||||
|
f->mod = u.mod;
|
||||||
|
up -= u.n;
|
||||||
|
|
||||||
|
Strinsure(&f->name, u.n+1);
|
||||||
|
bufread(delta, up, f->name.s, u.n);
|
||||||
|
f->name.s[u.n] = 0;
|
||||||
|
f->name.n = u.n;
|
||||||
|
fixname(&f->name);
|
||||||
|
sortname(f);
|
||||||
|
break;
|
||||||
|
case Dot:
|
||||||
|
f->seq = u.seq;
|
||||||
|
if(canredo)
|
||||||
|
fileunsetdot(f, epsilon, f->dot.r);
|
||||||
|
f->mod = u.mod;
|
||||||
|
f->dot.r.p1 = u.p0;
|
||||||
|
f->dot.r.p2 = u.p0 + u.n;
|
||||||
|
break;
|
||||||
|
case Mark:
|
||||||
|
f->seq = u.seq;
|
||||||
|
if(canredo)
|
||||||
|
fileunsetmark(f, epsilon, f->mark);
|
||||||
|
f->mod = u.mod;
|
||||||
|
f->mark.p1 = u.p0;
|
||||||
|
f->mark.p2 = u.p0 + u.n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bufdelete(delta, up, delta->nc);
|
||||||
|
}
|
||||||
|
if(isundo)
|
||||||
|
f->seq = 0;
|
||||||
|
raspdone(f, flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
filereset(File *f)
|
||||||
|
{
|
||||||
|
bufreset(&f->delta);
|
||||||
|
bufreset(&f->epsilon);
|
||||||
|
f->seq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fileclose(File *f)
|
||||||
|
{
|
||||||
|
Strclose(&f->name);
|
||||||
|
bufclose(&f->b);
|
||||||
|
bufclose(&f->delta);
|
||||||
|
bufclose(&f->epsilon);
|
||||||
|
if(f->rasp)
|
||||||
|
listfree(f->rasp);
|
||||||
|
free(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
filemark(File *f)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(f->unread)
|
||||||
|
return;
|
||||||
|
if(f->epsilon.nc)
|
||||||
|
bufdelete(&f->epsilon, 0, f->epsilon.nc);
|
||||||
|
|
||||||
|
if(f != cmd){
|
||||||
|
f->prevdot = f->dot.r;
|
||||||
|
f->prevmark = f->mark;
|
||||||
|
f->prevseq = f->seq;
|
||||||
|
f->prevmod = f->mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
f->ndot = f->dot;
|
||||||
|
f->seq = seq;
|
||||||
|
f->hiposn = 0;
|
||||||
|
}
|
278
sam/io.c
Normal file
278
sam/io.c
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
|
||||||
|
#define NSYSFILE 3
|
||||||
|
#define NOFILE 128
|
||||||
|
|
||||||
|
void
|
||||||
|
checkqid(File *f)
|
||||||
|
{
|
||||||
|
int i, w;
|
||||||
|
File *g;
|
||||||
|
|
||||||
|
w = whichmenu(f);
|
||||||
|
for(i=1; i<file.nused; i++){
|
||||||
|
g = file.filepptr[i];
|
||||||
|
if(w == i)
|
||||||
|
continue;
|
||||||
|
if(f->dev==g->dev && f->qidpath==g->qidpath)
|
||||||
|
warn_SS(Wdupfile, &f->name, &g->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
writef(File *f)
|
||||||
|
{
|
||||||
|
Posn n;
|
||||||
|
char *name;
|
||||||
|
int i, samename, newfile;
|
||||||
|
ulong dev;
|
||||||
|
uvlong qid;
|
||||||
|
long mtime, appendonly, length;
|
||||||
|
|
||||||
|
newfile = 0;
|
||||||
|
samename = Strcmp(&genstr, &f->name) == 0;
|
||||||
|
name = Strtoc(&f->name);
|
||||||
|
i = statfile(name, &dev, &qid, &mtime, 0, 0);
|
||||||
|
if(i == -1)
|
||||||
|
newfile++;
|
||||||
|
else if(samename &&
|
||||||
|
(f->dev!=dev || f->qidpath!=qid || f->mtime<mtime)){
|
||||||
|
f->dev = dev;
|
||||||
|
f->qidpath = qid;
|
||||||
|
f->mtime = mtime;
|
||||||
|
warn_S(Wdate, &genstr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(genc)
|
||||||
|
free(genc);
|
||||||
|
genc = Strtoc(&genstr);
|
||||||
|
if((io=create(genc, 1, 0666L)) < 0)
|
||||||
|
error_r(Ecreate, genc);
|
||||||
|
dprint("%s: ", genc);
|
||||||
|
if(statfd(io, 0, 0, 0, &length, &appendonly) > 0 && appendonly && length>0)
|
||||||
|
error(Eappend);
|
||||||
|
n = writeio(f);
|
||||||
|
if(f->name.s[0]==0 || samename){
|
||||||
|
if(addr.r.p1==0 && addr.r.p2==f->b.nc)
|
||||||
|
f->cleanseq = f->seq;
|
||||||
|
state(f, f->cleanseq==f->seq? Clean : Dirty);
|
||||||
|
}
|
||||||
|
if(newfile)
|
||||||
|
dprint("(new file) ");
|
||||||
|
if(addr.r.p2>0 && filereadc(f, addr.r.p2-1)!='\n')
|
||||||
|
warn(Wnotnewline);
|
||||||
|
closeio(n);
|
||||||
|
if(f->name.s[0]==0 || samename){
|
||||||
|
if(statfile(name, &dev, &qid, &mtime, 0, 0) > 0){
|
||||||
|
f->dev = dev;
|
||||||
|
f->qidpath = qid;
|
||||||
|
f->mtime = mtime;
|
||||||
|
checkqid(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Posn
|
||||||
|
readio(File *f, int *nulls, int setdate, int toterm)
|
||||||
|
{
|
||||||
|
int n, b, w;
|
||||||
|
Rune *r;
|
||||||
|
Posn nt;
|
||||||
|
Posn p = addr.r.p2;
|
||||||
|
ulong dev;
|
||||||
|
uvlong qid;
|
||||||
|
long mtime;
|
||||||
|
char buf[BLOCKSIZE+1], *s;
|
||||||
|
|
||||||
|
*nulls = FALSE;
|
||||||
|
b = 0;
|
||||||
|
if(f->unread){
|
||||||
|
nt = bufload(&f->b, 0, io, nulls);
|
||||||
|
if(toterm)
|
||||||
|
raspload(f);
|
||||||
|
}else
|
||||||
|
for(nt = 0; (n = read(io, buf+b, BLOCKSIZE-b))>0; nt+=(r-genbuf)){
|
||||||
|
n += b;
|
||||||
|
b = 0;
|
||||||
|
r = genbuf;
|
||||||
|
s = buf;
|
||||||
|
while(n > 0){
|
||||||
|
if((*r = *(uchar*)s) < Runeself){
|
||||||
|
if(*r)
|
||||||
|
r++;
|
||||||
|
else
|
||||||
|
*nulls = TRUE;
|
||||||
|
--n;
|
||||||
|
s++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(fullrune(s, n)){
|
||||||
|
w = chartorune(r, s);
|
||||||
|
if(*r)
|
||||||
|
r++;
|
||||||
|
else
|
||||||
|
*nulls = TRUE;
|
||||||
|
n -= w;
|
||||||
|
s += w;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
b = n;
|
||||||
|
memmove(buf, s, b);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loginsert(f, p, genbuf, r-genbuf);
|
||||||
|
}
|
||||||
|
if(b)
|
||||||
|
*nulls = TRUE;
|
||||||
|
if(*nulls)
|
||||||
|
warn(Wnulls);
|
||||||
|
if(setdate){
|
||||||
|
if(statfd(io, &dev, &qid, &mtime, 0, 0) > 0){
|
||||||
|
f->dev = dev;
|
||||||
|
f->qidpath = qid;
|
||||||
|
f->mtime = mtime;
|
||||||
|
checkqid(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Posn
|
||||||
|
writeio(File *f)
|
||||||
|
{
|
||||||
|
int m, n;
|
||||||
|
Posn p = addr.r.p1;
|
||||||
|
char *c;
|
||||||
|
|
||||||
|
while(p < addr.r.p2){
|
||||||
|
if(addr.r.p2-p>BLOCKSIZE)
|
||||||
|
n = BLOCKSIZE;
|
||||||
|
else
|
||||||
|
n = addr.r.p2-p;
|
||||||
|
bufread(&f->b, p, genbuf, n);
|
||||||
|
c = Strtoc(tmprstr(genbuf, n));
|
||||||
|
m = strlen(c);
|
||||||
|
if(Write(io, c, m) != m){
|
||||||
|
free(c);
|
||||||
|
if(p > 0)
|
||||||
|
p += n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
free(c);
|
||||||
|
p += n;
|
||||||
|
}
|
||||||
|
return p-addr.r.p1;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
closeio(Posn p)
|
||||||
|
{
|
||||||
|
close(io);
|
||||||
|
io = 0;
|
||||||
|
if(p >= 0)
|
||||||
|
dprint("#%lud\n", p);
|
||||||
|
}
|
||||||
|
|
||||||
|
int remotefd0 = 0;
|
||||||
|
int remotefd1 = 1;
|
||||||
|
|
||||||
|
void
|
||||||
|
bootterm(char *machine, char **argv)
|
||||||
|
{
|
||||||
|
int ph2t[2], pt2h[2];
|
||||||
|
|
||||||
|
if(machine){
|
||||||
|
dup(remotefd0, 0);
|
||||||
|
dup(remotefd1, 1);
|
||||||
|
close(remotefd0);
|
||||||
|
close(remotefd1);
|
||||||
|
argv[0] = "samterm";
|
||||||
|
execvp(samterm, argv);
|
||||||
|
fprint(2, "can't exec %s: %r\n", samterm);
|
||||||
|
_exits("damn");
|
||||||
|
}
|
||||||
|
if(pipe(ph2t)==-1 || pipe(pt2h)==-1)
|
||||||
|
panic("pipe");
|
||||||
|
switch(fork()){
|
||||||
|
case 0:
|
||||||
|
dup(ph2t[0], 0);
|
||||||
|
dup(pt2h[1], 1);
|
||||||
|
close(ph2t[0]);
|
||||||
|
close(ph2t[1]);
|
||||||
|
close(pt2h[0]);
|
||||||
|
close(pt2h[1]);
|
||||||
|
argv[0] = "samterm";
|
||||||
|
execvp(samterm, argv);
|
||||||
|
fprint(2, "can't exec: ");
|
||||||
|
perror(samterm);
|
||||||
|
_exits("damn");
|
||||||
|
case -1:
|
||||||
|
panic("can't fork samterm");
|
||||||
|
}
|
||||||
|
dup(pt2h[0], 0);
|
||||||
|
dup(ph2t[1], 1);
|
||||||
|
close(ph2t[0]);
|
||||||
|
close(ph2t[1]);
|
||||||
|
close(pt2h[0]);
|
||||||
|
close(pt2h[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
connectto(char *machine, char **argv)
|
||||||
|
{
|
||||||
|
int p1[2], p2[2];
|
||||||
|
char **av;
|
||||||
|
int ac;
|
||||||
|
|
||||||
|
/* count args */
|
||||||
|
for(av = argv; *av; av++)
|
||||||
|
;
|
||||||
|
av = malloc(sizeof(char*)*((av-argv) + 5));
|
||||||
|
if(av == nil){
|
||||||
|
dprint("out of memory\n");
|
||||||
|
exits("fork/exec");
|
||||||
|
}
|
||||||
|
ac = 0;
|
||||||
|
av[ac++] = RX;
|
||||||
|
av[ac++] = machine;
|
||||||
|
av[ac++] = rsamname;
|
||||||
|
av[ac++] = "-R";
|
||||||
|
while(*argv)
|
||||||
|
av[ac++] = *argv++;
|
||||||
|
av[ac] = 0;
|
||||||
|
if(pipe(p1)<0 || pipe(p2)<0){
|
||||||
|
dprint("can't pipe\n");
|
||||||
|
exits("pipe");
|
||||||
|
}
|
||||||
|
remotefd0 = p1[0];
|
||||||
|
remotefd1 = p2[1];
|
||||||
|
switch(fork()){
|
||||||
|
case 0:
|
||||||
|
dup(p2[0], 0);
|
||||||
|
dup(p1[1], 1);
|
||||||
|
close(p1[0]);
|
||||||
|
close(p1[1]);
|
||||||
|
close(p2[0]);
|
||||||
|
close(p2[1]);
|
||||||
|
execvp(RXPATH, av);
|
||||||
|
dprint("can't exec %s\n", RXPATH);
|
||||||
|
exits("exec");
|
||||||
|
|
||||||
|
case -1:
|
||||||
|
dprint("can't fork\n");
|
||||||
|
exits("fork");
|
||||||
|
}
|
||||||
|
free(av);
|
||||||
|
close(p1[1]);
|
||||||
|
close(p2[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
startup(char *machine, int Rflag, char **argv, char **files)
|
||||||
|
{
|
||||||
|
if(machine)
|
||||||
|
connectto(machine, files);
|
||||||
|
if(!Rflag)
|
||||||
|
bootterm(machine, argv);
|
||||||
|
downloaded = 1;
|
||||||
|
outTs(Hversion, VERSION);
|
||||||
|
}
|
96
sam/list.c
Normal file
96
sam/list.c
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check that list has room for one more element.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
growlist(List *l, int esize)
|
||||||
|
{
|
||||||
|
uchar *p;
|
||||||
|
|
||||||
|
if(l->listptr == nil || l->nalloc == 0){
|
||||||
|
l->nalloc = INCR;
|
||||||
|
l->listptr = emalloc(INCR*esize);
|
||||||
|
l->nused = 0;
|
||||||
|
}
|
||||||
|
else if(l->nused == l->nalloc){
|
||||||
|
p = erealloc(l->listptr, (l->nalloc+INCR)*esize);
|
||||||
|
l->listptr = p;
|
||||||
|
memset(p+l->nalloc*esize, 0, INCR*esize);
|
||||||
|
l->nalloc += INCR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove the ith element from the list
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
dellist(List *l, int i)
|
||||||
|
{
|
||||||
|
Posn *pp;
|
||||||
|
void **vpp;
|
||||||
|
|
||||||
|
l->nused--;
|
||||||
|
|
||||||
|
switch(l->type){
|
||||||
|
case 'P':
|
||||||
|
pp = l->posnptr+i;
|
||||||
|
memmove(pp, pp+1, (l->nused-i)*sizeof(*pp));
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
vpp = l->voidpptr+i;
|
||||||
|
memmove(vpp, vpp+1, (l->nused-i)*sizeof(*vpp));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a new element, whose position is i, to the list
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
inslist(List *l, int i, ...)
|
||||||
|
{
|
||||||
|
Posn *pp;
|
||||||
|
void **vpp;
|
||||||
|
va_list list;
|
||||||
|
|
||||||
|
|
||||||
|
va_start(list, i);
|
||||||
|
switch(l->type){
|
||||||
|
case 'P':
|
||||||
|
growlist(l, sizeof(*pp));
|
||||||
|
pp = l->posnptr+i;
|
||||||
|
memmove(pp+1, pp, (l->nused-i)*sizeof(*pp));
|
||||||
|
*pp = va_arg(list, Posn);
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
growlist(l, sizeof(*vpp));
|
||||||
|
vpp = l->voidpptr+i;
|
||||||
|
memmove(vpp+1, vpp, (l->nused-i)*sizeof(*vpp));
|
||||||
|
*vpp = va_arg(list, void*);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
va_end(list);
|
||||||
|
|
||||||
|
l->nused++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
listfree(List *l)
|
||||||
|
{
|
||||||
|
free(l->listptr);
|
||||||
|
free(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
List*
|
||||||
|
listalloc(int type)
|
||||||
|
{
|
||||||
|
List *l;
|
||||||
|
|
||||||
|
l = emalloc(sizeof(List));
|
||||||
|
l->type = type;
|
||||||
|
l->nalloc = 0;
|
||||||
|
l->nused = 0;
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
848
sam/mesg.c
Normal file
848
sam/mesg.c
Normal file
@@ -0,0 +1,848 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
Header h;
|
||||||
|
uchar indata[DATASIZE];
|
||||||
|
uchar outdata[2*DATASIZE+3]; /* room for overflow message */
|
||||||
|
uchar *inp;
|
||||||
|
uchar *outp;
|
||||||
|
uchar *outmsg = outdata;
|
||||||
|
Posn cmdpt;
|
||||||
|
Posn cmdptadv;
|
||||||
|
Buffer snarfbuf;
|
||||||
|
int waitack;
|
||||||
|
int outbuffered;
|
||||||
|
int tversion;
|
||||||
|
|
||||||
|
int inshort(void);
|
||||||
|
long inlong(void);
|
||||||
|
vlong invlong(void);
|
||||||
|
int inmesg(Tmesg);
|
||||||
|
|
||||||
|
void outshort(int);
|
||||||
|
void outlong(long);
|
||||||
|
void outvlong(vlong);
|
||||||
|
void outcopy(int, void*);
|
||||||
|
void outsend(void);
|
||||||
|
void outstart(Hmesg);
|
||||||
|
|
||||||
|
void setgenstr(File*, Posn, Posn);
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
char *hname[] = {
|
||||||
|
[Hversion] "Hversion",
|
||||||
|
[Hbindname] "Hbindname",
|
||||||
|
[Hcurrent] "Hcurrent",
|
||||||
|
[Hnewname] "Hnewname",
|
||||||
|
[Hmovname] "Hmovname",
|
||||||
|
[Hgrow] "Hgrow",
|
||||||
|
[Hcheck0] "Hcheck0",
|
||||||
|
[Hcheck] "Hcheck",
|
||||||
|
[Hunlock] "Hunlock",
|
||||||
|
[Hdata] "Hdata",
|
||||||
|
[Horigin] "Horigin",
|
||||||
|
[Hunlockfile] "Hunlockfile",
|
||||||
|
[Hsetdot] "Hsetdot",
|
||||||
|
[Hgrowdata] "Hgrowdata",
|
||||||
|
[Hmoveto] "Hmoveto",
|
||||||
|
[Hclean] "Hclean",
|
||||||
|
[Hdirty] "Hdirty",
|
||||||
|
[Hcut] "Hcut",
|
||||||
|
[Hsetpat] "Hsetpat",
|
||||||
|
[Hdelname] "Hdelname",
|
||||||
|
[Hclose] "Hclose",
|
||||||
|
[Hsetsnarf] "Hsetsnarf",
|
||||||
|
[Hsnarflen] "Hsnarflen",
|
||||||
|
[Hack] "Hack",
|
||||||
|
[Hexit] "Hexit",
|
||||||
|
// [Hplumb] "Hplumb"
|
||||||
|
};
|
||||||
|
|
||||||
|
char *tname[] = {
|
||||||
|
[Tversion] "Tversion",
|
||||||
|
[Tstartcmdfile] "Tstartcmdfile",
|
||||||
|
[Tcheck] "Tcheck",
|
||||||
|
[Trequest] "Trequest",
|
||||||
|
[Torigin] "Torigin",
|
||||||
|
[Tstartfile] "Tstartfile",
|
||||||
|
[Tworkfile] "Tworkfile",
|
||||||
|
[Ttype] "Ttype",
|
||||||
|
[Tcut] "Tcut",
|
||||||
|
[Tpaste] "Tpaste",
|
||||||
|
[Tsnarf] "Tsnarf",
|
||||||
|
[Tstartnewfile] "Tstartnewfile",
|
||||||
|
[Twrite] "Twrite",
|
||||||
|
[Tclose] "Tclose",
|
||||||
|
[Tlook] "Tlook",
|
||||||
|
[Tsearch] "Tsearch",
|
||||||
|
[Tsend] "Tsend",
|
||||||
|
[Tdclick] "Tdclick",
|
||||||
|
[Tstartsnarf] "Tstartsnarf",
|
||||||
|
[Tsetsnarf] "Tsetsnarf",
|
||||||
|
[Tack] "Tack",
|
||||||
|
[Texit] "Texit",
|
||||||
|
// [Tplumb] "Tplumb"
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
journal(int out, char *s)
|
||||||
|
{
|
||||||
|
static int fd = 0;
|
||||||
|
|
||||||
|
if(fd <= 0)
|
||||||
|
fd = create("/tmp/sam.out", 1, 0666L);
|
||||||
|
fprint(fd, "%s%s\n", out? "out: " : "in: ", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
journaln(int out, long n)
|
||||||
|
{
|
||||||
|
char buf[32];
|
||||||
|
|
||||||
|
snprint(buf, sizeof buf, "%ld", n);
|
||||||
|
journal(out, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
journalv(int out, vlong v)
|
||||||
|
{
|
||||||
|
char buf[32];
|
||||||
|
|
||||||
|
snprint(buf, sizeof buf, "%lld", v);
|
||||||
|
journal(out, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define journal(a, b)
|
||||||
|
#define journaln(a, b)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int
|
||||||
|
rcvchar(void){
|
||||||
|
static uchar buf[64];
|
||||||
|
static int i, nleft = 0;
|
||||||
|
|
||||||
|
if(nleft <= 0){
|
||||||
|
nleft = read(0, (char *)buf, sizeof buf);
|
||||||
|
if(nleft <= 0)
|
||||||
|
return -1;
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
--nleft;
|
||||||
|
return buf[i++];
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
rcv(void){
|
||||||
|
int c;
|
||||||
|
static int state = 0;
|
||||||
|
static int count = 0;
|
||||||
|
static int i = 0;
|
||||||
|
|
||||||
|
while((c=rcvchar()) != -1)
|
||||||
|
switch(state){
|
||||||
|
case 0:
|
||||||
|
h.type = c;
|
||||||
|
state++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
h.count0 = c;
|
||||||
|
state++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
h.count1 = c;
|
||||||
|
count = h.count0|(h.count1<<8);
|
||||||
|
i = 0;
|
||||||
|
if(count > DATASIZE)
|
||||||
|
panic("count>DATASIZE");
|
||||||
|
if(count == 0)
|
||||||
|
goto zerocount;
|
||||||
|
state++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
indata[i++] = c;
|
||||||
|
if(i == count){
|
||||||
|
zerocount:
|
||||||
|
indata[i] = 0;
|
||||||
|
state = count = 0;
|
||||||
|
return inmesg(h.type);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
File *
|
||||||
|
whichfile(int tag)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i<file.nused; i++)
|
||||||
|
if(file.filepptr[i]->tag==tag)
|
||||||
|
return file.filepptr[i];
|
||||||
|
hiccough((char *)0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
inmesg(Tmesg type)
|
||||||
|
{
|
||||||
|
Rune buf[1025];
|
||||||
|
char cbuf[64];
|
||||||
|
int i, m;
|
||||||
|
short s;
|
||||||
|
long l, l1;
|
||||||
|
vlong v;
|
||||||
|
File *f;
|
||||||
|
Posn p0, p1, p;
|
||||||
|
Range r;
|
||||||
|
String *str;
|
||||||
|
char *c, *wdir;
|
||||||
|
Rune *rp;
|
||||||
|
Plumbmsg *pm;
|
||||||
|
|
||||||
|
if(type > TMAX)
|
||||||
|
panic("inmesg");
|
||||||
|
|
||||||
|
journal(0, tname[type]);
|
||||||
|
|
||||||
|
inp = indata;
|
||||||
|
switch(type){
|
||||||
|
case -1:
|
||||||
|
panic("rcv error");
|
||||||
|
|
||||||
|
default:
|
||||||
|
fprint(2, "unknown type %d\n", type);
|
||||||
|
panic("rcv unknown");
|
||||||
|
|
||||||
|
case Tversion:
|
||||||
|
tversion = inshort();
|
||||||
|
journaln(0, tversion);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tstartcmdfile:
|
||||||
|
v = invlong(); /* for 64-bit pointers */
|
||||||
|
journaln(0, v);
|
||||||
|
Strdupl(&genstr, samname);
|
||||||
|
cmd = newfile();
|
||||||
|
cmd->unread = 0;
|
||||||
|
outTsv(Hbindname, cmd->tag, v);
|
||||||
|
outTs(Hcurrent, cmd->tag);
|
||||||
|
logsetname(cmd, &genstr);
|
||||||
|
cmd->rasp = listalloc('P');
|
||||||
|
cmd->mod = 0;
|
||||||
|
if(cmdstr.n){
|
||||||
|
loginsert(cmd, 0L, cmdstr.s, cmdstr.n);
|
||||||
|
Strdelete(&cmdstr, 0L, (Posn)cmdstr.n);
|
||||||
|
}
|
||||||
|
fileupdate(cmd, FALSE, TRUE);
|
||||||
|
outT0(Hunlock);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tcheck:
|
||||||
|
/* go through whichfile to check the tag */
|
||||||
|
outTs(Hcheck, whichfile(inshort())->tag);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Trequest:
|
||||||
|
f = whichfile(inshort());
|
||||||
|
p0 = inlong();
|
||||||
|
p1 = p0+inshort();
|
||||||
|
journaln(0, p0);
|
||||||
|
journaln(0, p1-p0);
|
||||||
|
if(f->unread)
|
||||||
|
panic("Trequest: unread");
|
||||||
|
if(p1>f->b.nc)
|
||||||
|
p1 = f->b.nc;
|
||||||
|
if(p0>f->b.nc) /* can happen e.g. scrolling during command */
|
||||||
|
p0 = f->b.nc;
|
||||||
|
if(p0 == p1){
|
||||||
|
i = 0;
|
||||||
|
r.p1 = r.p2 = p0;
|
||||||
|
}else{
|
||||||
|
r = rdata(f->rasp, p0, p1-p0);
|
||||||
|
i = r.p2-r.p1;
|
||||||
|
bufread(&f->b, r.p1, buf, i);
|
||||||
|
}
|
||||||
|
buf[i]=0;
|
||||||
|
outTslS(Hdata, f->tag, r.p1, tmprstr(buf, i+1));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Torigin:
|
||||||
|
s = inshort();
|
||||||
|
l = inlong();
|
||||||
|
l1 = inlong();
|
||||||
|
journaln(0, l1);
|
||||||
|
lookorigin(whichfile(s), l, l1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tstartfile:
|
||||||
|
termlocked++;
|
||||||
|
f = whichfile(inshort());
|
||||||
|
if(!f->rasp) /* this might be a duplicate message */
|
||||||
|
f->rasp = listalloc('P');
|
||||||
|
current(f);
|
||||||
|
outTsv(Hbindname, f->tag, invlong()); /* for 64-bit pointers */
|
||||||
|
outTs(Hcurrent, f->tag);
|
||||||
|
journaln(0, f->tag);
|
||||||
|
if(f->unread)
|
||||||
|
load(f);
|
||||||
|
else{
|
||||||
|
if(f->b.nc>0){
|
||||||
|
rgrow(f->rasp, 0L, f->b.nc);
|
||||||
|
outTsll(Hgrow, f->tag, 0L, f->b.nc);
|
||||||
|
}
|
||||||
|
outTs(Hcheck0, f->tag);
|
||||||
|
moveto(f, f->dot.r);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tworkfile:
|
||||||
|
i = inshort();
|
||||||
|
f = whichfile(i);
|
||||||
|
current(f);
|
||||||
|
f->dot.r.p1 = inlong();
|
||||||
|
f->dot.r.p2 = inlong();
|
||||||
|
f->tdot = f->dot.r;
|
||||||
|
journaln(0, i);
|
||||||
|
journaln(0, f->dot.r.p1);
|
||||||
|
journaln(0, f->dot.r.p2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Ttype:
|
||||||
|
f = whichfile(inshort());
|
||||||
|
p0 = inlong();
|
||||||
|
journaln(0, p0);
|
||||||
|
journal(0, (char*)inp);
|
||||||
|
str = tmpcstr((char*)inp);
|
||||||
|
i = str->n;
|
||||||
|
loginsert(f, p0, str->s, str->n);
|
||||||
|
if(fileupdate(f, FALSE, FALSE))
|
||||||
|
seq++;
|
||||||
|
if(f==cmd && p0==f->b.nc-i && i>0 && str->s[i-1]=='\n'){
|
||||||
|
freetmpstr(str);
|
||||||
|
termlocked++;
|
||||||
|
termcommand();
|
||||||
|
}else
|
||||||
|
freetmpstr(str);
|
||||||
|
f->dot.r.p1 = f->dot.r.p2 = p0+i; /* terminal knows this already */
|
||||||
|
f->tdot = f->dot.r;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tcut:
|
||||||
|
f = whichfile(inshort());
|
||||||
|
p0 = inlong();
|
||||||
|
p1 = inlong();
|
||||||
|
journaln(0, p0);
|
||||||
|
journaln(0, p1);
|
||||||
|
logdelete(f, p0, p1);
|
||||||
|
if(fileupdate(f, FALSE, FALSE))
|
||||||
|
seq++;
|
||||||
|
f->dot.r.p1 = f->dot.r.p2 = p0;
|
||||||
|
f->tdot = f->dot.r; /* terminal knows the value of dot already */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tpaste:
|
||||||
|
f = whichfile(inshort());
|
||||||
|
p0 = inlong();
|
||||||
|
journaln(0, p0);
|
||||||
|
for(l=0; l<snarfbuf.nc; l+=m){
|
||||||
|
m = snarfbuf.nc-l;
|
||||||
|
if(m>BLOCKSIZE)
|
||||||
|
m = BLOCKSIZE;
|
||||||
|
bufread(&snarfbuf, l, genbuf, m);
|
||||||
|
loginsert(f, p0, tmprstr(genbuf, m)->s, m);
|
||||||
|
}
|
||||||
|
if(fileupdate(f, FALSE, TRUE))
|
||||||
|
seq++;
|
||||||
|
f->dot.r.p1 = p0;
|
||||||
|
f->dot.r.p2 = p0+snarfbuf.nc;
|
||||||
|
f->tdot.p1 = -1; /* force telldot to tell (arguably a BUG) */
|
||||||
|
telldot(f);
|
||||||
|
outTs(Hunlockfile, f->tag);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tsnarf:
|
||||||
|
i = inshort();
|
||||||
|
p0 = inlong();
|
||||||
|
p1 = inlong();
|
||||||
|
snarf(whichfile(i), p0, p1, &snarfbuf, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tstartnewfile:
|
||||||
|
v = invlong();
|
||||||
|
Strdupl(&genstr, empty);
|
||||||
|
f = newfile();
|
||||||
|
f->rasp = listalloc('P');
|
||||||
|
outTsv(Hbindname, f->tag, v);
|
||||||
|
logsetname(f, &genstr);
|
||||||
|
outTs(Hcurrent, f->tag);
|
||||||
|
current(f);
|
||||||
|
load(f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Twrite:
|
||||||
|
termlocked++;
|
||||||
|
i = inshort();
|
||||||
|
journaln(0, i);
|
||||||
|
f = whichfile(i);
|
||||||
|
addr.r.p1 = 0;
|
||||||
|
addr.r.p2 = f->b.nc;
|
||||||
|
if(f->name.s[0] == 0)
|
||||||
|
error(Enoname);
|
||||||
|
Strduplstr(&genstr, &f->name);
|
||||||
|
writef(f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tclose:
|
||||||
|
termlocked++;
|
||||||
|
i = inshort();
|
||||||
|
journaln(0, i);
|
||||||
|
f = whichfile(i);
|
||||||
|
current(f);
|
||||||
|
trytoclose(f);
|
||||||
|
/* if trytoclose fails, will error out */
|
||||||
|
delete(f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tlook:
|
||||||
|
f = whichfile(inshort());
|
||||||
|
termlocked++;
|
||||||
|
p0 = inlong();
|
||||||
|
p1 = inlong();
|
||||||
|
journaln(0, p0);
|
||||||
|
journaln(0, p1);
|
||||||
|
setgenstr(f, p0, p1);
|
||||||
|
for(l = 0; l<genstr.n; l++){
|
||||||
|
i = genstr.s[l];
|
||||||
|
if(utfrune(".*+?(|)\\[]^$", i)){
|
||||||
|
str = tmpcstr("\\");
|
||||||
|
Strinsert(&genstr, str, l++);
|
||||||
|
freetmpstr(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Straddc(&genstr, '\0');
|
||||||
|
nextmatch(f, &genstr, p1, 1);
|
||||||
|
moveto(f, sel.p[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tsearch:
|
||||||
|
termlocked++;
|
||||||
|
if(curfile == 0)
|
||||||
|
error(Enofile);
|
||||||
|
if(lastpat.s[0] == 0)
|
||||||
|
panic("Tsearch");
|
||||||
|
nextmatch(curfile, &lastpat, curfile->dot.r.p2, 1);
|
||||||
|
moveto(curfile, sel.p[0]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tsend:
|
||||||
|
termlocked++;
|
||||||
|
inshort(); /* ignored */
|
||||||
|
p0 = inlong();
|
||||||
|
p1 = inlong();
|
||||||
|
setgenstr(cmd, p0, p1);
|
||||||
|
bufreset(&snarfbuf);
|
||||||
|
bufinsert(&snarfbuf, (Posn)0, genstr.s, genstr.n);
|
||||||
|
outTl(Hsnarflen, genstr.n);
|
||||||
|
if(genstr.s[genstr.n-1] != '\n')
|
||||||
|
Straddc(&genstr, '\n');
|
||||||
|
loginsert(cmd, cmd->b.nc, genstr.s, genstr.n);
|
||||||
|
fileupdate(cmd, FALSE, TRUE);
|
||||||
|
cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->b.nc;
|
||||||
|
telldot(cmd);
|
||||||
|
termcommand();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tdclick:
|
||||||
|
f = whichfile(inshort());
|
||||||
|
p1 = inlong();
|
||||||
|
doubleclick(f, p1);
|
||||||
|
f->tdot.p1 = f->tdot.p2 = p1;
|
||||||
|
telldot(f);
|
||||||
|
outTs(Hunlockfile, f->tag);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tstartsnarf:
|
||||||
|
if (snarfbuf.nc <= 0) { /* nothing to export */
|
||||||
|
outTs(Hsetsnarf, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
c = 0;
|
||||||
|
i = 0;
|
||||||
|
m = snarfbuf.nc;
|
||||||
|
if(m > SNARFSIZE) {
|
||||||
|
m = SNARFSIZE;
|
||||||
|
dprint("?warning: snarf buffer truncated\n");
|
||||||
|
}
|
||||||
|
rp = malloc(m*sizeof(Rune));
|
||||||
|
if(rp){
|
||||||
|
bufread(&snarfbuf, 0, rp, m);
|
||||||
|
c = Strtoc(tmprstr(rp, m));
|
||||||
|
free(rp);
|
||||||
|
i = strlen(c);
|
||||||
|
}
|
||||||
|
outTs(Hsetsnarf, i);
|
||||||
|
if(c){
|
||||||
|
Write(1, c, i);
|
||||||
|
free(c);
|
||||||
|
} else
|
||||||
|
dprint("snarf buffer too long\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tsetsnarf:
|
||||||
|
m = inshort();
|
||||||
|
if(m > SNARFSIZE)
|
||||||
|
error(Etoolong);
|
||||||
|
c = malloc(m+1);
|
||||||
|
if(c){
|
||||||
|
for(i=0; i<m; i++)
|
||||||
|
c[i] = rcvchar();
|
||||||
|
c[m] = 0;
|
||||||
|
str = tmpcstr(c);
|
||||||
|
free(c);
|
||||||
|
bufreset(&snarfbuf);
|
||||||
|
bufinsert(&snarfbuf, (Posn)0, str->s, str->n);
|
||||||
|
freetmpstr(str);
|
||||||
|
outT0(Hunlock);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Tack:
|
||||||
|
waitack = 0;
|
||||||
|
break;
|
||||||
|
#if 0
|
||||||
|
case Tplumb:
|
||||||
|
f = whichfile(inshort());
|
||||||
|
p0 = inlong();
|
||||||
|
p1 = inlong();
|
||||||
|
pm = emalloc(sizeof(Plumbmsg));
|
||||||
|
pm->src = strdup("sam");
|
||||||
|
pm->dst = 0;
|
||||||
|
/* construct current directory */
|
||||||
|
c = Strtoc(&f->name);
|
||||||
|
if(c[0] == '/')
|
||||||
|
pm->wdir = c;
|
||||||
|
else{
|
||||||
|
wdir = emalloc(1024);
|
||||||
|
getwd(wdir, 1024);
|
||||||
|
pm->wdir = emalloc(1024);
|
||||||
|
snprint(pm->wdir, 1024, "%s/%s", wdir, c);
|
||||||
|
cleanname(pm->wdir);
|
||||||
|
free(wdir);
|
||||||
|
free(c);
|
||||||
|
}
|
||||||
|
c = strrchr(pm->wdir, '/');
|
||||||
|
if(c)
|
||||||
|
*c = '\0';
|
||||||
|
pm->type = strdup("text");
|
||||||
|
if(p1 > p0)
|
||||||
|
pm->attr = nil;
|
||||||
|
else{
|
||||||
|
p = p0;
|
||||||
|
while(p0>0 && (i=filereadc(f, p0 - 1))!=' ' && i!='\t' && i!='\n')
|
||||||
|
p0--;
|
||||||
|
while(p1<f->b.nc && (i=filereadc(f, p1))!=' ' && i!='\t' && i!='\n')
|
||||||
|
p1++;
|
||||||
|
sprint(cbuf, "click=%ld", p-p0);
|
||||||
|
pm->attr = plumbunpackattr(cbuf);
|
||||||
|
}
|
||||||
|
if(p0==p1 || p1-p0>=BLOCKSIZE){
|
||||||
|
plumbfree(pm);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
setgenstr(f, p0, p1);
|
||||||
|
pm->data = Strtoc(&genstr);
|
||||||
|
pm->ndata = strlen(pm->data);
|
||||||
|
c = plumbpack(pm, &i);
|
||||||
|
if(c != 0){
|
||||||
|
outTs(Hplumb, i);
|
||||||
|
Write(1, c, i);
|
||||||
|
free(c);
|
||||||
|
}
|
||||||
|
plumbfree(pm);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case Texit:
|
||||||
|
exits(0);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
snarf(File *f, Posn p1, Posn p2, Buffer *buf, int emptyok)
|
||||||
|
{
|
||||||
|
Posn l;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(!emptyok && p1==p2)
|
||||||
|
return;
|
||||||
|
bufreset(buf);
|
||||||
|
/* Stage through genbuf to avoid compaction problems (vestigial) */
|
||||||
|
if(p2 > f->b.nc){
|
||||||
|
fprint(2, "bad snarf addr p1=%ld p2=%ld f->b.nc=%d\n", p1, p2, f->b.nc); /*ZZZ should never happen, can remove */
|
||||||
|
p2 = f->b.nc;
|
||||||
|
}
|
||||||
|
for(l=p1; l<p2; l+=i){
|
||||||
|
i = p2-l>BLOCKSIZE? BLOCKSIZE : p2-l;
|
||||||
|
bufread(&f->b, l, genbuf, i);
|
||||||
|
bufinsert(buf, buf->nc, tmprstr(genbuf, i)->s, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
inshort(void)
|
||||||
|
{
|
||||||
|
ushort n;
|
||||||
|
|
||||||
|
n = inp[0] | (inp[1]<<8);
|
||||||
|
inp += 2;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
long
|
||||||
|
inlong(void)
|
||||||
|
{
|
||||||
|
ulong n;
|
||||||
|
|
||||||
|
n = inp[0] | (inp[1]<<8) | (inp[2]<<16) | (inp[3]<<24);
|
||||||
|
inp += 4;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
vlong
|
||||||
|
invlong(void)
|
||||||
|
{
|
||||||
|
vlong v;
|
||||||
|
|
||||||
|
v = (inp[7]<<24) | (inp[6]<<16) | (inp[5]<<8) | inp[4];
|
||||||
|
v = (v<<16) | (inp[3]<<8) | inp[2];
|
||||||
|
v = (v<<16) | (inp[1]<<8) | inp[0];
|
||||||
|
inp += 8;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setgenstr(File *f, Posn p0, Posn p1)
|
||||||
|
{
|
||||||
|
if(p0 != p1){
|
||||||
|
if(p1-p0 >= TBLOCKSIZE)
|
||||||
|
error(Etoolong);
|
||||||
|
Strinsure(&genstr, p1-p0);
|
||||||
|
bufread(&f->b, p0, genbuf, p1-p0);
|
||||||
|
memmove(genstr.s, genbuf, RUNESIZE*(p1-p0));
|
||||||
|
genstr.n = p1-p0;
|
||||||
|
}else{
|
||||||
|
if(snarfbuf.nc == 0)
|
||||||
|
error(Eempty);
|
||||||
|
if(snarfbuf.nc > TBLOCKSIZE)
|
||||||
|
error(Etoolong);
|
||||||
|
bufread(&snarfbuf, (Posn)0, genbuf, snarfbuf.nc);
|
||||||
|
Strinsure(&genstr, snarfbuf.nc);
|
||||||
|
memmove(genstr.s, genbuf, RUNESIZE*snarfbuf.nc);
|
||||||
|
genstr.n = snarfbuf.nc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outT0(Hmesg type)
|
||||||
|
{
|
||||||
|
outstart(type);
|
||||||
|
outsend();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outTl(Hmesg type, long l)
|
||||||
|
{
|
||||||
|
outstart(type);
|
||||||
|
outlong(l);
|
||||||
|
outsend();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outTs(Hmesg type, int s)
|
||||||
|
{
|
||||||
|
outstart(type);
|
||||||
|
journaln(1, s);
|
||||||
|
outshort(s);
|
||||||
|
outsend();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outS(String *s)
|
||||||
|
{
|
||||||
|
char *c;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
c = Strtoc(s);
|
||||||
|
i = strlen(c);
|
||||||
|
outcopy(i, c);
|
||||||
|
if(i > 99)
|
||||||
|
c[99] = 0;
|
||||||
|
journaln(1, i);
|
||||||
|
journal(1, c);
|
||||||
|
free(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outTsS(Hmesg type, int s1, String *s)
|
||||||
|
{
|
||||||
|
outstart(type);
|
||||||
|
outshort(s1);
|
||||||
|
outS(s);
|
||||||
|
outsend();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outTslS(Hmesg type, int s1, Posn l1, String *s)
|
||||||
|
{
|
||||||
|
outstart(type);
|
||||||
|
outshort(s1);
|
||||||
|
journaln(1, s1);
|
||||||
|
outlong(l1);
|
||||||
|
journaln(1, l1);
|
||||||
|
outS(s);
|
||||||
|
outsend();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outTS(Hmesg type, String *s)
|
||||||
|
{
|
||||||
|
outstart(type);
|
||||||
|
outS(s);
|
||||||
|
outsend();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outTsllS(Hmesg type, int s1, Posn l1, Posn l2, String *s)
|
||||||
|
{
|
||||||
|
outstart(type);
|
||||||
|
outshort(s1);
|
||||||
|
outlong(l1);
|
||||||
|
outlong(l2);
|
||||||
|
journaln(1, l1);
|
||||||
|
journaln(1, l2);
|
||||||
|
outS(s);
|
||||||
|
outsend();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outTsll(Hmesg type, int s, Posn l1, Posn l2)
|
||||||
|
{
|
||||||
|
outstart(type);
|
||||||
|
outshort(s);
|
||||||
|
outlong(l1);
|
||||||
|
outlong(l2);
|
||||||
|
journaln(1, l1);
|
||||||
|
journaln(1, l2);
|
||||||
|
outsend();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outTsl(Hmesg type, int s, Posn l)
|
||||||
|
{
|
||||||
|
outstart(type);
|
||||||
|
outshort(s);
|
||||||
|
outlong(l);
|
||||||
|
journaln(1, l);
|
||||||
|
outsend();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outTsv(Hmesg type, int s, vlong v)
|
||||||
|
{
|
||||||
|
outstart(type);
|
||||||
|
outshort(s);
|
||||||
|
outvlong(v);
|
||||||
|
journaln(1, v);
|
||||||
|
outsend();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outstart(Hmesg type)
|
||||||
|
{
|
||||||
|
journal(1, hname[type]);
|
||||||
|
outmsg[0] = type;
|
||||||
|
outp = outmsg+3;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outcopy(int count, void *data)
|
||||||
|
{
|
||||||
|
memmove(outp, data, count);
|
||||||
|
outp += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outshort(int s)
|
||||||
|
{
|
||||||
|
*outp++ = s;
|
||||||
|
*outp++ = s>>8;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outlong(long l)
|
||||||
|
{
|
||||||
|
*outp++ = l;
|
||||||
|
*outp++ = l>>8;
|
||||||
|
*outp++ = l>>16;
|
||||||
|
*outp++ = l>>24;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outvlong(vlong v)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < 8; i++){
|
||||||
|
*outp++ = v;
|
||||||
|
v >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outsend(void)
|
||||||
|
{
|
||||||
|
int outcount;
|
||||||
|
|
||||||
|
if(outp >= outdata+nelem(outdata))
|
||||||
|
panic("outsend");
|
||||||
|
outcount = outp-outmsg;
|
||||||
|
outcount -= 3;
|
||||||
|
outmsg[1] = outcount;
|
||||||
|
outmsg[2] = outcount>>8;
|
||||||
|
outmsg = outp;
|
||||||
|
if(!outbuffered){
|
||||||
|
outcount = outmsg-outdata;
|
||||||
|
if (write(1, (char*) outdata, outcount) != outcount)
|
||||||
|
rescue();
|
||||||
|
outmsg = outdata;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
needoutflush(void)
|
||||||
|
{
|
||||||
|
return outmsg >= outdata+DATASIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outflush(void)
|
||||||
|
{
|
||||||
|
if(outmsg == outdata)
|
||||||
|
return;
|
||||||
|
outbuffered = 0;
|
||||||
|
/* flow control */
|
||||||
|
outT0(Hack);
|
||||||
|
waitack = 1;
|
||||||
|
do
|
||||||
|
if(rcv() == 0){
|
||||||
|
rescue();
|
||||||
|
exits("eof");
|
||||||
|
}
|
||||||
|
while(waitack);
|
||||||
|
outmsg = outdata;
|
||||||
|
outbuffered = 1;
|
||||||
|
}
|
131
sam/mesg.h
Normal file
131
sam/mesg.h
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/* VERSION 1 introduces plumbing
|
||||||
|
2 increases SNARFSIZE from 4096 to 32000
|
||||||
|
*/
|
||||||
|
#define VERSION 2
|
||||||
|
|
||||||
|
#define TBLOCKSIZE 512 /* largest piece of text sent to terminal */
|
||||||
|
#define DATASIZE (UTFmax*TBLOCKSIZE+30) /* ... including protocol header stuff */
|
||||||
|
#define SNARFSIZE 32000 /* maximum length of exchanged snarf buffer, must fit in 15 bits */
|
||||||
|
/*
|
||||||
|
* Messages originating at the terminal
|
||||||
|
*/
|
||||||
|
typedef enum Tmesg
|
||||||
|
{
|
||||||
|
Tversion, /* version */
|
||||||
|
Tstartcmdfile, /* terminal just opened command frame */
|
||||||
|
Tcheck, /* ask host to poke with Hcheck */
|
||||||
|
Trequest, /* request data to fill a hole */
|
||||||
|
Torigin, /* gimme an Horigin near here */
|
||||||
|
Tstartfile, /* terminal just opened a file's frame */
|
||||||
|
Tworkfile, /* set file to which commands apply */
|
||||||
|
Ttype, /* add some characters, but terminal already knows */
|
||||||
|
Tcut,
|
||||||
|
Tpaste,
|
||||||
|
Tsnarf,
|
||||||
|
Tstartnewfile, /* terminal just opened a new frame */
|
||||||
|
Twrite, /* write file */
|
||||||
|
Tclose, /* terminal requests file close; check mod. status */
|
||||||
|
Tlook, /* search for literal current text */
|
||||||
|
Tsearch, /* search for last regular expression */
|
||||||
|
Tsend, /* pretend he typed stuff */
|
||||||
|
Tdclick, /* double click */
|
||||||
|
Tstartsnarf, /* initiate snarf buffer exchange */
|
||||||
|
Tsetsnarf, /* remember string in snarf buffer */
|
||||||
|
Tack, /* acknowledge Hack */
|
||||||
|
Texit, /* exit */
|
||||||
|
Tplumb, /* send plumb message */
|
||||||
|
TMAX
|
||||||
|
}Tmesg;
|
||||||
|
/*
|
||||||
|
* Messages originating at the host
|
||||||
|
*/
|
||||||
|
typedef enum Hmesg
|
||||||
|
{
|
||||||
|
Hversion, /* version */
|
||||||
|
Hbindname, /* attach name[0] to text in terminal */
|
||||||
|
Hcurrent, /* make named file the typing file */
|
||||||
|
Hnewname, /* create "" name in menu */
|
||||||
|
Hmovname, /* move file name in menu */
|
||||||
|
Hgrow, /* insert space in rasp */
|
||||||
|
Hcheck0, /* see below */
|
||||||
|
Hcheck, /* ask terminal to check whether it needs more data */
|
||||||
|
Hunlock, /* command is finished; user can do things */
|
||||||
|
Hdata, /* store this data in previously allocated space */
|
||||||
|
Horigin, /* set origin of file/frame in terminal */
|
||||||
|
Hunlockfile, /* unlock file in terminal */
|
||||||
|
Hsetdot, /* set dot in terminal */
|
||||||
|
Hgrowdata, /* Hgrow + Hdata folded together */
|
||||||
|
Hmoveto, /* scrolling, context search, etc. */
|
||||||
|
Hclean, /* named file is now 'clean' */
|
||||||
|
Hdirty, /* named file is now 'dirty' */
|
||||||
|
Hcut, /* remove space from rasp */
|
||||||
|
Hsetpat, /* set remembered regular expression */
|
||||||
|
Hdelname, /* delete file name from menu */
|
||||||
|
Hclose, /* close file and remove from menu */
|
||||||
|
Hsetsnarf, /* remember string in snarf buffer */
|
||||||
|
Hsnarflen, /* report length of implicit snarf */
|
||||||
|
Hack, /* request acknowledgement */
|
||||||
|
Hexit,
|
||||||
|
Hplumb, /* return plumb message to terminal - version 1 */
|
||||||
|
HMAX
|
||||||
|
}Hmesg;
|
||||||
|
typedef struct Header{
|
||||||
|
uchar type; /* one of the above */
|
||||||
|
uchar count0; /* low bits of data size */
|
||||||
|
uchar count1; /* high bits of data size */
|
||||||
|
uchar data[1]; /* variable size */
|
||||||
|
}Header;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* File transfer protocol schematic, a la Holzmann
|
||||||
|
* #define N 6
|
||||||
|
*
|
||||||
|
* chan h = [4] of { mtype };
|
||||||
|
* chan t = [4] of { mtype };
|
||||||
|
*
|
||||||
|
* mtype = { Hgrow, Hdata,
|
||||||
|
* Hcheck, Hcheck0,
|
||||||
|
* Trequest, Tcheck,
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* active proctype host()
|
||||||
|
* { byte n;
|
||||||
|
*
|
||||||
|
* do
|
||||||
|
* :: n < N -> n++; t!Hgrow
|
||||||
|
* :: n == N -> n++; t!Hcheck0
|
||||||
|
*
|
||||||
|
* :: h?Trequest -> t!Hdata
|
||||||
|
* :: h?Tcheck -> t!Hcheck
|
||||||
|
* od
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* active proctype term()
|
||||||
|
* {
|
||||||
|
* do
|
||||||
|
* :: t?Hgrow -> h!Trequest
|
||||||
|
* :: t?Hdata -> skip
|
||||||
|
* :: t?Hcheck0 -> h!Tcheck
|
||||||
|
* :: t?Hcheck ->
|
||||||
|
* if
|
||||||
|
* :: h!Trequest -> progress: h!Tcheck
|
||||||
|
* :: break
|
||||||
|
* fi
|
||||||
|
* od;
|
||||||
|
* printf("term exits\n")
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* From: gerard@research.bell-labs.com
|
||||||
|
* Date: Tue Jul 17 13:47:23 EDT 2001
|
||||||
|
* To: rob@research.bell-labs.com
|
||||||
|
*
|
||||||
|
* spin -c (or -a) spec
|
||||||
|
* pcc -DNP -o pan pan.c
|
||||||
|
* pan -l
|
||||||
|
*
|
||||||
|
* proves that there are no non-progress cycles
|
||||||
|
* (infinite executions *not* passing through
|
||||||
|
* the statement marked with a label starting
|
||||||
|
* with the prefix "progress")
|
||||||
|
*
|
||||||
|
*/
|
173
sam/moveto.c
Normal file
173
sam/moveto.c
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
moveto(File *f, Range r)
|
||||||
|
{
|
||||||
|
Posn p1 = r.p1, p2 = r.p2;
|
||||||
|
|
||||||
|
f->dot.r.p1 = p1;
|
||||||
|
f->dot.r.p2 = p2;
|
||||||
|
if(f->rasp){
|
||||||
|
telldot(f);
|
||||||
|
outTsl(Hmoveto, f->tag, f->dot.r.p1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
telldot(File *f)
|
||||||
|
{
|
||||||
|
if(f->rasp == 0)
|
||||||
|
panic("telldot");
|
||||||
|
if(f->dot.r.p1==f->tdot.p1 && f->dot.r.p2==f->tdot.p2)
|
||||||
|
return;
|
||||||
|
outTsll(Hsetdot, f->tag, f->dot.r.p1, f->dot.r.p2);
|
||||||
|
f->tdot = f->dot.r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tellpat(void)
|
||||||
|
{
|
||||||
|
outTS(Hsetpat, &lastpat);
|
||||||
|
patset = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHARSHIFT 128
|
||||||
|
|
||||||
|
void
|
||||||
|
lookorigin(File *f, Posn p0, Posn ls)
|
||||||
|
{
|
||||||
|
int nl, nc, c;
|
||||||
|
Posn p, oldp0;
|
||||||
|
|
||||||
|
if(p0 > f->b.nc)
|
||||||
|
p0 = f->b.nc;
|
||||||
|
oldp0 = p0;
|
||||||
|
p = p0;
|
||||||
|
for(nl=nc=c=0; c!=-1 && nl<ls && nc<ls*CHARSHIFT; nc++)
|
||||||
|
if((c=filereadc(f, --p)) == '\n'){
|
||||||
|
nl++;
|
||||||
|
oldp0 = p0-nc;
|
||||||
|
}
|
||||||
|
if(c == -1)
|
||||||
|
p0 = 0;
|
||||||
|
else if(nl==0){
|
||||||
|
if(p0>=CHARSHIFT/2)
|
||||||
|
p0-=CHARSHIFT/2;
|
||||||
|
else
|
||||||
|
p0 = 0;
|
||||||
|
}else
|
||||||
|
p0 = oldp0;
|
||||||
|
outTsl(Horigin, f->tag, p0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
alnum(int c)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Hard to get absolutely right. Use what we know about ASCII
|
||||||
|
* and assume anything above the Latin control characters is
|
||||||
|
* potentially an alphanumeric.
|
||||||
|
*/
|
||||||
|
if(c<=' ')
|
||||||
|
return 0;
|
||||||
|
if(0x7F<=c && c<=0xA0)
|
||||||
|
return 0;
|
||||||
|
if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
clickmatch(File *f, int cl, int cr, int dir, Posn *p)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
int nest = 1;
|
||||||
|
|
||||||
|
for(;;){
|
||||||
|
if(dir > 0){
|
||||||
|
if(*p >= f->b.nc)
|
||||||
|
break;
|
||||||
|
c = filereadc(f, (*p)++);
|
||||||
|
}else{
|
||||||
|
if(*p == 0)
|
||||||
|
break;
|
||||||
|
c = filereadc(f, --(*p));
|
||||||
|
}
|
||||||
|
if(c == cr){
|
||||||
|
if(--nest==0)
|
||||||
|
return 1;
|
||||||
|
}else if(c == cl)
|
||||||
|
nest++;
|
||||||
|
}
|
||||||
|
return cl=='\n' && nest==1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rune*
|
||||||
|
strrune(Rune *s, Rune c)
|
||||||
|
{
|
||||||
|
Rune c1;
|
||||||
|
|
||||||
|
if(c == 0) {
|
||||||
|
while(*s++)
|
||||||
|
;
|
||||||
|
return s-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(c1 = *s++)
|
||||||
|
if(c1 == c)
|
||||||
|
return s-1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
doubleclick(File *f, Posn p1)
|
||||||
|
{
|
||||||
|
int c, i;
|
||||||
|
Rune *r, *l;
|
||||||
|
Posn p;
|
||||||
|
|
||||||
|
if(p1 > f->b.nc)
|
||||||
|
return;
|
||||||
|
f->dot.r.p1 = f->dot.r.p2 = p1;
|
||||||
|
for(i=0; left[i]; i++){
|
||||||
|
l = left[i];
|
||||||
|
r = right[i];
|
||||||
|
/* try left match */
|
||||||
|
p = p1;
|
||||||
|
if(p1 == 0)
|
||||||
|
c = '\n';
|
||||||
|
else
|
||||||
|
c = filereadc(f, p - 1);
|
||||||
|
if(strrune(l, c)){
|
||||||
|
if(clickmatch(f, c, r[strrune(l, c)-l], 1, &p)){
|
||||||
|
f->dot.r.p1 = p1;
|
||||||
|
f->dot.r.p2 = p-(c!='\n');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* try right match */
|
||||||
|
p = p1;
|
||||||
|
if(p1 == f->b.nc)
|
||||||
|
c = '\n';
|
||||||
|
else
|
||||||
|
c = filereadc(f, p);
|
||||||
|
if(strrune(r, c)){
|
||||||
|
if(clickmatch(f, c, l[strrune(r, c)-r], -1, &p)){
|
||||||
|
f->dot.r.p1 = p;
|
||||||
|
if(c!='\n' || p!=0 || filereadc(f, 0)=='\n')
|
||||||
|
f->dot.r.p1++;
|
||||||
|
f->dot.r.p2 = p1+(p1<f->b.nc && c=='\n');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* try filling out word to right */
|
||||||
|
p = p1;
|
||||||
|
while(p < f->b.nc && alnum(filereadc(f, p++)))
|
||||||
|
f->dot.r.p2++;
|
||||||
|
/* try filling out word to left */
|
||||||
|
p = p1;
|
||||||
|
while(--p >= 0 && alnum(filereadc(f, p)))
|
||||||
|
f->dot.r.p1--;
|
||||||
|
}
|
||||||
|
|
123
sam/multi.c
Normal file
123
sam/multi.c
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
|
||||||
|
List file = { 'p' };
|
||||||
|
ushort tag;
|
||||||
|
|
||||||
|
File *
|
||||||
|
newfile(void)
|
||||||
|
{
|
||||||
|
File *f;
|
||||||
|
|
||||||
|
f = fileopen();
|
||||||
|
inslist(&file, 0, f);
|
||||||
|
f->tag = tag++;
|
||||||
|
if(downloaded)
|
||||||
|
outTs(Hnewname, f->tag);
|
||||||
|
/* already sorted; file name is "" */
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
whichmenu(File *f)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; i<file.nused; i++)
|
||||||
|
if(file.filepptr[i]==f)
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
delfile(File *f)
|
||||||
|
{
|
||||||
|
int w = whichmenu(f);
|
||||||
|
|
||||||
|
if(w < 0) /* e.g. x/./D */
|
||||||
|
return;
|
||||||
|
if(downloaded)
|
||||||
|
outTs(Hdelname, f->tag);
|
||||||
|
dellist(&file, w);
|
||||||
|
fileclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fullname(String *name)
|
||||||
|
{
|
||||||
|
if(name->n > 0 && name->s[0]!='/' && name->s[0]!=0)
|
||||||
|
Strinsert(name, &curwd, (Posn)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fixname(String *name)
|
||||||
|
{
|
||||||
|
String *t;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
fullname(name);
|
||||||
|
s = Strtoc(name);
|
||||||
|
if(strlen(s) > 0)
|
||||||
|
s = cleanname(s);
|
||||||
|
t = tmpcstr(s);
|
||||||
|
Strduplstr(name, t);
|
||||||
|
free(s);
|
||||||
|
freetmpstr(t);
|
||||||
|
|
||||||
|
if(Strispre(&curwd, name))
|
||||||
|
Strdelete(name, 0, curwd.n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sortname(File *f)
|
||||||
|
{
|
||||||
|
int i, cmp, w;
|
||||||
|
int dupwarned;
|
||||||
|
|
||||||
|
w = whichmenu(f);
|
||||||
|
dupwarned = FALSE;
|
||||||
|
dellist(&file, w);
|
||||||
|
if(f == cmd)
|
||||||
|
i = 0;
|
||||||
|
else{
|
||||||
|
for(i=0; i<file.nused; i++){
|
||||||
|
cmp = Strcmp(&f->name, &file.filepptr[i]->name);
|
||||||
|
if(cmp==0 && !dupwarned){
|
||||||
|
dupwarned = TRUE;
|
||||||
|
warn_S(Wdupname, &f->name);
|
||||||
|
}else if(cmp<0 && (i>0 || cmd==0))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inslist(&file, i, f);
|
||||||
|
if(downloaded)
|
||||||
|
outTsS(Hmovname, f->tag, &f->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
state(File *f, int cleandirty)
|
||||||
|
{
|
||||||
|
if(f == cmd)
|
||||||
|
return;
|
||||||
|
f->unread = FALSE;
|
||||||
|
if(downloaded && whichmenu(f)>=0){ /* else flist or menu */
|
||||||
|
if(f->mod && cleandirty!=Dirty)
|
||||||
|
outTs(Hclean, f->tag);
|
||||||
|
else if(!f->mod && cleandirty==Dirty)
|
||||||
|
outTs(Hdirty, f->tag);
|
||||||
|
}
|
||||||
|
if(cleandirty == Clean)
|
||||||
|
f->mod = FALSE;
|
||||||
|
else
|
||||||
|
f->mod = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
File *
|
||||||
|
lookfile(String *s)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; i<file.nused; i++)
|
||||||
|
if(Strcmp(&file.filepptr[i]->name, s) == 0)
|
||||||
|
return file.filepptr[i];
|
||||||
|
return 0;
|
||||||
|
}
|
68
sam/parse.h
Normal file
68
sam/parse.h
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
typedef struct Addr Addr;
|
||||||
|
typedef struct Cmd Cmd;
|
||||||
|
struct Addr
|
||||||
|
{
|
||||||
|
char type; /* # (char addr), l (line addr), / ? . $ + - , ; */
|
||||||
|
union{
|
||||||
|
String *re;
|
||||||
|
Addr *aleft; /* left side of , and ; */
|
||||||
|
} g;
|
||||||
|
Posn num;
|
||||||
|
Addr *next; /* or right side of , and ; */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define are g.re
|
||||||
|
#define left g.aleft
|
||||||
|
|
||||||
|
struct Cmd
|
||||||
|
{
|
||||||
|
Addr *addr; /* address (range of text) */
|
||||||
|
String *re; /* regular expression for e.g. 'x' */
|
||||||
|
union{
|
||||||
|
Cmd *cmd; /* target of x, g, {, etc. */
|
||||||
|
String *text; /* text of a, c, i; rhs of s */
|
||||||
|
Addr *addr; /* address for m, t */
|
||||||
|
} g;
|
||||||
|
Cmd *next; /* pointer to next element in {} */
|
||||||
|
short num;
|
||||||
|
ushort flag; /* whatever */
|
||||||
|
ushort cmdc; /* command character; 'x' etc. */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ccmd g.cmd
|
||||||
|
#define ctext g.text
|
||||||
|
#define caddr g.addr
|
||||||
|
|
||||||
|
extern struct cmdtab{
|
||||||
|
ushort cmdc; /* command character */
|
||||||
|
uchar text; /* takes a textual argument? */
|
||||||
|
uchar regexp; /* takes a regular expression? */
|
||||||
|
uchar addr; /* takes an address (m or t)? */
|
||||||
|
uchar defcmd; /* default command; 0==>none */
|
||||||
|
uchar defaddr; /* default address */
|
||||||
|
uchar count; /* takes a count e.g. s2/// */
|
||||||
|
char *token; /* takes text terminated by one of these */
|
||||||
|
int (*fn)(File*, Cmd*); /* function to call with parse tree */
|
||||||
|
}cmdtab[];
|
||||||
|
|
||||||
|
enum Defaddr{ /* default addresses */
|
||||||
|
aNo,
|
||||||
|
aDot,
|
||||||
|
aAll
|
||||||
|
};
|
||||||
|
|
||||||
|
int nl_cmd(File*, Cmd*), a_cmd(File*, Cmd*), b_cmd(File*, Cmd*);
|
||||||
|
int c_cmd(File*, Cmd*), cd_cmd(File*, Cmd*), d_cmd(File*, Cmd*);
|
||||||
|
int D_cmd(File*, Cmd*), e_cmd(File*, Cmd*);
|
||||||
|
int f_cmd(File*, Cmd*), g_cmd(File*, Cmd*), i_cmd(File*, Cmd*);
|
||||||
|
int k_cmd(File*, Cmd*), m_cmd(File*, Cmd*), n_cmd(File*, Cmd*);
|
||||||
|
int p_cmd(File*, Cmd*), q_cmd(File*, Cmd*);
|
||||||
|
int s_cmd(File*, Cmd*), u_cmd(File*, Cmd*), w_cmd(File*, Cmd*);
|
||||||
|
int x_cmd(File*, Cmd*), X_cmd(File*, Cmd*), plan9_cmd(File*, Cmd*);
|
||||||
|
int eq_cmd(File*, Cmd*);
|
||||||
|
|
||||||
|
|
||||||
|
String *getregexp(int);
|
||||||
|
Addr *newaddr(void);
|
||||||
|
Address address(Addr*, Address, int);
|
||||||
|
int cmdexec(File*, Cmd*);
|
185
sam/plan9.c
Normal file
185
sam/plan9.c
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
|
||||||
|
Rune samname[] = L"~~sam~~";
|
||||||
|
|
||||||
|
Rune *left[]= {
|
||||||
|
L"{[(<«",
|
||||||
|
L"\n",
|
||||||
|
L"'\"`",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
Rune *right[]= {
|
||||||
|
L"}])>»",
|
||||||
|
L"\n",
|
||||||
|
L"'\"`",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
char RSAM[] = "sam";
|
||||||
|
char SAMTERM[] = "/bin/aux/samterm";
|
||||||
|
char HOME[] = "HOME";
|
||||||
|
char TMPDIR[] = "/tmp";
|
||||||
|
char SH[] = "rc";
|
||||||
|
char SHPATH[] = "/bin/rc";
|
||||||
|
char RX[] = "rx";
|
||||||
|
char RXPATH[] = "/bin/rx";
|
||||||
|
char SAMSAVECMD[] = "/bin/rc\n/sys/lib/samsave";
|
||||||
|
|
||||||
|
void
|
||||||
|
dprint(char *z, ...)
|
||||||
|
{
|
||||||
|
char buf[BLOCKSIZE];
|
||||||
|
va_list arg;
|
||||||
|
|
||||||
|
va_start(arg, z);
|
||||||
|
vseprint(buf, &buf[BLOCKSIZE], z, arg);
|
||||||
|
va_end(arg);
|
||||||
|
termwrite(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
print_ss(char *s, String *a, String *b)
|
||||||
|
{
|
||||||
|
dprint("?warning: %s: `%.*S' and `%.*S'\n", s, a->n, a->s, b->n, b->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
print_s(char *s, String *a)
|
||||||
|
{
|
||||||
|
dprint("?warning: %s `%.*S'\n", s, a->n, a->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
getuser(void)
|
||||||
|
{
|
||||||
|
static char user[64];
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if(user[0] == 0){
|
||||||
|
fd = open("/dev/user", 0);
|
||||||
|
if(fd<0 || read(fd, user, sizeof user-1)<=0)
|
||||||
|
strcpy(user, "none");
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
statfile(char *name, ulong *dev, uvlong *id, long *time, long *length, long *appendonly)
|
||||||
|
{
|
||||||
|
Dir *dirb;
|
||||||
|
|
||||||
|
dirb = dirstat(name);
|
||||||
|
if(dirb == nil)
|
||||||
|
return -1;
|
||||||
|
if(dev)
|
||||||
|
*dev = dirb->type|(dirb->dev<<16);
|
||||||
|
if(id)
|
||||||
|
*id = dirb->qid.path;
|
||||||
|
if(time)
|
||||||
|
*time = dirb->mtime;
|
||||||
|
if(length)
|
||||||
|
*length = dirb->length;
|
||||||
|
if(appendonly)
|
||||||
|
*appendonly = dirb->mode & DMAPPEND;
|
||||||
|
free(dirb);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
statfd(int fd, ulong *dev, uvlong *id, long *time, long *length, long *appendonly)
|
||||||
|
{
|
||||||
|
Dir *dirb;
|
||||||
|
|
||||||
|
dirb = dirfstat(fd);
|
||||||
|
if(dirb == nil)
|
||||||
|
return -1;
|
||||||
|
if(dev)
|
||||||
|
*dev = dirb->type|(dirb->dev<<16);
|
||||||
|
if(id)
|
||||||
|
*id = dirb->qid.path;
|
||||||
|
if(time)
|
||||||
|
*time = dirb->mtime;
|
||||||
|
if(length)
|
||||||
|
*length = dirb->length;
|
||||||
|
if(appendonly)
|
||||||
|
*appendonly = dirb->mode & DMAPPEND;
|
||||||
|
free(dirb);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
notifyf(void *a, char *s)
|
||||||
|
{
|
||||||
|
USED(a);
|
||||||
|
if(bpipeok && strcmp(s, "sys: write on closed pipe") == 0)
|
||||||
|
noted(NCONT);
|
||||||
|
if(strcmp(s, "interrupt") == 0)
|
||||||
|
noted(NCONT);
|
||||||
|
panicking = 1;
|
||||||
|
rescue();
|
||||||
|
noted(NDFLT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
newtmp(int num)
|
||||||
|
{
|
||||||
|
int i, fd;
|
||||||
|
static char tempnam[30];
|
||||||
|
|
||||||
|
i = getpid();
|
||||||
|
do
|
||||||
|
snprint(tempnam, sizeof tempnam, "%s/%d%.4s%dsam", TMPDIR, num, getuser(), i++);
|
||||||
|
while(access(tempnam, 0) == 0);
|
||||||
|
fd = create(tempnam, ORDWR|OCEXEC|ORCLOSE, 0000);
|
||||||
|
if(fd < 0){
|
||||||
|
remove(tempnam);
|
||||||
|
fd = create(tempnam, ORDWR|OCEXEC|ORCLOSE, 0000);
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
waitfor(int pid)
|
||||||
|
{
|
||||||
|
int msg;
|
||||||
|
Waitmsg *w;
|
||||||
|
|
||||||
|
while((w = wait()) != nil){
|
||||||
|
if(w->pid != pid){
|
||||||
|
free(w);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
msg = (w->msg[0] != '\0');
|
||||||
|
free(w);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
samerr(char *buf)
|
||||||
|
{
|
||||||
|
sprint(buf, "%s/sam.err", TMPDIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
emalloc(ulong n)
|
||||||
|
{
|
||||||
|
void *p;
|
||||||
|
|
||||||
|
p = malloc(n);
|
||||||
|
if(p == 0)
|
||||||
|
panic("malloc fails");
|
||||||
|
memset(p, 0, n);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
erealloc(void *p, ulong n)
|
||||||
|
{
|
||||||
|
p = realloc(p, n);
|
||||||
|
if(p == 0)
|
||||||
|
panic("realloc fails");
|
||||||
|
return p;
|
||||||
|
}
|
17
sam/plumb.h
Normal file
17
sam/plumb.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
typedef struct Plumbmsg Plumbmsg;
|
||||||
|
|
||||||
|
struct Plumbmsg {
|
||||||
|
char *src;
|
||||||
|
char *dst;
|
||||||
|
char *wdir;
|
||||||
|
char *type;
|
||||||
|
char *attr;
|
||||||
|
char *data;
|
||||||
|
int ndata;
|
||||||
|
};
|
||||||
|
|
||||||
|
char *plumbunpackattr(char*);
|
||||||
|
char *plumbpack(Plumbmsg *, int *);
|
||||||
|
int plumbfree(Plumbmsg *);
|
||||||
|
char *cleanname(char*);
|
||||||
|
|
340
sam/rasp.c
Normal file
340
sam/rasp.c
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
/*
|
||||||
|
* GROWDATASIZE must be big enough that all errors go out as Hgrowdata's,
|
||||||
|
* so they will be scrolled into visibility in the ~~sam~~ window (yuck!).
|
||||||
|
*/
|
||||||
|
#define GROWDATASIZE 50 /* if size is <= this, send data with grow */
|
||||||
|
|
||||||
|
void rcut(List*, Posn, Posn);
|
||||||
|
int rterm(List*, Posn);
|
||||||
|
void rgrow(List*, Posn, Posn);
|
||||||
|
|
||||||
|
static Posn growpos;
|
||||||
|
static Posn grown;
|
||||||
|
static Posn shrinkpos;
|
||||||
|
static Posn shrunk;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rasp routines inform the terminal of changes to the file.
|
||||||
|
*
|
||||||
|
* a rasp is a list of spans within the file, and an indication
|
||||||
|
* of whether the terminal knows about the span.
|
||||||
|
*
|
||||||
|
* optimize by coalescing multiple updates to the same span
|
||||||
|
* if it is not known by the terminal.
|
||||||
|
*
|
||||||
|
* other possible optimizations: flush terminal's rasp by cut everything,
|
||||||
|
* insert everything if rasp gets too large.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* only called for initial load of file
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
raspload(File *f)
|
||||||
|
{
|
||||||
|
if(f->rasp == nil)
|
||||||
|
return;
|
||||||
|
grown = f->b.nc;
|
||||||
|
growpos = 0;
|
||||||
|
if(f->b.nc)
|
||||||
|
rgrow(f->rasp, 0, f->b.nc);
|
||||||
|
raspdone(f, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
raspstart(File *f)
|
||||||
|
{
|
||||||
|
if(f->rasp == nil)
|
||||||
|
return;
|
||||||
|
grown = 0;
|
||||||
|
shrunk = 0;
|
||||||
|
outbuffered = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
raspdone(File *f, int toterm)
|
||||||
|
{
|
||||||
|
if(f->dot.r.p1 > f->b.nc)
|
||||||
|
f->dot.r.p1 = f->b.nc;
|
||||||
|
if(f->dot.r.p2 > f->b.nc)
|
||||||
|
f->dot.r.p2 = f->b.nc;
|
||||||
|
if(f->mark.p1 > f->b.nc)
|
||||||
|
f->mark.p1 = f->b.nc;
|
||||||
|
if(f->mark.p2 > f->b.nc)
|
||||||
|
f->mark.p2 = f->b.nc;
|
||||||
|
if(f->rasp == nil)
|
||||||
|
return;
|
||||||
|
if(grown)
|
||||||
|
outTsll(Hgrow, f->tag, growpos, grown);
|
||||||
|
else if(shrunk)
|
||||||
|
outTsll(Hcut, f->tag, shrinkpos, shrunk);
|
||||||
|
if(toterm)
|
||||||
|
outTs(Hcheck0, f->tag);
|
||||||
|
outflush();
|
||||||
|
outbuffered = 0;
|
||||||
|
if(f == cmd){
|
||||||
|
cmdpt += cmdptadv;
|
||||||
|
cmdptadv = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
raspflush(File *f)
|
||||||
|
{
|
||||||
|
if(grown){
|
||||||
|
outTsll(Hgrow, f->tag, growpos, grown);
|
||||||
|
grown = 0;
|
||||||
|
}
|
||||||
|
else if(shrunk){
|
||||||
|
outTsll(Hcut, f->tag, shrinkpos, shrunk);
|
||||||
|
shrunk = 0;
|
||||||
|
}
|
||||||
|
outflush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
raspdelete(File *f, uint p1, uint p2, int toterm)
|
||||||
|
{
|
||||||
|
long n;
|
||||||
|
|
||||||
|
n = p2 - p1;
|
||||||
|
if(n == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(p2 <= f->dot.r.p1){
|
||||||
|
f->dot.r.p1 -= n;
|
||||||
|
f->dot.r.p2 -= n;
|
||||||
|
}
|
||||||
|
if(p2 <= f->mark.p1){
|
||||||
|
f->mark.p1 -= n;
|
||||||
|
f->mark.p2 -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(f->rasp == nil)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(f==cmd && p1<cmdpt){
|
||||||
|
if(p2 <= cmdpt)
|
||||||
|
cmdpt -= n;
|
||||||
|
else
|
||||||
|
cmdpt = p1;
|
||||||
|
}
|
||||||
|
if(toterm){
|
||||||
|
if(grown){
|
||||||
|
outTsll(Hgrow, f->tag, growpos, grown);
|
||||||
|
grown = 0;
|
||||||
|
}else if(shrunk && shrinkpos!=p1 && shrinkpos!=p2){
|
||||||
|
outTsll(Hcut, f->tag, shrinkpos, shrunk);
|
||||||
|
shrunk = 0;
|
||||||
|
}
|
||||||
|
if(!shrunk || shrinkpos==p2)
|
||||||
|
shrinkpos = p1;
|
||||||
|
shrunk += n;
|
||||||
|
}
|
||||||
|
rcut(f->rasp, p1, p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
raspinsert(File *f, uint p1, Rune *buf, uint n, int toterm)
|
||||||
|
{
|
||||||
|
Range r;
|
||||||
|
|
||||||
|
if(n == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(p1 < f->dot.r.p1){
|
||||||
|
f->dot.r.p1 += n;
|
||||||
|
f->dot.r.p2 += n;
|
||||||
|
}
|
||||||
|
if(p1 < f->mark.p1){
|
||||||
|
f->mark.p1 += n;
|
||||||
|
f->mark.p2 += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(f->rasp == nil)
|
||||||
|
return;
|
||||||
|
if(f==cmd && p1<cmdpt)
|
||||||
|
cmdpt += n;
|
||||||
|
if(toterm){
|
||||||
|
if(shrunk){
|
||||||
|
outTsll(Hcut, f->tag, shrinkpos, shrunk);
|
||||||
|
shrunk = 0;
|
||||||
|
}
|
||||||
|
if(n>GROWDATASIZE || !rterm(f->rasp, p1)){
|
||||||
|
rgrow(f->rasp, p1, n);
|
||||||
|
if(grown && growpos+grown!=p1 && growpos!=p1){
|
||||||
|
outTsll(Hgrow, f->tag, growpos, grown);
|
||||||
|
grown = 0;
|
||||||
|
}
|
||||||
|
if(!grown)
|
||||||
|
growpos = p1;
|
||||||
|
grown += n;
|
||||||
|
}else{
|
||||||
|
if(grown){
|
||||||
|
outTsll(Hgrow, f->tag, growpos, grown);
|
||||||
|
grown = 0;
|
||||||
|
}
|
||||||
|
rgrow(f->rasp, p1, n);
|
||||||
|
r = rdata(f->rasp, p1, n);
|
||||||
|
if(r.p1!=p1 || r.p2!=p1+n)
|
||||||
|
panic("rdata in toterminal");
|
||||||
|
outTsllS(Hgrowdata, f->tag, p1, n, tmprstr(buf, n));
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
rgrow(f->rasp, p1, n);
|
||||||
|
r = rdata(f->rasp, p1, n);
|
||||||
|
if(r.p1!=p1 || r.p2!=p1+n)
|
||||||
|
panic("rdata in toterminal");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define M 0x80000000L
|
||||||
|
#define P(i) r->posnptr[i]
|
||||||
|
#define T(i) (P(i)&M) /* in terminal */
|
||||||
|
#define L(i) (P(i)&~M) /* length of this piece */
|
||||||
|
|
||||||
|
void
|
||||||
|
rcut(List *r, Posn p1, Posn p2)
|
||||||
|
{
|
||||||
|
Posn p, x;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(p1 == p2)
|
||||||
|
panic("rcut 0");
|
||||||
|
for(p=0,i=0; i<r->nused && p+L(i)<=p1; p+=L(i++))
|
||||||
|
;
|
||||||
|
if(i == r->nused)
|
||||||
|
panic("rcut 1");
|
||||||
|
if(p < p1){ /* chop this piece */
|
||||||
|
if(p+L(i) < p2){
|
||||||
|
x = p1-p;
|
||||||
|
p += L(i);
|
||||||
|
}else{
|
||||||
|
x = L(i)-(p2-p1);
|
||||||
|
p = p2;
|
||||||
|
}
|
||||||
|
if(T(i))
|
||||||
|
P(i) = x|M;
|
||||||
|
else
|
||||||
|
P(i) = x;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
while(i<r->nused && p+L(i)<=p2){
|
||||||
|
p += L(i);
|
||||||
|
dellist(r, i);
|
||||||
|
}
|
||||||
|
if(p < p2){
|
||||||
|
if(i == r->nused)
|
||||||
|
panic("rcut 2");
|
||||||
|
x = L(i)-(p2-p);
|
||||||
|
if(T(i))
|
||||||
|
P(i) = x|M;
|
||||||
|
else
|
||||||
|
P(i) = x;
|
||||||
|
}
|
||||||
|
/* can we merge i and i-1 ? */
|
||||||
|
if(i>0 && i<r->nused && T(i-1)==T(i)){
|
||||||
|
x = L(i-1)+L(i);
|
||||||
|
dellist(r, i--);
|
||||||
|
if(T(i))
|
||||||
|
P(i)=x|M;
|
||||||
|
else
|
||||||
|
P(i)=x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rgrow(List *r, Posn p1, Posn n)
|
||||||
|
{
|
||||||
|
Posn p;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(n == 0)
|
||||||
|
panic("rgrow 0");
|
||||||
|
for(p=0,i=0; i<r->nused && p+L(i)<=p1; p+=L(i++))
|
||||||
|
;
|
||||||
|
if(i == r->nused){ /* stick on end of file */
|
||||||
|
if(p!=p1)
|
||||||
|
panic("rgrow 1");
|
||||||
|
if(i>0 && !T(i-1))
|
||||||
|
P(i-1)+=n;
|
||||||
|
else
|
||||||
|
inslist(r, i, n);
|
||||||
|
}else if(!T(i)) /* goes in this empty piece */
|
||||||
|
P(i)+=n;
|
||||||
|
else if(p==p1 && i>0 && !T(i-1)) /* special case; simplifies life */
|
||||||
|
P(i-1)+=n;
|
||||||
|
else if(p==p1)
|
||||||
|
inslist(r, i, n);
|
||||||
|
else{ /* must break piece in terminal */
|
||||||
|
inslist(r, i+1, (L(i)-(p1-p))|M);
|
||||||
|
inslist(r, i+1, n);
|
||||||
|
P(i) = (p1-p)|M;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
rterm(List *r, Posn p1)
|
||||||
|
{
|
||||||
|
Posn p;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(p = 0,i = 0; i<r->nused && p+L(i)<=p1; p+=L(i++))
|
||||||
|
;
|
||||||
|
if(i==r->nused && (i==0 || !T(i-1)))
|
||||||
|
return 0;
|
||||||
|
return T(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
Range
|
||||||
|
rdata(List *r, Posn p1, Posn n)
|
||||||
|
{
|
||||||
|
Posn p;
|
||||||
|
int i;
|
||||||
|
Range rg;
|
||||||
|
|
||||||
|
if(n==0)
|
||||||
|
panic("rdata 0");
|
||||||
|
for(p = 0,i = 0; i<r->nused && p+L(i)<=p1; p+=L(i++))
|
||||||
|
;
|
||||||
|
if(i==r->nused)
|
||||||
|
panic("rdata 1");
|
||||||
|
if(T(i)){
|
||||||
|
n-=L(i)-(p1-p);
|
||||||
|
if(n<=0){
|
||||||
|
rg.p1 = rg.p2 = p1;
|
||||||
|
return rg;
|
||||||
|
}
|
||||||
|
p+=L(i++);
|
||||||
|
p1 = p;
|
||||||
|
}
|
||||||
|
if(T(i) || i==r->nused)
|
||||||
|
panic("rdata 2");
|
||||||
|
if(p+L(i)<p1+n)
|
||||||
|
n = L(i)-(p1-p);
|
||||||
|
rg.p1 = p1;
|
||||||
|
rg.p2 = p1+n;
|
||||||
|
if(p!=p1){
|
||||||
|
inslist(r, i+1, L(i)-(p1-p));
|
||||||
|
P(i)=p1-p;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if(L(i)!=n){
|
||||||
|
inslist(r, i+1, L(i)-n);
|
||||||
|
P(i)=n;
|
||||||
|
}
|
||||||
|
P(i)|=M;
|
||||||
|
/* now i is set; can we merge? */
|
||||||
|
if(i<r->nused-1 && T(i+1)){
|
||||||
|
P(i)=(n+=L(i+1))|M;
|
||||||
|
dellist(r, i+1);
|
||||||
|
}
|
||||||
|
if(i>0 && T(i-1)){
|
||||||
|
P(i)=(n+L(i-1))|M;
|
||||||
|
dellist(r, i-1);
|
||||||
|
}
|
||||||
|
return rg;
|
||||||
|
}
|
||||||
|
|
802
sam/regexp.c
Normal file
802
sam/regexp.c
Normal file
@@ -0,0 +1,802 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
|
||||||
|
Rangeset sel;
|
||||||
|
String lastregexp;
|
||||||
|
/*
|
||||||
|
* Machine Information
|
||||||
|
*/
|
||||||
|
typedef struct Inst Inst;
|
||||||
|
|
||||||
|
struct Inst
|
||||||
|
{
|
||||||
|
long type; /* < 0x10000 ==> literal, otherwise action */
|
||||||
|
union {
|
||||||
|
int rsid;
|
||||||
|
int rsubid;
|
||||||
|
int class;
|
||||||
|
struct Inst *rother;
|
||||||
|
struct Inst *rright;
|
||||||
|
} r;
|
||||||
|
union{
|
||||||
|
struct Inst *lleft;
|
||||||
|
struct Inst *lnext;
|
||||||
|
} l;
|
||||||
|
};
|
||||||
|
#define sid r.rsid
|
||||||
|
#define subid r.rsubid
|
||||||
|
#define rclass r.class
|
||||||
|
#define other r.rother
|
||||||
|
#define right r.rright
|
||||||
|
#define left l.lleft
|
||||||
|
#define next l.lnext
|
||||||
|
|
||||||
|
#define NPROG 1024
|
||||||
|
Inst program[NPROG];
|
||||||
|
Inst *progp;
|
||||||
|
Inst *startinst; /* First inst. of program; might not be program[0] */
|
||||||
|
Inst *bstartinst; /* same for backwards machine */
|
||||||
|
|
||||||
|
typedef struct Ilist Ilist;
|
||||||
|
struct Ilist
|
||||||
|
{
|
||||||
|
Inst *inst; /* Instruction of the thread */
|
||||||
|
Rangeset se;
|
||||||
|
Posn startp; /* first char of match */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NLIST 127
|
||||||
|
|
||||||
|
Ilist *tl, *nl; /* This list, next list */
|
||||||
|
Ilist list[2][NLIST+1]; /* +1 for trailing null */
|
||||||
|
static Rangeset sempty;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Actions and Tokens
|
||||||
|
*
|
||||||
|
* 0x100xx are operators, value == precedence
|
||||||
|
* 0x200xx are tokens, i.e. operands for operators
|
||||||
|
*/
|
||||||
|
#define OPERATOR 0x10000 /* Bitmask of all operators */
|
||||||
|
#define START 0x10000 /* Start, used for marker on stack */
|
||||||
|
#define RBRA 0x10001 /* Right bracket, ) */
|
||||||
|
#define LBRA 0x10002 /* Left bracket, ( */
|
||||||
|
#define OR 0x10003 /* Alternation, | */
|
||||||
|
#define CAT 0x10004 /* Concatentation, implicit operator */
|
||||||
|
#define STAR 0x10005 /* Closure, * */
|
||||||
|
#define PLUS 0x10006 /* a+ == aa* */
|
||||||
|
#define QUEST 0x10007 /* a? == a|nothing, i.e. 0 or 1 a's */
|
||||||
|
#define ANY 0x20000 /* Any character but newline, . */
|
||||||
|
#define NOP 0x20001 /* No operation, internal use only */
|
||||||
|
#define BOL 0x20002 /* Beginning of line, ^ */
|
||||||
|
#define EOL 0x20003 /* End of line, $ */
|
||||||
|
#define CCLASS 0x20004 /* Character class, [] */
|
||||||
|
#define NCCLASS 0x20005 /* Negated character class, [^] */
|
||||||
|
#define END 0x20077 /* Terminate: match found */
|
||||||
|
|
||||||
|
#define ISATOR 0x10000
|
||||||
|
#define ISAND 0x20000
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parser Information
|
||||||
|
*/
|
||||||
|
typedef struct Node Node;
|
||||||
|
struct Node
|
||||||
|
{
|
||||||
|
Inst *first;
|
||||||
|
Inst *last;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NSTACK 20
|
||||||
|
Node andstack[NSTACK];
|
||||||
|
Node *andp;
|
||||||
|
int atorstack[NSTACK];
|
||||||
|
int *atorp;
|
||||||
|
int lastwasand; /* Last token was operand */
|
||||||
|
int cursubid;
|
||||||
|
int subidstack[NSTACK];
|
||||||
|
int *subidp;
|
||||||
|
int backwards;
|
||||||
|
int nbra;
|
||||||
|
Rune *exprp; /* pointer to next character in source expression */
|
||||||
|
#define DCLASS 10 /* allocation increment */
|
||||||
|
int nclass; /* number active */
|
||||||
|
int Nclass; /* high water mark */
|
||||||
|
Rune **class;
|
||||||
|
int negateclass;
|
||||||
|
|
||||||
|
int addinst(Ilist *l, Inst *inst, Rangeset *sep);
|
||||||
|
void newmatch(Rangeset*);
|
||||||
|
void bnewmatch(Rangeset*);
|
||||||
|
void pushand(Inst*, Inst*);
|
||||||
|
void pushator(int);
|
||||||
|
Node *popand(int);
|
||||||
|
int popator(void);
|
||||||
|
void startlex(Rune*);
|
||||||
|
int lex(void);
|
||||||
|
void operator(int);
|
||||||
|
void operand(int);
|
||||||
|
void evaluntil(int);
|
||||||
|
void optimize(Inst*);
|
||||||
|
void bldcclass(void);
|
||||||
|
|
||||||
|
void
|
||||||
|
regerror(Err e)
|
||||||
|
{
|
||||||
|
Strzero(&lastregexp);
|
||||||
|
error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
regerror_c(Err e, int c)
|
||||||
|
{
|
||||||
|
Strzero(&lastregexp);
|
||||||
|
error_c(e, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
Inst *
|
||||||
|
newinst(int t)
|
||||||
|
{
|
||||||
|
if(progp >= &program[NPROG])
|
||||||
|
regerror(Etoolong);
|
||||||
|
progp->type = t;
|
||||||
|
progp->left = 0;
|
||||||
|
progp->right = 0;
|
||||||
|
return progp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Inst *
|
||||||
|
realcompile(Rune *s)
|
||||||
|
{
|
||||||
|
int token;
|
||||||
|
|
||||||
|
startlex(s);
|
||||||
|
atorp = atorstack;
|
||||||
|
andp = andstack;
|
||||||
|
subidp = subidstack;
|
||||||
|
cursubid = 0;
|
||||||
|
lastwasand = FALSE;
|
||||||
|
/* Start with a low priority operator to prime parser */
|
||||||
|
pushator(START-1);
|
||||||
|
while((token=lex()) != END){
|
||||||
|
if((token&ISATOR) == OPERATOR)
|
||||||
|
operator(token);
|
||||||
|
else
|
||||||
|
operand(token);
|
||||||
|
}
|
||||||
|
/* Close with a low priority operator */
|
||||||
|
evaluntil(START);
|
||||||
|
/* Force END */
|
||||||
|
operand(END);
|
||||||
|
evaluntil(START);
|
||||||
|
if(nbra)
|
||||||
|
regerror(Eleftpar);
|
||||||
|
--andp; /* points to first and only operand */
|
||||||
|
return andp->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
compile(String *s)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
Inst *oprogp;
|
||||||
|
|
||||||
|
if(Strcmp(s, &lastregexp)==0)
|
||||||
|
return;
|
||||||
|
for(i=0; i<nclass; i++)
|
||||||
|
free(class[i]);
|
||||||
|
nclass = 0;
|
||||||
|
progp = program;
|
||||||
|
backwards = FALSE;
|
||||||
|
startinst = realcompile(s->s);
|
||||||
|
optimize(program);
|
||||||
|
oprogp = progp;
|
||||||
|
backwards = TRUE;
|
||||||
|
bstartinst = realcompile(s->s);
|
||||||
|
optimize(oprogp);
|
||||||
|
Strduplstr(&lastregexp, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operand(int t)
|
||||||
|
{
|
||||||
|
Inst *i;
|
||||||
|
if(lastwasand)
|
||||||
|
operator(CAT); /* catenate is implicit */
|
||||||
|
i = newinst(t);
|
||||||
|
if(t == CCLASS){
|
||||||
|
if(negateclass)
|
||||||
|
i->type = NCCLASS; /* UGH */
|
||||||
|
i->rclass = nclass-1; /* UGH */
|
||||||
|
}
|
||||||
|
pushand(i, i);
|
||||||
|
lastwasand = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator(int t)
|
||||||
|
{
|
||||||
|
if(t==RBRA && --nbra<0)
|
||||||
|
regerror(Erightpar);
|
||||||
|
if(t==LBRA){
|
||||||
|
/*
|
||||||
|
* if(++cursubid >= NSUBEXP)
|
||||||
|
* regerror(Esubexp);
|
||||||
|
*/
|
||||||
|
cursubid++; /* silently ignored */
|
||||||
|
nbra++;
|
||||||
|
if(lastwasand)
|
||||||
|
operator(CAT);
|
||||||
|
}else
|
||||||
|
evaluntil(t);
|
||||||
|
if(t!=RBRA)
|
||||||
|
pushator(t);
|
||||||
|
lastwasand = FALSE;
|
||||||
|
if(t==STAR || t==QUEST || t==PLUS || t==RBRA)
|
||||||
|
lastwasand = TRUE; /* these look like operands */
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cant(char *s)
|
||||||
|
{
|
||||||
|
char buf[100];
|
||||||
|
|
||||||
|
sprint(buf, "regexp: can't happen: %s", s);
|
||||||
|
panic(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pushand(Inst *f, Inst *l)
|
||||||
|
{
|
||||||
|
if(andp >= &andstack[NSTACK])
|
||||||
|
cant("operand stack overflow");
|
||||||
|
andp->first = f;
|
||||||
|
andp->last = l;
|
||||||
|
andp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pushator(int t)
|
||||||
|
{
|
||||||
|
if(atorp >= &atorstack[NSTACK])
|
||||||
|
cant("operator stack overflow");
|
||||||
|
*atorp++=t;
|
||||||
|
if(cursubid >= NSUBEXP)
|
||||||
|
*subidp++= -1;
|
||||||
|
else
|
||||||
|
*subidp++=cursubid;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node *
|
||||||
|
popand(int op)
|
||||||
|
{
|
||||||
|
if(andp <= &andstack[0])
|
||||||
|
if(op)
|
||||||
|
regerror_c(Emissop, op);
|
||||||
|
else
|
||||||
|
regerror(Ebadregexp);
|
||||||
|
return --andp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
popator(void)
|
||||||
|
{
|
||||||
|
if(atorp <= &atorstack[0])
|
||||||
|
cant("operator stack underflow");
|
||||||
|
--subidp;
|
||||||
|
return *--atorp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
evaluntil(int pri)
|
||||||
|
{
|
||||||
|
Node *op1, *op2, *t;
|
||||||
|
Inst *inst1, *inst2;
|
||||||
|
|
||||||
|
while(pri==RBRA || atorp[-1]>=pri){
|
||||||
|
switch(popator()){
|
||||||
|
case LBRA:
|
||||||
|
op1 = popand('(');
|
||||||
|
inst2 = newinst(RBRA);
|
||||||
|
inst2->subid = *subidp;
|
||||||
|
op1->last->next = inst2;
|
||||||
|
inst1 = newinst(LBRA);
|
||||||
|
inst1->subid = *subidp;
|
||||||
|
inst1->next = op1->first;
|
||||||
|
pushand(inst1, inst2);
|
||||||
|
return; /* must have been RBRA */
|
||||||
|
default:
|
||||||
|
panic("unknown regexp operator");
|
||||||
|
break;
|
||||||
|
case OR:
|
||||||
|
op2 = popand('|');
|
||||||
|
op1 = popand('|');
|
||||||
|
inst2 = newinst(NOP);
|
||||||
|
op2->last->next = inst2;
|
||||||
|
op1->last->next = inst2;
|
||||||
|
inst1 = newinst(OR);
|
||||||
|
inst1->right = op1->first;
|
||||||
|
inst1->left = op2->first;
|
||||||
|
pushand(inst1, inst2);
|
||||||
|
break;
|
||||||
|
case CAT:
|
||||||
|
op2 = popand(0);
|
||||||
|
op1 = popand(0);
|
||||||
|
if(backwards && op2->first->type!=END)
|
||||||
|
t = op1, op1 = op2, op2 = t;
|
||||||
|
op1->last->next = op2->first;
|
||||||
|
pushand(op1->first, op2->last);
|
||||||
|
break;
|
||||||
|
case STAR:
|
||||||
|
op2 = popand('*');
|
||||||
|
inst1 = newinst(OR);
|
||||||
|
op2->last->next = inst1;
|
||||||
|
inst1->right = op2->first;
|
||||||
|
pushand(inst1, inst1);
|
||||||
|
break;
|
||||||
|
case PLUS:
|
||||||
|
op2 = popand('+');
|
||||||
|
inst1 = newinst(OR);
|
||||||
|
op2->last->next = inst1;
|
||||||
|
inst1->right = op2->first;
|
||||||
|
pushand(op2->first, inst1);
|
||||||
|
break;
|
||||||
|
case QUEST:
|
||||||
|
op2 = popand('?');
|
||||||
|
inst1 = newinst(OR);
|
||||||
|
inst2 = newinst(NOP);
|
||||||
|
inst1->left = inst2;
|
||||||
|
inst1->right = op2->first;
|
||||||
|
op2->last->next = inst2;
|
||||||
|
pushand(inst1, inst2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
optimize(Inst *start)
|
||||||
|
{
|
||||||
|
Inst *inst, *target;
|
||||||
|
|
||||||
|
for(inst=start; inst->type!=END; inst++){
|
||||||
|
target = inst->next;
|
||||||
|
while(target->type == NOP)
|
||||||
|
target = target->next;
|
||||||
|
inst->next = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
void
|
||||||
|
dumpstack(void){
|
||||||
|
Node *stk;
|
||||||
|
int *ip;
|
||||||
|
|
||||||
|
dprint("operators\n");
|
||||||
|
for(ip = atorstack; ip<atorp; ip++)
|
||||||
|
dprint("0%o\n", *ip);
|
||||||
|
dprint("operands\n");
|
||||||
|
for(stk = andstack; stk<andp; stk++)
|
||||||
|
dprint("0%o\t0%o\n", stk->first->type, stk->last->type);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
dump(void){
|
||||||
|
Inst *l;
|
||||||
|
|
||||||
|
l = program;
|
||||||
|
do{
|
||||||
|
dprint("%d:\t0%o\t%d\t%d\n", l-program, l->type,
|
||||||
|
l->left-program, l->right-program);
|
||||||
|
}while(l++->type);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
startlex(Rune *s)
|
||||||
|
{
|
||||||
|
exprp = s;
|
||||||
|
nbra = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
lex(void){
|
||||||
|
int c= *exprp++;
|
||||||
|
|
||||||
|
switch(c){
|
||||||
|
case '\\':
|
||||||
|
if(*exprp)
|
||||||
|
if((c= *exprp++)=='n')
|
||||||
|
c='\n';
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
c = END;
|
||||||
|
--exprp; /* In case we come here again */
|
||||||
|
break;
|
||||||
|
case '*':
|
||||||
|
c = STAR;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
c = QUEST;
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
c = PLUS;
|
||||||
|
break;
|
||||||
|
case '|':
|
||||||
|
c = OR;
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
c = ANY;
|
||||||
|
break;
|
||||||
|
case '(':
|
||||||
|
c = LBRA;
|
||||||
|
break;
|
||||||
|
case ')':
|
||||||
|
c = RBRA;
|
||||||
|
break;
|
||||||
|
case '^':
|
||||||
|
c = BOL;
|
||||||
|
break;
|
||||||
|
case '$':
|
||||||
|
c = EOL;
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
c = CCLASS;
|
||||||
|
bldcclass();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
long
|
||||||
|
nextrec(void){
|
||||||
|
if(exprp[0]==0 || (exprp[0]=='\\' && exprp[1]==0))
|
||||||
|
regerror(Ebadclass);
|
||||||
|
if(exprp[0] == '\\'){
|
||||||
|
exprp++;
|
||||||
|
if(*exprp=='n'){
|
||||||
|
exprp++;
|
||||||
|
return '\n';
|
||||||
|
}
|
||||||
|
return *exprp++|0x10000;
|
||||||
|
}
|
||||||
|
return *exprp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bldcclass(void)
|
||||||
|
{
|
||||||
|
long c1, c2, n, na;
|
||||||
|
Rune *classp;
|
||||||
|
|
||||||
|
classp = emalloc(DCLASS*RUNESIZE);
|
||||||
|
n = 0;
|
||||||
|
na = DCLASS;
|
||||||
|
/* we have already seen the '[' */
|
||||||
|
if(*exprp == '^'){
|
||||||
|
classp[n++] = '\n'; /* don't match newline in negate case */
|
||||||
|
negateclass = TRUE;
|
||||||
|
exprp++;
|
||||||
|
}else
|
||||||
|
negateclass = FALSE;
|
||||||
|
while((c1 = nextrec()) != ']'){
|
||||||
|
if(c1 == '-'){
|
||||||
|
Error:
|
||||||
|
free(classp);
|
||||||
|
regerror(Ebadclass);
|
||||||
|
}
|
||||||
|
if(n+4 >= na){ /* 3 runes plus NUL */
|
||||||
|
na += DCLASS;
|
||||||
|
classp = erealloc(classp, na*RUNESIZE);
|
||||||
|
}
|
||||||
|
if(*exprp == '-'){
|
||||||
|
exprp++; /* eat '-' */
|
||||||
|
if((c2 = nextrec()) == ']')
|
||||||
|
goto Error;
|
||||||
|
classp[n+0] = Runemax;
|
||||||
|
classp[n+1] = c1;
|
||||||
|
classp[n+2] = c2;
|
||||||
|
n += 3;
|
||||||
|
}else
|
||||||
|
classp[n++] = c1;
|
||||||
|
}
|
||||||
|
classp[n] = 0;
|
||||||
|
if(nclass == Nclass){
|
||||||
|
Nclass += DCLASS;
|
||||||
|
class = erealloc(class, Nclass*sizeof(Rune*));
|
||||||
|
}
|
||||||
|
class[nclass++] = classp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
classmatch(int classno, int c, int negate)
|
||||||
|
{
|
||||||
|
Rune *p;
|
||||||
|
|
||||||
|
p = class[classno];
|
||||||
|
while(*p){
|
||||||
|
if(*p == Runemax){
|
||||||
|
if(p[1]<=c && c<=p[2])
|
||||||
|
return !negate;
|
||||||
|
p += 3;
|
||||||
|
}else if(*p++ == c)
|
||||||
|
return !negate;
|
||||||
|
}
|
||||||
|
return negate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note optimization in addinst:
|
||||||
|
* *l must be pending when addinst called; if *l has been looked
|
||||||
|
* at already, the optimization is a bug.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
addinst(Ilist *l, Inst *inst, Rangeset *sep)
|
||||||
|
{
|
||||||
|
Ilist *p;
|
||||||
|
|
||||||
|
for(p = l; p->inst; p++){
|
||||||
|
if(p->inst==inst){
|
||||||
|
if((sep)->p[0].p1 < p->se.p[0].p1)
|
||||||
|
p->se= *sep; /* this would be bug */
|
||||||
|
return 0; /* It's already there */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p->inst = inst;
|
||||||
|
p->se= *sep;
|
||||||
|
(p+1)->inst = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
execute(File *f, Posn startp, Posn eof)
|
||||||
|
{
|
||||||
|
int flag = 0;
|
||||||
|
Inst *inst;
|
||||||
|
Ilist *tlp;
|
||||||
|
Posn p = startp;
|
||||||
|
int nnl = 0, ntl;
|
||||||
|
int c;
|
||||||
|
int wrapped = 0;
|
||||||
|
int startchar = startinst->type<OPERATOR? startinst->type : 0;
|
||||||
|
|
||||||
|
list[0][0].inst = list[1][0].inst = 0;
|
||||||
|
sel.p[0].p1 = -1;
|
||||||
|
/* Execute machine once for each character */
|
||||||
|
for(;;p++){
|
||||||
|
doloop:
|
||||||
|
c = filereadc(f, p);
|
||||||
|
if(p>=eof || c<0){
|
||||||
|
switch(wrapped++){
|
||||||
|
case 0: /* let loop run one more click */
|
||||||
|
case 2:
|
||||||
|
break;
|
||||||
|
case 1: /* expired; wrap to beginning */
|
||||||
|
if(sel.p[0].p1>=0 || eof!=INFINITY)
|
||||||
|
goto Return;
|
||||||
|
list[0][0].inst = list[1][0].inst = 0;
|
||||||
|
p = 0;
|
||||||
|
goto doloop;
|
||||||
|
default:
|
||||||
|
goto Return;
|
||||||
|
}
|
||||||
|
}else if(((wrapped && p>=startp) || sel.p[0].p1>0) && nnl==0)
|
||||||
|
break;
|
||||||
|
/* fast check for first char */
|
||||||
|
if(startchar && nnl==0 && c!=startchar)
|
||||||
|
continue;
|
||||||
|
tl = list[flag];
|
||||||
|
nl = list[flag^=1];
|
||||||
|
nl->inst = 0;
|
||||||
|
ntl = nnl;
|
||||||
|
nnl = 0;
|
||||||
|
if(sel.p[0].p1<0 && (!wrapped || p<startp || startp==eof)){
|
||||||
|
/* Add first instruction to this list */
|
||||||
|
sempty.p[0].p1 = p;
|
||||||
|
if(addinst(tl, startinst, &sempty))
|
||||||
|
if(++ntl >= NLIST)
|
||||||
|
Overflow:
|
||||||
|
error(Eoverflow);
|
||||||
|
}
|
||||||
|
/* Execute machine until this list is empty */
|
||||||
|
for(tlp = tl; inst = tlp->inst; tlp++){ /* assignment = */
|
||||||
|
Switchstmt:
|
||||||
|
switch(inst->type){
|
||||||
|
default: /* regular character */
|
||||||
|
if(inst->type==c){
|
||||||
|
Addinst:
|
||||||
|
if(addinst(nl, inst->next, &tlp->se))
|
||||||
|
if(++nnl >= NLIST)
|
||||||
|
goto Overflow;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LBRA:
|
||||||
|
if(inst->subid>=0)
|
||||||
|
tlp->se.p[inst->subid].p1 = p;
|
||||||
|
inst = inst->next;
|
||||||
|
goto Switchstmt;
|
||||||
|
case RBRA:
|
||||||
|
if(inst->subid>=0)
|
||||||
|
tlp->se.p[inst->subid].p2 = p;
|
||||||
|
inst = inst->next;
|
||||||
|
goto Switchstmt;
|
||||||
|
case ANY:
|
||||||
|
if(c!='\n')
|
||||||
|
goto Addinst;
|
||||||
|
break;
|
||||||
|
case BOL:
|
||||||
|
if(p==0 || filereadc(f, p - 1)=='\n'){
|
||||||
|
Step:
|
||||||
|
inst = inst->next;
|
||||||
|
goto Switchstmt;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EOL:
|
||||||
|
if(c == '\n')
|
||||||
|
goto Step;
|
||||||
|
break;
|
||||||
|
case CCLASS:
|
||||||
|
if(c>=0 && classmatch(inst->rclass, c, 0))
|
||||||
|
goto Addinst;
|
||||||
|
break;
|
||||||
|
case NCCLASS:
|
||||||
|
if(c>=0 && classmatch(inst->rclass, c, 1))
|
||||||
|
goto Addinst;
|
||||||
|
break;
|
||||||
|
case OR:
|
||||||
|
/* evaluate right choice later */
|
||||||
|
if(addinst(tl, inst->right, &tlp->se))
|
||||||
|
if(++ntl >= NLIST)
|
||||||
|
goto Overflow;
|
||||||
|
/* efficiency: advance and re-evaluate */
|
||||||
|
inst = inst->left;
|
||||||
|
goto Switchstmt;
|
||||||
|
case END: /* Match! */
|
||||||
|
tlp->se.p[0].p2 = p;
|
||||||
|
newmatch(&tlp->se);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Return:
|
||||||
|
return sel.p[0].p1>=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
newmatch(Rangeset *sp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(sel.p[0].p1<0 || sp->p[0].p1<sel.p[0].p1 ||
|
||||||
|
(sp->p[0].p1==sel.p[0].p1 && sp->p[0].p2>sel.p[0].p2))
|
||||||
|
for(i = 0; i<NSUBEXP; i++)
|
||||||
|
sel.p[i] = sp->p[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
bexecute(File *f, Posn startp)
|
||||||
|
{
|
||||||
|
int flag = 0;
|
||||||
|
Inst *inst;
|
||||||
|
Ilist *tlp;
|
||||||
|
Posn p = startp;
|
||||||
|
int nnl = 0, ntl;
|
||||||
|
int c;
|
||||||
|
int wrapped = 0;
|
||||||
|
int startchar = bstartinst->type<OPERATOR? bstartinst->type : 0;
|
||||||
|
|
||||||
|
list[0][0].inst = list[1][0].inst = 0;
|
||||||
|
sel.p[0].p1= -1;
|
||||||
|
/* Execute machine once for each character, including terminal NUL */
|
||||||
|
for(;;--p){
|
||||||
|
doloop:
|
||||||
|
if((c = filereadc(f, p - 1))==-1){
|
||||||
|
switch(wrapped++){
|
||||||
|
case 0: /* let loop run one more click */
|
||||||
|
case 2:
|
||||||
|
break;
|
||||||
|
case 1: /* expired; wrap to end */
|
||||||
|
if(sel.p[0].p1>=0)
|
||||||
|
case 3:
|
||||||
|
goto Return;
|
||||||
|
list[0][0].inst = list[1][0].inst = 0;
|
||||||
|
p = f->b.nc;
|
||||||
|
goto doloop;
|
||||||
|
default:
|
||||||
|
goto Return;
|
||||||
|
}
|
||||||
|
}else if(((wrapped && p<=startp) || sel.p[0].p1>0) && nnl==0)
|
||||||
|
break;
|
||||||
|
/* fast check for first char */
|
||||||
|
if(startchar && nnl==0 && c!=startchar)
|
||||||
|
continue;
|
||||||
|
tl = list[flag];
|
||||||
|
nl = list[flag^=1];
|
||||||
|
nl->inst = 0;
|
||||||
|
ntl = nnl;
|
||||||
|
nnl = 0;
|
||||||
|
if(sel.p[0].p1<0 && (!wrapped || p>startp)){
|
||||||
|
/* Add first instruction to this list */
|
||||||
|
/* the minus is so the optimizations in addinst work */
|
||||||
|
sempty.p[0].p1 = -p;
|
||||||
|
if(addinst(tl, bstartinst, &sempty))
|
||||||
|
if(++ntl >= NLIST)
|
||||||
|
Overflow:
|
||||||
|
error(Eoverflow);
|
||||||
|
}
|
||||||
|
/* Execute machine until this list is empty */
|
||||||
|
for(tlp = tl; inst = tlp->inst; tlp++){ /* assignment = */
|
||||||
|
Switchstmt:
|
||||||
|
switch(inst->type){
|
||||||
|
default: /* regular character */
|
||||||
|
if(inst->type == c){
|
||||||
|
Addinst:
|
||||||
|
if(addinst(nl, inst->next, &tlp->se))
|
||||||
|
if(++nnl >= NLIST)
|
||||||
|
goto Overflow;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LBRA:
|
||||||
|
if(inst->subid>=0)
|
||||||
|
tlp->se.p[inst->subid].p1 = p;
|
||||||
|
inst = inst->next;
|
||||||
|
goto Switchstmt;
|
||||||
|
case RBRA:
|
||||||
|
if(inst->subid >= 0)
|
||||||
|
tlp->se.p[inst->subid].p2 = p;
|
||||||
|
inst = inst->next;
|
||||||
|
goto Switchstmt;
|
||||||
|
case ANY:
|
||||||
|
if(c != '\n')
|
||||||
|
goto Addinst;
|
||||||
|
break;
|
||||||
|
case BOL:
|
||||||
|
if(c=='\n' || p==0){
|
||||||
|
Step:
|
||||||
|
inst = inst->next;
|
||||||
|
goto Switchstmt;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EOL:
|
||||||
|
if(p==f->b.nc || filereadc(f, p)=='\n')
|
||||||
|
goto Step;
|
||||||
|
break;
|
||||||
|
case CCLASS:
|
||||||
|
if(c>=0 && classmatch(inst->rclass, c, 0))
|
||||||
|
goto Addinst;
|
||||||
|
break;
|
||||||
|
case NCCLASS:
|
||||||
|
if(c>=0 && classmatch(inst->rclass, c, 1))
|
||||||
|
goto Addinst;
|
||||||
|
break;
|
||||||
|
case OR:
|
||||||
|
/* evaluate right choice later */
|
||||||
|
if(addinst(tlp, inst->right, &tlp->se))
|
||||||
|
if(++ntl >= NLIST)
|
||||||
|
goto Overflow;
|
||||||
|
/* efficiency: advance and re-evaluate */
|
||||||
|
inst = inst->left;
|
||||||
|
goto Switchstmt;
|
||||||
|
case END: /* Match! */
|
||||||
|
tlp->se.p[0].p1 = -tlp->se.p[0].p1; /* minus sign */
|
||||||
|
tlp->se.p[0].p2 = p;
|
||||||
|
bnewmatch(&tlp->se);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Return:
|
||||||
|
return sel.p[0].p1>=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bnewmatch(Rangeset *sp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
if(sel.p[0].p1<0 || sp->p[0].p1>sel.p[0].p2 || (sp->p[0].p1==sel.p[0].p2 && sp->p[0].p2<sel.p[0].p1))
|
||||||
|
for(i = 0; i<NSUBEXP; i++){ /* note the reversal; p1<=p2 */
|
||||||
|
sel.p[i].p1 = sp->p[i].p2;
|
||||||
|
sel.p[i].p2 = sp->p[i].p1;
|
||||||
|
}
|
||||||
|
}
|
908
sam/sam.1
Normal file
908
sam/sam.1
Normal file
@@ -0,0 +1,908 @@
|
|||||||
|
.TH SAM 1
|
||||||
|
.ds a \fR*\ \fP
|
||||||
|
.SH NAME
|
||||||
|
sam, B, E, sam.save, samterm, samsave \- screen editor with structural regular expressions
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B sam
|
||||||
|
[
|
||||||
|
.I option ...
|
||||||
|
] [
|
||||||
|
.I files
|
||||||
|
]
|
||||||
|
.PP
|
||||||
|
.B sam
|
||||||
|
.B -r
|
||||||
|
.I machine
|
||||||
|
.PP
|
||||||
|
.B sam.save
|
||||||
|
.PP
|
||||||
|
.B B
|
||||||
|
.IB file \fR[\fP: line \fR]
|
||||||
|
\&...
|
||||||
|
.PP
|
||||||
|
.B E
|
||||||
|
.I file
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.I Sam
|
||||||
|
is a multi-file editor.
|
||||||
|
It modifies a local copy of an external file.
|
||||||
|
The copy is here called a
|
||||||
|
.IR file .
|
||||||
|
The files are listed in a menu available through mouse button 3
|
||||||
|
or the
|
||||||
|
.B n
|
||||||
|
command.
|
||||||
|
Each file has an associated name, usually the name of the
|
||||||
|
external file from which it was read, and a `modified' bit that indicates whether
|
||||||
|
the editor's file agrees with the external file.
|
||||||
|
The external file is not read into
|
||||||
|
the editor's file until it first becomes the current file\(emthat to
|
||||||
|
which editing commands apply\(emwhereupon its menu entry is printed.
|
||||||
|
The options are
|
||||||
|
.TF -rmachine
|
||||||
|
.TP
|
||||||
|
.B -a
|
||||||
|
Autoindent. In this mode, when a newline character is typed
|
||||||
|
in the terminal interface,
|
||||||
|
.I samterm
|
||||||
|
copies leading white space on the current line to the new line.
|
||||||
|
.TP
|
||||||
|
.B -d
|
||||||
|
Do not `download' the terminal part of
|
||||||
|
.IR sam .
|
||||||
|
Editing will be done with the command language only, as in
|
||||||
|
.IR ed (1).
|
||||||
|
.TP
|
||||||
|
.BI -r " machine
|
||||||
|
Run the host part remotely
|
||||||
|
on the specified machine, the terminal part locally.
|
||||||
|
.TP
|
||||||
|
.BI -s " path
|
||||||
|
Start the host part from the specified file on the remote host.
|
||||||
|
Only meaningful with the
|
||||||
|
.BI -r
|
||||||
|
option.
|
||||||
|
.TP
|
||||||
|
.BI -t " path
|
||||||
|
Start the terminal part from the specified file. Useful
|
||||||
|
for debugging.
|
||||||
|
.PD
|
||||||
|
.SS Regular expressions
|
||||||
|
Regular expressions are as in
|
||||||
|
.IR regexp (7)
|
||||||
|
with the addition of
|
||||||
|
.BR \en
|
||||||
|
to represent newlines.
|
||||||
|
A regular expression may never contain a literal newline character.
|
||||||
|
The empty
|
||||||
|
regular expression stands for the last complete expression encountered.
|
||||||
|
A regular expression in
|
||||||
|
.I sam
|
||||||
|
matches the longest leftmost substring formally
|
||||||
|
matched by the expression.
|
||||||
|
Searching in the reverse direction is equivalent
|
||||||
|
to searching backwards with the catenation operations reversed in
|
||||||
|
the expression.
|
||||||
|
.SS Addresses
|
||||||
|
An address identifies a substring in a file.
|
||||||
|
In the following, `character
|
||||||
|
.IR n '
|
||||||
|
means the null string
|
||||||
|
after the
|
||||||
|
.IR n -th
|
||||||
|
character in the file, with 1 the
|
||||||
|
first character in the file.
|
||||||
|
`Line
|
||||||
|
.IR n '
|
||||||
|
means the
|
||||||
|
.IR n -th
|
||||||
|
match,
|
||||||
|
starting at the beginning of the file, of the regular expression
|
||||||
|
.LR .*\en? .
|
||||||
|
All files always have a current substring, called dot,
|
||||||
|
that is the default address.
|
||||||
|
.SS Simple Addresses
|
||||||
|
.PD 0
|
||||||
|
.TP
|
||||||
|
.BI # n
|
||||||
|
The empty string after character
|
||||||
|
.IR n ;
|
||||||
|
.B #0
|
||||||
|
is the beginning of the file.
|
||||||
|
.TP
|
||||||
|
.I n
|
||||||
|
Line
|
||||||
|
.IR n ;
|
||||||
|
.B 0
|
||||||
|
is the beginning of the file.
|
||||||
|
.TP
|
||||||
|
.BI / regexp /
|
||||||
|
.PD 0
|
||||||
|
.TP
|
||||||
|
.BI ? regexp ?
|
||||||
|
The substring that matches the regular expression,
|
||||||
|
found by looking toward the end
|
||||||
|
.RB ( / )
|
||||||
|
or beginning
|
||||||
|
.RB ( ? )
|
||||||
|
of the file,
|
||||||
|
and if necessary continuing the search from the other end to the
|
||||||
|
starting point of the search.
|
||||||
|
The matched substring may straddle
|
||||||
|
the starting point.
|
||||||
|
When entering a pattern containing a literal question mark
|
||||||
|
for a backward search, the question mark should be
|
||||||
|
specified as a member of a class.
|
||||||
|
.PD
|
||||||
|
.TP
|
||||||
|
.B 0
|
||||||
|
The string before the first full line.
|
||||||
|
This is not necessarily
|
||||||
|
the null string; see
|
||||||
|
.B +
|
||||||
|
and
|
||||||
|
.B -
|
||||||
|
below.
|
||||||
|
.TP
|
||||||
|
.B $
|
||||||
|
The null string at the end of the file.
|
||||||
|
.TP
|
||||||
|
.B .
|
||||||
|
Dot.
|
||||||
|
.TP
|
||||||
|
.B \&'
|
||||||
|
The mark in the file (see the
|
||||||
|
.B k
|
||||||
|
command below).
|
||||||
|
.TP
|
||||||
|
\fB"\f2regexp\fB"\f1\f1
|
||||||
|
Preceding a simple address (default
|
||||||
|
.BR . ),
|
||||||
|
refers to the address evaluated in the unique file whose menu line
|
||||||
|
matches the regular expression.
|
||||||
|
.PD
|
||||||
|
.SS Compound Addresses
|
||||||
|
In the following,
|
||||||
|
.I a1
|
||||||
|
and
|
||||||
|
.I a2
|
||||||
|
are addresses.
|
||||||
|
.TF a1+a2
|
||||||
|
.TP
|
||||||
|
.IB a1 + a2
|
||||||
|
The address
|
||||||
|
.I a2
|
||||||
|
evaluated starting at the end of
|
||||||
|
.IR a1 .
|
||||||
|
.TP
|
||||||
|
.IB a1 - a2
|
||||||
|
The address
|
||||||
|
.I a2
|
||||||
|
evaluated looking in the reverse direction
|
||||||
|
starting at the beginning of
|
||||||
|
.IR a1 .
|
||||||
|
.TP
|
||||||
|
.IB a1 , a2
|
||||||
|
The substring from the beginning of
|
||||||
|
.I a1
|
||||||
|
to the end of
|
||||||
|
.IR a2 .
|
||||||
|
If
|
||||||
|
.I a1
|
||||||
|
is missing,
|
||||||
|
.B 0
|
||||||
|
is substituted.
|
||||||
|
If
|
||||||
|
.I a2
|
||||||
|
is missing,
|
||||||
|
.B $
|
||||||
|
is substituted.
|
||||||
|
.TP
|
||||||
|
.IB a1 ; a2
|
||||||
|
Like
|
||||||
|
.IB a1 , a2\f1,
|
||||||
|
but with
|
||||||
|
.I a2
|
||||||
|
evaluated at the end of, and dot set to,
|
||||||
|
.IR a1 .
|
||||||
|
.PD
|
||||||
|
.PP
|
||||||
|
The operators
|
||||||
|
.B +
|
||||||
|
and
|
||||||
|
.B -
|
||||||
|
are high precedence, while
|
||||||
|
.B ,
|
||||||
|
and
|
||||||
|
.B ;
|
||||||
|
are low precedence.
|
||||||
|
.PP
|
||||||
|
In both
|
||||||
|
.B +
|
||||||
|
and
|
||||||
|
.B -
|
||||||
|
forms, if
|
||||||
|
.I a2
|
||||||
|
is a line or character address with a missing
|
||||||
|
number, the number defaults to 1.
|
||||||
|
If
|
||||||
|
.I a1
|
||||||
|
is missing,
|
||||||
|
.L .
|
||||||
|
is substituted.
|
||||||
|
If both
|
||||||
|
.I a1
|
||||||
|
and
|
||||||
|
.I a2
|
||||||
|
are present and distinguishable,
|
||||||
|
.B +
|
||||||
|
may be elided.
|
||||||
|
.I a2
|
||||||
|
may be a regular
|
||||||
|
expression; if it is delimited by
|
||||||
|
.LR ? 's,
|
||||||
|
the effect of the
|
||||||
|
.B +
|
||||||
|
or
|
||||||
|
.B -
|
||||||
|
is reversed.
|
||||||
|
.PP
|
||||||
|
It is an error for a compound address to represent a malformed substring.
|
||||||
|
Some useful idioms:
|
||||||
|
.IB a1 +-
|
||||||
|
\%(\f2a1\fB-+\f1)
|
||||||
|
selects the line containing
|
||||||
|
the end (beginning) of a1.
|
||||||
|
.BI 0/ regexp /
|
||||||
|
locates the first match of the expression in the file.
|
||||||
|
(The form
|
||||||
|
.B 0;//
|
||||||
|
sets dot unnecessarily.)
|
||||||
|
.BI ./ regexp ///
|
||||||
|
finds the second following occurrence of the expression,
|
||||||
|
and
|
||||||
|
.BI .,/ regexp /
|
||||||
|
extends dot.
|
||||||
|
.SS Commands
|
||||||
|
In the following, text demarcated by slashes represents text delimited
|
||||||
|
by any printable
|
||||||
|
character except alphanumerics.
|
||||||
|
Any number of
|
||||||
|
trailing delimiters may be elided, with multiple elisions then representing
|
||||||
|
null strings, but the first delimiter must always
|
||||||
|
be present.
|
||||||
|
In any delimited text,
|
||||||
|
newline may not appear literally;
|
||||||
|
.B \en
|
||||||
|
may be typed for newline; and
|
||||||
|
.B \e/
|
||||||
|
quotes the delimiter, here
|
||||||
|
.LR / .
|
||||||
|
Backslash is otherwise interpreted literally, except in
|
||||||
|
.B s
|
||||||
|
commands.
|
||||||
|
.PP
|
||||||
|
Most commands may be prefixed by an address to indicate their range
|
||||||
|
of operation.
|
||||||
|
Those that may not are marked with a
|
||||||
|
.L *
|
||||||
|
below.
|
||||||
|
If a command takes
|
||||||
|
an address and none is supplied, dot is used.
|
||||||
|
The sole exception is
|
||||||
|
the
|
||||||
|
.B w
|
||||||
|
command, which defaults to
|
||||||
|
.BR 0,$ .
|
||||||
|
In the description, `range' is used
|
||||||
|
to represent whatever address is supplied.
|
||||||
|
Many commands set the
|
||||||
|
value of dot as a side effect.
|
||||||
|
If so, it is always set to the `result'
|
||||||
|
of the change: the empty string for a deletion, the new text for an
|
||||||
|
insertion, etc. (but see the
|
||||||
|
.B s
|
||||||
|
and
|
||||||
|
.B e
|
||||||
|
commands).
|
||||||
|
.br
|
||||||
|
.ne 1.2i
|
||||||
|
.SS Text commands
|
||||||
|
.PD 0
|
||||||
|
.TP
|
||||||
|
.BI a/ text /
|
||||||
|
.TP
|
||||||
|
or
|
||||||
|
.TP
|
||||||
|
.B a
|
||||||
|
.TP
|
||||||
|
.I lines of text
|
||||||
|
.TP
|
||||||
|
.B .
|
||||||
|
Insert the text into the file after the range.
|
||||||
|
Set dot.
|
||||||
|
.PD
|
||||||
|
.TP
|
||||||
|
.B c\fP
|
||||||
|
.br
|
||||||
|
.ns
|
||||||
|
.TP
|
||||||
|
.B i\fP
|
||||||
|
Same as
|
||||||
|
.BR a ,
|
||||||
|
but
|
||||||
|
.B c
|
||||||
|
replaces the text, while
|
||||||
|
.B i
|
||||||
|
inserts
|
||||||
|
.I before
|
||||||
|
the range.
|
||||||
|
.TP
|
||||||
|
.B d
|
||||||
|
Delete the text in the range.
|
||||||
|
Set dot.
|
||||||
|
.TP
|
||||||
|
.BI s/ regexp / text /
|
||||||
|
Substitute
|
||||||
|
.I text
|
||||||
|
for the first match to the regular expression in the range.
|
||||||
|
Set dot to the modified range.
|
||||||
|
In
|
||||||
|
.I text
|
||||||
|
the character
|
||||||
|
.B &
|
||||||
|
stands for the string
|
||||||
|
that matched the expression.
|
||||||
|
Backslash behaves as usual unless followed by
|
||||||
|
a digit:
|
||||||
|
.BI \e d
|
||||||
|
stands for the string that matched the
|
||||||
|
subexpression begun by the
|
||||||
|
.IR d -th
|
||||||
|
left parenthesis.
|
||||||
|
If
|
||||||
|
.I s
|
||||||
|
is followed immediately by a
|
||||||
|
number
|
||||||
|
.IR n ,
|
||||||
|
as in
|
||||||
|
.BR s2/x/y/ ,
|
||||||
|
the
|
||||||
|
.IR n -th
|
||||||
|
match in the range is substituted.
|
||||||
|
If the
|
||||||
|
command is followed by a
|
||||||
|
.BR g ,
|
||||||
|
as in
|
||||||
|
.BR s/x/y/g ,
|
||||||
|
all matches in the range
|
||||||
|
are substituted.
|
||||||
|
.TP
|
||||||
|
.BI m " a1
|
||||||
|
.br
|
||||||
|
.ns
|
||||||
|
.TP
|
||||||
|
.BI t " a1
|
||||||
|
Move
|
||||||
|
.RB ( m )
|
||||||
|
or copy
|
||||||
|
.RB ( t )
|
||||||
|
the range to after
|
||||||
|
.IR a1 .
|
||||||
|
Set dot.
|
||||||
|
.SS Display commands
|
||||||
|
.PD 0
|
||||||
|
.TP
|
||||||
|
.B p
|
||||||
|
Print the text in the range.
|
||||||
|
Set dot.
|
||||||
|
.TP
|
||||||
|
.B =
|
||||||
|
Print the line address and character address of the range.
|
||||||
|
.TP
|
||||||
|
.B =#
|
||||||
|
Print just the character address of the range.
|
||||||
|
.PD
|
||||||
|
.SS File commands
|
||||||
|
.PD 0
|
||||||
|
.TP
|
||||||
|
.BI \*ab " file-list
|
||||||
|
Set the current file to the first file named in the list
|
||||||
|
that
|
||||||
|
.I sam
|
||||||
|
also has in its menu.
|
||||||
|
The list may be expressed
|
||||||
|
.BI < "Plan 9 command"
|
||||||
|
in which case the file names are taken as words (in the shell sense)
|
||||||
|
generated by the Plan 9 command.
|
||||||
|
.TP
|
||||||
|
.BI \*aB " file-list
|
||||||
|
Same as
|
||||||
|
.BR b ,
|
||||||
|
except that file names not in the menu are entered there,
|
||||||
|
and all file names in the list are examined.
|
||||||
|
.TP
|
||||||
|
.B \*an
|
||||||
|
Print a menu of files.
|
||||||
|
The format is:
|
||||||
|
.RS
|
||||||
|
.TP 11
|
||||||
|
.BR ' " or blank
|
||||||
|
indicating the file is modified or clean,
|
||||||
|
.TP 11
|
||||||
|
.BR - " or \&" +
|
||||||
|
indicating the file is unread or has been read
|
||||||
|
(in the terminal,
|
||||||
|
.B *
|
||||||
|
means more than one window is open),
|
||||||
|
.TP 11
|
||||||
|
.BR . " or blank
|
||||||
|
indicating the current file,
|
||||||
|
.TP 11
|
||||||
|
a blank,
|
||||||
|
.TP 11
|
||||||
|
and the file name.
|
||||||
|
.RE
|
||||||
|
.TP 0
|
||||||
|
.BI \*aD " file-list
|
||||||
|
Delete the named files from the menu.
|
||||||
|
If no files are named, the current file is deleted.
|
||||||
|
It is an error to
|
||||||
|
.B D
|
||||||
|
a modified file, but a subsequent
|
||||||
|
.B D
|
||||||
|
will delete such a file.
|
||||||
|
.PD
|
||||||
|
.SS I/O Commands
|
||||||
|
.PD 0
|
||||||
|
.TP
|
||||||
|
.BI \*ae " filename
|
||||||
|
Replace the file by the contents of the named external file.
|
||||||
|
Set dot to the beginning of the file.
|
||||||
|
.TP
|
||||||
|
.BI r " filename
|
||||||
|
Replace the text in the range by the contents of the named external file.
|
||||||
|
Set dot.
|
||||||
|
.TP
|
||||||
|
.BI w " filename
|
||||||
|
Write the range (default
|
||||||
|
.BR 0,$ )
|
||||||
|
to the named external file.
|
||||||
|
.TP
|
||||||
|
.BI \*af " filename
|
||||||
|
Set the file name and print the resulting menu entry.
|
||||||
|
.PP
|
||||||
|
If the file name is absent from any of these, the current file name is used.
|
||||||
|
.B e
|
||||||
|
always sets the file name;
|
||||||
|
.B r
|
||||||
|
and
|
||||||
|
.B w
|
||||||
|
do so if the file has no name.
|
||||||
|
.TP
|
||||||
|
.BI < " Plan 9-command
|
||||||
|
Replace the range by the standard output of the
|
||||||
|
Plan 9 command.
|
||||||
|
.TP
|
||||||
|
.BI > " Plan 9-command
|
||||||
|
Send the range to the standard input of the
|
||||||
|
Plan 9 command.
|
||||||
|
.TP
|
||||||
|
.BI | " Plan 9-command
|
||||||
|
Send the range to the standard input, and replace it by
|
||||||
|
the standard output, of the
|
||||||
|
Plan 9 command.
|
||||||
|
.TP
|
||||||
|
.BI \*a! " Plan 9-command
|
||||||
|
Run the
|
||||||
|
Plan 9 command.
|
||||||
|
.TP
|
||||||
|
.BI \*acd " directory
|
||||||
|
Change working directory.
|
||||||
|
If no directory is specified,
|
||||||
|
.B $home
|
||||||
|
is used.
|
||||||
|
.PD
|
||||||
|
.PP
|
||||||
|
In any of
|
||||||
|
.BR < ,
|
||||||
|
.BR > ,
|
||||||
|
.B |
|
||||||
|
or
|
||||||
|
.BR ! ,
|
||||||
|
if the
|
||||||
|
.I Plan 9 command
|
||||||
|
is omitted the last
|
||||||
|
.I Plan 9 command
|
||||||
|
(of any type) is substituted.
|
||||||
|
If
|
||||||
|
.I sam
|
||||||
|
is
|
||||||
|
.I downloaded
|
||||||
|
(using the mouse and raster display, i.e. not using option
|
||||||
|
.BR -d ),
|
||||||
|
.B !
|
||||||
|
sets standard input to
|
||||||
|
.BR /dev/null ,
|
||||||
|
and otherwise
|
||||||
|
unassigned output
|
||||||
|
.RB ( stdout
|
||||||
|
for
|
||||||
|
.B !
|
||||||
|
and
|
||||||
|
.BR > ,
|
||||||
|
.B stderr
|
||||||
|
for all) is placed in
|
||||||
|
.B /tmp/sam.err
|
||||||
|
and the first few lines are printed.
|
||||||
|
.SS Loops and Conditionals
|
||||||
|
.PD 0
|
||||||
|
.TP
|
||||||
|
.BI x/ regexp / " command
|
||||||
|
For each match of the regular expression in the range, run the command
|
||||||
|
with dot set to the match.
|
||||||
|
Set dot to the last match.
|
||||||
|
If the regular
|
||||||
|
expression and its slashes are omitted,
|
||||||
|
.L /.*\en/
|
||||||
|
is assumed.
|
||||||
|
Null string matches potentially occur before every character
|
||||||
|
of the range and at the end of the range.
|
||||||
|
.TP
|
||||||
|
.BI y/ regexp / " command
|
||||||
|
Like
|
||||||
|
.BR x ,
|
||||||
|
but run the command for each substring that lies before, between,
|
||||||
|
or after
|
||||||
|
the matches that would be generated by
|
||||||
|
.BR x .
|
||||||
|
There is no default regular expression.
|
||||||
|
Null substrings potentially occur before every character
|
||||||
|
in the range.
|
||||||
|
.TP
|
||||||
|
.BI \*aX/ regexp / " command
|
||||||
|
For each file whose menu entry matches the regular expression,
|
||||||
|
make that the current file and
|
||||||
|
run the command.
|
||||||
|
If the expression is omitted, the command is run
|
||||||
|
in every file.
|
||||||
|
.TP
|
||||||
|
.BI \*aY/ regexp / " command
|
||||||
|
Same as
|
||||||
|
.BR X ,
|
||||||
|
but for files that do not match the regular expression,
|
||||||
|
and the expression is required.
|
||||||
|
.TP
|
||||||
|
.BI g/ regexp / " command
|
||||||
|
.br
|
||||||
|
.ns
|
||||||
|
.TP
|
||||||
|
.BI v/ regexp / " command
|
||||||
|
If the range contains
|
||||||
|
.RB ( g )
|
||||||
|
or does not contain
|
||||||
|
.RB ( v )
|
||||||
|
a match for the expression,
|
||||||
|
set dot to the range and run the command.
|
||||||
|
.PP
|
||||||
|
These may be nested arbitrarily deeply, but only one instance of either
|
||||||
|
.B X
|
||||||
|
or
|
||||||
|
.B Y
|
||||||
|
may appear in a \%single command.
|
||||||
|
An empty command in an
|
||||||
|
.B x
|
||||||
|
or
|
||||||
|
.B y
|
||||||
|
defaults to
|
||||||
|
.BR p ;
|
||||||
|
an empty command in
|
||||||
|
.B X
|
||||||
|
or
|
||||||
|
.B Y
|
||||||
|
defaults to
|
||||||
|
.BR f .
|
||||||
|
.B g
|
||||||
|
and
|
||||||
|
.B v
|
||||||
|
do not have defaults.
|
||||||
|
.PD
|
||||||
|
.SS Miscellany
|
||||||
|
.TF (empty)
|
||||||
|
.TP
|
||||||
|
.B k
|
||||||
|
Set the current file's mark to the range. Does not set dot.
|
||||||
|
.TP
|
||||||
|
.B \*aq
|
||||||
|
Quit.
|
||||||
|
It is an error to quit with modified files, but a second
|
||||||
|
.B q
|
||||||
|
will succeed.
|
||||||
|
.TP
|
||||||
|
.BI \*au " n
|
||||||
|
Undo the last
|
||||||
|
.I n
|
||||||
|
(default 1)
|
||||||
|
top-level commands that changed the contents or name of the
|
||||||
|
current file, and any other file whose most recent change was simultaneous
|
||||||
|
with the current file's change.
|
||||||
|
Successive
|
||||||
|
.BR u 's
|
||||||
|
move further back in time.
|
||||||
|
The only commands for which u is ineffective are
|
||||||
|
.BR cd ,
|
||||||
|
.BR u ,
|
||||||
|
.BR q ,
|
||||||
|
.B w
|
||||||
|
and
|
||||||
|
.BR D .
|
||||||
|
If
|
||||||
|
.I n
|
||||||
|
is negative,
|
||||||
|
.B u
|
||||||
|
`redoes,' undoing the undo, going forwards in time again.
|
||||||
|
.TP
|
||||||
|
(empty)
|
||||||
|
If the range is explicit, set dot to the range.
|
||||||
|
If
|
||||||
|
.I sam
|
||||||
|
is downloaded, the resulting dot is selected on the screen;
|
||||||
|
otherwise it is printed.
|
||||||
|
If no address is specified (the
|
||||||
|
command is a newline) dot is extended in either direction to
|
||||||
|
line boundaries and printed.
|
||||||
|
If dot is thereby unchanged, it is set to
|
||||||
|
.B .+1
|
||||||
|
and printed.
|
||||||
|
.PD
|
||||||
|
.SS Grouping and multiple changes
|
||||||
|
Commands may be grouped by enclosing them in braces
|
||||||
|
.BR {} .
|
||||||
|
Commands within the braces must appear on separate lines (no backslashes are
|
||||||
|
required between commands).
|
||||||
|
Semantically, an opening brace is like a command:
|
||||||
|
it takes an (optional) address and sets dot for each sub-command.
|
||||||
|
Commands within the braces are executed sequentially, but changes made
|
||||||
|
by one command are not visible to other commands (see the next
|
||||||
|
paragraph).
|
||||||
|
Braces may be nested arbitrarily.
|
||||||
|
.PP
|
||||||
|
When a command makes a number of changes to a file, as in
|
||||||
|
.BR x/re/c/text/ ,
|
||||||
|
the addresses of all changes to the file are computed in the original file.
|
||||||
|
If the changes are in sequence,
|
||||||
|
they are applied to the file.
|
||||||
|
Successive insertions at the same address are catenated into a single
|
||||||
|
insertion composed of the several insertions in the order applied.
|
||||||
|
.SS The terminal
|
||||||
|
What follows refers to behavior of
|
||||||
|
.I sam
|
||||||
|
when downloaded, that is, when
|
||||||
|
operating as a display editor on a raster display.
|
||||||
|
This is the default
|
||||||
|
behavior; invoking
|
||||||
|
.I sam
|
||||||
|
with the
|
||||||
|
.B -d
|
||||||
|
(no download) option provides access
|
||||||
|
to the command language only.
|
||||||
|
.PP
|
||||||
|
Each file may have zero or more windows open.
|
||||||
|
Each window is equivalent
|
||||||
|
and is updated simultaneously with changes in other windows on the same file.
|
||||||
|
Each window has an independent value of dot, indicated by a highlighted
|
||||||
|
substring on the display.
|
||||||
|
Dot may be in a region not within
|
||||||
|
the window.
|
||||||
|
There is usually a `current window',
|
||||||
|
marked with a dark border, to which typed text and editing
|
||||||
|
commands apply.
|
||||||
|
Text may be typed and edited as in
|
||||||
|
.IR rio (1);
|
||||||
|
also the escape key (ESC) selects (sets dot to) text typed
|
||||||
|
since the last mouse button hit.
|
||||||
|
.PP
|
||||||
|
The button 3 menu controls window operations.
|
||||||
|
The top of the menu
|
||||||
|
provides the following operators, each of which uses one or
|
||||||
|
more
|
||||||
|
.IR rio -like
|
||||||
|
cursors to prompt for selection of a window or sweeping
|
||||||
|
of a rectangle.
|
||||||
|
`Sweeping' a null rectangle gets a large window, disjoint
|
||||||
|
from the command window or the whole screen, depending on
|
||||||
|
where the null rectangle is.
|
||||||
|
.TF resize
|
||||||
|
.TP
|
||||||
|
.B new
|
||||||
|
Create a new, empty file.
|
||||||
|
.TP
|
||||||
|
.B zerox
|
||||||
|
Create a copy of an existing window.
|
||||||
|
.TP
|
||||||
|
.B resize
|
||||||
|
As in
|
||||||
|
.IR rio .
|
||||||
|
.TP
|
||||||
|
.B close
|
||||||
|
Delete the window.
|
||||||
|
In the last window of a file,
|
||||||
|
.B close
|
||||||
|
is equivalent to a
|
||||||
|
.B D
|
||||||
|
for the file.
|
||||||
|
.TP
|
||||||
|
.B write
|
||||||
|
Equivalent to a
|
||||||
|
.B w
|
||||||
|
for the file.
|
||||||
|
.PD
|
||||||
|
.PP
|
||||||
|
Below these operators is a list of available files, starting with
|
||||||
|
.BR ~~sam~~ ,
|
||||||
|
the command window.
|
||||||
|
Selecting a file from the list makes the most recently
|
||||||
|
used window on that file current, unless it is already current, in which
|
||||||
|
case selections cycle through the open windows.
|
||||||
|
If no windows are open
|
||||||
|
on the file, the user is prompted to open one.
|
||||||
|
Files other than
|
||||||
|
.B ~~sam~~
|
||||||
|
are marked with one of the characters
|
||||||
|
.B -+*
|
||||||
|
according as zero, one, or more windows
|
||||||
|
are open on the file.
|
||||||
|
A further mark
|
||||||
|
.L .
|
||||||
|
appears on the file in the current window and
|
||||||
|
a single quote,
|
||||||
|
.BR ' ,
|
||||||
|
on a file modified since last write.
|
||||||
|
.PP
|
||||||
|
The command window, created automatically when
|
||||||
|
.B sam
|
||||||
|
starts, is an ordinary window except that text typed to it
|
||||||
|
is interpreted as commands for the editor rather than passive text,
|
||||||
|
and text printed by editor commands appears in it.
|
||||||
|
The behavior is like
|
||||||
|
.IR rio ,
|
||||||
|
with an `output point' that separates commands being typed from
|
||||||
|
previous output.
|
||||||
|
Commands typed in the command window apply to the
|
||||||
|
current open file\(emthe file in the most recently
|
||||||
|
current window.
|
||||||
|
.SS Manipulating text
|
||||||
|
Button 1 changes selection, much like
|
||||||
|
.IR rio .
|
||||||
|
Pointing to a non-current window with button 1 makes it current;
|
||||||
|
within the current window, button 1 selects text, thus setting dot.
|
||||||
|
Double-clicking selects text to the boundaries of words, lines,
|
||||||
|
quoted strings or bracketed strings, depending on the text at the click.
|
||||||
|
.PP
|
||||||
|
Button 2 provides a menu of editing commands:
|
||||||
|
.TF /regexp
|
||||||
|
.TP
|
||||||
|
.B cut
|
||||||
|
Delete dot and save the deleted text in the snarf buffer.
|
||||||
|
.TP
|
||||||
|
.B paste
|
||||||
|
Replace the text in dot by the contents of the snarf buffer.
|
||||||
|
.TP
|
||||||
|
.B snarf
|
||||||
|
Save the text in dot in the snarf buffer.
|
||||||
|
.TP
|
||||||
|
.B plumb
|
||||||
|
Send the text in the selection as a plumb
|
||||||
|
message. If the selection is empty,
|
||||||
|
the white-space-delimited block of text is sent as a plumb message
|
||||||
|
with a
|
||||||
|
.B click
|
||||||
|
attribute defining where the selection lies (see
|
||||||
|
.IR plumb (7)).
|
||||||
|
.TP
|
||||||
|
.B look
|
||||||
|
Search forward for the next occurrence of the literal text in dot.
|
||||||
|
If dot is the null string, the text in the snarf buffer is
|
||||||
|
used.
|
||||||
|
The snarf buffer is unaffected.
|
||||||
|
.TP
|
||||||
|
.B <rio>
|
||||||
|
Exchange snarf buffers with
|
||||||
|
.IR rio .
|
||||||
|
.TP
|
||||||
|
.BI / regexp
|
||||||
|
Search forward for the next match of the last regular expression
|
||||||
|
typed in a command.
|
||||||
|
(Not in command window.)
|
||||||
|
.TP
|
||||||
|
.B send
|
||||||
|
Send the text in dot, or the snarf buffer if
|
||||||
|
dot is the null string, as if it were typed to the command window.
|
||||||
|
Saves the sent text in the snarf buffer.
|
||||||
|
(Command window only.)
|
||||||
|
.PD
|
||||||
|
.SS External communication
|
||||||
|
.I Sam
|
||||||
|
listens to the
|
||||||
|
.B edit
|
||||||
|
plumb port.
|
||||||
|
If plumbing is not active,
|
||||||
|
on invocation
|
||||||
|
.I sam
|
||||||
|
creates a named pipe
|
||||||
|
.BI /srv/sam. user
|
||||||
|
which acts as an additional source of commands. Characters written to
|
||||||
|
the named pipe are treated as if they had been typed in the command window.
|
||||||
|
.PP
|
||||||
|
.I B
|
||||||
|
is a shell-level command that causes an instance of
|
||||||
|
.I sam
|
||||||
|
running on the same terminal to load the named
|
||||||
|
.IR files .
|
||||||
|
.I B
|
||||||
|
uses either plumbing or the named pipe, whichever service is available.
|
||||||
|
If plumbing is not enabled,
|
||||||
|
the option allows a line number to be specified for
|
||||||
|
the initial position to display in the last named file
|
||||||
|
(plumbing provides a more general mechanism for this ability).
|
||||||
|
.PP
|
||||||
|
.I E
|
||||||
|
is a shell-level command that can be used as
|
||||||
|
.B $EDITOR
|
||||||
|
in a Unix environment.
|
||||||
|
It runs
|
||||||
|
.I B
|
||||||
|
on
|
||||||
|
.I file
|
||||||
|
and then does not exit until
|
||||||
|
.I file
|
||||||
|
is changed, which is taken as a signal that
|
||||||
|
.I file
|
||||||
|
is done being edited.
|
||||||
|
.SS Abnormal termination
|
||||||
|
If
|
||||||
|
.I sam
|
||||||
|
terminates other than by a
|
||||||
|
.B q
|
||||||
|
command (by hangup, deleting its window, etc.), modified
|
||||||
|
files are saved in an
|
||||||
|
executable file,
|
||||||
|
.BR $HOME/sam.save .
|
||||||
|
This program, when executed, asks whether to write
|
||||||
|
each file back to a external file.
|
||||||
|
The answer
|
||||||
|
.L y
|
||||||
|
causes writing; anything else skips the file.
|
||||||
|
.SH FILES
|
||||||
|
.TF $HOME/sam.save
|
||||||
|
.TP
|
||||||
|
.B $HOME/sam.save
|
||||||
|
.TP
|
||||||
|
.B $HOME/sam.err
|
||||||
|
.TP
|
||||||
|
.B \*9/bin/samsave
|
||||||
|
the program called to unpack
|
||||||
|
.BR $HOME/sam.save .
|
||||||
|
.SH SOURCE
|
||||||
|
.TF \*9/src/cmd/samterm
|
||||||
|
.TP
|
||||||
|
.B \*9/src/cmd/sam
|
||||||
|
source for
|
||||||
|
.I sam
|
||||||
|
itself
|
||||||
|
.TP
|
||||||
|
.B \*9/src/cmd/samterm
|
||||||
|
source for the separate terminal part
|
||||||
|
.TP
|
||||||
|
.B \*9/bin/B
|
||||||
|
.TP
|
||||||
|
.B \*9/bin/E
|
||||||
|
.SH SEE ALSO
|
||||||
|
.IR ed (1),
|
||||||
|
.IR sed (1),
|
||||||
|
.IR grep (1),
|
||||||
|
.IR rio (1),
|
||||||
|
.IR regexp (7).
|
||||||
|
.PP
|
||||||
|
Rob Pike,
|
||||||
|
``The text editor sam''.
|
741
sam/sam.c
Normal file
741
sam/sam.c
Normal file
@@ -0,0 +1,741 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
|
||||||
|
Rune genbuf[BLOCKSIZE];
|
||||||
|
int io;
|
||||||
|
int panicking;
|
||||||
|
int rescuing;
|
||||||
|
String genstr;
|
||||||
|
String rhs;
|
||||||
|
String curwd;
|
||||||
|
String cmdstr;
|
||||||
|
Rune empty[] = { 0 };
|
||||||
|
char *genc;
|
||||||
|
File *curfile;
|
||||||
|
File *flist;
|
||||||
|
File *cmd;
|
||||||
|
jmp_buf mainloop;
|
||||||
|
List tempfile = { 'p' };
|
||||||
|
int quitok = TRUE;
|
||||||
|
int downloaded;
|
||||||
|
int dflag;
|
||||||
|
int Rflag;
|
||||||
|
char *machine;
|
||||||
|
char *home;
|
||||||
|
int bpipeok;
|
||||||
|
int termlocked;
|
||||||
|
char *samterm = SAMTERM;
|
||||||
|
char *rsamname = RSAM;
|
||||||
|
File *lastfile;
|
||||||
|
Disk *disk;
|
||||||
|
long seq;
|
||||||
|
|
||||||
|
char *winsize;
|
||||||
|
|
||||||
|
Rune baddir[] = { '<', 'b', 'a', 'd', 'd', 'i', 'r', '>', '\n'};
|
||||||
|
|
||||||
|
void usage(void);
|
||||||
|
|
||||||
|
extern int notify(void(*)(void*,char*));
|
||||||
|
|
||||||
|
void
|
||||||
|
main(int _argc, char **_argv)
|
||||||
|
{
|
||||||
|
volatile int i, argc;
|
||||||
|
char **volatile argv;
|
||||||
|
String *t;
|
||||||
|
char *termargs[10], **ap;
|
||||||
|
|
||||||
|
argc = _argc;
|
||||||
|
argv = _argv;
|
||||||
|
ap = termargs;
|
||||||
|
*ap++ = "samterm";
|
||||||
|
ARGBEGIN{
|
||||||
|
case 'd':
|
||||||
|
dflag++;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
machine = EARGF(usage());
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
Rflag++;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
samterm = EARGF(usage());
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
rsamname = EARGF(usage());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dprint("sam: unknown flag %c\n", ARGC());
|
||||||
|
usage();
|
||||||
|
/* options for samterm */
|
||||||
|
case 'a':
|
||||||
|
*ap++ = "-a";
|
||||||
|
break;
|
||||||
|
case 'W':
|
||||||
|
*ap++ = "-W";
|
||||||
|
*ap++ = EARGF(usage());
|
||||||
|
break;
|
||||||
|
}ARGEND
|
||||||
|
*ap = nil;
|
||||||
|
|
||||||
|
Strinit(&cmdstr);
|
||||||
|
Strinit0(&lastpat);
|
||||||
|
Strinit0(&lastregexp);
|
||||||
|
Strinit0(&genstr);
|
||||||
|
Strinit0(&rhs);
|
||||||
|
Strinit0(&curwd);
|
||||||
|
Strinit0(&plan9cmd);
|
||||||
|
home = getenv(HOME);
|
||||||
|
disk = diskinit();
|
||||||
|
if(home == 0)
|
||||||
|
home = "/";
|
||||||
|
if(!dflag)
|
||||||
|
startup(machine, Rflag, termargs, (char**)argv);
|
||||||
|
notify(notifyf);
|
||||||
|
getcurwd();
|
||||||
|
if(argc>0){
|
||||||
|
for(i=0; i<argc; i++){
|
||||||
|
if(!setjmp(mainloop)){
|
||||||
|
t = tmpcstr(argv[i]);
|
||||||
|
Straddc(t, '\0');
|
||||||
|
Strduplstr(&genstr, t);
|
||||||
|
freetmpstr(t);
|
||||||
|
fixname(&genstr);
|
||||||
|
logsetname(newfile(), &genstr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else if(!downloaded)
|
||||||
|
newfile();
|
||||||
|
seq++;
|
||||||
|
if(file.nused)
|
||||||
|
current(file.filepptr[0]);
|
||||||
|
setjmp(mainloop);
|
||||||
|
cmdloop();
|
||||||
|
trytoquit(); /* if we already q'ed, quitok will be TRUE */
|
||||||
|
exits(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
usage(void)
|
||||||
|
{
|
||||||
|
dprint("usage: sam [-d] [-t samterm] [-s sam name] [-r machine] [file ...]\n");
|
||||||
|
exits("usage");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rescue(void)
|
||||||
|
{
|
||||||
|
int i, nblank = 0;
|
||||||
|
File *f;
|
||||||
|
char *c;
|
||||||
|
char buf[256];
|
||||||
|
char *root;
|
||||||
|
|
||||||
|
if(rescuing++)
|
||||||
|
return;
|
||||||
|
io = -1;
|
||||||
|
for(i=0; i<file.nused; i++){
|
||||||
|
f = file.filepptr[i];
|
||||||
|
if(f==cmd || f->b.nc==0 || !fileisdirty(f))
|
||||||
|
continue;
|
||||||
|
if(io == -1){
|
||||||
|
sprint(buf, "%s/sam.save", home);
|
||||||
|
io = create(buf, 1, 0777);
|
||||||
|
if(io<0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(f->name.s[0]){
|
||||||
|
c = Strtoc(&f->name);
|
||||||
|
strncpy(buf, c, sizeof buf-1);
|
||||||
|
buf[sizeof buf-1] = 0;
|
||||||
|
free(c);
|
||||||
|
}else
|
||||||
|
sprint(buf, "nameless.%d", nblank++);
|
||||||
|
root = getenv("PLAN9");
|
||||||
|
if(root == nil)
|
||||||
|
root = "/usr/local/plan9";
|
||||||
|
fprint(io, "#!/bin/sh\n%s/bin/samsave '%s' $* <<'---%s'\n", root, buf, buf);
|
||||||
|
addr.r.p1 = 0, addr.r.p2 = f->b.nc;
|
||||||
|
writeio(f);
|
||||||
|
fprint(io, "\n---%s\n", (char *)buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
panic(char *s)
|
||||||
|
{
|
||||||
|
int wasd;
|
||||||
|
|
||||||
|
if(!panicking++ && !setjmp(mainloop)){
|
||||||
|
wasd = downloaded;
|
||||||
|
downloaded = 0;
|
||||||
|
dprint("sam: panic: %s: %r\n", s);
|
||||||
|
if(wasd)
|
||||||
|
fprint(2, "sam: panic: %s: %r\n", s);
|
||||||
|
rescue();
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hiccough(char *s)
|
||||||
|
{
|
||||||
|
File *f;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(rescuing)
|
||||||
|
exits("rescue");
|
||||||
|
if(s)
|
||||||
|
dprint("%s\n", s);
|
||||||
|
resetcmd();
|
||||||
|
resetxec();
|
||||||
|
resetsys();
|
||||||
|
if(io > 0)
|
||||||
|
close(io);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* back out any logged changes & restore old sequences
|
||||||
|
*/
|
||||||
|
for(i=0; i<file.nused; i++){
|
||||||
|
f = file.filepptr[i];
|
||||||
|
if(f==cmd)
|
||||||
|
continue;
|
||||||
|
if(f->seq==seq){
|
||||||
|
bufdelete(&f->epsilon, 0, f->epsilon.nc);
|
||||||
|
f->seq = f->prevseq;
|
||||||
|
f->dot.r = f->prevdot;
|
||||||
|
f->mark = f->prevmark;
|
||||||
|
state(f, f->prevmod ? Dirty: Clean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
if (curfile) {
|
||||||
|
if (curfile->unread)
|
||||||
|
curfile->unread = FALSE;
|
||||||
|
else if (downloaded)
|
||||||
|
outTs(Hcurrent, curfile->tag);
|
||||||
|
}
|
||||||
|
longjmp(mainloop, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
intr(void)
|
||||||
|
{
|
||||||
|
error(Eintr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
trytoclose(File *f)
|
||||||
|
{
|
||||||
|
char *t;
|
||||||
|
char buf[256];
|
||||||
|
|
||||||
|
if(f == cmd) /* possible? */
|
||||||
|
return;
|
||||||
|
if(f->deleted)
|
||||||
|
return;
|
||||||
|
if(fileisdirty(f) && !f->closeok){
|
||||||
|
f->closeok = TRUE;
|
||||||
|
if(f->name.s[0]){
|
||||||
|
t = Strtoc(&f->name);
|
||||||
|
strncpy(buf, t, sizeof buf-1);
|
||||||
|
free(t);
|
||||||
|
}else
|
||||||
|
strcpy(buf, "nameless file");
|
||||||
|
error_s(Emodified, buf);
|
||||||
|
}
|
||||||
|
f->deleted = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
trytoquit(void)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
File *f;
|
||||||
|
|
||||||
|
if(!quitok){
|
||||||
|
for(c = 0; c<file.nused; c++){
|
||||||
|
f = file.filepptr[c];
|
||||||
|
if(f!=cmd && fileisdirty(f)){
|
||||||
|
quitok = TRUE;
|
||||||
|
eof = FALSE;
|
||||||
|
error(Echanges);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
load(File *f)
|
||||||
|
{
|
||||||
|
Address saveaddr;
|
||||||
|
|
||||||
|
Strduplstr(&genstr, &f->name);
|
||||||
|
filename(f);
|
||||||
|
if(f->name.s[0]){
|
||||||
|
saveaddr = addr;
|
||||||
|
edit(f, 'I');
|
||||||
|
addr = saveaddr;
|
||||||
|
}else{
|
||||||
|
f->unread = 0;
|
||||||
|
f->cleanseq = f->seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileupdate(f, TRUE, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cmdupdate(void)
|
||||||
|
{
|
||||||
|
if(cmd && cmd->seq!=0){
|
||||||
|
fileupdate(cmd, FALSE, downloaded);
|
||||||
|
cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->b.nc;
|
||||||
|
telldot(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
delete(File *f)
|
||||||
|
{
|
||||||
|
if(downloaded && f->rasp)
|
||||||
|
outTs(Hclose, f->tag);
|
||||||
|
delfile(f);
|
||||||
|
if(f == curfile)
|
||||||
|
current(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
update(void)
|
||||||
|
{
|
||||||
|
int i, anymod;
|
||||||
|
File *f;
|
||||||
|
|
||||||
|
settempfile();
|
||||||
|
for(anymod = i=0; i<tempfile.nused; i++){
|
||||||
|
f = tempfile.filepptr[i];
|
||||||
|
if(f==cmd) /* cmd gets done in main() */
|
||||||
|
continue;
|
||||||
|
if(f->deleted) {
|
||||||
|
delete(f);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(f->seq==seq && fileupdate(f, FALSE, downloaded))
|
||||||
|
anymod++;
|
||||||
|
if(f->rasp)
|
||||||
|
telldot(f);
|
||||||
|
}
|
||||||
|
if(anymod)
|
||||||
|
seq++;
|
||||||
|
}
|
||||||
|
|
||||||
|
File *
|
||||||
|
current(File *f)
|
||||||
|
{
|
||||||
|
return curfile = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
edit(File *f, int cmd)
|
||||||
|
{
|
||||||
|
int empty = TRUE;
|
||||||
|
Posn p;
|
||||||
|
int nulls;
|
||||||
|
|
||||||
|
if(cmd == 'r')
|
||||||
|
logdelete(f, addr.r.p1, addr.r.p2);
|
||||||
|
if(cmd=='e' || cmd=='I'){
|
||||||
|
logdelete(f, (Posn)0, f->b.nc);
|
||||||
|
addr.r.p2 = f->b.nc;
|
||||||
|
}else if(f->b.nc!=0 || (f->name.s[0] && Strcmp(&genstr, &f->name)!=0))
|
||||||
|
empty = FALSE;
|
||||||
|
if((io = open(genc, OREAD))<0) {
|
||||||
|
if (curfile && curfile->unread)
|
||||||
|
curfile->unread = FALSE;
|
||||||
|
error_r(Eopen, genc);
|
||||||
|
}
|
||||||
|
p = readio(f, &nulls, empty, TRUE);
|
||||||
|
closeio((cmd=='e' || cmd=='I')? -1 : p);
|
||||||
|
if(cmd == 'r')
|
||||||
|
f->ndot.r.p1 = addr.r.p2, f->ndot.r.p2 = addr.r.p2+p;
|
||||||
|
else
|
||||||
|
f->ndot.r.p1 = f->ndot.r.p2 = 0;
|
||||||
|
f->closeok = empty;
|
||||||
|
if (quitok)
|
||||||
|
quitok = empty;
|
||||||
|
else
|
||||||
|
quitok = FALSE;
|
||||||
|
state(f, empty && !nulls? Clean : Dirty);
|
||||||
|
if(empty && !nulls)
|
||||||
|
f->cleanseq = f->seq;
|
||||||
|
if(cmd == 'e')
|
||||||
|
filename(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
getname(File *f, String *s, int save)
|
||||||
|
{
|
||||||
|
int c, i;
|
||||||
|
|
||||||
|
Strzero(&genstr);
|
||||||
|
if(genc){
|
||||||
|
free(genc);
|
||||||
|
genc = 0;
|
||||||
|
}
|
||||||
|
if(s==0 || (c = s->s[0])==0){ /* no name provided */
|
||||||
|
if(f)
|
||||||
|
Strduplstr(&genstr, &f->name);
|
||||||
|
goto Return;
|
||||||
|
}
|
||||||
|
if(c!=' ' && c!='\t')
|
||||||
|
error(Eblank);
|
||||||
|
for(i=0; (c=s->s[i])==' ' || c=='\t'; i++)
|
||||||
|
;
|
||||||
|
while(s->s[i] > ' ')
|
||||||
|
Straddc(&genstr, s->s[i++]);
|
||||||
|
if(s->s[i])
|
||||||
|
error(Enewline);
|
||||||
|
fixname(&genstr);
|
||||||
|
if(f && (save || f->name.s[0]==0)){
|
||||||
|
logsetname(f, &genstr);
|
||||||
|
if(Strcmp(&f->name, &genstr)){
|
||||||
|
quitok = f->closeok = FALSE;
|
||||||
|
f->qidpath = 0;
|
||||||
|
f->mtime = 0;
|
||||||
|
state(f, Dirty); /* if it's 'e', fix later */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Return:
|
||||||
|
genc = Strtoc(&genstr);
|
||||||
|
i = genstr.n;
|
||||||
|
if(i && genstr.s[i-1]==0)
|
||||||
|
i--;
|
||||||
|
return i; /* strlen(name) */
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
filename(File *f)
|
||||||
|
{
|
||||||
|
if(genc)
|
||||||
|
free(genc);
|
||||||
|
genc = Strtoc(&genstr);
|
||||||
|
dprint("%c%c%c %s\n", " '"[f->mod],
|
||||||
|
"-+"[f->rasp!=0], " ."[f==curfile], genc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
undostep(File *f, int isundo)
|
||||||
|
{
|
||||||
|
uint p1, p2;
|
||||||
|
int mod;
|
||||||
|
|
||||||
|
mod = f->mod;
|
||||||
|
fileundo(f, isundo, 1, &p1, &p2, TRUE);
|
||||||
|
f->ndot = f->dot;
|
||||||
|
if(f->mod){
|
||||||
|
f->closeok = 0;
|
||||||
|
quitok = 0;
|
||||||
|
}else
|
||||||
|
f->closeok = 1;
|
||||||
|
|
||||||
|
if(f->mod != mod){
|
||||||
|
f->mod = mod;
|
||||||
|
if(mod)
|
||||||
|
mod = Clean;
|
||||||
|
else
|
||||||
|
mod = Dirty;
|
||||||
|
state(f, mod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
undo(int isundo)
|
||||||
|
{
|
||||||
|
File *f;
|
||||||
|
int i;
|
||||||
|
Mod max;
|
||||||
|
|
||||||
|
max = undoseq(curfile, isundo);
|
||||||
|
if(max == 0)
|
||||||
|
return 0;
|
||||||
|
settempfile();
|
||||||
|
for(i = 0; i<tempfile.nused; i++){
|
||||||
|
f = tempfile.filepptr[i];
|
||||||
|
if(f!=cmd && undoseq(f, isundo)==max)
|
||||||
|
undostep(f, isundo);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
readcmd(String *s)
|
||||||
|
{
|
||||||
|
int retcode;
|
||||||
|
|
||||||
|
if(flist != 0)
|
||||||
|
fileclose(flist);
|
||||||
|
flist = fileopen();
|
||||||
|
|
||||||
|
addr.r.p1 = 0, addr.r.p2 = flist->b.nc;
|
||||||
|
retcode = plan9(flist, '<', s, FALSE);
|
||||||
|
fileupdate(flist, FALSE, FALSE);
|
||||||
|
flist->seq = 0;
|
||||||
|
if (flist->b.nc > BLOCKSIZE)
|
||||||
|
error(Etoolong);
|
||||||
|
Strzero(&genstr);
|
||||||
|
Strinsure(&genstr, flist->b.nc);
|
||||||
|
bufread(&flist->b, (Posn)0, genbuf, flist->b.nc);
|
||||||
|
memmove(genstr.s, genbuf, flist->b.nc*RUNESIZE);
|
||||||
|
genstr.n = flist->b.nc;
|
||||||
|
Straddc(&genstr, '\0');
|
||||||
|
return retcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
getcurwd(void)
|
||||||
|
{
|
||||||
|
String *t;
|
||||||
|
char buf[256];
|
||||||
|
|
||||||
|
buf[0] = 0;
|
||||||
|
getwd(buf, sizeof(buf));
|
||||||
|
t = tmpcstr(buf);
|
||||||
|
Strduplstr(&curwd, t);
|
||||||
|
freetmpstr(t);
|
||||||
|
if(curwd.n == 0)
|
||||||
|
warn(Wpwd);
|
||||||
|
else if(curwd.s[curwd.n-1] != '/')
|
||||||
|
Straddc(&curwd, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cd(String *str)
|
||||||
|
{
|
||||||
|
int i, fd;
|
||||||
|
char *s;
|
||||||
|
File *f;
|
||||||
|
String owd;
|
||||||
|
|
||||||
|
getcurwd();
|
||||||
|
if(getname((File *)0, str, FALSE))
|
||||||
|
s = genc;
|
||||||
|
else
|
||||||
|
s = home;
|
||||||
|
if(chdir(s))
|
||||||
|
syserror("chdir");
|
||||||
|
fd = open("/dev/wdir", OWRITE);
|
||||||
|
if(fd > 0)
|
||||||
|
write(fd, s, strlen(s));
|
||||||
|
dprint("!\n");
|
||||||
|
Strinit(&owd);
|
||||||
|
Strduplstr(&owd, &curwd);
|
||||||
|
getcurwd();
|
||||||
|
settempfile();
|
||||||
|
/*
|
||||||
|
* Two passes so that if we have open
|
||||||
|
* /a/foo.c and /b/foo.c and cd from /b to /a,
|
||||||
|
* we don't ever have two foo.c simultaneously.
|
||||||
|
*/
|
||||||
|
for(i=0; i<tempfile.nused; i++){
|
||||||
|
f = tempfile.filepptr[i];
|
||||||
|
if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){
|
||||||
|
Strinsert(&f->name, &owd, (Posn)0);
|
||||||
|
fixname(&f->name);
|
||||||
|
sortname(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(i=0; i<tempfile.nused; i++){
|
||||||
|
f = tempfile.filepptr[i];
|
||||||
|
if(f != cmd && Strispre(&curwd, &f->name)){
|
||||||
|
fixname(&f->name);
|
||||||
|
sortname(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Strclose(&owd);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
loadflist(String *s)
|
||||||
|
{
|
||||||
|
int c, i;
|
||||||
|
|
||||||
|
c = s->s[0];
|
||||||
|
for(i = 0; s->s[i]==' ' || s->s[i]=='\t'; i++)
|
||||||
|
;
|
||||||
|
if((c==' ' || c=='\t') && s->s[i]!='\n'){
|
||||||
|
if(s->s[i]=='<'){
|
||||||
|
Strdelete(s, 0L, (long)i+1);
|
||||||
|
readcmd(s);
|
||||||
|
}else{
|
||||||
|
Strzero(&genstr);
|
||||||
|
while((c = s->s[i++]) && c!='\n')
|
||||||
|
Straddc(&genstr, c);
|
||||||
|
Straddc(&genstr, '\0');
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(c != '\n')
|
||||||
|
error(Eblank);
|
||||||
|
Strdupl(&genstr, empty);
|
||||||
|
}
|
||||||
|
if(genc)
|
||||||
|
free(genc);
|
||||||
|
genc = Strtoc(&genstr);
|
||||||
|
return genstr.s[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
File *
|
||||||
|
readflist(int readall, int delete)
|
||||||
|
{
|
||||||
|
Posn i;
|
||||||
|
int c;
|
||||||
|
File *f;
|
||||||
|
String t;
|
||||||
|
|
||||||
|
Strinit(&t);
|
||||||
|
for(i=0,f=0; f==0 || readall || delete; i++){ /* ++ skips blank */
|
||||||
|
Strdelete(&genstr, (Posn)0, i);
|
||||||
|
for(i=0; (c = genstr.s[i])==' ' || c=='\t' || c=='\n'; i++)
|
||||||
|
;
|
||||||
|
if(i >= genstr.n)
|
||||||
|
break;
|
||||||
|
Strdelete(&genstr, (Posn)0, i);
|
||||||
|
for(i=0; (c=genstr.s[i]) && c!=' ' && c!='\t' && c!='\n'; i++)
|
||||||
|
;
|
||||||
|
|
||||||
|
if(i == 0)
|
||||||
|
break;
|
||||||
|
genstr.s[i] = 0;
|
||||||
|
Strduplstr(&t, tmprstr(genstr.s, i+1));
|
||||||
|
fixname(&t);
|
||||||
|
f = lookfile(&t);
|
||||||
|
if(delete){
|
||||||
|
if(f == 0)
|
||||||
|
warn_S(Wfile, &t);
|
||||||
|
else
|
||||||
|
trytoclose(f);
|
||||||
|
}else if(f==0 && readall)
|
||||||
|
logsetname(f = newfile(), &t);
|
||||||
|
}
|
||||||
|
Strclose(&t);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
File *
|
||||||
|
tofile(String *s)
|
||||||
|
{
|
||||||
|
File *f;
|
||||||
|
|
||||||
|
if(s->s[0] != ' ')
|
||||||
|
error(Eblank);
|
||||||
|
if(loadflist(s) == 0){
|
||||||
|
f = lookfile(&genstr); /* empty string ==> nameless file */
|
||||||
|
if(f == 0)
|
||||||
|
error_s(Emenu, genc);
|
||||||
|
}else if((f=readflist(FALSE, FALSE)) == 0)
|
||||||
|
error_s(Emenu, genc);
|
||||||
|
return current(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
File *
|
||||||
|
getfile(String *s)
|
||||||
|
{
|
||||||
|
File *f;
|
||||||
|
|
||||||
|
if(loadflist(s) == 0)
|
||||||
|
logsetname(f = newfile(), &genstr);
|
||||||
|
else if((f=readflist(TRUE, FALSE)) == 0)
|
||||||
|
error(Eblank);
|
||||||
|
return current(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
closefiles(File *f, String *s)
|
||||||
|
{
|
||||||
|
if(s->s[0] == 0){
|
||||||
|
if(f == 0)
|
||||||
|
error(Enofile);
|
||||||
|
trytoclose(f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(s->s[0] != ' ')
|
||||||
|
error(Eblank);
|
||||||
|
if(loadflist(s) == 0)
|
||||||
|
error(Enewline);
|
||||||
|
readflist(FALSE, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
copy(File *f, Address addr2)
|
||||||
|
{
|
||||||
|
Posn p;
|
||||||
|
int ni;
|
||||||
|
for(p=addr.r.p1; p<addr.r.p2; p+=ni){
|
||||||
|
ni = addr.r.p2-p;
|
||||||
|
if(ni > BLOCKSIZE)
|
||||||
|
ni = BLOCKSIZE;
|
||||||
|
bufread(&f->b, p, genbuf, ni);
|
||||||
|
loginsert(addr2.f, addr2.r.p2, tmprstr(genbuf, ni)->s, ni);
|
||||||
|
}
|
||||||
|
addr2.f->ndot.r.p2 = addr2.r.p2+(f->dot.r.p2-f->dot.r.p1);
|
||||||
|
addr2.f->ndot.r.p1 = addr2.r.p2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
move(File *f, Address addr2)
|
||||||
|
{
|
||||||
|
if(addr.r.p2 <= addr2.r.p2){
|
||||||
|
logdelete(f, addr.r.p1, addr.r.p2);
|
||||||
|
copy(f, addr2);
|
||||||
|
}else if(addr.r.p1 >= addr2.r.p2){
|
||||||
|
copy(f, addr2);
|
||||||
|
logdelete(f, addr.r.p1, addr.r.p2);
|
||||||
|
}else
|
||||||
|
error(Eoverlap);
|
||||||
|
}
|
||||||
|
|
||||||
|
Posn
|
||||||
|
nlcount(File *f, Posn p0, Posn p1)
|
||||||
|
{
|
||||||
|
Posn nl = 0;
|
||||||
|
|
||||||
|
while(p0 < p1)
|
||||||
|
if(filereadc(f, p0++)=='\n')
|
||||||
|
nl++;
|
||||||
|
return nl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
printposn(File *f, int charsonly)
|
||||||
|
{
|
||||||
|
Posn l1, l2;
|
||||||
|
|
||||||
|
if(!charsonly){
|
||||||
|
l1 = 1+nlcount(f, (Posn)0, addr.r.p1);
|
||||||
|
l2 = l1+nlcount(f, addr.r.p1, addr.r.p2);
|
||||||
|
/* check if addr ends with '\n' */
|
||||||
|
if(addr.r.p2>0 && addr.r.p2>addr.r.p1 && filereadc(f, addr.r.p2-1)=='\n')
|
||||||
|
--l2;
|
||||||
|
dprint("%lud", l1);
|
||||||
|
if(l2 != l1)
|
||||||
|
dprint(",%lud", l2);
|
||||||
|
dprint("; ");
|
||||||
|
}
|
||||||
|
dprint("#%lud", addr.r.p1);
|
||||||
|
if(addr.r.p2 != addr.r.p1)
|
||||||
|
dprint(",#%lud", addr.r.p2);
|
||||||
|
dprint("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
settempfile(void)
|
||||||
|
{
|
||||||
|
if(tempfile.nalloc < file.nused){
|
||||||
|
if(tempfile.filepptr)
|
||||||
|
free(tempfile.filepptr);
|
||||||
|
tempfile.filepptr = emalloc(sizeof(File*)*file.nused);
|
||||||
|
tempfile.nalloc = file.nused;
|
||||||
|
}
|
||||||
|
memmove(tempfile.filepptr, file.filepptr, sizeof(File*)*file.nused);
|
||||||
|
tempfile.nused = file.nused;
|
||||||
|
}
|
408
sam/sam.h
Normal file
408
sam/sam.h
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
#include <plumb.h>
|
||||||
|
#include "errors.h"
|
||||||
|
|
||||||
|
#undef waitfor
|
||||||
|
#define waitfor samwaitfor
|
||||||
|
|
||||||
|
#undef warn
|
||||||
|
#define warn samwarn
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BLOCKSIZE is relatively small to keep memory consumption down.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define BLOCKSIZE 2048
|
||||||
|
#define RUNESIZE sizeof(Rune)
|
||||||
|
#define NDISC 5
|
||||||
|
#define NBUFFILES 3+2*NDISC /* plan 9+undo+snarf+NDISC*(transcript+buf) */
|
||||||
|
#define NSUBEXP 10
|
||||||
|
|
||||||
|
#define TRUE 1
|
||||||
|
#define FALSE 0
|
||||||
|
|
||||||
|
#undef INFINITY /* Darwin declares this as HUGE_VAL */
|
||||||
|
#define INFINITY 0x7FFFFFFFL
|
||||||
|
#define INCR 25
|
||||||
|
#define STRSIZE (2*BLOCKSIZE)
|
||||||
|
|
||||||
|
typedef long Posn; /* file position or address */
|
||||||
|
typedef ushort Mod; /* modification number */
|
||||||
|
|
||||||
|
typedef struct Address Address;
|
||||||
|
typedef struct Block Block;
|
||||||
|
typedef struct Buffer Buffer;
|
||||||
|
typedef struct Disk Disk;
|
||||||
|
typedef struct Discdesc Discdesc;
|
||||||
|
typedef struct File File;
|
||||||
|
typedef struct List List;
|
||||||
|
typedef struct Range Range;
|
||||||
|
typedef struct Rangeset Rangeset;
|
||||||
|
typedef struct String String;
|
||||||
|
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
Clean = ' ',
|
||||||
|
Dirty = '\'',
|
||||||
|
Unread = '-'
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Range
|
||||||
|
{
|
||||||
|
Posn p1, p2;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Rangeset
|
||||||
|
{
|
||||||
|
Range p[NSUBEXP];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Address
|
||||||
|
{
|
||||||
|
Range r;
|
||||||
|
File *f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct String
|
||||||
|
{
|
||||||
|
short n;
|
||||||
|
short size;
|
||||||
|
Rune *s;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct List /* code depends on a long being able to hold a pointer */
|
||||||
|
{
|
||||||
|
int type; /* 'p' for pointer, 'P' for Posn */
|
||||||
|
int nalloc;
|
||||||
|
int nused;
|
||||||
|
union{
|
||||||
|
void* listp;
|
||||||
|
void** voidp;
|
||||||
|
Posn* posnp;
|
||||||
|
String**stringp;
|
||||||
|
File** filep;
|
||||||
|
}g;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define listptr g.listp
|
||||||
|
#define voidpptr g.voidp
|
||||||
|
#define posnptr g.posnp
|
||||||
|
#define stringpptr g.stringp
|
||||||
|
#define filepptr g.filep
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
Blockincr = 256,
|
||||||
|
Maxblock = 8*1024,
|
||||||
|
|
||||||
|
BUFSIZE = Maxblock, /* size from fbufalloc() */
|
||||||
|
RBUFSIZE = BUFSIZE/sizeof(Rune)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
Null = '-',
|
||||||
|
Delete = 'd',
|
||||||
|
Insert = 'i',
|
||||||
|
Filename = 'f',
|
||||||
|
Dot = 'D',
|
||||||
|
Mark = 'm'
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Block
|
||||||
|
{
|
||||||
|
uint addr; /* disk address in bytes */
|
||||||
|
union {
|
||||||
|
uint n; /* number of used runes in block */
|
||||||
|
Block *next; /* pointer to next in free list */
|
||||||
|
} u;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Disk
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
uint addr; /* length of temp file */
|
||||||
|
Block *free[Maxblock/Blockincr+1];
|
||||||
|
};
|
||||||
|
|
||||||
|
Disk* diskinit(void);
|
||||||
|
Block* disknewblock(Disk*, uint);
|
||||||
|
void diskrelease(Disk*, Block*);
|
||||||
|
void diskread(Disk*, Block*, Rune*, uint);
|
||||||
|
void diskwrite(Disk*, Block**, Rune*, uint);
|
||||||
|
|
||||||
|
struct Buffer
|
||||||
|
{
|
||||||
|
uint nc;
|
||||||
|
Rune *c; /* cache */
|
||||||
|
uint cnc; /* bytes in cache */
|
||||||
|
uint cmax; /* size of allocated cache */
|
||||||
|
uint cq; /* position of cache */
|
||||||
|
int cdirty; /* cache needs to be written */
|
||||||
|
uint cbi; /* index of cache Block */
|
||||||
|
Block **bl; /* array of blocks */
|
||||||
|
uint nbl; /* number of blocks */
|
||||||
|
};
|
||||||
|
void bufinsert(Buffer*, uint, Rune*, uint);
|
||||||
|
void bufdelete(Buffer*, uint, uint);
|
||||||
|
uint bufload(Buffer*, uint, int, int*);
|
||||||
|
void bufread(Buffer*, uint, Rune*, uint);
|
||||||
|
void bufclose(Buffer*);
|
||||||
|
void bufreset(Buffer*);
|
||||||
|
|
||||||
|
struct File
|
||||||
|
{
|
||||||
|
Buffer b; /* the data */
|
||||||
|
Buffer delta; /* transcript of changes */
|
||||||
|
Buffer epsilon; /* inversion of delta for redo */
|
||||||
|
String name; /* name of associated file */
|
||||||
|
uvlong qidpath; /* of file when read */
|
||||||
|
uint mtime; /* of file when read */
|
||||||
|
int dev; /* of file when read */
|
||||||
|
int unread; /* file has not been read from disk */
|
||||||
|
|
||||||
|
long seq; /* if seq==0, File acts like Buffer */
|
||||||
|
long cleanseq; /* f->seq at last read/write of file */
|
||||||
|
int mod; /* file appears modified in menu */
|
||||||
|
char rescuing; /* sam exiting; this file unusable */
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// Text *curtext; /* most recently used associated text */
|
||||||
|
// Text **text; /* list of associated texts */
|
||||||
|
// int ntext;
|
||||||
|
// int dumpid; /* used in dumping zeroxed windows */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Posn hiposn; /* highest address touched this Mod */
|
||||||
|
Address dot; /* current position */
|
||||||
|
Address ndot; /* new current position after update */
|
||||||
|
Range tdot; /* what terminal thinks is current range */
|
||||||
|
Range mark; /* tagged spot in text (don't confuse with Mark) */
|
||||||
|
List *rasp; /* map of what terminal's got */
|
||||||
|
short tag; /* for communicating with terminal */
|
||||||
|
char closeok; /* ok to close file? */
|
||||||
|
char deleted; /* delete at completion of command */
|
||||||
|
Range prevdot; /* state before start of change */
|
||||||
|
Range prevmark;
|
||||||
|
long prevseq;
|
||||||
|
int prevmod;
|
||||||
|
};
|
||||||
|
/*File* fileaddtext(File*, Text*); */
|
||||||
|
void fileclose(File*);
|
||||||
|
void filedelete(File*, uint, uint);
|
||||||
|
/*void filedeltext(File*, Text*); */
|
||||||
|
void fileinsert(File*, uint, Rune*, uint);
|
||||||
|
uint fileload(File*, uint, int, int*);
|
||||||
|
void filemark(File*);
|
||||||
|
void filereset(File*);
|
||||||
|
void filesetname(File*, String*);
|
||||||
|
void fileundelete(File*, Buffer*, uint, uint);
|
||||||
|
void fileuninsert(File*, Buffer*, uint, uint);
|
||||||
|
void fileunsetname(File*, Buffer*);
|
||||||
|
void fileundo(File*, int, int, uint*, uint*, int);
|
||||||
|
int fileupdate(File*, int, int);
|
||||||
|
|
||||||
|
int filereadc(File*, uint);
|
||||||
|
File *fileopen(void);
|
||||||
|
void loginsert(File*, uint, Rune*, uint);
|
||||||
|
void logdelete(File*, uint, uint);
|
||||||
|
void logsetname(File*, String*);
|
||||||
|
int fileisdirty(File*);
|
||||||
|
long undoseq(File*, int);
|
||||||
|
long prevseq(Buffer*);
|
||||||
|
|
||||||
|
void raspload(File*);
|
||||||
|
void raspstart(File*);
|
||||||
|
void raspdelete(File*, uint, uint, int);
|
||||||
|
void raspinsert(File*, uint, Rune*, uint, int);
|
||||||
|
void raspdone(File*, int);
|
||||||
|
void raspflush(File*);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* acme fns
|
||||||
|
*/
|
||||||
|
void* fbufalloc(void);
|
||||||
|
void fbuffree(void*);
|
||||||
|
uint min(uint, uint);
|
||||||
|
void cvttorunes(char*, int, Rune*, int*, int*, int*);
|
||||||
|
|
||||||
|
#define runemalloc(a) (Rune*)emalloc((a)*sizeof(Rune))
|
||||||
|
#define runerealloc(a, b) (Rune*)realloc((a), (b)*sizeof(Rune))
|
||||||
|
#define runemove(a, b, c) memmove((a), (b), (c)*sizeof(Rune))
|
||||||
|
|
||||||
|
int alnum(int);
|
||||||
|
int Read(int, void*, int);
|
||||||
|
void Seek(int, long, int);
|
||||||
|
int plan9(File*, int, String*, int);
|
||||||
|
int Write(int, void*, int);
|
||||||
|
int bexecute(File*, Posn);
|
||||||
|
void cd(String*);
|
||||||
|
void closefiles(File*, String*);
|
||||||
|
void closeio(Posn);
|
||||||
|
void cmdloop(void);
|
||||||
|
void cmdupdate(void);
|
||||||
|
void compile(String*);
|
||||||
|
void copy(File*, Address);
|
||||||
|
File *current(File*);
|
||||||
|
void delete(File*);
|
||||||
|
void delfile(File*);
|
||||||
|
void dellist(List*, int);
|
||||||
|
void doubleclick(File*, Posn);
|
||||||
|
void dprint(char*, ...);
|
||||||
|
void edit(File*, int);
|
||||||
|
void *emalloc(ulong);
|
||||||
|
void *erealloc(void*, ulong);
|
||||||
|
void error(Err);
|
||||||
|
void error_c(Err, int);
|
||||||
|
void error_r(Err, char*);
|
||||||
|
void error_s(Err, char*);
|
||||||
|
int execute(File*, Posn, Posn);
|
||||||
|
int filematch(File*, String*);
|
||||||
|
void filename(File*);
|
||||||
|
void fixname(String*);
|
||||||
|
void fullname(String*);
|
||||||
|
void getcurwd(void);
|
||||||
|
File *getfile(String*);
|
||||||
|
int getname(File*, String*, int);
|
||||||
|
long getnum(int);
|
||||||
|
void hiccough(char*);
|
||||||
|
void inslist(List*, int, ...);
|
||||||
|
Address lineaddr(Posn, Address, int);
|
||||||
|
List *listalloc(int);
|
||||||
|
void listfree(List*);
|
||||||
|
void load(File*);
|
||||||
|
File *lookfile(String*);
|
||||||
|
void lookorigin(File*, Posn, Posn);
|
||||||
|
int lookup(int);
|
||||||
|
void move(File*, Address);
|
||||||
|
void moveto(File*, Range);
|
||||||
|
File *newfile(void);
|
||||||
|
void nextmatch(File*, String*, Posn, int);
|
||||||
|
int newtmp(int);
|
||||||
|
void notifyf(void*, char*);
|
||||||
|
void panic(char*);
|
||||||
|
void printposn(File*, int);
|
||||||
|
void print_ss(char*, String*, String*);
|
||||||
|
void print_s(char*, String*);
|
||||||
|
int rcv(void);
|
||||||
|
Range rdata(List*, Posn, Posn);
|
||||||
|
Posn readio(File*, int*, int, int);
|
||||||
|
void rescue(void);
|
||||||
|
void resetcmd(void);
|
||||||
|
void resetsys(void);
|
||||||
|
void resetxec(void);
|
||||||
|
void rgrow(List*, Posn, Posn);
|
||||||
|
void samerr(char*);
|
||||||
|
void settempfile(void);
|
||||||
|
int skipbl(void);
|
||||||
|
void snarf(File*, Posn, Posn, Buffer*, int);
|
||||||
|
void sortname(File*);
|
||||||
|
void startup(char*, int, char**, char**);
|
||||||
|
void state(File*, int);
|
||||||
|
int statfd(int, ulong*, uvlong*, long*, long*, long*);
|
||||||
|
int statfile(char*, ulong*, uvlong*, long*, long*, long*);
|
||||||
|
void Straddc(String*, int);
|
||||||
|
void Strclose(String*);
|
||||||
|
int Strcmp(String*, String*);
|
||||||
|
void Strdelete(String*, Posn, Posn);
|
||||||
|
void Strdupl(String*, Rune*);
|
||||||
|
void Strduplstr(String*, String*);
|
||||||
|
void Strinit(String*);
|
||||||
|
void Strinit0(String*);
|
||||||
|
void Strinsert(String*, String*, Posn);
|
||||||
|
void Strinsure(String*, ulong);
|
||||||
|
int Strispre(String*, String*);
|
||||||
|
void Strzero(String*);
|
||||||
|
int Strlen(Rune*);
|
||||||
|
char *Strtoc(String*);
|
||||||
|
void syserror(char*);
|
||||||
|
void telldot(File*);
|
||||||
|
void tellpat(void);
|
||||||
|
String *tmpcstr(char*);
|
||||||
|
String *tmprstr(Rune*, int);
|
||||||
|
void freetmpstr(String*);
|
||||||
|
void termcommand(void);
|
||||||
|
void termwrite(char*);
|
||||||
|
File *tofile(String*);
|
||||||
|
void trytoclose(File*);
|
||||||
|
void trytoquit(void);
|
||||||
|
int undo(int);
|
||||||
|
void update(void);
|
||||||
|
int waitfor(int);
|
||||||
|
void warn(Warn);
|
||||||
|
void warn_s(Warn, char*);
|
||||||
|
void warn_SS(Warn, String*, String*);
|
||||||
|
void warn_S(Warn, String*);
|
||||||
|
int whichmenu(File*);
|
||||||
|
void writef(File*);
|
||||||
|
Posn writeio(File*);
|
||||||
|
Discdesc *Dstart(void);
|
||||||
|
|
||||||
|
extern Rune samname[]; /* compiler dependent */
|
||||||
|
extern Rune *left[];
|
||||||
|
extern Rune *right[];
|
||||||
|
|
||||||
|
extern char RSAM[]; /* system dependent */
|
||||||
|
extern char SAMTERM[];
|
||||||
|
extern char HOME[];
|
||||||
|
extern char TMPDIR[];
|
||||||
|
extern char SH[];
|
||||||
|
extern char SHPATH[];
|
||||||
|
extern char RX[];
|
||||||
|
extern char RXPATH[];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* acme globals
|
||||||
|
*/
|
||||||
|
extern long seq;
|
||||||
|
extern Disk *disk;
|
||||||
|
|
||||||
|
extern char *rsamname; /* globals */
|
||||||
|
extern char *samterm;
|
||||||
|
extern Rune genbuf[];
|
||||||
|
extern char *genc;
|
||||||
|
extern int io;
|
||||||
|
extern int patset;
|
||||||
|
extern int quitok;
|
||||||
|
extern Address addr;
|
||||||
|
extern Buffer snarfbuf;
|
||||||
|
extern Buffer plan9buf;
|
||||||
|
extern List file;
|
||||||
|
extern List tempfile;
|
||||||
|
extern File *cmd;
|
||||||
|
extern File *curfile;
|
||||||
|
extern File *lastfile;
|
||||||
|
extern Mod modnum;
|
||||||
|
extern Posn cmdpt;
|
||||||
|
extern Posn cmdptadv;
|
||||||
|
extern Rangeset sel;
|
||||||
|
extern String curwd;
|
||||||
|
extern String cmdstr;
|
||||||
|
extern String genstr;
|
||||||
|
extern String lastpat;
|
||||||
|
extern String lastregexp;
|
||||||
|
extern String plan9cmd;
|
||||||
|
extern int downloaded;
|
||||||
|
extern int eof;
|
||||||
|
extern int bpipeok;
|
||||||
|
extern int panicking;
|
||||||
|
extern Rune empty[];
|
||||||
|
extern int termlocked;
|
||||||
|
extern int outbuffered;
|
||||||
|
|
||||||
|
#include "mesg.h"
|
||||||
|
|
||||||
|
void outTs(Hmesg, int);
|
||||||
|
void outT0(Hmesg);
|
||||||
|
void outTl(Hmesg, long);
|
||||||
|
void outTslS(Hmesg, int, long, String*);
|
||||||
|
void outTS(Hmesg, String*);
|
||||||
|
void outTsS(Hmesg, int, String*);
|
||||||
|
void outTsllS(Hmesg, int, long, long, String*);
|
||||||
|
void outTsll(Hmesg, int, long, long);
|
||||||
|
void outTsl(Hmesg, int, long);
|
||||||
|
void outTsv(Hmesg, int, vlong);
|
||||||
|
void outflush(void);
|
||||||
|
int needoutflush(void);
|
165
sam/shell.c
Normal file
165
sam/shell.c
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
#include "parse.h"
|
||||||
|
|
||||||
|
extern jmp_buf mainloop;
|
||||||
|
|
||||||
|
char errfile[64];
|
||||||
|
String plan9cmd; /* null terminated */
|
||||||
|
Buffer plan9buf;
|
||||||
|
void checkerrs(void);
|
||||||
|
|
||||||
|
void
|
||||||
|
setname(File *f)
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
if(f)
|
||||||
|
snprint(buf, sizeof buf, "%.*S", f->name.n, f->name.s);
|
||||||
|
else
|
||||||
|
buf[0] = 0;
|
||||||
|
putenv("samfile", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
plan9(File *f, int type, String *s, int nest)
|
||||||
|
{
|
||||||
|
long l;
|
||||||
|
int m;
|
||||||
|
int volatile pid;
|
||||||
|
int fd;
|
||||||
|
int retcode;
|
||||||
|
int pipe1[2], pipe2[2];
|
||||||
|
|
||||||
|
if(s->s[0]==0 && plan9cmd.s[0]==0)
|
||||||
|
error(Enocmd);
|
||||||
|
else if(s->s[0])
|
||||||
|
Strduplstr(&plan9cmd, s);
|
||||||
|
if(downloaded){
|
||||||
|
samerr(errfile);
|
||||||
|
remove(errfile);
|
||||||
|
}
|
||||||
|
if(type!='!' && pipe(pipe1)==-1)
|
||||||
|
error(Epipe);
|
||||||
|
if(type=='|')
|
||||||
|
snarf(f, addr.r.p1, addr.r.p2, &plan9buf, 1);
|
||||||
|
if((pid=fork()) == 0){
|
||||||
|
setname(f);
|
||||||
|
if(downloaded){ /* also put nasty fd's into errfile */
|
||||||
|
fd = create(errfile, 1, 0666L);
|
||||||
|
if(fd < 0)
|
||||||
|
fd = create("/dev/null", 1, 0666L);
|
||||||
|
dup(fd, 2);
|
||||||
|
close(fd);
|
||||||
|
/* 2 now points at err file */
|
||||||
|
if(type == '>')
|
||||||
|
dup(2, 1);
|
||||||
|
else if(type=='!'){
|
||||||
|
dup(2, 1);
|
||||||
|
fd = open("/dev/null", 0);
|
||||||
|
dup(fd, 0);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(type != '!') {
|
||||||
|
if(type=='<' || type=='|')
|
||||||
|
dup(pipe1[1], 1);
|
||||||
|
else if(type == '>')
|
||||||
|
dup(pipe1[0], 0);
|
||||||
|
close(pipe1[0]);
|
||||||
|
close(pipe1[1]);
|
||||||
|
}
|
||||||
|
if(type == '|'){
|
||||||
|
if(pipe(pipe2) == -1)
|
||||||
|
exits("pipe");
|
||||||
|
if((pid = fork())==0){
|
||||||
|
/*
|
||||||
|
* It's ok if we get SIGPIPE here
|
||||||
|
*/
|
||||||
|
close(pipe2[0]);
|
||||||
|
io = pipe2[1];
|
||||||
|
if(retcode=!setjmp(mainloop)){ /* assignment = */
|
||||||
|
char *c;
|
||||||
|
for(l = 0; l<plan9buf.nc; l+=m){
|
||||||
|
m = plan9buf.nc-l;
|
||||||
|
if(m>BLOCKSIZE-1)
|
||||||
|
m = BLOCKSIZE-1;
|
||||||
|
bufread(&plan9buf, l, genbuf, m);
|
||||||
|
genbuf[m] = 0;
|
||||||
|
c = Strtoc(tmprstr(genbuf, m+1));
|
||||||
|
Write(pipe2[1], c, strlen(c));
|
||||||
|
free(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exits(retcode? "error" : 0);
|
||||||
|
}
|
||||||
|
if(pid==-1){
|
||||||
|
fprint(2, "Can't fork?!\n");
|
||||||
|
exits("fork");
|
||||||
|
}
|
||||||
|
dup(pipe2[0], 0);
|
||||||
|
close(pipe2[0]);
|
||||||
|
close(pipe2[1]);
|
||||||
|
}
|
||||||
|
if(type=='<'){
|
||||||
|
close(0); /* so it won't read from terminal */
|
||||||
|
open("/dev/null", 0);
|
||||||
|
}
|
||||||
|
execl(SHPATH, SH, "-c", Strtoc(&plan9cmd), (char *)0);
|
||||||
|
exits("exec");
|
||||||
|
}
|
||||||
|
if(pid == -1)
|
||||||
|
error(Efork);
|
||||||
|
if(type=='<' || type=='|'){
|
||||||
|
int nulls;
|
||||||
|
if(downloaded && addr.r.p1 != addr.r.p2)
|
||||||
|
outTl(Hsnarflen, addr.r.p2-addr.r.p1);
|
||||||
|
snarf(f, addr.r.p1, addr.r.p2, &snarfbuf, 0);
|
||||||
|
logdelete(f, addr.r.p1, addr.r.p2);
|
||||||
|
close(pipe1[1]);
|
||||||
|
io = pipe1[0];
|
||||||
|
f->tdot.p1 = -1;
|
||||||
|
f->ndot.r.p2 = addr.r.p2+readio(f, &nulls, 0, FALSE);
|
||||||
|
f->ndot.r.p1 = addr.r.p2;
|
||||||
|
closeio((Posn)-1);
|
||||||
|
}else if(type=='>'){
|
||||||
|
close(pipe1[0]);
|
||||||
|
io = pipe1[1];
|
||||||
|
bpipeok = 1;
|
||||||
|
writeio(f);
|
||||||
|
bpipeok = 0;
|
||||||
|
closeio((Posn)-1);
|
||||||
|
}
|
||||||
|
retcode = waitfor(pid);
|
||||||
|
if(type=='|' || type=='<')
|
||||||
|
if(retcode!=0)
|
||||||
|
warn(Wbadstatus);
|
||||||
|
if(downloaded)
|
||||||
|
checkerrs();
|
||||||
|
if(!nest)
|
||||||
|
dprint("!\n");
|
||||||
|
return retcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
checkerrs(void)
|
||||||
|
{
|
||||||
|
char buf[BLOCKSIZE-10];
|
||||||
|
int f, n, nl;
|
||||||
|
char *p;
|
||||||
|
long l;
|
||||||
|
|
||||||
|
if(statfile(errfile, 0, 0, 0, &l, 0) > 0 && l != 0){
|
||||||
|
if((f=open(errfile, 0)) != -1){
|
||||||
|
if((n=read(f, buf, sizeof buf-1)) > 0){
|
||||||
|
for(nl=0,p=buf; nl<25 && p<&buf[n]; p++)
|
||||||
|
if(*p=='\n')
|
||||||
|
nl++;
|
||||||
|
*p = 0;
|
||||||
|
dprint("%s", buf);
|
||||||
|
if(p-buf < l-1)
|
||||||
|
dprint("(sam: more in %s)\n", errfile);
|
||||||
|
}
|
||||||
|
close(f);
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
remove(errfile);
|
||||||
|
}
|
193
sam/string.c
Normal file
193
sam/string.c
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
|
||||||
|
#define MINSIZE 16 /* minimum number of chars allocated */
|
||||||
|
#define MAXSIZE 256 /* maximum number of chars for an empty string */
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Strinit(String *p)
|
||||||
|
{
|
||||||
|
p->s = emalloc(MINSIZE*RUNESIZE);
|
||||||
|
p->n = 0;
|
||||||
|
p->size = MINSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Strinit0(String *p)
|
||||||
|
{
|
||||||
|
p->s = emalloc(MINSIZE*RUNESIZE);
|
||||||
|
p->s[0] = 0;
|
||||||
|
p->n = 1;
|
||||||
|
p->size = MINSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Strclose(String *p)
|
||||||
|
{
|
||||||
|
free(p->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Strzero(String *p)
|
||||||
|
{
|
||||||
|
if(p->size > MAXSIZE){
|
||||||
|
p->s = erealloc(p->s, RUNESIZE*MAXSIZE); /* throw away the garbage */
|
||||||
|
p->size = MAXSIZE;
|
||||||
|
}
|
||||||
|
p->n = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Strlen(Rune *r)
|
||||||
|
{
|
||||||
|
Rune *s;
|
||||||
|
|
||||||
|
for(s=r; *s; s++)
|
||||||
|
;
|
||||||
|
return s-r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Strdupl(String *p, Rune *s) /* copies the null */
|
||||||
|
{
|
||||||
|
p->n = Strlen(s)+1;
|
||||||
|
Strinsure(p, p->n);
|
||||||
|
memmove(p->s, s, p->n*RUNESIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Strduplstr(String *p, String *q) /* will copy the null if there's one there */
|
||||||
|
{
|
||||||
|
Strinsure(p, q->n);
|
||||||
|
p->n = q->n;
|
||||||
|
memmove(p->s, q->s, q->n*RUNESIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Straddc(String *p, int c)
|
||||||
|
{
|
||||||
|
Strinsure(p, p->n+1);
|
||||||
|
p->s[p->n++] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Strinsure(String *p, ulong n)
|
||||||
|
{
|
||||||
|
if(n > STRSIZE)
|
||||||
|
error(Etoolong);
|
||||||
|
if(p->size < n){ /* p needs to grow */
|
||||||
|
n += 100;
|
||||||
|
p->s = erealloc(p->s, n*RUNESIZE);
|
||||||
|
p->size = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Strinsert(String *p, String *q, Posn p0)
|
||||||
|
{
|
||||||
|
Strinsure(p, p->n+q->n);
|
||||||
|
memmove(p->s+p0+q->n, p->s+p0, (p->n-p0)*RUNESIZE);
|
||||||
|
memmove(p->s+p0, q->s, q->n*RUNESIZE);
|
||||||
|
p->n += q->n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Strdelete(String *p, Posn p1, Posn p2)
|
||||||
|
{
|
||||||
|
memmove(p->s+p1, p->s+p2, (p->n-p2)*RUNESIZE);
|
||||||
|
p->n -= p2-p1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Strcmp(String *a, String *b)
|
||||||
|
{
|
||||||
|
int i, c;
|
||||||
|
|
||||||
|
for(i=0; i<a->n && i<b->n; i++)
|
||||||
|
if(c = (a->s[i] - b->s[i])) /* assign = */
|
||||||
|
return c;
|
||||||
|
/* damn NULs confuse everything */
|
||||||
|
i = a->n - b->n;
|
||||||
|
if(i == 1){
|
||||||
|
if(a->s[a->n-1] == 0)
|
||||||
|
return 0;
|
||||||
|
}else if(i == -1){
|
||||||
|
if(b->s[b->n-1] == 0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Strispre(String *a, String *b)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; i<a->n && i<b->n; i++){
|
||||||
|
if(a->s[i] - b->s[i]){ /* assign = */
|
||||||
|
if(a->s[i] == 0)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i == a->n;
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
Strtoc(String *s)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *c, *d;
|
||||||
|
Rune *r;
|
||||||
|
c = emalloc(s->n*UTFmax + 1); /* worst case UTFmax bytes per rune, plus NUL */
|
||||||
|
d = c;
|
||||||
|
r = s->s;
|
||||||
|
for(i=0; i<s->n; i++)
|
||||||
|
d += runetochar(d, r++);
|
||||||
|
if(d==c || d[-1]!=0)
|
||||||
|
*d = 0;
|
||||||
|
return c;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build very temporary String from Rune*
|
||||||
|
*/
|
||||||
|
String*
|
||||||
|
tmprstr(Rune *r, int n)
|
||||||
|
{
|
||||||
|
static String p;
|
||||||
|
|
||||||
|
p.s = r;
|
||||||
|
p.n = n;
|
||||||
|
p.size = n;
|
||||||
|
return &p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert null-terminated char* into String
|
||||||
|
*/
|
||||||
|
String*
|
||||||
|
tmpcstr(char *s)
|
||||||
|
{
|
||||||
|
String *p;
|
||||||
|
Rune *r;
|
||||||
|
int i, n;
|
||||||
|
|
||||||
|
n = utflen(s); /* don't include NUL */
|
||||||
|
p = emalloc(sizeof(String));
|
||||||
|
r = emalloc(n*RUNESIZE);
|
||||||
|
p->s = r;
|
||||||
|
for(i=0; i<n; i++,r++)
|
||||||
|
s += chartorune(r, s);
|
||||||
|
p->n = n;
|
||||||
|
p->size = n;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
freetmpstr(String *s)
|
||||||
|
{
|
||||||
|
free(s->s);
|
||||||
|
free(s);
|
||||||
|
}
|
60
sam/sys.c
Normal file
60
sam/sys.c
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
|
||||||
|
static int inerror=FALSE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A reasonable interface to the system calls
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
resetsys(void)
|
||||||
|
{
|
||||||
|
inerror = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
syserror(char *a)
|
||||||
|
{
|
||||||
|
char buf[ERRMAX];
|
||||||
|
|
||||||
|
if(!inerror){
|
||||||
|
inerror=TRUE;
|
||||||
|
errstr(buf, sizeof buf);
|
||||||
|
dprint("%s: ", a);
|
||||||
|
error_s(Eio, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Read(int f, void *a, int n)
|
||||||
|
{
|
||||||
|
char buf[ERRMAX];
|
||||||
|
|
||||||
|
if(read(f, (char *)a, n)!=n) {
|
||||||
|
if (lastfile)
|
||||||
|
lastfile->rescuing = 1;
|
||||||
|
errstr(buf, sizeof buf);
|
||||||
|
if (downloaded)
|
||||||
|
fprint(2, "read error: %s\n", buf);
|
||||||
|
rescue();
|
||||||
|
exits("read");
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Write(int f, void *a, int n)
|
||||||
|
{
|
||||||
|
int m;
|
||||||
|
|
||||||
|
if((m=write(f, (char *)a, n))!=n)
|
||||||
|
syserror("write");
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Seek(int f, long n, int w)
|
||||||
|
{
|
||||||
|
if(seek(f, n, w)==-1)
|
||||||
|
syserror("seek");
|
||||||
|
}
|
222
sam/unix.c
Normal file
222
sam/unix.c
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
#include <u.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "sam.h"
|
||||||
|
|
||||||
|
Rune samname[] = { '~', '~', 's', 'a', 'm', '~', '~', 0 };
|
||||||
|
|
||||||
|
static Rune l1[] = { '{', '[', '(', '<', 0253, 0};
|
||||||
|
static Rune l2[] = { '\n', 0};
|
||||||
|
static Rune l3[] = { '\'', '"', '`', 0};
|
||||||
|
Rune *left[]= { l1, l2, l3, 0};
|
||||||
|
|
||||||
|
static Rune r1[] = {'}', ']', ')', '>', 0273, 0};
|
||||||
|
static Rune r2[] = {'\n', 0};
|
||||||
|
static Rune r3[] = {'\'', '"', '`', 0};
|
||||||
|
Rune *right[]= { r1, r2, r3, 0};
|
||||||
|
|
||||||
|
#ifndef SAMTERMNAME
|
||||||
|
#define SAMTERMNAME "samterm"
|
||||||
|
#endif
|
||||||
|
#ifndef TMPDIRNAME
|
||||||
|
#define TMPDIRNAME "/tmp"
|
||||||
|
#endif
|
||||||
|
#ifndef SHNAME
|
||||||
|
#define SHNAME "sh"
|
||||||
|
#endif
|
||||||
|
#ifndef SHPATHNAME
|
||||||
|
#define SHPATHNAME "/bin/sh"
|
||||||
|
#endif
|
||||||
|
#ifndef RXNAME
|
||||||
|
#define RXNAME "ssh"
|
||||||
|
#endif
|
||||||
|
#ifndef RXPATHNAME
|
||||||
|
#define RXPATHNAME "ssh"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char RSAM[] = "sam";
|
||||||
|
char SAMTERM[] = SAMTERMNAME;
|
||||||
|
char HOME[] = "HOME";
|
||||||
|
char TMPDIR[] = TMPDIRNAME;
|
||||||
|
char SH[] = SHNAME;
|
||||||
|
char SHPATH[] = SHPATHNAME;
|
||||||
|
char RX[] = RXNAME;
|
||||||
|
char RXPATH[] = RXPATHNAME;
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
dprint(char *z, ...)
|
||||||
|
{
|
||||||
|
char buf[BLOCKSIZE];
|
||||||
|
va_list arg;
|
||||||
|
|
||||||
|
va_start(arg, z);
|
||||||
|
vseprint(buf, &buf[BLOCKSIZE], z, arg);
|
||||||
|
va_end(arg);
|
||||||
|
termwrite(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
print_ss(char *s, String *a, String *b)
|
||||||
|
{
|
||||||
|
dprint("?warning: %s: `%.*S' and `%.*S'\n", s, a->n, a->s, b->n, b->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
print_s(char *s, String *a)
|
||||||
|
{
|
||||||
|
dprint("?warning: %s `%.*S'\n", s, a->n, a->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
getuser(void)
|
||||||
|
{
|
||||||
|
static char user[64];
|
||||||
|
if(user[0] == 0){
|
||||||
|
struct passwd *pw = getpwuid(getuid());
|
||||||
|
strcpy(user, pw ? pw->pw_name : "nobody");
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
statfile(char *name, ulong *dev, uvlong *id, long *time, long *length, long *appendonly)
|
||||||
|
{
|
||||||
|
struct stat dirb;
|
||||||
|
|
||||||
|
if (stat(name, &dirb) == -1)
|
||||||
|
return -1;
|
||||||
|
if (dev)
|
||||||
|
*dev = dirb.st_dev;
|
||||||
|
if (id)
|
||||||
|
*id = dirb.st_ino;
|
||||||
|
if (time)
|
||||||
|
*time = dirb.st_mtime;
|
||||||
|
if (length)
|
||||||
|
*length = dirb.st_size;
|
||||||
|
if(appendonly)
|
||||||
|
*appendonly = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
statfd(int fd, ulong *dev, uvlong *id, long *time, long *length, long *appendonly)
|
||||||
|
{
|
||||||
|
struct stat dirb;
|
||||||
|
|
||||||
|
if (fstat(fd, &dirb) == -1)
|
||||||
|
return -1;
|
||||||
|
if (dev)
|
||||||
|
*dev = dirb.st_dev;
|
||||||
|
if (id)
|
||||||
|
*id = dirb.st_ino;
|
||||||
|
if (time)
|
||||||
|
*time = dirb.st_mtime;
|
||||||
|
if (length)
|
||||||
|
*length = dirb.st_size;
|
||||||
|
if(appendonly)
|
||||||
|
*appendonly = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hup(int sig)
|
||||||
|
{
|
||||||
|
panicking = 1; /* ??? */
|
||||||
|
rescue();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
notify(void(*f)(void *, char *))
|
||||||
|
{
|
||||||
|
signal(SIGINT, SIG_IGN);
|
||||||
|
signal(SIGPIPE, SIG_IGN); /* XXX - bpipeok? */
|
||||||
|
signal(SIGHUP, hup);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
notifyf(void *a, char *b) /* never called; hup is instead */
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
temp_file(char *buf, int bufsize)
|
||||||
|
{
|
||||||
|
char *tmp;
|
||||||
|
int n, fd;
|
||||||
|
|
||||||
|
tmp = getenv("TMPDIR");
|
||||||
|
if (!tmp)
|
||||||
|
tmp = TMPDIR;
|
||||||
|
|
||||||
|
n = snprint(buf, bufsize, "%s/sam.%d.XXXXXXX", tmp, getuid());
|
||||||
|
if (bufsize <= n)
|
||||||
|
return -1;
|
||||||
|
if ((fd = mkstemp(buf)) < 0) /* SES - linux sometimes uses mode 0666 */
|
||||||
|
return -1;
|
||||||
|
if (fcntl(fd, F_SETFD, fcntl(fd,F_GETFD,0) | FD_CLOEXEC) < 0)
|
||||||
|
return -1;
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
tempdisk(void)
|
||||||
|
{
|
||||||
|
char buf[4096];
|
||||||
|
int fd = temp_file(buf, sizeof buf);
|
||||||
|
if (fd >= 0)
|
||||||
|
remove(buf);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef waitfor
|
||||||
|
int
|
||||||
|
samwaitfor(int pid)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
Waitmsg *w;
|
||||||
|
|
||||||
|
w = p9waitfor(pid);
|
||||||
|
if(w == nil)
|
||||||
|
return -1;
|
||||||
|
r = atoi(w->msg);
|
||||||
|
free(w);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
samerr(char *buf)
|
||||||
|
{
|
||||||
|
sprint(buf, "%s/sam.%s.err", TMPDIR, getuser());
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
emalloc(ulong n)
|
||||||
|
{
|
||||||
|
void *p;
|
||||||
|
|
||||||
|
p = malloc(n);
|
||||||
|
if(p == 0)
|
||||||
|
panic("malloc fails");
|
||||||
|
memset(p, 0, n);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
erealloc(void *p, ulong n)
|
||||||
|
{
|
||||||
|
p = realloc(p, n);
|
||||||
|
if(p == 0)
|
||||||
|
panic("realloc fails");
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
54
sam/util.c
Normal file
54
sam/util.c
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls)
|
||||||
|
{
|
||||||
|
uchar *q;
|
||||||
|
Rune *s;
|
||||||
|
int j, w;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Always guaranteed that n bytes may be interpreted
|
||||||
|
* without worrying about partial runes. This may mean
|
||||||
|
* reading up to UTFmax-1 more bytes than n; the caller
|
||||||
|
* knows this. If n is a firm limit, the caller should
|
||||||
|
* set p[n] = 0.
|
||||||
|
*/
|
||||||
|
q = (uchar*)p;
|
||||||
|
s = r;
|
||||||
|
for(j=0; j<n; j+=w){
|
||||||
|
if(*q < Runeself){
|
||||||
|
w = 1;
|
||||||
|
*s = *q++;
|
||||||
|
}else{
|
||||||
|
w = chartorune(s, (char*)q);
|
||||||
|
q += w;
|
||||||
|
}
|
||||||
|
if(*s)
|
||||||
|
s++;
|
||||||
|
else if(nulls)
|
||||||
|
*nulls = TRUE;
|
||||||
|
}
|
||||||
|
*nb = (char*)q-p;
|
||||||
|
*nr = s-r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
fbufalloc(void)
|
||||||
|
{
|
||||||
|
return emalloc(BUFSIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fbuffree(void *f)
|
||||||
|
{
|
||||||
|
free(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint
|
||||||
|
min(uint a, uint b)
|
||||||
|
{
|
||||||
|
if(a < b)
|
||||||
|
return a;
|
||||||
|
return b;
|
||||||
|
}
|
508
sam/xec.c
Normal file
508
sam/xec.c
Normal file
@@ -0,0 +1,508 @@
|
|||||||
|
#include "sam.h"
|
||||||
|
#include "parse.h"
|
||||||
|
|
||||||
|
int Glooping;
|
||||||
|
int nest;
|
||||||
|
|
||||||
|
int append(File*, Cmd*, Posn);
|
||||||
|
int display(File*);
|
||||||
|
void looper(File*, Cmd*, int);
|
||||||
|
void filelooper(Cmd*, int);
|
||||||
|
void linelooper(File*, Cmd*);
|
||||||
|
|
||||||
|
void
|
||||||
|
resetxec(void)
|
||||||
|
{
|
||||||
|
Glooping = nest = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
cmdexec(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
Addr *ap;
|
||||||
|
Address a;
|
||||||
|
|
||||||
|
if(f && f->unread)
|
||||||
|
load(f);
|
||||||
|
if(f==0 && (cp->addr==0 || cp->addr->type!='"') &&
|
||||||
|
!utfrune("bBnqUXY!", cp->cmdc) &&
|
||||||
|
cp->cmdc!=('c'|0x100) && !(cp->cmdc=='D' && cp->ctext))
|
||||||
|
error(Enofile);
|
||||||
|
i = lookup(cp->cmdc);
|
||||||
|
if(i >= 0 && cmdtab[i].defaddr != aNo){
|
||||||
|
if((ap=cp->addr)==0 && cp->cmdc!='\n'){
|
||||||
|
cp->addr = ap = newaddr();
|
||||||
|
ap->type = '.';
|
||||||
|
if(cmdtab[i].defaddr == aAll)
|
||||||
|
ap->type = '*';
|
||||||
|
}else if(ap && ap->type=='"' && ap->next==0 && cp->cmdc!='\n'){
|
||||||
|
ap->next = newaddr();
|
||||||
|
ap->next->type = '.';
|
||||||
|
if(cmdtab[i].defaddr == aAll)
|
||||||
|
ap->next->type = '*';
|
||||||
|
}
|
||||||
|
if(cp->addr){ /* may be false for '\n' (only) */
|
||||||
|
static Address none = {0,0,0};
|
||||||
|
if(f)
|
||||||
|
addr = address(ap, f->dot, 0);
|
||||||
|
else /* a " */
|
||||||
|
addr = address(ap, none, 0);
|
||||||
|
f = addr.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current(f);
|
||||||
|
switch(cp->cmdc){
|
||||||
|
case '{':
|
||||||
|
a = cp->addr? address(cp->addr, f->dot, 0): f->dot;
|
||||||
|
for(cp = cp->ccmd; cp; cp = cp->next){
|
||||||
|
a.f->dot = a;
|
||||||
|
cmdexec(a.f, cp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
i=(*cmdtab[i].fn)(f, cp);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
a_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
return append(f, cp, addr.r.p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
b_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
USED(f);
|
||||||
|
f = cp->cmdc=='b'? tofile(cp->ctext) : getfile(cp->ctext);
|
||||||
|
if(f->unread)
|
||||||
|
load(f);
|
||||||
|
else if(nest == 0)
|
||||||
|
filename(f);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
c_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
logdelete(f, addr.r.p1, addr.r.p2);
|
||||||
|
f->ndot.r.p1 = f->ndot.r.p2 = addr.r.p2;
|
||||||
|
return append(f, cp, addr.r.p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
d_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
USED(cp);
|
||||||
|
logdelete(f, addr.r.p1, addr.r.p2);
|
||||||
|
f->ndot.r.p1 = f->ndot.r.p2 = addr.r.p1;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
D_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
closefiles(f, cp->ctext);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
e_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
if(getname(f, cp->ctext, cp->cmdc=='e')==0)
|
||||||
|
error(Enoname);
|
||||||
|
edit(f, cp->cmdc);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
f_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
getname(f, cp->ctext, TRUE);
|
||||||
|
filename(f);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
g_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
if(f!=addr.f)panic("g_cmd f!=addr.f");
|
||||||
|
compile(cp->re);
|
||||||
|
if(execute(f, addr.r.p1, addr.r.p2) ^ cp->cmdc=='v'){
|
||||||
|
f->dot = addr;
|
||||||
|
return cmdexec(f, cp->ccmd);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
i_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
return append(f, cp, addr.r.p1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
k_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
USED(cp);
|
||||||
|
f->mark = addr.r;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
m_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
Address addr2;
|
||||||
|
|
||||||
|
addr2 = address(cp->caddr, f->dot, 0);
|
||||||
|
if(cp->cmdc=='m')
|
||||||
|
move(f, addr2);
|
||||||
|
else
|
||||||
|
copy(f, addr2);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
n_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
USED(f);
|
||||||
|
USED(cp);
|
||||||
|
for(i = 0; i<file.nused; i++){
|
||||||
|
if(file.filepptr[i] == cmd)
|
||||||
|
continue;
|
||||||
|
f = file.filepptr[i];
|
||||||
|
Strduplstr(&genstr, &f->name);
|
||||||
|
filename(f);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
p_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
USED(cp);
|
||||||
|
return display(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
q_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
USED(cp);
|
||||||
|
USED(f);
|
||||||
|
trytoquit();
|
||||||
|
if(downloaded){
|
||||||
|
outT0(Hexit);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
s_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
int i, j, c, n;
|
||||||
|
Posn p1, op, didsub = 0, delta = 0;
|
||||||
|
|
||||||
|
n = cp->num;
|
||||||
|
op= -1;
|
||||||
|
compile(cp->re);
|
||||||
|
for(p1 = addr.r.p1; p1<=addr.r.p2 && execute(f, p1, addr.r.p2); ){
|
||||||
|
if(sel.p[0].p1==sel.p[0].p2){ /* empty match? */
|
||||||
|
if(sel.p[0].p1==op){
|
||||||
|
p1++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
p1 = sel.p[0].p2+1;
|
||||||
|
}else
|
||||||
|
p1 = sel.p[0].p2;
|
||||||
|
op = sel.p[0].p2;
|
||||||
|
if(--n>0)
|
||||||
|
continue;
|
||||||
|
Strzero(&genstr);
|
||||||
|
for(i = 0; i<cp->ctext->n; i++)
|
||||||
|
if((c = cp->ctext->s[i])=='\\' && i<cp->ctext->n-1){
|
||||||
|
c = cp->ctext->s[++i];
|
||||||
|
if('1'<=c && c<='9') {
|
||||||
|
j = c-'0';
|
||||||
|
if(sel.p[j].p2-sel.p[j].p1>BLOCKSIZE)
|
||||||
|
error(Elongtag);
|
||||||
|
bufread(&f->b, sel.p[j].p1, genbuf, sel.p[j].p2-sel.p[j].p1);
|
||||||
|
Strinsert(&genstr, tmprstr(genbuf, (sel.p[j].p2-sel.p[j].p1)), genstr.n);
|
||||||
|
}else
|
||||||
|
Straddc(&genstr, c);
|
||||||
|
}else if(c!='&')
|
||||||
|
Straddc(&genstr, c);
|
||||||
|
else{
|
||||||
|
if(sel.p[0].p2-sel.p[0].p1>BLOCKSIZE)
|
||||||
|
error(Elongrhs);
|
||||||
|
bufread(&f->b, sel.p[0].p1, genbuf, sel.p[0].p2-sel.p[0].p1);
|
||||||
|
Strinsert(&genstr,
|
||||||
|
tmprstr(genbuf, (int)(sel.p[0].p2-sel.p[0].p1)),
|
||||||
|
genstr.n);
|
||||||
|
}
|
||||||
|
if(sel.p[0].p1!=sel.p[0].p2){
|
||||||
|
logdelete(f, sel.p[0].p1, sel.p[0].p2);
|
||||||
|
delta-=sel.p[0].p2-sel.p[0].p1;
|
||||||
|
}
|
||||||
|
if(genstr.n){
|
||||||
|
loginsert(f, sel.p[0].p2, genstr.s, genstr.n);
|
||||||
|
delta+=genstr.n;
|
||||||
|
}
|
||||||
|
didsub = 1;
|
||||||
|
if(!cp->flag)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!didsub && nest==0)
|
||||||
|
error(Enosub);
|
||||||
|
f->ndot.r.p1 = addr.r.p1, f->ndot.r.p2 = addr.r.p2+delta;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
u_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
|
USED(f);
|
||||||
|
USED(cp);
|
||||||
|
n = cp->num;
|
||||||
|
if(n >= 0)
|
||||||
|
while(n-- && undo(TRUE))
|
||||||
|
;
|
||||||
|
else
|
||||||
|
while(n++ && undo(FALSE))
|
||||||
|
;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
w_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
int fseq;
|
||||||
|
|
||||||
|
fseq = f->seq;
|
||||||
|
if(getname(f, cp->ctext, FALSE)==0)
|
||||||
|
error(Enoname);
|
||||||
|
if(fseq == seq)
|
||||||
|
error_s(Ewseq, genc);
|
||||||
|
writef(f);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
x_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
if(cp->re)
|
||||||
|
looper(f, cp, cp->cmdc=='x');
|
||||||
|
else
|
||||||
|
linelooper(f, cp);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
X_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
USED(f);
|
||||||
|
filelooper(cp, cp->cmdc=='X');
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
plan9_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
plan9(f, cp->cmdc, cp->ctext, nest);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
eq_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
int charsonly;
|
||||||
|
|
||||||
|
switch(cp->ctext->n){
|
||||||
|
case 1:
|
||||||
|
charsonly = FALSE;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if(cp->ctext->s[0]=='#'){
|
||||||
|
charsonly = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
SET(charsonly);
|
||||||
|
error(Enewline);
|
||||||
|
}
|
||||||
|
printposn(f, charsonly);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nl_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
Address a;
|
||||||
|
|
||||||
|
if(cp->addr == 0){
|
||||||
|
/* First put it on newline boundaries */
|
||||||
|
addr = lineaddr((Posn)0, f->dot, -1);
|
||||||
|
a = lineaddr((Posn)0, f->dot, 1);
|
||||||
|
addr.r.p2 = a.r.p2;
|
||||||
|
if(addr.r.p1==f->dot.r.p1 && addr.r.p2==f->dot.r.p2)
|
||||||
|
addr = lineaddr((Posn)1, f->dot, 1);
|
||||||
|
display(f);
|
||||||
|
}else if(downloaded)
|
||||||
|
moveto(f, addr.r);
|
||||||
|
else
|
||||||
|
display(f);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
cd_cmd(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
USED(f);
|
||||||
|
cd(cp->ctext);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
append(File *f, Cmd *cp, Posn p)
|
||||||
|
{
|
||||||
|
if(cp->ctext->n>0 && cp->ctext->s[cp->ctext->n-1]==0)
|
||||||
|
--cp->ctext->n;
|
||||||
|
if(cp->ctext->n>0)
|
||||||
|
loginsert(f, p, cp->ctext->s, cp->ctext->n);
|
||||||
|
f->ndot.r.p1 = p;
|
||||||
|
f->ndot.r.p2 = p+cp->ctext->n;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
display(File *f)
|
||||||
|
{
|
||||||
|
Posn p1, p2;
|
||||||
|
int np;
|
||||||
|
char *c;
|
||||||
|
|
||||||
|
p1 = addr.r.p1;
|
||||||
|
p2 = addr.r.p2;
|
||||||
|
if(p2 > f->b.nc){
|
||||||
|
fprint(2, "bad display addr p1=%ld p2=%ld f->b.nc=%d\n", p1, p2, f->b.nc); /*ZZZ should never happen, can remove */
|
||||||
|
p2 = f->b.nc;
|
||||||
|
}
|
||||||
|
while(p1 < p2){
|
||||||
|
np = p2-p1;
|
||||||
|
if(np>BLOCKSIZE-1)
|
||||||
|
np = BLOCKSIZE-1;
|
||||||
|
bufread(&f->b, p1, genbuf, np);
|
||||||
|
genbuf[np] = 0;
|
||||||
|
c = Strtoc(tmprstr(genbuf, np+1));
|
||||||
|
if(downloaded)
|
||||||
|
termwrite(c);
|
||||||
|
else
|
||||||
|
Write(1, c, strlen(c));
|
||||||
|
free(c);
|
||||||
|
p1 += np;
|
||||||
|
}
|
||||||
|
f->dot = addr;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
looper(File *f, Cmd *cp, int xy)
|
||||||
|
{
|
||||||
|
Posn p, op;
|
||||||
|
Range r;
|
||||||
|
|
||||||
|
r = addr.r;
|
||||||
|
op= xy? -1 : r.p1;
|
||||||
|
nest++;
|
||||||
|
compile(cp->re);
|
||||||
|
for(p = r.p1; p<=r.p2; ){
|
||||||
|
if(!execute(f, p, r.p2)){ /* no match, but y should still run */
|
||||||
|
if(xy || op>r.p2)
|
||||||
|
break;
|
||||||
|
f->dot.r.p1 = op, f->dot.r.p2 = r.p2;
|
||||||
|
p = r.p2+1; /* exit next loop */
|
||||||
|
}else{
|
||||||
|
if(sel.p[0].p1==sel.p[0].p2){ /* empty match? */
|
||||||
|
if(sel.p[0].p1==op){
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
p = sel.p[0].p2+1;
|
||||||
|
}else
|
||||||
|
p = sel.p[0].p2;
|
||||||
|
if(xy)
|
||||||
|
f->dot.r = sel.p[0];
|
||||||
|
else
|
||||||
|
f->dot.r.p1 = op, f->dot.r.p2 = sel.p[0].p1;
|
||||||
|
}
|
||||||
|
op = sel.p[0].p2;
|
||||||
|
cmdexec(f, cp->ccmd);
|
||||||
|
compile(cp->re);
|
||||||
|
}
|
||||||
|
--nest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
linelooper(File *f, Cmd *cp)
|
||||||
|
{
|
||||||
|
Posn p;
|
||||||
|
Range r, linesel;
|
||||||
|
Address a, a3;
|
||||||
|
|
||||||
|
nest++;
|
||||||
|
r = addr.r;
|
||||||
|
a3.f = f;
|
||||||
|
a3.r.p1 = a3.r.p2 = r.p1;
|
||||||
|
for(p = r.p1; p<r.p2; p = a3.r.p2){
|
||||||
|
a3.r.p1 = a3.r.p2;
|
||||||
|
/*pjw if(p!=r.p1 || (linesel = lineaddr((Posn)0, a3, 1)).r.p2==p)*/
|
||||||
|
if(p!=r.p1 || (a = lineaddr((Posn)0, a3, 1), linesel = a.r, linesel.p2==p)){
|
||||||
|
a = lineaddr((Posn)1, a3, 1);
|
||||||
|
linesel = a.r;
|
||||||
|
}
|
||||||
|
if(linesel.p1 >= r.p2)
|
||||||
|
break;
|
||||||
|
if(linesel.p2 >= r.p2)
|
||||||
|
linesel.p2 = r.p2;
|
||||||
|
if(linesel.p2 > linesel.p1)
|
||||||
|
if(linesel.p1>=a3.r.p2 && linesel.p2>a3.r.p2){
|
||||||
|
f->dot.r = linesel;
|
||||||
|
cmdexec(f, cp->ccmd);
|
||||||
|
a3.r = linesel;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
--nest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
filelooper(Cmd *cp, int XY)
|
||||||
|
{
|
||||||
|
File *f, *cur;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(Glooping++)
|
||||||
|
error(EnestXY);
|
||||||
|
nest++;
|
||||||
|
settempfile();
|
||||||
|
cur = curfile;
|
||||||
|
for(i = 0; i<tempfile.nused; i++){
|
||||||
|
f = tempfile.filepptr[i];
|
||||||
|
if(f==cmd)
|
||||||
|
continue;
|
||||||
|
if(cp->re==0 || filematch(f, cp->re)==XY)
|
||||||
|
cmdexec(f, cp->ccmd);
|
||||||
|
}
|
||||||
|
if(cur && whichmenu(cur)>=0) /* check that cur is still a file */
|
||||||
|
current(cur);
|
||||||
|
--Glooping;
|
||||||
|
--nest;
|
||||||
|
}
|
Reference in New Issue
Block a user