From 76193d7cb0457807b2f0b95f909ab5de19480cd7 Mon Sep 17 00:00:00 2001 From: rsc Date: Tue, 30 Sep 2003 17:47:42 +0000 Subject: Initial revision --- src/cmd/sam/LICENSE | 258 ++++++++++++++++ src/cmd/sam/Makefile | 18 ++ src/cmd/sam/address.c | 240 +++++++++++++++ src/cmd/sam/buff.c | 302 +++++++++++++++++++ src/cmd/sam/cmd.c | 594 ++++++++++++++++++++++++++++++++++++ src/cmd/sam/disk.c | 122 ++++++++ src/cmd/sam/error.c | 144 +++++++++ src/cmd/sam/errors.h | 65 ++++ src/cmd/sam/file.c | 631 ++++++++++++++++++++++++++++++++++++++ src/cmd/sam/io.c | 262 ++++++++++++++++ src/cmd/sam/list.c | 47 +++ src/cmd/sam/mesg.c | 821 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/cmd/sam/mesg.h | 131 ++++++++ src/cmd/sam/mkfile | 40 +++ src/cmd/sam/moveto.c | 173 +++++++++++ src/cmd/sam/multi.c | 123 ++++++++ src/cmd/sam/parse.h | 68 +++++ src/cmd/sam/plan9.c | 185 ++++++++++++ src/cmd/sam/plumb.c | 9 + src/cmd/sam/rasp.c | 325 ++++++++++++++++++++ src/cmd/sam/regexp.c | 801 ++++++++++++++++++++++++++++++++++++++++++++++++ src/cmd/sam/sam | Bin 0 -> 267893 bytes src/cmd/sam/sam.c | 739 +++++++++++++++++++++++++++++++++++++++++++++ src/cmd/sam/sam.h | 407 +++++++++++++++++++++++++ src/cmd/sam/shell.c | 152 ++++++++++ src/cmd/sam/unix.c | 272 +++++++++++++++++ src/cmd/sam/xec.c | 508 +++++++++++++++++++++++++++++++ 27 files changed, 7437 insertions(+) create mode 100644 src/cmd/sam/LICENSE create mode 100644 src/cmd/sam/Makefile create mode 100644 src/cmd/sam/address.c create mode 100644 src/cmd/sam/buff.c create mode 100644 src/cmd/sam/cmd.c create mode 100644 src/cmd/sam/disk.c create mode 100644 src/cmd/sam/error.c create mode 100644 src/cmd/sam/errors.h create mode 100644 src/cmd/sam/file.c create mode 100644 src/cmd/sam/io.c create mode 100644 src/cmd/sam/list.c create mode 100644 src/cmd/sam/mesg.c create mode 100644 src/cmd/sam/mesg.h create mode 100644 src/cmd/sam/mkfile create mode 100644 src/cmd/sam/moveto.c create mode 100644 src/cmd/sam/multi.c create mode 100644 src/cmd/sam/parse.h create mode 100644 src/cmd/sam/plan9.c create mode 100644 src/cmd/sam/plumb.c create mode 100644 src/cmd/sam/rasp.c create mode 100644 src/cmd/sam/regexp.c create mode 100755 src/cmd/sam/sam create mode 100644 src/cmd/sam/sam.c create mode 100644 src/cmd/sam/sam.h create mode 100644 src/cmd/sam/shell.c create mode 100644 src/cmd/sam/unix.c create mode 100644 src/cmd/sam/xec.c (limited to 'src/cmd/sam') diff --git a/src/cmd/sam/LICENSE b/src/cmd/sam/LICENSE new file mode 100644 index 00000000..a5d7d87d --- /dev/null +++ b/src/cmd/sam/LICENSE @@ -0,0 +1,258 @@ +The Plan 9 software is provided under the terms of the +Lucent Public License, Version 1.02, reproduced below, +with the following exceptions: + +1. No right is granted to create derivative works of or + to redistribute (other than with the Plan 9 Operating System) + the screen imprinter fonts identified in subdirectory + /lib/font/bit/lucida and printer fonts (Lucida Sans Unicode, Lucida + Sans Italic, Lucida Sans Demibold, Lucida Typewriter, Lucida Sans + Typewriter83), identified in subdirectory /sys/lib/postscript/font. + These directories contain material copyrights by B&H Inc. and Y&Y Inc. + +2. The printer fonts identified in subdirectory /sys/lib/ghostscript/font + are subject to the GNU GPL, reproduced in the file /LICENSE.gpl. + +3. The ghostscript program in the subdirectory /sys/src/cmd/gs is + covered by the Aladdin Free Public License, reproduced in the file + /LICENSE.afpl. + +=================================================================== + +Lucent Public License Version 1.02 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE +PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original + Program, and + b. in the case of each Contributor, + + i. changes to the Program, and + ii. additions to the Program; + + where such changes and/or additions to the Program were added to the + Program by such Contributor itself or anyone acting on such + Contributor's behalf, and the Contributor explicitly consents, in + accordance with Section 3C, to characterization of the changes and/or + additions as Contributions. + +"Contributor" means LUCENT and any other entity that has Contributed a +Contribution to the Program. + +"Distributor" means a Recipient that distributes the Program, +modifications to the Program, or any part thereof. + +"Licensed Patents" mean patent claims licensable by a Contributor +which are necessarily infringed by the use or sale of its Contribution +alone or when combined with the Program. + +"Original Program" means the original version of the software +accompanying this Agreement as released by LUCENT, including source +code, object code and documentation, if any. + +"Program" means the Original Program and Contributions or any part +thereof + +"Recipient" means anyone who receives the Program under this +Agreement, including all Contributors. + +2. GRANT OF RIGHTS + + a. Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free copyright + license to reproduce, prepare derivative works of, publicly display, + publicly perform, distribute and sublicense the Contribution of such + Contributor, if any, and such derivative works, in source code and + object code form. + + b. Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free patent + license under Licensed Patents to make, use, sell, offer to sell, + import and otherwise transfer the Contribution of such Contributor, if + any, in source code and object code form. The patent license granted + by a Contributor shall also apply to the combination of the + Contribution of that Contributor and the Program if, at the time the + Contribution is added by the Contributor, such addition of the + Contribution causes such combination to be covered by the Licensed + Patents. The patent license granted by a Contributor shall not apply + to (i) any other combinations which include the Contribution, nor to + (ii) Contributions of other Contributors. No hardware per se is + licensed hereunder. + + c. Recipient understands that although each Contributor grants the + licenses to its Contributions set forth herein, no assurances are + provided by any Contributor that the Program does not infringe the + patent or other intellectual property rights of any other entity. Each + Contributor disclaims any liability to Recipient for claims brought by + any other entity based on infringement of intellectual property rights + or otherwise. As a condition to exercising the rights and licenses + granted hereunder, each Recipient hereby assumes sole responsibility + to secure any other intellectual property rights needed, if any. For + example, if a third party patent license is required to allow + Recipient to distribute the Program, it is Recipient's responsibility + to acquire that license before distributing the Program. + + d. Each Contributor represents that to its knowledge it has sufficient + copyright rights in its Contribution, if any, to grant the copyright + license set forth in this Agreement. + +3. REQUIREMENTS + +A. Distributor may choose to distribute the Program in any form under +this Agreement or under its own license agreement, provided that: + + a. it complies with the terms and conditions of this Agreement; + + b. if the Program is distributed in source code or other tangible + form, a copy of this Agreement or Distributor's own license agreement + is included with each copy of the Program; and + + c. if distributed under Distributor's own license agreement, such + license agreement: + + i. effectively disclaims on behalf of all Contributors all warranties + and conditions, express and implied, including warranties or + conditions of title and non-infringement, and implied warranties or + conditions of merchantability and fitness for a particular purpose; + ii. effectively excludes on behalf of all Contributors all liability + for damages, including direct, indirect, special, incidental and + consequential damages, such as lost profits; and + iii. states that any provisions which differ from this Agreement are + offered by that Contributor alone and not by any other party. + +B. Each Distributor must include the following in a conspicuous + location in the Program: + + Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights + Reserved. + +C. In addition, each Contributor must identify itself as the +originator of its Contribution in a manner that reasonably allows +subsequent Recipients to identify the originator of the Contribution. +Also, each Contributor must agree that the additions and/or changes +are intended to be a Contribution. Once a Contribution is contributed, +it may not thereafter be revoked. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain +responsibilities with respect to end users, business partners and the +like. While this license is intended to facilitate the commercial use +of the Program, the Distributor who includes the Program in a +commercial product offering should do so in a manner which does not +create potential liability for Contributors. Therefore, if a +Distributor includes the Program in a commercial product offering, +such Distributor ("Commercial Distributor") hereby agrees to defend +and indemnify every Contributor ("Indemnified Contributor") against +any losses, damages and costs (collectively"Losses") arising from +claims, lawsuits and other legal actions brought by a third party +against the Indemnified Contributor to the extent caused by the acts +or omissions of such Commercial Distributor in connection with its +distribution of the Program in a commercial product offering. The +obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. +In order to qualify, an Indemnified Contributor must: a) promptly +notify the Commercial Distributor in writing of such claim, and b) +allow the Commercial Distributor to control, and cooperate with the +Commercial Distributor in, the defense and any related settlement +negotiations. The Indemnified Contributor may participate in any such +claim at its own expense. + +For example, a Distributor might include the Program in a commercial +product offering, Product X. That Distributor is then a Commercial +Distributor. If that Commercial Distributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Distributor's responsibility +alone. Under this section, the Commercial Distributor would have to +defend claims against the Contributors related to those performance +claims and warranties, and if a court requires any Contributor to pay +any damages as a result, the Commercial Distributor must pay those +damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS +PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY +WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY +OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement, including but not limited to +the risks and costs of program errors, compliance with applicable +laws, damage to or loss of data, programs or equipment, and +unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR +ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING +WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR +DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. EXPORT CONTROL + +Recipient agrees that Recipient alone is responsible for compliance +with the United States export administration regulations (and the +export control laws and regulation of any other countries). + +8. GENERAL + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further +action by the parties hereto, such provision shall be reformed to the +minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against a Contributor with +respect to a patent applicable to software (including a cross-claim or +counterclaim in a lawsuit), then any patent licenses granted by that +Contributor to such Recipient under this Agreement shall terminate as +of the date such litigation is filed. In addition, if Recipient +institutes patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Program +itself (excluding combinations of the Program with other software or +hardware) infringes such Recipient's patent(s), then such Recipient's +rights granted under Section 2(b) shall terminate as of the date such +litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of +time after becoming aware of such noncompliance. If all Recipient's +rights under this Agreement terminate, Recipient agrees to cease use +and distribution of the Program as soon as reasonably practicable. +However, Recipient's obligations under this Agreement and any licenses +granted by Recipient relating to the Program shall continue and +survive. + +LUCENT may publish new versions (including revisions) of this +Agreement from time to time. Each new version of the Agreement will be +given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new +version of the Agreement is published, Contributor may elect to +distribute the Program (including its Contributions) under the new +version. No one other than LUCENT has the right to modify this +Agreement. Except as expressly stated in Sections 2(a) and 2(b) above, +Recipient receives no rights or licenses to the intellectual property +of any Contributor under this Agreement, whether expressly, by +implication, estoppel or otherwise. All rights in the Program not +expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and +the intellectual property laws of the United States of America. No +party to this Agreement will bring a legal action under this Agreement +more than one year after the cause of action arose. Each party waives +its rights to a jury trial in any resulting litigation. + diff --git a/src/cmd/sam/Makefile b/src/cmd/sam/Makefile new file mode 100644 index 00000000..f5e91463 --- /dev/null +++ b/src/cmd/sam/Makefile @@ -0,0 +1,18 @@ +H=errors.h mesg.h parse.h plumb.h sam.h +SRC= address.c buff.c cmd.c disk.c error.c file.c io.c\ + list.c mesg.c moveto.c multi.c unix.c rasp.c regexp.c\ + sam.c shell.c string.c sys.c util.c xec.c plumb.c + +CC=gcc +PREFIX=$(HOME) +#PREFIX=/usr/local +CFLAGS=-I. -I$(PREFIX)/include -O -g +LDFLAGS=-L$(PREFIX)/lib +LDLIBS=-l9 -lfmt -lutf + +all: sam +sam: $(SRC) $(H) + $(CC) -o $@ $(CFLAGS) $(SRC) $(LDFLAGS) $(LDLIBS) +clean: + rm -f *.o *~ + rm -f sam diff --git a/src/cmd/sam/address.c b/src/cmd/sam/address.c new file mode 100644 index 00000000..85cca170 --- /dev/null +++ b/src/cmd/sam/address.c @@ -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->_.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->_.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->_.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->_.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->_.nc; + if(!bexecute(f, p)) + panic("address"); + } + } +} + +File * +matchfile(String *r) +{ + File *f; + File *match = 0; + int i; + + for(i = 0; iname); + 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); + bufinsert(menu, 0, genstr.s, genstr.n); + compile(r); + return execute(menu, 0, menu->_.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->_.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->_.nc) + error(Erange); + if(filereadc(f, p++) == '\n') + n++; + } + a.r.p1 = p; + } + while(p < f->_.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 0) + p--; + } + while(p > 0 && filereadc(f, p-1)!='\n') /* lines start after a newline */ + p--; + a.r.p1 = p; + } + return a; +} diff --git a/src/cmd/sam/buff.c b/src/cmd/sam/buff.c new file mode 100644 index 00000000..30493c3e --- /dev/null +++ b/src/cmd/sam/buff.c @@ -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 && q0cq+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)->_.n <= q0 && q+(*blp)->_.n < b->nc){ + q += (*blp)->_.n; + i++; + blp++; + if(i >= b->nbl) + panic("block not found"); + } + bl = *blp; + /* remember position */ + b->cbi = i; + b->cq = q; + sizecache(b, bl->_.n); + b->cnc = bl->_.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; +} diff --git a/src/cmd/sam/cmd.c b/src/cmd/sam/cmd.c new file mode 100644 index 00000000..8c152f94 --- /dev/null +++ b/src/cmd/sam/cmd.c @@ -0,0 +1,594 @@ +#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; +List addrlist; +List relist; +List stringlist; +int eof; + +void +resetcmd(void) +{ + linep = line; + *linep = 0; + terminp = termoutp = termline; + freecmd(); +} + +int +inputc(void) +{ + int n, nbuf; + char buf[3]; + 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; + + linep = line; + i = 0; + do{ + if((c = inputc())<=0) + return -1; + if(i == (sizeof line)/RUNESIZE-1) + error(Etoolong); + }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'= 0) + ungetch(); + return c; +} + +void +termcommand(void) +{ + Posn p; + + for(p=cmdpt; p_.nc; p++){ + if(terminp >= &termline[BLOCKSIZE]){ + cmdpt = cmd->_.nc; + error(Etoolong); + } + *terminp++ = filereadc(cmd, p); + } + cmdpt = cmd->_.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.ucharpptr[--cmdlist.nused]); + while(addrlist.nused > 0) + free(addrlist.ucharpptr[--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; + 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; +} diff --git a/src/cmd/sam/disk.c b/src/cmd/sam/disk.c new file mode 100644 index 00000000..83b2553d --- /dev/null +++ b/src/cmd/sam/disk.c @@ -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() +{ + 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->_.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]._.next = &blist[j+1]; + } + b = blist; + blist = b->_.next; + b->addr = d->addr; + d->addr += size; + } + b->_.n = n; + return b; +} + +void +diskrelease(Disk *d, Block *b) +{ + uint i; + + ntosize(b->_.n, &i); + b->_.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->_.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->_.n = n; +} + +void +diskread(Disk *d, Block *b, Rune *r, uint n) +{ + if(n > b->_.n) + panic("internal error: diskread"); + + ntosize(b->_.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"); +} diff --git a/src/cmd/sam/error.c b/src/cmd/sam/error.c new file mode 100644 index 00000000..d19b9621 --- /dev/null +++ b/src/cmd/sam/error.c @@ -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)); +} diff --git a/src/cmd/sam/errors.h b/src/cmd/sam/errors.h new file mode 100644 index 00000000..7bf46ea1 --- /dev/null +++ b/src/cmd/sam/errors.h @@ -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; diff --git a/src/cmd/sam/file.c b/src/cmd/sam/file.c new file mode 100644 index 00000000..0428379d --- /dev/null +++ b/src/cmd/sam/file.c @@ -0,0 +1,631 @@ +#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, 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; intext; 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 +fileinsert(File *f, uint p0, Rune *s, uint ns) +{ + if(p0 > f->_.nc) + panic("internal error: fileinsert"); + if(f->seq > 0) + fileuninsert(f, &f->delta, p0, ns); + bufinsert(f, p0, s, ns); + if(ns) + f->mod = TRUE; +} + +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 +filedelete(File *f, uint p0, uint p1) +{ + if(!(p0<=p1 && p0<=f->_.nc && p1<=f->_.nc)) + panic("internal error: filedelete"); + if(f->seq > 0) + fileundelete(f, &f->delta, p0, p1); + bufdelete(f, p0, p1); + if(p1 > p0) + f->mod = TRUE; +} + +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 RBUFSIZE) + n = RBUFSIZE; + bufread(f, 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->_.nc) + return -1; + bufread(f, 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, 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){ + 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, 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 RBUFSIZE) + n = RBUFSIZE; + bufread(delta, up+i, buf, n); + bufinsert(f, 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); + 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; +} diff --git a/src/cmd/sam/io.c b/src/cmd/sam/io.c new file mode 100644 index 00000000..236090a0 --- /dev/null +++ b/src/cmd/sam/io.c @@ -0,0 +1,262 @@ +#include "sam.h" + +#define NSYSFILE 3 +#define NOFILE 128 + +void +checkqid(File *f) +{ + int i, w; + File *g; + + w = whichmenu(f); + for(i=1; idev==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->mtimedev = 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->_.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, 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, 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, char **end) +{ + int ph2t[2], pt2h[2]; + + if(machine){ + dup(remotefd0, 0); + dup(remotefd1, 1); + close(remotefd0); + close(remotefd1); + argv[0] = "samterm"; + *end = 0; + exec(samterm, argv); + fprint(2, "can't exec: "); + perror(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"; + *end = 0; + exec(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) +{ + int p1[2], p2[2]; + + 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]); + execl(RXPATH, RX, machine, rsamname, "-R", (char*)0); + dprint("can't exec %s\n", RXPATH); + exits("exec"); + + case -1: + dprint("can't fork\n"); + exits("fork"); + } + close(p1[1]); + close(p2[0]); +} + +void +startup(char *machine, int Rflag, char **argv, char **end) +{ + if(machine) + connectto(machine); + if(!Rflag) + bootterm(machine, argv, end); + downloaded = 1; + outTs(Hversion, VERSION); +} diff --git a/src/cmd/sam/list.c b/src/cmd/sam/list.c new file mode 100644 index 00000000..a8105425 --- /dev/null +++ b/src/cmd/sam/list.c @@ -0,0 +1,47 @@ +#include "sam.h" + +/* + * Check that list has room for one more element. + */ +void +growlist(List *l) +{ + if(l->listptr==0 || l->nalloc==0){ + l->nalloc = INCR; + l->listptr = emalloc(INCR*sizeof(long)); + l->nused = 0; + }else if(l->nused == l->nalloc){ + l->listptr = erealloc(l->listptr, (l->nalloc+INCR)*sizeof(long)); + memset((void*)(l->longptr+l->nalloc), 0, INCR*sizeof(long)); + l->nalloc += INCR; + } +} + +/* + * Remove the ith element from the list + */ +void +dellist(List *l, int i) +{ + memmove(&l->longptr[i], &l->longptr[i+1], (l->nused-(i+1))*sizeof(long)); + l->nused--; +} + +/* + * Add a new element, whose position is i, to the list + */ +void +inslist(List *l, int i, long val) +{ + growlist(l); + memmove(&l->longptr[i+1], &l->longptr[i], (l->nused-i)*sizeof(long)); + l->longptr[i] = val; + l->nused++; +} + +void +listfree(List *l) +{ + free(l->listptr); + free(l); +} diff --git a/src/cmd/sam/mesg.c b/src/cmd/sam/mesg.c new file mode 100644 index 00000000..189c11ac --- /dev/null +++ b/src/cmd/sam/mesg.c @@ -0,0 +1,821 @@ +#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 noflush; +int tversion; + +long inlong(void); +long invlong(void); +int inshort(void); +int inmesg(Tmesg); +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]; + + sprint(buf, "%ld", n); + journal(out, buf); +} +#else +#define journal(a, b) +#define journaln(a, b) +#endif + +int +rcvchar(void){ + static uchar buf[64]; + static 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 state = 0; + static count = 0; + static 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; itag==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; + 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: + l = invlong(); /* for 64-bit pointers */ + journaln(0, l); + Strdupl(&genstr, samname); + cmd = newfile(); + cmd->unread = 0; + outTsv(Hbindname, cmd->tag, l); + outTs(Hcurrent, cmd->tag); + logsetname(cmd, &genstr); + cmd->rasp = emalloc(sizeof(List)); + 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->_.nc) + p1 = f->_.nc; + if(p0>f->_.nc) /* can happen e.g. scrolling during command */ + p0 = f->_.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, 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 = emalloc(sizeof(List)); + 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->_.nc>0){ + rgrow(f->rasp, 0L, f->_.nc); + outTsll(Hgrow, f->tag, 0L, f->_.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->_.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; lBLOCKSIZE) + 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: + l = invlong(); + Strdupl(&genstr, empty); + f = newfile(); + f->rasp = emalloc(sizeof(List)); + outTsv(Hbindname, f->tag, l); + 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->_.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; ldot.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->_.nc, genstr.s, genstr.n); + fileupdate(cmd, FALSE, TRUE); + cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->_.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; is, str->n); + freetmpstr(str); + outT0(Hunlock); + } + break; + + case Tack: + waitack = 0; + break; + + 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_.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; + + 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->_.nc){ + fprint(2, "bad snarf addr p1=%ld p2=%ld f->_.nc=%d\n", p1, p2, f->_.nc); /*ZZZ should never happen, can remove */ + p2 = f->_.nc; + } + for(l=p1; lBLOCKSIZE? BLOCKSIZE : p2-l; + bufread(f, 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; +} + +long +invlong(void) +{ + ulong n; + + n = (inp[7]<<24) | (inp[6]<<16) | (inp[5]<<8) | inp[4]; + n = (n<<16) | (inp[3]<<8) | inp[2]; + n = (n<<16) | (inp[1]<<8) | inp[0]; + inp += 8; + return n; +} + +void +setgenstr(File *f, Posn p0, Posn p1) +{ + if(p0 != p1){ + if(p1-p0 >= TBLOCKSIZE) + error(Etoolong); + Strinsure(&genstr, p1-p0); + bufread(f, 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, Posn l) +{ + outstart(type); + outshort(s); + outvlong((void*)l); + journaln(1, l); + 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(void *v) +{ + int i; + ulong l; + + l = (ulong) v; + for(i = 0; i < 8; i++, l >>= 8) + *outp++ = l; +} + +void +outsend(void) +{ + int outcount; + + outcount = outp-outmsg; + outcount -= 3; + outmsg[1] = outcount; + outmsg[2] = outcount>>8; + outmsg = outp; + if(!noflush){ + outcount = outmsg-outdata; + if (write(1, (char*) outdata, outcount) != outcount) + rescue(); + outmsg = outdata; + return; + } + if(outmsg < outdata+DATASIZE) + return; + outflush(); +} + +void +outflush(void) +{ + if(outmsg == outdata) + return; + noflush = 0; + outT0(Hack); + waitack = 1; + do + if(rcv() == 0){ + rescue(); + exits("eof"); + } + while(waitack); + outmsg = outdata; + noflush = 1; +} diff --git a/src/cmd/sam/mesg.h b/src/cmd/sam/mesg.h new file mode 100644 index 00000000..b88bf148 --- /dev/null +++ b/src/cmd/sam/mesg.h @@ -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 */ + 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") + * + */ diff --git a/src/cmd/sam/mkfile b/src/cmd/sam/mkfile new file mode 100644 index 00000000..cb604976 --- /dev/null +++ b/src/cmd/sam/mkfile @@ -0,0 +1,40 @@ +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->_.nc) + p0 = f->_.nc; + oldp0 = p0; + p = p0; + for(nl=nc=c=0; c!=-1 && nl=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->_.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->_.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->_.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_.nc && c=='\n'); + } + return; + } + } + /* try filling out word to right */ + p = p1; + while(p < f->_.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--; +} + diff --git a/src/cmd/sam/multi.c b/src/cmd/sam/multi.c new file mode 100644 index 00000000..3df0dbee --- /dev/null +++ b/src/cmd/sam/multi.c @@ -0,0 +1,123 @@ +#include "sam.h" + +List file; +ushort tag; + +File * +newfile(void) +{ + File *f; + + f = fileopen(); + inslist(&file, 0, (long)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; itag); + 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; iname, &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, (long)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; iname, s) == 0) + return file.filepptr[i]; + return 0; +} diff --git a/src/cmd/sam/parse.h b/src/cmd/sam/parse.h new file mode 100644 index 00000000..12f293f2 --- /dev/null +++ b/src/cmd/sam/parse.h @@ -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*); diff --git a/src/cmd/sam/plan9.c b/src/cmd/sam/plan9.c new file mode 100644 index 00000000..6c3a60e9 --- /dev/null +++ b/src/cmd/sam/plan9.c @@ -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; +} diff --git a/src/cmd/sam/plumb.c b/src/cmd/sam/plumb.c new file mode 100644 index 00000000..d8db33d4 --- /dev/null +++ b/src/cmd/sam/plumb.c @@ -0,0 +1,9 @@ +#include +#include "plumb.h" + +/* XXX - Can we do better than this? */ +char *cleanname(char *s) { return s; } +char *plumbunpackattr(char *cbuf) { return 0; } +char *plumbpack(Plumbmsg *pm, int *i) { return 0; } +int plumbfree(Plumbmsg *pm) { return 0; } + diff --git a/src/cmd/sam/rasp.c b/src/cmd/sam/rasp.c new file mode 100644 index 00000000..45ac8206 --- /dev/null +++ b/src/cmd/sam/rasp.c @@ -0,0 +1,325 @@ +#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->_.nc; + growpos = 0; + if(f->_.nc) + rgrow(f->rasp, 0, f->_.nc); + raspdone(f, 1); +} + +void +raspstart(File *f) +{ + if(f->rasp == nil) + return; + grown = 0; + shrunk = 0; + noflush = 1; +} + +void +raspdone(File *f, int toterm) +{ + if(f->dot.r.p1 > f->_.nc) + f->dot.r.p1 = f->_.nc; + if(f->dot.r.p2 > f->_.nc) + f->dot.r.p2 = f->_.nc; + if(f->mark.p1 > f->_.nc) + f->mark.p1 = f->_.nc; + if(f->mark.p2 > f->_.nc) + f->mark.p2 = f->_.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(); + noflush = 0; + if(f == cmd){ + cmdpt += cmdptadv; + cmdptadv = 0; + } +} + +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 && p1tag, 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 && p1tag, 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->longptr[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; inused && 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(inused && 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 && inused && 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; inused && 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; inused && 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; inused && 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)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; +} diff --git a/src/cmd/sam/regexp.c b/src/cmd/sam/regexp.c new file mode 100644 index 00000000..dee4377d --- /dev/null +++ b/src/cmd/sam/regexp.c @@ -0,0 +1,801 @@ +#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 128 + +Ilist *tl, *nl; /* This list, next list */ +Ilist list[2][NLIST]; +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; + +void 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; is); + 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; ipfirst->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] = 0xFFFF; + 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 == 0xFFFF){ + 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. + */ +void +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; /* It's already there */ + } + } + p->inst = inst; + p->se= *sep; + (p+1)->inst = 0; +} + +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->typetype : 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= NLIST) + Overflow: + error(Eoverflow); + sempty.p[0].p1 = p; + addinst(tl, startinst, &sempty); + } + /* 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(++nnl >= NLIST) + goto Overflow; + addinst(nl, inst->next, &tlp->se); + } + 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(++ntl >= NLIST) + goto Overflow; + addinst(tlp, inst->right, &tlp->se); + /* 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].p1p[0].p1==sel.p[0].p1 && sp->p[0].p2>sel.p[0].p2)) + for(i = 0; ip[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->typetype : 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->_.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 */ + if(++ntl >= NLIST) + Overflow: + error(Eoverflow); + /* the minus is so the optimizations in addinst work */ + sempty.p[0].p1 = -p; + addinst(tl, bstartinst, &sempty); + } + /* 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(++nnl >= NLIST) + goto Overflow; + addinst(nl, inst->next, &tlp->se); + } + 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->_.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(++ntl >= NLIST) + goto Overflow; + addinst(tlp, inst->right, &tlp->se); + /* 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].p2p[i].p2; + sel.p[i].p2 = sp->p[i].p1; + } +} diff --git a/src/cmd/sam/sam b/src/cmd/sam/sam new file mode 100755 index 00000000..733b555c Binary files /dev/null and b/src/cmd/sam/sam differ diff --git a/src/cmd/sam/sam.c b/src/cmd/sam/sam.c new file mode 100644 index 00000000..81ccaf78 --- /dev/null +++ b/src/cmd/sam/sam.c @@ -0,0 +1,739 @@ +#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; +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; + +Rune baddir[] = { '<', 'b', 'a', 'd', 'd', 'i', 'r', '>', '\n'}; + +void usage(void); + +int main(int argc, char *argv[]) +{ + int i; + String *t; + char **ap, **arg; + + { // libfmt-2.0 uses %lu where we need %lud + extern int __flagfmt(Fmt*); + fmtinstall('u', __flagfmt); + } + + arg = argv++; + ap = argv; + while(argc>1 && argv[0] && argv[0][0]=='-'){ + switch(argv[0][1]){ + case 'd': + dflag++; + break; + + case 'r': + --argc, argv++; + if(argc == 1) + usage(); + machine = *argv; + break; + + case 'R': + Rflag++; + break; + + case 't': + --argc, argv++; + if(argc == 1) + usage(); + samterm = *argv; + break; + + case 's': + --argc, argv++; + if(argc == 1) + usage(); + rsamname = *argv; + break; + + case 'x': /* x11 option - strip the x */ + strcpy(*argv+1, *argv+2); + *ap++ = *argv++; + *ap++ = *argv; + argc--; + break; + + default: + dprint("sam: unknown flag %c\n", argv[0][1]); + exits("usage"); + } + --argc, argv++; + } + Strinit(&cmdstr); + Strinit0(&lastpat); + Strinit0(&lastregexp); + Strinit0(&genstr); + Strinit0(&rhs); + Strinit0(&curwd); + tempfile.listptr = emalloc(1); /* so it can be freed later */ + Strinit0(&plan9cmd); + home = getenv(HOME); + disk = diskinit(); + if(home == 0) + home = "/"; + if(!dflag) + startup(machine, Rflag, arg, ap); + notify(notifyf); + getcurwd(); + if(argc>1){ + for(i=0; i_.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++); + fprint(io, "#!%s '%s' $* <<'---%s'\n", SAMSAVECMD, buf, buf); + addr.r.p1 = 0, addr.r.p2 = f->_.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; iseq==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; cname); + 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->_.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; ideleted) { + 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->_.nc); + addr.r.p2 = f->_.nc; + }else if(f->_.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_.nc; + retcode = plan9(flist, '<', s, FALSE); + fileupdate(flist, FALSE, FALSE); + flist->seq = 0; + if (flist->_.nc > BLOCKSIZE) + error(Etoolong); + Strzero(&genstr); + Strinsure(&genstr, flist->_.nc); + bufread(flist, (Posn)0, genbuf, flist->_.nc); + memmove(genstr.s, genbuf, flist->_.nc*RUNESIZE); + genstr.n = flist->_.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(); + for(i=0; iname.s[0]!='/' && f->name.s[0]!=0){ + Strinsert(&f->name, &owd, (Posn)0); + fixname(&f->name); + sortname(f); + }else 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 BLOCKSIZE) + ni = BLOCKSIZE; + bufread(f, 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){ + free(tempfile.listptr); + tempfile.listptr = emalloc(sizeof(*tempfile.filepptr)*file.nused); + tempfile.nalloc = file.nused; + } + tempfile.nused = file.nused; + memmove(&tempfile.filepptr[0], &file.filepptr[0], file.nused*sizeof(File*)); +} diff --git a/src/cmd/sam/sam.h b/src/cmd/sam/sam.h new file mode 100644 index 00000000..6a2708c1 --- /dev/null +++ b/src/cmd/sam/sam.h @@ -0,0 +1,407 @@ +#include +#include +#include +#include "errors.h" + +/* + * 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 + +#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 nalloc; + int nused; + union{ + void *listp; + Block *blkp; + long *longp; + uchar* *ucharp; + String* *stringp; + File* *filep; + long listv; + }g; +}; + +#define listptr g.listp +#define blkptr g.blkp +#define longptr g.longp +#define ucharpptr g.ucharp +#define stringpptr g.stringp +#define filepptr g.filep +#define listval g.listv + +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 */ + } _; +}; + +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 _; /* 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 */ + +// Text *curtext; /* most recently used associated text */ +// Text **text; /* list of associated texts */ +// int ntext; +// int dumpid; /* used in dumping zeroxed windows */ + + 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); + +/* + * 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, long); +Address lineaddr(Posn, Address, 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[]; +extern char SAMSAVECMD[]; + +/* + * 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 noflush; + +#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, long); +void outstart(Hmesg); +void outcopy(int, void*); +void outshort(int); +void outlong(long); +void outvlong(void*); +void outsend(void); +void outflush(void); diff --git a/src/cmd/sam/shell.c b/src/cmd/sam/shell.c new file mode 100644 index 00000000..2cac31bc --- /dev/null +++ b/src/cmd/sam/shell.c @@ -0,0 +1,152 @@ +#include "sam.h" +#include "parse.h" + +extern jmp_buf mainloop; + +char errfile[64]; +String plan9cmd; /* null terminated */ +Buffer plan9buf; +void checkerrs(void); + +int +plan9(File *f, int type, String *s, int nest) +{ + long l; + int m; + int pid, 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){ + 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; lBLOCKSIZE-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[256]; + int f, n, nl; + char *p; + long l; + + if(statfile(errfile, 0, 0, 0, &l, 0) > 0 && l != 0){ + if((f=open((char *)errfile, 0)) != -1){ + if((n=read(f, buf, sizeof buf-1)) > 0){ + for(nl=0,p=buf; nl<3 && 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((char *)errfile); +} diff --git a/src/cmd/sam/unix.c b/src/cmd/sam/unix.c new file mode 100644 index 00000000..cc15db44 --- /dev/null +++ b/src/cmd/sam/unix.c @@ -0,0 +1,272 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 "/usr/local/bin/samterm" +#endif +#ifndef TMPDIRNAME +#define TMPDIRNAME "/tmp" +#endif +#ifndef SHNAME +#define SHNAME "rc" +#endif +#ifndef SHPATHNAME +#define SHPATHNAME "/bin/rc" +#endif +#ifndef RXNAME +#define RXNAME "ssh" +#endif +#ifndef RXPATHNAME +#define RXPATHNAME "/usr/local/bin/ssh" +#endif +#ifndef SAMSAVECMDNAME +#define SAMSAVECMDNAME "/bin/rc\n/usr/local/bin/samsave" +#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; +char SAMSAVECMD[] = SAMSAVECMDNAME; + + +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 wait +int +waitfor(int pid) +{ + int wm; + int rpid; + + do; while((rpid = wait(&wm)) != pid && rpid != -1); + return (WEXITSTATUS(wm)); +} + +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; +} + +#if 0 +char * +strdup(const char *s) +{ + return strcpy(emalloc(strlen(s)), s); +} +#endif + +/* +void exits(const char *s) +{ + if (s) fprint(2, "exit: %s\n", s); + exit(s != 0); +} + +void +_exits(const char *s) +{ + if (s) fprint(2, "exit: %s\n", s); + _exit(s != 0); +} + +int errstr(char *buf, int size) +{ + extern int errno; + + snprint(buf, size, "%s", strerror(errno)); + return 1; +} +*/ + +int create(char *name, int omode, int perm) +{ + int mode; + int fd; + + if (omode & OWRITE) mode = O_WRONLY; + else if (omode & OREAD) mode = O_RDONLY; + else mode = O_RDWR; + + if ((fd = open(name, mode|O_CREAT|O_TRUNC, perm)) < 0) + return fd; + + if (omode & OCEXEC) + fcntl(fd, F_SETFD, fcntl(fd,F_GETFD,0) | FD_CLOEXEC); + + /* SES - not exactly right, but hopefully good enough. */ + if (omode & ORCLOSE) + remove(name); + + return fd; +} diff --git a/src/cmd/sam/xec.c b/src/cmd/sam/xec.c new file mode 100644 index 00000000..42acab0e --- /dev/null +++ b/src/cmd/sam/xec.c @@ -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; iname); + 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; ictext->n; i++) + if((c = cp->ctext->s[i])=='\\' && ictext->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, 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, 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->_.nc){ + fprint(2, "bad display addr p1=%ld p2=%ld f->_.nc=%d\n", p1, p2, f->_.nc); /*ZZZ should never happen, can remove */ + p2 = f->_.nc; + } + while(p1 < p2){ + np = p2-p1; + if(np>BLOCKSIZE-1) + np = BLOCKSIZE-1; + bufread(f, 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) + 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; ire==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; +} -- cgit v1.2.3