1implement Exec; 2 3include "common.m"; 4 5sys : Sys; 6dat : Dat; 7acme : Acme; 8utils : Utils; 9graph : Graph; 10gui : Gui; 11lookx : Look; 12bufferm : Bufferm; 13textm : Textm; 14scrl : Scroll; 15filem : Filem; 16windowm : Windowm; 17rowm : Rowm; 18columnm : Columnm; 19fsys : Fsys; 20editm: Edit; 21 22Dir, OREAD, OWRITE : import Sys; 23EVENTSIZE, QWaddr, QWdata, QWevent, Astring : import dat; 24Lock, Reffont, Ref, seltext, seq, row : import dat; 25warning, error, skipbl, findbl, stralloc, strfree, exec : import utils; 26dirname : import lookx; 27Body, Text : import textm; 28File : import filem; 29sprint : import sys; 30TRUE, FALSE, XXX, BUFSIZE : import Dat; 31Buffer : import bufferm; 32Row : import rowm; 33Column : import columnm; 34Window : import windowm; 35setalphabet: import textm; 36 37# snarfbuf : ref Buffer; 38 39init(mods : ref Dat->Mods) 40{ 41 sys = mods.sys; 42 dat = mods.dat; 43 acme = mods.acme; 44 utils = mods.utils; 45 graph = mods.graph; 46 gui = mods.gui; 47 lookx = mods.look; 48 bufferm = mods.bufferm; 49 textm = mods.textm; 50 scrl = mods.scroll; 51 filem = mods.filem; 52 rowm = mods.rowm; 53 windowm = mods.windowm; 54 columnm = mods.columnm; 55 fsys = mods.fsys; 56 editm = mods.edit; 57 58 snarfbuf = bufferm->newbuffer(); 59} 60 61Exectab : adt { 62 name : string; 63 fun : int; 64 mark : int; 65 flag1 : int; 66 flag2 : int; 67}; 68 69F_ALPHABET, F_CUT, F_DEL, F_DELCOL, F_DUMP, F_EDIT, F_EXITX, F_FONTX, F_GET, F_ID, F_INCL, F_KILL, F_LIMBO, F_LINENO, F_LOCAL, F_LOOK, F_NEW, F_NEWCOL, F_PASTE, F_PUT, F_PUTALL, F_UNDO, F_SEND, F_SORT, F_TAB, F_ZEROX : con iota; 70 71exectab := array[] of { 72 Exectab ( "Alphabet", F_ALPHABET, FALSE, XXX, XXX ), 73 Exectab ( "Cut", F_CUT, TRUE, TRUE, TRUE ), 74 Exectab ( "Del", F_DEL, FALSE, FALSE, XXX ), 75 Exectab ( "Delcol", F_DELCOL, FALSE, XXX, XXX ), 76 Exectab ( "Delete", F_DEL, FALSE, TRUE, XXX ), 77 Exectab ( "Dump", F_DUMP, FALSE, TRUE, XXX ), 78 Exectab ( "Edit", F_EDIT, FALSE, XXX, XXX ), 79 Exectab ( "Exit", F_EXITX, FALSE, XXX, XXX ), 80 Exectab ( "Font", F_FONTX, FALSE, XXX, XXX ), 81 Exectab ( "Get", F_GET, FALSE, TRUE, XXX ), 82 Exectab ( "ID", F_ID, FALSE, XXX, XXX ), 83 Exectab ( "Incl", F_INCL, FALSE, XXX, XXX ), 84 Exectab ( "Kill", F_KILL, FALSE, XXX, XXX ), 85 Exectab ( "Limbo", F_LIMBO, FALSE, XXX, XXX ), 86 Exectab ( "Lineno", F_LINENO, FALSE, XXX, XXX ), 87 Exectab ( "Load", F_DUMP, FALSE, FALSE, XXX ), 88 Exectab ( "Local", F_LOCAL, FALSE, XXX, XXX ), 89 Exectab ( "Look", F_LOOK, FALSE, XXX, XXX ), 90 Exectab ( "New", F_NEW, FALSE, XXX, XXX ), 91 Exectab ( "Newcol", F_NEWCOL, FALSE, XXX, XXX ), 92 Exectab ( "Paste", F_PASTE, TRUE, TRUE, XXX ), 93 Exectab ( "Put", F_PUT, FALSE, XXX, XXX ), 94 Exectab ( "Putall", F_PUTALL, FALSE, XXX, XXX ), 95 Exectab ( "Redo", F_UNDO, FALSE, FALSE, XXX ), 96 Exectab ( "Send", F_SEND, TRUE, XXX, XXX ), 97 Exectab ( "Snarf", F_CUT, FALSE, TRUE, FALSE ), 98 Exectab ( "Sort", F_SORT, FALSE, XXX, XXX ), 99 Exectab ( "Tab", F_TAB, FALSE, XXX, XXX ), 100 Exectab ( "Undo", F_UNDO, FALSE, TRUE, XXX ), 101 Exectab ( "Zerox", F_ZEROX, FALSE, XXX, XXX ), 102 Exectab ( nil, 0, 0, 0, 0 ), 103}; 104 105runfun(fun : int, et, t, argt : ref Text, flag1, flag2 : int, arg : string, narg : int) 106{ 107 case (fun) { 108 F_ALPHABET => alphabet(et, argt, arg, narg); 109 F_CUT => cut(et, t, flag1, flag2); 110 F_DEL => del(et, flag1); 111 F_DELCOL => delcol(et); 112 F_DUMP => dump(argt, flag1, arg, narg); 113 F_EDIT => edit(et, argt, arg, narg); 114 F_EXITX => exitx(); 115 F_FONTX => fontx(et, t, argt, arg, narg); 116 F_GET => get(et, t, argt, flag1, arg, narg); 117 F_ID => id(et); 118 F_INCL => incl(et, argt, arg, narg); 119 F_KILL => kill(argt, arg, narg); 120 F_LIMBO => limbo(et); 121 F_LINENO => lineno(et); 122 F_LOCAL => local(et, argt, arg); 123 F_LOOK => look(et, t, argt); 124 F_NEW => lookx->new(et, t, argt, flag1, flag2, arg, narg); 125 F_NEWCOL => newcol(et); 126 F_PASTE => paste(et, t, flag1, flag2); 127 F_PUT => put(et, argt, arg, narg); 128 F_PUTALL => putall(); 129 F_UNDO => undo(et, flag1); 130 F_SEND => send(et, t); 131 F_SORT => sort(et); 132 F_TAB => tab(et, argt, arg, narg); 133 F_ZEROX => zerox(et, t); 134 * => error("bad case in runfun()"); 135 } 136} 137 138lookup(r : string, n : int) : int 139{ 140 nr : int; 141 142 (r, n) = skipbl(r, n); 143 if(n == 0) 144 return -1; 145 (nil, nr) = findbl(r, n); 146 nr = n-nr; 147 for(i := 0; exectab[i].name != nil; i++) 148 if (r[0:nr] == exectab[i].name) 149 return i; 150 return -1; 151} 152 153isexecc(c : int) : int 154{ 155 if(lookx->isfilec(c)) 156 return 1; 157 return c=='<' || c=='|' || c=='>'; 158} 159 160execute(t : ref Text, aq0 : int, aq1 : int, external : int, argt : ref Text) 161{ 162 q0, q1 : int; 163 r : ref Astring; 164 s, dir, aa, a : string; 165 e : int; 166 c, n, f : int; 167 168 q0 = aq0; 169 q1 = aq1; 170 if(q1 == q0){ # expand to find word (actually file name) 171 # if in selection, choose selection 172 if(t.q1>t.q0 && t.q0<=q0 && q0<=t.q1){ 173 q0 = t.q0; 174 q1 = t.q1; 175 }else{ 176 while(q1<t.file.buf.nc && isexecc(c=t.readc(q1)) && c!=':') 177 q1++; 178 while(q0>0 && isexecc(c=t.readc(q0-1)) && c!=':') 179 q0--; 180 if(q1 == q0) 181 return; 182 } 183 } 184 r = stralloc(q1-q0); 185 t.file.buf.read(q0, r, 0, q1-q0); 186 e = lookup(r.s, q1-q0); 187 if(!external && t.w!=nil && t.w.nopen[QWevent]>byte 0){ 188 f = 0; 189 if(e >= 0) 190 f |= 1; 191 if(q0!=aq0 || q1!=aq1){ 192 t.file.buf.read(aq0, r, 0, aq1-aq0); 193 f |= 2; 194 } 195 (aa, a) = getbytearg(argt, TRUE, TRUE); 196 if(a != nil){ 197 if(len a > EVENTSIZE){ # too big; too bad 198 aa = a = nil; 199 warning(nil, "`argument string too long\n"); 200 return; 201 } 202 f |= 8; 203 } 204 c = 'x'; 205 if(t.what == Body) 206 c = 'X'; 207 n = aq1-aq0; 208 if(n <= EVENTSIZE) 209 t.w.event(sprint("%c%d %d %d %d %s\n", c, aq0, aq1, f, n, r.s[0:n])); 210 else 211 t.w.event(sprint("%c%d %d %d 0 \n", c, aq0, aq1, f)); 212 if(q0!=aq0 || q1!=aq1){ 213 n = q1-q0; 214 t.file.buf.read(q0, r, 0, n); 215 if(n <= EVENTSIZE) 216 t.w.event(sprint("%c%d %d 0 %d %s\n", c, q0, q1, n, r.s[0:n])); 217 else 218 t.w.event(sprint("%c%d %d 0 0 \n", c, q0, q1)); 219 } 220 if(a != nil){ 221 t.w.event(sprint("%c0 0 0 %d %s\n", c, len a, a)); 222 if(aa != nil) 223 t.w.event(sprint("%c0 0 0 %d %s\n", c, len aa, aa)); 224 else 225 t.w.event(sprint("%c0 0 0 0 \n", c)); 226 } 227 strfree(r); 228 r = nil; 229 a = aa = nil; 230 return; 231 } 232 if(e >= 0){ 233 if(exectab[e].mark && seltext!=nil) 234 if(seltext.what == Body){ 235 seq++; 236 seltext.w.body.file.mark(); 237 } 238 (s, n) = skipbl(r.s, q1-q0); 239 (s, n) = findbl(s, n); 240 (s, n) = skipbl(s, n); 241 runfun(exectab[e].fun, t, seltext, argt, exectab[e].flag1, exectab[e].flag2, s, n); 242 strfree(r); 243 r = nil; 244 return; 245 } 246 247 (dir, n) = dirname(t, nil, 0); 248 if(n==1 && dir[0]=='.'){ # sigh 249 dir = nil; 250 n = 0; 251 } 252 (aa, a) = getbytearg(argt, TRUE, TRUE); 253 if(t.w != nil) 254 t.w.refx.inc(); 255 spawn run(t.w, r.s, dir, n, TRUE, aa, a, FALSE); 256} 257 258printarg(argt : ref Text, q0 : int, q1 : int) : string 259{ 260 buf : string; 261 262 if(argt.what!=Body || argt.file.name==nil) 263 return nil; 264 if(q0 == q1) 265 buf = sprint("%s:#%d", argt.file.name, q0); 266 else 267 buf = sprint("%s:#%d,#%d", argt.file.name, q0, q1); 268 return buf; 269} 270 271getarg(argt : ref Text, doaddr : int, dofile : int) : (string, string, int) 272{ 273 r : ref Astring; 274 n : int; 275 e : Dat->Expand; 276 a : string; 277 ok : int; 278 279 if(argt == nil) 280 return (nil, nil, 0); 281 a = nil; 282 argt.commit(TRUE); 283 (ok, e) = lookx->expand(argt, argt.q0, argt.q1); 284 if (ok) { 285 e.bname = nil; 286 if(len e.name && dofile){ 287 if(doaddr) 288 a = printarg(argt, e.q0, e.q1); 289 return (a, e.name, len e.name); 290 } 291 e.name = nil; 292 }else{ 293 e.q0 = argt.q0; 294 e.q1 = argt.q1; 295 } 296 n = e.q1 - e.q0; 297 r = stralloc(n); 298 argt.file.buf.read(e.q0, r, 0, n); 299 if(doaddr) 300 a = printarg(argt, e.q0, e.q1); 301 return(a, r.s, n); 302} 303 304getbytearg(argt : ref Text, doaddr : int, dofile : int) : (string, string) 305{ 306 r : string; 307 n : int; 308 aa : string; 309 310 (aa, r, n) = getarg(argt, doaddr, dofile); 311 if(r == nil) 312 return (nil, nil); 313 return (aa, r); 314} 315 316newcol(et : ref Text) 317{ 318 c : ref Column; 319 320 c = et.row.add(nil, -1); 321 if(c != nil) 322 c.add(nil, nil, -1).settag(); 323} 324 325delcol(et : ref Text) 326{ 327 c := et.col; 328 if(c==nil || !c.clean(FALSE)) 329 return; 330 for(i:=0; i<c.nw; i++){ 331 w := c.w[i]; 332 if(int w.nopen[QWevent]+int w.nopen[QWaddr]+int w.nopen[QWdata] > 0){ 333 warning(nil, sys->sprint("can't delete column; %s is running an external command\n", w.body.file.name)); 334 return; 335 } 336 } 337 c.row.close(c, TRUE); 338} 339 340del(et : ref Text, flag1 : int) 341{ 342 if(et.col==nil || et.w == nil) 343 return; 344 if(flag1 || et.w.body.file.ntext>1 || et.w.clean(FALSE, FALSE)) 345 et.col.close(et.w, TRUE); 346} 347 348sort(et : ref Text) 349{ 350 if(et.col != nil) 351 et.col.sort(); 352} 353 354seqof(w: ref Window, isundo: int): int 355{ 356 # if it's undo, see who changed with us 357 if(isundo) 358 return w.body.file.seq; 359 # if it's redo, see who we'll be sync'ed up with 360 return w.body.file.redoseq(); 361} 362 363undo(et : ref Text, flag1 : int) 364{ 365 i, j: int; 366 c: ref Column; 367 w: ref Window; 368 seq: int; 369 370 if(et==nil || et.w== nil) 371 return; 372 seq = seqof(et.w, flag1); 373 for(i=0; i<row.ncol; i++){ 374 c = row.col[i]; 375 for(j=0; j<c.nw; j++){ 376 w = c.w[j]; 377 if(seqof(w, flag1) == seq) 378 w.undo(flag1); 379 } 380 } 381 # et.w.undo(flag1); 382} 383 384getname(t : ref Text, argt : ref Text, arg : string, narg : int, isput : int) : string 385{ 386 r, dir : string; 387 i, n, ndir, promote : int; 388 389 (nil, r, n) = getarg(argt, FALSE, TRUE); 390 promote = FALSE; 391 if(r == nil) 392 promote = TRUE; 393 else if(isput){ 394 # if are doing a Put, want to synthesize name even for non-existent file 395 # best guess is that file name doesn't contain a slash 396 promote = TRUE; 397 for(i=0; i<n; i++) 398 if(r[i] == '/'){ 399 promote = FALSE; 400 break; 401 } 402 if(promote){ 403 t = argt; 404 arg = r; 405 narg = n; 406 } 407 } 408 if(promote){ 409 n = narg; 410 if(n <= 0) 411 return t.file.name; 412 # prefix with directory name if necessary 413 dir = nil; 414 ndir = 0; 415 if(n>0 && arg[0]!='/'){ 416 (dir, ndir) = dirname(t, nil, 0); 417 if(n==1 && dir[0]=='.'){ # sigh 418 dir = nil; 419 ndir = 0; 420 } 421 } 422 if(dir != nil){ 423 r = dir[0:ndir] + arg[0:n]; 424 dir = nil; 425 n += ndir; 426 }else 427 r = arg[0:n]; 428 } 429 return r; 430} 431 432zerox(et : ref Text, t : ref Text) 433{ 434 nw : ref Window; 435 c, locked : int; 436 437 locked = FALSE; 438 if(t!=nil && t.w!=nil && t.w!=et.w){ 439 locked = TRUE; 440 c = 'M'; 441 if(et.w != nil) 442 c = et.w.owner; 443 t.w.lock(c); 444 } 445 if(t == nil) 446 t = et; 447 if(t==nil || t.w==nil) 448 return; 449 t = t.w.body; 450 if(t.w.isdir) 451 warning(nil, sprint("%s is a directory; Zerox illegal\n", t.file.name)); 452 else{ 453 nw = t.w.col.add(nil, t.w, -1); 454 # ugly: fix locks so w.unlock works 455 nw.lock1(t.w.owner); 456 } 457 if(locked) 458 t.w.unlock(); 459} 460 461get(et : ref Text, t : ref Text, argt : ref Text, flag1 : int, arg : string, narg : int) 462{ 463 name : string; 464 r : string; 465 i, n, dirty : int; 466 w : ref Window; 467 u : ref Text; 468 d : Dir; 469 ok : int; 470 471 if(flag1) 472 if(et==nil || et.w==nil) 473 return; 474 if(!et.w.isdir && (et.w.body.file.buf.nc>0 && !et.w.clean(TRUE, FALSE))) 475 return; 476 w = et.w; 477 t = w.body; 478 name = getname(t, argt, arg, narg, FALSE); 479 if(name == nil){ 480 warning(nil, "no file name\n"); 481 return; 482 } 483 if(t.file.ntext>1){ 484 (ok, d) = sys->stat(name); 485 if (ok == 0 && d.qid.qtype & Sys->QTDIR) { 486 warning(nil, sprint("%s is a directory; can't read with multiple windows on it\n", name)); 487 return; 488 } 489 } 490 r = name; 491 n = len name; 492 for(i=0; i<t.file.ntext; i++){ 493 u = t.file.text[i]; 494 # second and subsequent calls with zero an already empty buffer, but OK 495 u.reset(); 496 u.w.dirfree(); 497 } 498 samename := r[0:n] == t.file.name; 499 t.loadx(0, name, samename); 500 if(samename){ 501 t.file.mod = FALSE; 502 dirty = FALSE; 503 }else{ 504 t.file.mod = TRUE; 505 dirty = TRUE; 506 } 507 for(i=0; i<t.file.ntext; i++) 508 t.file.text[i].w.dirty = dirty; 509 name = nil; 510 r = nil; 511 w.settag(); 512 t.file.unread = FALSE; 513 for(i=0; i<t.file.ntext; i++){ 514 u = t.file.text[i]; 515 u.w.tag.setselect(u.w.tag.file.buf.nc, u.w.tag.file.buf.nc); 516 scrl->scrdraw(u); 517 } 518} 519 520putfile(f: ref File, q0: int, q1: int, name: string) 521{ 522 n : int; 523 r, s : ref Astring; 524 w : ref Window; 525 i, q : int; 526 fd : ref Sys->FD; 527 d : Dir; 528 ok : int; 529 530 w = f.curtext.w; 531 532 { 533 if(name == f.name){ 534 (ok, d) = sys->stat(name); 535 if(ok >= 0 && (f.dev!=d.dev || f.qidpath!=d.qid.path || f.mtime<d.mtime)){ 536 f.dev = d.dev; 537 f.qidpath = d.qid.path; 538 f.mtime = d.mtime; 539 if(f.unread) 540 warning(nil, sys->sprint("%s not written; file already exists\n", name)); 541 else 542 warning(nil, sys->sprint("%s modified since last read\n", name)); 543 raise "e"; 544 } 545 } 546 fd = sys->create(name, OWRITE, 8r664); # was 666 547 if(fd == nil){ 548 warning(nil, sprint("can't create file %s: %r\n", name)); 549 raise "e"; 550 } 551 r = stralloc(BUFSIZE); 552 s = stralloc(BUFSIZE); 553 554 { 555 (ok, d) = sys->fstat(fd); 556 if(ok>=0 && (d.mode&Sys->DMAPPEND) && d.length>big 0){ 557 warning(nil, sprint("%s not written; file is append only\n", name)); 558 raise "e"; 559 } 560 for(q = q0; q < q1; q += n){ 561 n = q1 - q; 562 if(n > BUFSIZE) 563 n = BUFSIZE; 564 f.buf.read(q, r, 0, n); 565 ab := array of byte r.s[0:n]; 566 if(sys->write(fd, ab, len ab) != len ab){ 567 ab = nil; 568 warning(nil, sprint("can't write file %s: %r\n", name)); 569 raise "e"; 570 } 571 ab = nil; 572 } 573 if(name == f.name){ 574 d0 : Dir; 575 576 if(q0 != 0 || q1 != f.buf.nc){ 577 f.mod = TRUE; 578 w.dirty = TRUE; 579 f.unread = TRUE; 580 } 581 else{ 582 (ok, d0) = sys->fstat(fd); # use old values if we failed 583 if (ok >= 0) 584 d = d0; 585 f.qidpath = d.qid.path; 586 f.dev = d.dev; 587 f.mtime = d.mtime; 588 f.mod = FALSE; 589 w.dirty = FALSE; 590 f.unread = FALSE; 591 } 592 for(i=0; i<f.ntext; i++){ 593 f.text[i].w.putseq = f.seq; 594 f.text[i].w.dirty = w.dirty; 595 } 596 } 597 strfree(s); 598 strfree(r); 599 s = r = nil; 600 name = nil; 601 fd = nil; 602 w.settag(); 603 } 604 exception{ 605 * => 606 strfree(s); 607 strfree(r); 608 s = r = nil; 609 fd = nil; 610 raise "e"; 611 } 612 } 613 exception{ 614 * => 615 name = nil; 616 return; 617 } 618} 619 620put(et : ref Text, argt : ref Text, arg : string, narg : int) 621{ 622 namer : string; 623 name : string; 624 w : ref Window; 625 626 if(et==nil || et.w==nil || et.w.isdir) 627 return; 628 w = et.w; 629 f := w.body.file; 630 631 name = getname(w.body, argt, arg, narg, TRUE); 632 if(name == nil){ 633 warning(nil, "no file name\n"); 634 return; 635 } 636 namer = name; 637 putfile(f, 0, f.buf.nc, namer); 638 name = nil; 639} 640 641dump(argt : ref Text, isdump : int, arg : string, narg : int) 642{ 643 name : string; 644 645 if(narg) 646 name = arg; 647 else 648 (nil, name) = getbytearg(argt, FALSE, TRUE); 649 if(isdump) 650 row.dump(name); 651 else { 652 if (!row.qlock.locked()) 653 error("row not locked in dump()"); 654 row.loadx(name, FALSE); 655 } 656 name = nil; 657} 658 659cut(et : ref Text, t : ref Text, dosnarf : int, docut : int) 660{ 661 q0, q1, n, locked, c : int; 662 r : ref Astring; 663 664 # use current window if snarfing and its selection is non-null 665 if(et!=t && dosnarf && et.w!=nil){ 666 if(et.w.body.q1>et.w.body.q0){ 667 t = et.w.body; 668 t.file.mark(); # seq has been incremented by execute 669 } 670 else if(et.w.tag.q1>et.w.tag.q0) 671 t = et.w.tag; 672 } 673 if(t == nil) 674 return; 675 locked = FALSE; 676 if(t.w!=nil && et.w!=t.w){ 677 locked = TRUE; 678 c = 'M'; 679 if(et.w != nil) 680 c = et.w.owner; 681 t.w.lock(c); 682 } 683 if(t.q0 == t.q1){ 684 if(locked) 685 t.w.unlock(); 686 return; 687 } 688 if(dosnarf){ 689 q0 = t.q0; 690 q1 = t.q1; 691 snarfbuf.delete(0, snarfbuf.nc); 692 r = stralloc(BUFSIZE); 693 while(q0 < q1){ 694 n = q1 - q0; 695 if(n > BUFSIZE) 696 n = BUFSIZE; 697 t.file.buf.read(q0, r, 0, n); 698 snarfbuf.insert(snarfbuf.nc, r.s, n); 699 q0 += n; 700 } 701 strfree(r); 702 r = nil; 703 acme->putsnarf(); 704 } 705 if(docut){ 706 t.delete(t.q0, t.q1, TRUE); 707 t.setselect(t.q0, t.q0); 708 if(t.w != nil){ 709 scrl->scrdraw(t); 710 t.w.settag(); 711 } 712 }else if(dosnarf) # Snarf command 713 dat->argtext = t; 714 if(locked) 715 t.w.unlock(); 716} 717 718paste(et : ref Text, t : ref Text, selectall : int, tobody: int) 719{ 720 c : int; 721 q, q0, q1, n : int; 722 r : ref Astring; 723 724 # if(tobody), use body of executing window (Paste or Send command) 725 if(tobody && et!=nil && et.w!=nil){ 726 t = et.w.body; 727 t.file.mark(); # seq has been incremented by execute 728 } 729 if(t == nil) 730 return; 731 732 acme->getsnarf(); 733 if(t==nil || snarfbuf.nc==0) 734 return; 735 if(t.w!=nil && et.w!=t.w){ 736 c = 'M'; 737 if(et.w != nil) 738 c = et.w.owner; 739 t.w.lock(c); 740 } 741 cut(t, t, FALSE, TRUE); 742 q = 0; 743 q0 = t.q0; 744 q1 = t.q0+snarfbuf.nc; 745 r = stralloc(BUFSIZE); 746 while(q0 < q1){ 747 n = q1 - q0; 748 if(n > BUFSIZE) 749 n = BUFSIZE; 750 if(r == nil) 751 r = stralloc(n); 752 snarfbuf.read(q, r, 0, n); 753 t.insert(q0, r.s, n, TRUE, 0); 754 q += n; 755 q0 += n; 756 } 757 strfree(r); 758 r = nil; 759 if(selectall) 760 t.setselect(t.q0, q1); 761 else 762 t.setselect(q1, q1); 763 if(t.w != nil){ 764 scrl->scrdraw(t); 765 t.w.settag(); 766 } 767 if(t.w!=nil && et.w!=t.w) 768 t.w.unlock(); 769} 770 771look(et : ref Text, t : ref Text, argt : ref Text) 772{ 773 r : string; 774 s : ref Astring; 775 n : int; 776 777 if(et != nil && et.w != nil){ 778 t = et.w.body; 779 (nil, r, n) = getarg(argt, FALSE, FALSE); 780 if(r == nil){ 781 n = t.q1-t.q0; 782 s = stralloc(n); 783 t.file.buf.read(t.q0, s, 0, n); 784 r = s.s; 785 } 786 lookx->search(t, r, n); 787 r = nil; 788 } 789} 790 791send(et : ref Text, t : ref Text) 792{ 793 if(et.w==nil) 794 return; 795 t = et.w.body; 796 if(t.q0 != t.q1) 797 cut(t, t, TRUE, FALSE); 798 t.setselect(t.file.buf.nc, t.file.buf.nc); 799 paste(t, t, TRUE, TRUE); 800 if(t.readc(t.file.buf.nc-1) != '\n'){ 801 t.insert(t.file.buf.nc, "\n", 1, TRUE, 0); 802 t.setselect(t.file.buf.nc, t.file.buf.nc); 803 } 804} 805 806edit(et: ref Text, argt: ref Text, arg: string, narg: int) 807{ 808 r: string; 809 leng: int; 810 811 if(et == nil) 812 return; 813 (nil, r, leng) = getarg(argt, FALSE, TRUE); 814 seq++; 815 if(r != nil){ 816 editm->editcmd(et, r, leng); 817 r = nil; 818 }else 819 editm->editcmd(et, arg, narg); 820} 821 822exitx() 823{ 824 if(row.clean(TRUE)) 825 acme->acmeexit(nil); 826} 827 828putall() 829{ 830 i, j, e : int; 831 w : ref Window; 832 c : ref Column; 833 a : string; 834 835 for(i=0; i<row.ncol; i++){ 836 c = row.col[i]; 837 for(j=0; j<c.nw; j++){ 838 w = c.w[j]; 839 if(w.isscratch || w.isdir || len w.body.file.name==0) 840 continue; 841 if(w.nopen[QWevent] > byte 0) 842 continue; 843 a = w.body.file.name; 844 e = utils->access(a); 845 if(w.body.file.mod || w.body.ncache) 846 if(e < 0) 847 warning(nil, sprint("no auto-Put of %s: %r\n", a)); 848 else{ 849 w.commit(w.body); 850 put(w.body, nil, nil, 0); 851 } 852 a = nil; 853 } 854 } 855} 856 857id(et : ref Text) 858{ 859 if(et != nil && et.w != nil) 860 warning(nil, sprint("/mnt/acme/%d/\n", et.w.id)); 861} 862 863limbo(et: ref Text) 864{ 865 s := getname(et.w.body, nil, nil, 0, 0); 866 if(s == nil) 867 return; 868 for(l := len s; l > 0 && s[--l] != '/'; ) 869 ; 870 if(s[l] == '/') 871 s = s[l+1: ]; 872 s = "limbo -gw " + s; 873 (dir, n) := dirname(et, nil, 0); 874 if(n==1 && dir[0]=='.'){ # sigh 875 dir = nil; 876 n = 0; 877 } 878 spawn run(nil, s, dir, n, TRUE, nil, nil, FALSE); 879} 880 881local(et : ref Text, argt : ref Text, arg : string) 882{ 883 a, aa : string; 884 dir : string; 885 n : int; 886 887 (aa, a) = getbytearg(argt, TRUE, TRUE); 888 889 (dir, n) = dirname(et, nil, 0); 890 if(n==1 && dir[0]=='.'){ # sigh 891 dir = nil; 892 n = 0; 893 } 894 spawn run(nil, arg, dir, n, FALSE, aa, a, FALSE); 895} 896 897kill(argt : ref Text, arg : string, narg : int) 898{ 899 a, cmd, r : string; 900 na : int; 901 902 (nil, r, na) = getarg(argt, FALSE, FALSE); 903 if(r != nil) 904 kill(nil, r, na); 905 # loop condition: *arg is not a blank 906 for(;;){ 907 (a, na) = findbl(arg, narg); 908 if(a == arg) 909 break; 910 cmd = arg[0:narg-na]; 911 dat->ckill <-= cmd; 912 (arg, narg) = skipbl(a, na); 913 } 914} 915 916lineno(et : ref Text) 917{ 918 n : int; 919 920 if (et == nil || et.w == nil || (et = et.w.body) == nil) 921 return; 922 q0 := et.q0; 923 q1 := et.q1; 924 if (q0 < 0 || q1 < 0 || q0 > q1) 925 return; 926 ln0 := 1; 927 ln1 := 1; 928 rp := stralloc(BUFSIZE); 929 nc := et.file.buf.nc; 930 if (q0 >= nc) 931 q0 = nc-1; 932 if (q1 >= nc) 933 q1 = nc-1; 934 for (q := 0; q < q1; ) { 935 if (q+BUFSIZE > nc) 936 n = nc-q; 937 else 938 n = BUFSIZE; 939 et.file.buf.read(q, rp, 0, n); 940 for (i := 0; i < n && q < q1; i++) { 941 if (rp.s[i] == '\n') { 942 if (q < q0) 943 ln0++; 944 if (q < q1-1) 945 ln1++; 946 } 947 q++; 948 } 949 } 950 rp = nil; 951 if (et.file.name != nil) 952 file := et.file.name + ":"; 953 else 954 file = nil; 955 if (ln0 == ln1) 956 warning(nil, sprint("%s%d\n", file, ln0)); 957 else 958 warning(nil, sprint("%s%d,%d\n", file, ln0, ln1)); 959} 960 961fontx(et : ref Text, t : ref Text, argt : ref Text, arg : string, narg : int) 962{ 963 a, r, flag, file : string; 964 na, nf : int; 965 aa : string; 966 newfont : ref Reffont; 967 dp : ref Dat->Dirlist; 968 i, fix : int; 969 970 if(et==nil || et.w==nil) 971 return; 972 t = et.w.body; 973 flag = nil; 974 file = nil; 975 # loop condition: *arg is not a blank 976 nf = 0; 977 for(;;){ 978 (a, na) = findbl(arg, narg); 979 if(a == arg) 980 break; 981 r = arg[0:narg-na]; 982 if(r == "fix" || r == "var"){ 983 flag = nil; 984 flag = r; 985 }else{ 986 file = r; 987 nf = narg-na; 988 } 989 (arg, narg) = skipbl(a, na); 990 } 991 (nil, r, na) = getarg(argt, FALSE, TRUE); 992 if(r != nil) 993 if(r == "fix" || r == "var"){ 994 flag = nil; 995 flag = r; 996 }else{ 997 file = r; 998 nf = na; 999 } 1000 fix = 1; 1001 if(flag != nil) 1002 fix = flag == "fix"; 1003 else if(file == nil){ 1004 newfont = Reffont.get(FALSE, FALSE, FALSE, nil); 1005 if(newfont != nil) 1006 fix = newfont.f.name == t.frame.font.name; 1007 } 1008 if(file != nil){ 1009 aa = file[0:nf]; 1010 newfont = Reffont.get(fix, flag!=nil, FALSE, aa); 1011 aa = nil; 1012 }else 1013 newfont = Reffont.get(fix, FALSE, FALSE, nil); 1014 if(newfont != nil){ 1015 graph->draw(gui->mainwin, t.w.r, acme->textcols[Framem->BACK], nil, (0, 0)); 1016 t.reffont.close(); 1017 t.reffont = newfont; 1018 t.frame.font = newfont.f; 1019 if(t.w.isdir){ 1020 t.all.min.x++; # force recolumnation; disgusting! 1021 for(i=0; i<t.w.ndl; i++){ 1022 dp = t.w.dlp[i]; 1023 aa = dp.r; 1024 dp.wid = graph->strwidth(newfont.f, aa); 1025 aa = nil; 1026 } 1027 } 1028 # avoid shrinking of window due to quantization 1029 t.w.col.grow(t.w, -1, 1); 1030 } 1031 file = nil; 1032 flag = nil; 1033} 1034 1035incl(et : ref Text, argt : ref Text, arg : string, narg : int) 1036{ 1037 a, r : string; 1038 w : ref Window; 1039 na, n, leng : int; 1040 1041 if(et==nil || et.w==nil) 1042 return; 1043 w = et.w; 1044 n = 0; 1045 (nil, r, leng) = getarg(argt, FALSE, TRUE); 1046 if(r != nil){ 1047 n++; 1048 w.addincl(r, leng); 1049 } 1050 # loop condition: *arg is not a blank 1051 for(;;){ 1052 (a, na) = findbl(arg, narg); 1053 if(a == arg) 1054 break; 1055 r = arg[0:narg-na]; 1056 n++; 1057 w.addincl(r, narg-na); 1058 (arg, narg) = skipbl(a, na); 1059 } 1060 if(n==0 && w.nincl){ 1061 for(n=w.nincl; --n>=0; ) 1062 warning(nil, sprint("%s ", w.incl[n])); 1063 warning(nil, "\n"); 1064 } 1065} 1066 1067tab(et : ref Text, argt : ref Text, arg : string, narg : int) 1068{ 1069 a, r, p : string; 1070 w : ref Window; 1071 na, leng, tab : int; 1072 1073 if(et==nil || et.w==nil) 1074 return; 1075 w = et.w; 1076 (nil, r, leng) = getarg(argt, FALSE, TRUE); 1077 tab = 0; 1078 if(r!=nil && leng>0){ 1079 p = r[0:leng]; 1080 if('0'<=p[0] && p[0]<='9') 1081 tab = int p; 1082 p = nil; 1083 }else{ 1084 (a, na) = findbl(arg, narg); 1085 if(a != arg){ 1086 p = arg[0:narg-na]; 1087 if('0'<=p[0] && p[0]<='9') 1088 tab = int p; 1089 p = nil; 1090 } 1091 } 1092 if(tab > 0){ 1093 if(w.body.tabstop != tab){ 1094 w.body.tabstop = tab; 1095 w.reshape(w.r, 1); 1096 } 1097 }else 1098 warning(nil, sys->sprint("%s: Tab %d\n", w.body.file.name, w.body.tabstop)); 1099} 1100 1101alphabet(et: ref Text, argt: ref Text, arg: string, narg: int) 1102{ 1103 r: string; 1104 leng: int; 1105 1106 if(et == nil) 1107 return; 1108 (nil, r, leng) = getarg(argt, FALSE, FALSE); 1109 if(r != nil) 1110 setalphabet(r[0:leng]); 1111 else 1112 setalphabet(arg[0:narg]); 1113} 1114 1115runfeed(p : array of ref Sys->FD, c : chan of int) 1116{ 1117 n : int; 1118 buf : array of byte; 1119 s : string; 1120 1121 sys->pctl(Sys->FORKFD, nil); 1122 c <-= 1; 1123 # p[1] = nil; 1124 buf = array[256] of byte; 1125 for(;;){ 1126 if((n = sys->read(p[0], buf, 256)) <= 0) 1127 break; 1128 s = string buf[0:n]; 1129 dat->cerr <-= s; 1130 s = nil; 1131 } 1132 buf = nil; 1133 exit; 1134} 1135 1136run(win : ref Window, s : string, rdir : string, ndir : int, newns : int, argaddr : string, arg : string, iseditcmd: int) 1137{ 1138 c : ref Dat->Command; 1139 name, dir : string; 1140 e, t : int; 1141 av : list of string; 1142 r : int; 1143 incl : array of string; 1144 inarg, i, nincl : int; 1145 tfd : ref Sys->FD; 1146 p : array of ref Sys->FD; 1147 pc : chan of int; 1148 winid : int; 1149 1150 c = ref Dat->Command; 1151 t = 0; 1152 while(t < len s && (s[t]==' ' || s[t]=='\n' || s[t]=='\t')) 1153 t++; 1154 for(e=t; e < len s; e++) 1155 if(s[e]==' ' || s[e]=='\n' || s[e]=='\t' ) 1156 break; 1157 name = s[t:e]; 1158 e = utils->strrchr(name, '/'); 1159 if(e >= 0) 1160 name = name[e+1:]; 1161 name += " "; # add blank here for ease in waittask 1162 c.name = name; 1163 name = nil; 1164 pipechar := 0; 1165 if (t < len s && (s[t] == '<' || s[t] == '|' || s[t] == '>')){ 1166 pipechar = s[t++]; 1167 s = s[t:]; 1168 } 1169 c.pid = sys->pctl(0, nil); 1170 c.iseditcmd = iseditcmd; 1171 c.text = s; 1172 dat->ccommand <-= c; 1173 # 1174 # must pctl() after communication because rendezvous name 1175 # space is part of RFNAMEG. 1176 # 1177 1178 if(newns){ 1179 wids : string = ""; 1180 filename: string; 1181 1182 if(win != nil){ 1183 filename = win.body.file.name; 1184 wids = string win.id; 1185 nincl = win.nincl; 1186 incl = array[nincl] of string; 1187 for(i=0; i<nincl; i++) 1188 incl[i] = win.incl[i]; 1189 winid = win.id; 1190 win.close(); 1191 }else{ 1192 winid = 0; 1193 nincl = 0; 1194 incl = nil; 1195 if(dat->activewin != nil) 1196 winid = (dat->activewin).id; 1197 } 1198 # sys->pctl(Sys->FORKNS|Sys->FORKFD|Sys->NEWPGRP, nil); 1199 sys->pctl(Sys->FORKNS|Sys->NEWFD|Sys->FORKENV|Sys->NEWPGRP, 0::1::2::fsys->fsyscfd()::nil); 1200 if(rdir != nil){ 1201 dir = rdir[0:ndir]; 1202 sys->chdir(dir); # ignore error: probably app. window 1203 dir = nil; 1204 } 1205 if(filename != nil) 1206 utils->setenv("%", filename); 1207 c.md = fsys->fsysmount(rdir, ndir, incl, nincl); 1208 if(c.md == nil){ 1209 # error("child: can't mount /mnt/acme"); 1210 warning(nil, "can't mount /mnt/acme"); 1211 exit; 1212 } 1213 if(winid > 0 && (pipechar=='|' || pipechar=='>')){ 1214 buf := sys->sprint("/mnt/acme/%d/rdsel", winid); 1215 tfd = sys->open(buf, OREAD); 1216 } 1217 else 1218 tfd = sys->open("/dev/null", OREAD); 1219 sys->dup(tfd.fd, 0); 1220 tfd = nil; 1221 if((winid > 0 || iseditcmd) && (pipechar=='|' || pipechar=='<')){ 1222 buf: string; 1223 1224 if(iseditcmd){ 1225 if(winid > 0) 1226 buf = sprint("/mnt/acme/%d/editout", winid); 1227 else 1228 buf = sprint("/mnt/acme/editout"); 1229 } 1230 else 1231 buf = sys->sprint("/mnt/acme/%d/wrsel", winid); 1232 tfd = sys->open(buf, OWRITE); 1233 } 1234 else 1235 tfd = sys->open("/dev/cons", OWRITE); 1236 sys->dup(tfd.fd, 1); 1237 tfd = nil; 1238 if(winid > 0 && (pipechar=='|' || pipechar=='<')){ 1239 tfd = sys->open("/dev/cons", OWRITE); 1240 sys->dup(tfd.fd, 2); 1241 } 1242 else 1243 sys->dup(1, 2); 1244 tfd = nil; 1245 utils->setenv("acmewin", wids); 1246 }else{ 1247 if(win != nil) 1248 win.close(); 1249 sys->pctl(Sys->FORKFD|Sys->NEWPGRP, nil); 1250 if(rdir != nil){ 1251 dir = rdir[0:ndir]; 1252 sys->chdir(dir); # ignore error: probably app. window 1253 dir = nil; 1254 } 1255 p = array[2] of ref Sys->FD; 1256 if(sys->pipe(p) < 0){ 1257 error("child: can't pipe"); 1258 exit; 1259 } 1260 pc = chan of int; 1261 spawn runfeed(p, pc); 1262 <-pc; 1263 pc = nil; 1264 fsys->fsysclose(); 1265 tfd = sys->open("/dev/null", OREAD); 1266 sys->dup(tfd.fd, 0); 1267 tfd = nil; 1268 sys->dup(p[1].fd, 1); 1269 sys->dup(1, 2); 1270 p[0] = p[1] = nil; 1271 } 1272 1273 if(argaddr != nil) 1274 utils->setenv("acmeaddr", argaddr); 1275 hard := 0; 1276 if(len s > 512-10) # may need to print into stack 1277 hard = 1; 1278 else { 1279 inarg = FALSE; 1280 for(e=0; e < len s; e++){ 1281 r = s[e]; 1282 if(r==' ' || r=='\t') 1283 continue; 1284 if(r < ' ') { 1285 hard = 1; 1286 break; 1287 } 1288 if(utils->strchr("#;&|^$=`'{}()<>[]*?^~`", r) >= 0) { 1289 hard = 1; 1290 break; 1291 } 1292 inarg = TRUE; 1293 } 1294 if (!hard) { 1295 if(!inarg) 1296 exit; 1297 av = nil; 1298 sa := -1; 1299 for(e=0; e < len s; e++){ 1300 r = s[e]; 1301 if(r==' ' || r=='\t'){ 1302 if (sa >= 0) { 1303 av = s[sa:e] :: av; 1304 sa = -1; 1305 } 1306 continue; 1307 } 1308 if (sa < 0) 1309 sa = e; 1310 } 1311 if (sa >= 0) 1312 av = s[sa:e] :: av; 1313 if (arg != nil) 1314 av = arg :: av; 1315 av = utils->reverse(av); 1316 c.av = av; 1317 exec(hd av, av); 1318 dat->cwait <-= string c.pid + " \"Exec\":"; 1319 exit; 1320 } 1321 } 1322 1323 if(arg != nil){ 1324 s = sprint("%s '%s'", s, arg); # BUG: what if quote in arg? 1325 c.text = s; 1326 } 1327 av = nil; 1328 av = s :: av; 1329 av = "-c" :: av; 1330 av = "/dis/sh" :: av; 1331 exec(hd av, av); 1332 dat->cwait <-= string c.pid + " \"Exec\":"; 1333 exit; 1334} 1335 1336# Nasty bug causes 1337# Edit ,|nonexistentcommand 1338# (or ,> or ,<) to lock up acme. Easy fix. Add these two lines 1339# to the failure case of runwaittask(): 1340# 1341# /sys/src/cmd/acme/exec.c:1287 a exec.c:1288,1289 1342# else{ 1343# if(c->iseditcmd) 1344# sendul(cedit, 0); 1345# free(c->name); 1346# free(c->text); 1347# free(c); 1348# } 1349 1350 1351