9fans.net/go@v0.0.7/cmd/acme/xfid.go (about) 1 // #include <u.h> 2 // #include <libc.h> 3 // #include <draw.h> 4 // #include <thread.h> 5 // #include <cursor.h> 6 // #include <mouse.h> 7 // #include <keyboard.h> 8 // #include <frame.h> 9 // #include <fcall.h> 10 // #include <plumb.h> 11 // #include <libsec.h> 12 // #include "dat.h" 13 // #include "fns.h" 14 15 package main 16 17 import ( 18 "bytes" 19 "fmt" 20 "io" 21 "strings" 22 "unicode/utf8" 23 24 addrpkg "9fans.net/go/cmd/acme/internal/addr" 25 "9fans.net/go/cmd/acme/internal/adraw" 26 "9fans.net/go/cmd/acme/internal/alog" 27 "9fans.net/go/cmd/acme/internal/bufs" 28 "9fans.net/go/cmd/acme/internal/disk" 29 editpkg "9fans.net/go/cmd/acme/internal/edit" 30 "9fans.net/go/cmd/acme/internal/exec" 31 "9fans.net/go/cmd/acme/internal/file" 32 "9fans.net/go/cmd/acme/internal/runes" 33 "9fans.net/go/cmd/acme/internal/ui" 34 "9fans.net/go/cmd/acme/internal/util" 35 "9fans.net/go/cmd/acme/internal/wind" 36 "9fans.net/go/plan9" 37 ) 38 39 const ( 40 Ctlsize = 5 * 12 41 ) 42 43 var Edel string = "deleted window" 44 var Ebadctl string = "ill-formed control message" 45 var Ebadaddr string = "bad address syntax" 46 var Eaddr string = "address out of range" 47 var Einuse string = "already in use" 48 var Ebadevent string = "bad event syntax" 49 50 // extern var Eperm [unknown]C.char 51 52 func clampaddr(w *wind.Window) { 53 if w.Addr.Pos < 0 { 54 w.Addr.Pos = 0 55 } 56 if w.Addr.End < 0 { 57 w.Addr.End = 0 58 } 59 if w.Addr.Pos > w.Body.Len() { 60 w.Addr.Pos = w.Body.Len() 61 } 62 if w.Addr.End > w.Body.Len() { 63 w.Addr.End = w.Body.Len() 64 } 65 } 66 67 func xfidctl(x *Xfid) { 68 for { 69 f := <-x.c 70 bigLock() 71 f(x) 72 adraw.Display.Flush() 73 bigUnlock() 74 cxfidfree <- x 75 } 76 } 77 78 func xfidflush(x *Xfid) { 79 xfidlogflush(x) 80 81 // search windows for matching tag 82 bigUnlock() 83 wind.TheRow.Lk.Lock() 84 bigLock() 85 for j := 0; j < len(wind.TheRow.Col); j++ { 86 c := wind.TheRow.Col[j] 87 for i := 0; i < len(c.W); i++ { 88 w := c.W[i] 89 wind.Winlock(w, 'E') 90 ch := w.Eventwait 91 if ch != nil && w.Eventtag == x.fcall.Oldtag { 92 w.Eventwait = nil 93 ch <- false // flushed 94 wind.Winunlock(w) 95 goto out 96 } 97 wind.Winunlock(w) 98 } 99 } 100 out: 101 wind.TheRow.Lk.Unlock() 102 var fc plan9.Fcall 103 respond(x, &fc, "") 104 } 105 106 func xfidopen(x *Xfid) { 107 w := x.f.w 108 q := FILE(x.f.qid) 109 var fc plan9.Fcall 110 if w != nil { 111 t := &w.Body 112 wind.Winlock(w, 'E') 113 switch q { 114 case QWaddr: 115 tmp30 := nopen[wq{w, q}] 116 nopen[wq{w, q}]++ 117 if tmp30 == 0 { 118 w.Addr = runes.Rng(0, 0) 119 w.Limit = runes.Rng(-1, -1) 120 } 121 case QWdata, 122 QWxdata: 123 nopen[wq{w, q}]++ 124 case QWevent: 125 tmp31 := nopen[wq{w, q}] 126 nopen[wq{w, q}]++ 127 if tmp31 == 0 { 128 129 w.External = true 130 if !w.IsDir && w.Col != nil { 131 w.Filemenu = false 132 wind.Winsettag(w) 133 } 134 } 135 /* 136 * Use a temporary file. 137 * A pipe would be the obvious, but we can't afford the 138 * broken pipe notification. Using the code to read QWbody 139 * is n², which should probably also be fixed. Even then, 140 * though, we'd need to squirrel away the data in case it's 141 * modified during the operation, e.g. by |sort 142 */ 143 case QWrdsel: 144 if w.Rdselfd != nil { 145 wind.Winunlock(w) 146 respond(x, &fc, Einuse) 147 return 148 } 149 w.Rdselfd = disk.TempFile() // TODO(rsc): who deletes this? 150 if w.Rdselfd == nil { // TODO(rsc): impossible 151 wind.Winunlock(w) 152 respond(x, &fc, "can't create temp file") 153 return 154 } 155 nopen[wq{w, q}]++ 156 q0 := t.Q0 157 q1 := t.Q1 158 r := bufs.AllocRunes() 159 s := bufs.AllocRunes() 160 for q0 < q1 { 161 n := q1 - q0 162 if n > bufs.Len/utf8.UTFMax { 163 n = bufs.Len / utf8.UTFMax 164 } 165 t.File.Read(q0, r[:n]) 166 s := []byte(string(r[:n])) // TODO(rsc) 167 if _, err := w.Rdselfd.Write(s); err != nil { 168 alog.Printf("can't write temp file for pipe command %v\n", err) 169 break 170 } 171 q0 += n 172 } 173 bufs.FreeRunes(s) 174 bufs.FreeRunes(r) 175 case QWwrsel: 176 nopen[wq{w, q}]++ 177 file.Seq++ 178 t.File.Mark() 179 ui.XCut(t, t, nil, false, true, nil) 180 w.Wrselrange = runes.Rng(t.Q1, t.Q1) 181 w.Nomark = true 182 case QWeditout: 183 if editpkg.Editing == editpkg.Inactive { 184 wind.Winunlock(w) 185 respond(x, &fc, Eperm) 186 return 187 } 188 if !w.Editoutlk.TryLock() { 189 wind.Winunlock(w) 190 respond(x, &fc, Einuse) 191 return 192 } 193 w.Wrselrange = runes.Rng(t.Q1, t.Q1) 194 } 195 wind.Winunlock(w) 196 } else { 197 switch q { 198 case Qlog: 199 xfidlogopen(x) 200 case Qeditout: 201 if !editpkg.Editoutlk.TryLock() { 202 respond(x, &fc, Einuse) 203 return 204 } 205 } 206 } 207 fc.Qid = x.f.qid 208 fc.Iounit = uint32(messagesize - plan9.IOHDRSZ) 209 x.f.open = true 210 respond(x, &fc, "") 211 } 212 213 func xfidclose(x *Xfid) { 214 w := x.f.w 215 x.f.busy = false 216 x.f.w = nil 217 var fc plan9.Fcall 218 if !x.f.open { 219 if w != nil { 220 wind.Winclose(w) 221 } 222 respond(x, &fc, "") 223 return 224 } 225 226 q := FILE(x.f.qid) 227 x.f.open = false 228 if w != nil { 229 wind.Winlock(w, 'E') 230 var t *wind.Text 231 switch q { 232 case QWctl: 233 if w.Ctlfid != ^0 && w.Ctlfid == x.f.fid { 234 w.Ctlfid = ^0 235 w.Ctllock.Unlock() 236 } 237 case QWdata, 238 QWxdata: 239 w.Nomark = false 240 fallthrough 241 // fall through 242 case QWaddr, 243 QWevent: // BUG: do we need to shut down Xfid? 244 nopen[wq{w, q}]-- 245 if nopen[wq{w, q}] == 0 { 246 if q == QWdata || q == QWxdata { 247 w.Nomark = false 248 } 249 if q == QWevent && !w.IsDir && w.Col != nil { 250 w.Filemenu = true 251 wind.Winsettag(w) 252 } 253 if q == QWevent { 254 255 w.External = false 256 w.Dumpstr = "" 257 w.Dumpdir = "" 258 } 259 } 260 case QWrdsel: 261 w.Rdselfd.Close() 262 w.Rdselfd = nil 263 case QWwrsel: 264 w.Nomark = false 265 t = &w.Body 266 // before: only did this if !w->noscroll, but that didn't seem right in practice 267 wind.Textshow(t, util.Min(w.Wrselrange.Pos, t.Len()), util.Min(w.Wrselrange.End, t.Len()), true) 268 wind.Textscrdraw(t) 269 case QWeditout: 270 w.Editoutlk.Unlock() 271 } 272 wind.Winunlock(w) 273 wind.Winclose(w) 274 } else { 275 switch q { 276 case Qeditout: 277 editpkg.Editoutlk.Unlock() 278 } 279 } 280 respond(x, &fc, "") 281 } 282 283 func xfidread(x *Xfid) { 284 q := FILE(x.f.qid) 285 w := x.f.w 286 var fc plan9.Fcall 287 if w == nil { 288 fc.Count = 0 289 switch q { 290 case Qcons, 291 Qlabel: 292 break 293 case Qindex: 294 xfidindexread(x) 295 return 296 case Qlog: 297 xfidlogread(x) 298 return 299 default: 300 alog.Printf("unknown qid %d\n", q) 301 } 302 respond(x, &fc, "") 303 return 304 } 305 306 wind.Winlock(w, 'F') 307 if w.Col == nil { 308 wind.Winunlock(w) 309 respond(x, &fc, Edel) 310 return 311 } 312 defer wind.Winunlock(w) 313 314 off := int64(x.fcall.Offset) 315 var buf []byte 316 switch q { 317 case QWaddr: 318 wind.Textcommit(&w.Body, true) 319 clampaddr(w) 320 buf = []byte(fmt.Sprintf("%11d %11d ", w.Addr.Pos, w.Addr.End)) 321 goto Readbuf 322 323 case QWbody: 324 xfidutfread(x, &w.Body, w.Body.Len(), QWbody) 325 326 case QWctl: 327 buf = []byte(wind.Winctlprint(w, true)) 328 goto Readbuf 329 330 case QWevent: 331 xfideventread(x, w) 332 333 case QWdata: 334 // BUG: what should happen if q1 > q0? 335 if w.Addr.Pos > w.Body.Len() { 336 respond(x, &fc, Eaddr) 337 break 338 } 339 w.Addr.Pos += xfidruneread(x, &w.Body, w.Addr.Pos, w.Body.Len()) 340 w.Addr.End = w.Addr.Pos 341 342 case QWxdata: 343 // BUG: what should happen if q1 > q0? 344 if w.Addr.Pos > w.Body.Len() { 345 respond(x, &fc, Eaddr) 346 break 347 } 348 w.Addr.Pos += xfidruneread(x, &w.Body, w.Addr.Pos, w.Addr.End) 349 350 case QWtag: 351 xfidutfread(x, &w.Tag, w.Tag.Len(), QWtag) 352 353 case QWrdsel: 354 w.Rdselfd.Seek(int64(off), 0) 355 n := int(x.fcall.Count) 356 if x.fcall.Count > bufs.Len { 357 n = bufs.Len 358 } 359 b := make([]byte, bufs.Len) // TODO fbufalloc() 360 n, err := w.Rdselfd.Read(b[:n]) 361 if err != nil && err != io.EOF { 362 respond(x, &fc, "I/O error in temp file") 363 break 364 } 365 fc.Count = uint32(n) 366 fc.Data = b[:n] 367 respond(x, &fc, "") 368 // fbuffree(b) 369 370 default: 371 respond(x, &fc, fmt.Sprintf("unknown qid %d in read", q)) 372 } 373 return 374 375 Readbuf: 376 if off > int64(len(buf)) { 377 off = int64(len(buf)) 378 } 379 fc.Data = buf[off:] 380 if int64(len(fc.Data)) > int64(x.fcall.Count) { 381 fc.Data = fc.Data[:x.fcall.Count] 382 } 383 fc.Count = uint32(len(fc.Data)) 384 respond(x, &fc, "") 385 } 386 387 func shouldscroll(t *wind.Text, q0 int, qid int) bool { 388 if qid == Qcons { 389 return true 390 } 391 return t.Org <= q0 && q0 <= t.Org+t.Fr.NumChars 392 } 393 394 func fullrunewrite(x *Xfid) []rune { 395 q := len(x.f.rpart) 396 cnt := len(x.fcall.Data) 397 if q > 0 { 398 x.fcall.Data = x.fcall.Data[:cnt+q] 399 copy(x.fcall.Data[q:], x.fcall.Data) 400 copy(x.fcall.Data, x.f.rpart[:q]) 401 x.f.rpart = x.f.rpart[:0] 402 } 403 r := make([]rune, cnt) 404 nb, nr, _ := runes.Convert(x.fcall.Data, r, false) 405 r = r[:nr] 406 // approach end of buffer 407 for utf8.FullRune(x.fcall.Data[nb:cnt]) { 408 ch, w := utf8.DecodeRune(x.fcall.Data[nb:]) 409 nb += w 410 if ch != 0 { 411 r = append(r, ch) 412 } 413 } 414 if nb < cnt { 415 if cap(x.f.rpart) < utf8.UTFMax { 416 x.f.rpart = make([]byte, 0, utf8.UTFMax) 417 } 418 x.f.rpart = append(x.f.rpart, x.fcall.Data[nb:]...) 419 } 420 return r 421 } 422 423 func xfidwrite(x *Xfid) { 424 qid := FILE(x.f.qid) 425 w := x.f.w 426 var fc plan9.Fcall 427 if w != nil { 428 c := 'F' 429 if qid == QWtag || qid == QWbody { 430 c = 'E' 431 } 432 wind.Winlock(w, c) 433 if w.Col == nil { 434 wind.Winunlock(w) 435 respond(x, &fc, Edel) 436 return 437 } 438 } 439 switch qid { 440 case Qlabel: 441 fc.Count = uint32(len(x.fcall.Data)) 442 respond(x, &fc, "") 443 444 case QWaddr: 445 r := []rune(string(x.fcall.Data)) 446 t := &w.Body 447 wind.Wincommit(w, t) 448 eval := true 449 var nb int 450 a := addrpkg.Eval(false, t, w.Limit, w.Addr, r, 0, len(r), rgetc, &eval, &nb) 451 if nb < len(r) { 452 respond(x, &fc, Ebadaddr) 453 break 454 } 455 if !eval { 456 respond(x, &fc, Eaddr) 457 break 458 } 459 w.Addr = a 460 fc.Count = uint32(len(x.fcall.Data)) 461 respond(x, &fc, "") 462 463 case Qeditout, 464 QWeditout: 465 r := fullrunewrite(x) 466 var err error 467 if w != nil { 468 err = editpkg.Edittext(w, w.Wrselrange.End, r) 469 } else { 470 err = editpkg.Edittext(nil, 0, r) 471 } 472 if err != nil { 473 respond(x, &fc, err.Error()) 474 break 475 } 476 fc.Count = uint32(len(x.fcall.Data)) 477 respond(x, &fc, "") 478 479 case QWctl: 480 xfidctlwrite(x, w) 481 482 case QWdata: 483 a := w.Addr 484 t := &w.Body 485 wind.Wincommit(w, t) 486 if a.Pos > t.Len() || a.End > t.Len() { 487 respond(x, &fc, Eaddr) 488 break 489 } 490 r := make([]rune, len(x.fcall.Data)) 491 _, nr, _ := runes.Convert(x.fcall.Data, r, true) 492 r = r[:nr] 493 if !w.Nomark { 494 file.Seq++ 495 t.File.Mark() 496 } 497 q0 := a.Pos 498 if a.End > q0 { 499 wind.Textdelete(t, q0, a.End, true) 500 w.Addr.End = q0 501 } 502 tq0 := t.Q0 503 tq1 := t.Q1 504 wind.Textinsert(t, q0, r, true) 505 if tq0 >= q0 { 506 tq0 += nr 507 } 508 if tq1 >= q0 { 509 tq1 += nr 510 } 511 wind.Textsetselect(t, tq0, tq1) 512 if shouldscroll(t, q0, qid) { 513 wind.Textshow(t, q0+nr, q0+nr, false) 514 } 515 wind.Textscrdraw(t) 516 wind.Winsettag(w) 517 w.Addr.Pos += nr 518 w.Addr.End = w.Addr.Pos 519 fc.Count = uint32(len(x.fcall.Data)) 520 respond(x, &fc, "") 521 522 case QWevent: 523 xfideventwrite(x, w) 524 525 case Qcons, QWerrors, QWbody, QWwrsel, QWtag: 526 var t *wind.Text 527 switch qid { 528 case Qcons: 529 w = errorwin(x.f.mntdir, 'X') 530 t = &w.Body 531 532 case QWerrors: 533 w = errorwinforwin(w) 534 t = &w.Body 535 536 case QWbody, 537 QWwrsel: 538 t = &w.Body 539 540 case QWtag: 541 t = &w.Tag 542 } 543 544 r := fullrunewrite(x) 545 if len(r) > 0 { 546 wind.Wincommit(w, t) 547 var q0 int 548 if qid == QWwrsel { 549 q0 = w.Wrselrange.End 550 if q0 > t.Len() { 551 q0 = t.Len() 552 } 553 } else { 554 q0 = t.Len() 555 } 556 nr := len(r) 557 if qid == QWtag { 558 wind.Textinsert(t, q0, r, true) 559 } else { 560 if !w.Nomark { 561 file.Seq++ 562 t.File.Mark() 563 } 564 q0 = wind.Textbsinsert(t, q0, r, true, &nr) 565 wind.Textsetselect(t, t.Q0, t.Q1) // insert could leave it somewhere else 566 if qid != QWwrsel && shouldscroll(t, q0, qid) { 567 wind.Textshow(t, q0+nr, q0+nr, true) 568 } 569 wind.Textscrdraw(t) 570 } 571 wind.Winsettag(w) 572 if qid == QWwrsel { 573 w.Wrselrange.End += nr 574 } 575 } 576 fc.Count = uint32(len(x.fcall.Data)) 577 respond(x, &fc, "") 578 579 default: 580 respond(x, &fc, fmt.Sprintf("unknown qid %d in write", qid)) 581 } 582 if w != nil { 583 // Note: Cannot defer above - w changes in errorwinforwin call. 584 wind.Winunlock(w) 585 } 586 } 587 588 func xfidctlwrite(x *Xfid, w *wind.Window) { 589 scrdraw := false 590 settag := false 591 isfbuf := true 592 var r []rune 593 if int(x.fcall.Count) < bufs.RuneLen { 594 r = bufs.AllocRunes() 595 } else { 596 isfbuf = false 597 r = make([]rune, x.fcall.Count*utf8.UTFMax) 598 } 599 wind.Textcommit(&w.Tag, true) 600 p := string(x.fcall.Data) 601 var err string 602 for p != "" { 603 if strings.HasPrefix(p, "lock") { // make window exclusive use 604 w.Ctllock.Lock() 605 w.Ctlfid = x.f.fid 606 p = p[4:] 607 } else if strings.HasPrefix(p, "unlock") { // release exclusive use 608 w.Ctlfid = ^0 609 w.Ctllock.Unlock() 610 p = p[6:] 611 } else if strings.HasPrefix(p, "clean") { // mark window 'clean', seq=0 612 t := &w.Body 613 t.Eq0 = ^0 614 t.File.ResetLogs() 615 t.File.SetMod(false) 616 w.Dirty = false 617 settag = true 618 p = p[5:] 619 } else if strings.HasPrefix(p, "dirty") { // mark window 'dirty' 620 t := &w.Body 621 // doesn't change sequence number, so "Put" won't appear. it shouldn't. 622 t.File.SetMod(true) 623 w.Dirty = true 624 settag = true 625 p = p[5:] 626 } else if strings.HasPrefix(p, "show") { // show dot 627 t := &w.Body 628 wind.Textshow(t, t.Q0, t.Q1, true) 629 p = p[4:] 630 } else if strings.HasPrefix(p, "name ") { // set file name 631 pp := p[5:] 632 p = p[5:] 633 i := strings.Index(pp, "\n") 634 if i <= 0 { 635 err = Ebadctl 636 break 637 } 638 pp = pp[:i] 639 p = p[i+1:] 640 r := make([]rune, len(pp)) 641 _, nr, nulls := runes.Convert([]byte(pp), r, true) 642 if nulls { 643 err = "nulls in file name" 644 break 645 } 646 r = r[:nr] 647 for i := 0; i < nr; i++ { 648 if r[i] <= ' ' { 649 err = "bad character in file name" 650 goto out // TODO(rsc): still set name? 651 } 652 } 653 out: 654 file.Seq++ 655 w.Body.File.Mark() 656 wind.Winsetname(w, r[:nr]) 657 } else if strings.HasPrefix(p, "font ") { // execute font command 658 pp := p[5:] 659 p = p[5:] 660 i := strings.Index(pp, "\n") 661 if i <= 0 { 662 err = Ebadctl 663 break 664 } 665 pp = pp[:i] 666 p = p[i+1:] 667 r := make([]rune, len(pp)) 668 _, nr, nulls := runes.Convert([]byte(pp), r, true) 669 if nulls { 670 err = "nulls in font string" 671 break 672 } 673 r = r[:nr] 674 ui.Fontx(&w.Body, nil, nil, false, exec.XXX, r) 675 } else if strings.HasPrefix(p, "dump ") { // set dump string 676 pp := p[5:] 677 p = p[5:] 678 i := strings.Index(pp, "\n") 679 if i <= 0 { 680 err = Ebadctl 681 break 682 } 683 pp = pp[:i] 684 p = p[i+1:] 685 r := make([]rune, len(pp)) 686 _, nr, nulls := runes.Convert([]byte(pp), r, true) 687 if nulls { 688 err = "nulls in dump string" 689 break 690 } 691 r = r[:nr] 692 w.Dumpstr = string(r) 693 } else if strings.HasPrefix(p, "dumpdir ") { // set dump directory 694 pp := p[8:] 695 p = p[8:] 696 i := strings.Index(pp, "\n") 697 if i <= 0 { 698 err = Ebadctl 699 break 700 } 701 pp = pp[:i] 702 p = p[i+1:] 703 r := make([]rune, len(pp)) 704 _, nr, nulls := runes.Convert([]byte(pp), r, true) 705 if nulls { 706 err = "nulls in dump string" 707 break 708 } 709 r = r[:nr] 710 w.Dumpdir = string(r) 711 } else if strings.HasPrefix(p, "delete") { // delete for sure 712 ui.ColcloseAndMouse(w.Col, w, true) 713 p = p[6:] 714 } else if strings.HasPrefix(p, "del") { // delete, but check dirty 715 if !wind.Winclean(w, true) { 716 err = "file dirty" 717 break 718 } 719 ui.ColcloseAndMouse(w.Col, w, true) 720 p = p[3:] 721 } else if strings.HasPrefix(p, "get") { // get file 722 exec.Get(&w.Body, nil, nil, false, exec.XXX, nil) 723 p = p[3:] 724 } else if strings.HasPrefix(p, "put") { // put file 725 exec.Put(&w.Body, nil, nil, exec.XXX, exec.XXX, nil) 726 p = p[3:] 727 } else if strings.HasPrefix(p, "dot=addr") { // set dot 728 wind.Textcommit(&w.Body, true) 729 clampaddr(w) 730 w.Body.Q0 = w.Addr.Pos 731 w.Body.Q1 = w.Addr.End 732 wind.Textsetselect(&w.Body, w.Body.Q0, w.Body.Q1) 733 settag = true 734 p = p[8:] 735 } else if strings.HasPrefix(p, "addr=dot") { // set addr 736 w.Addr.Pos = w.Body.Q0 737 w.Addr.End = w.Body.Q1 738 p = p[8:] 739 } else if strings.HasPrefix(p, "limit=addr") { // set limit 740 wind.Textcommit(&w.Body, true) 741 clampaddr(w) 742 w.Limit.Pos = w.Addr.Pos 743 w.Limit.End = w.Addr.End 744 p = p[10:] 745 } else if strings.HasPrefix(p, "nomark") { // turn off automatic marking 746 w.Nomark = true 747 p = p[6:] 748 } else if strings.HasPrefix(p, "mark") { // mark file 749 file.Seq++ 750 w.Body.File.Mark() 751 settag = true 752 p = p[4:] 753 } else if strings.HasPrefix(p, "nomenu") { // turn off automatic menu 754 w.Filemenu = false 755 settag = true 756 p = p[6:] 757 } else if strings.HasPrefix(p, "menu") { // enable automatic menu 758 w.Filemenu = true 759 settag = true 760 p = p[4:] 761 } else if strings.HasPrefix(p, "cleartag") { // wipe tag right of bar 762 wind.Wincleartatg(w) 763 settag = true 764 p = p[8:] 765 } else { 766 err = Ebadctl 767 break 768 } 769 for p != "" && p[0] == '\n' { 770 p = p[1:] 771 } 772 } 773 774 if isfbuf { 775 bufs.FreeRunes(r) 776 } 777 n := len(x.fcall.Data) 778 if err != "" { 779 n = 0 780 } 781 var fc plan9.Fcall 782 fc.Count = uint32(n) 783 respond(x, &fc, err) 784 if settag { 785 wind.Winsettag(w) 786 } 787 if scrdraw { 788 wind.Textscrdraw(&w.Body) 789 } 790 } 791 792 func xfideventwrite(x *Xfid, w *wind.Window) { 793 isfbuf := true 794 var r []rune 795 if len(x.fcall.Data) < bufs.RuneLen { 796 r = bufs.AllocRunes() 797 } else { 798 isfbuf = false 799 r = make([]rune, len(x.fcall.Data)*utf8.UTFMax) 800 } 801 var err string 802 p := x.fcall.Data 803 for len(p) > 0 { 804 // Parse event. 805 w.Owner = rune(p[0]) 806 p = p[1:] 807 if len(p) == 0 { 808 goto Rescue 809 } 810 c := p[0] 811 p = p[1:] 812 for len(p) > 0 && p[0] == ' ' { 813 p = p[1:] 814 } 815 q0, i := strtoul(p) 816 if i == 0 { 817 goto Rescue 818 } 819 p = p[i:] 820 for len(p) > 0 && p[0] == ' ' { 821 p = p[1:] 822 } 823 q1, i := strtoul(p) 824 if i == 0 { 825 goto Rescue 826 } 827 p = p[i:] 828 for len(p) > 0 && p[0] == ' ' { 829 p = p[1:] 830 } 831 if len(p) == 0 || p[0] != '\n' { 832 goto Rescue 833 } 834 p = p[1:] 835 836 // Apply event. 837 var t *wind.Text 838 if 'a' <= c && c <= 'z' { 839 t = &w.Tag 840 } else if 'A' <= c && c <= 'Z' { 841 t = &w.Body 842 } else { 843 goto Rescue 844 } 845 if q0 > t.Len() || q1 > t.Len() || q0 > q1 { 846 goto Rescue 847 } 848 849 bigUnlock() 850 wind.TheRow.Lk.Lock() // just like mousethread 851 bigLock() 852 switch c { 853 case 'x', 854 'X': 855 exec.Execute(t, q0, q1, true, nil) 856 case 'l', 857 'L': 858 ui.Look3(t, q0, q1, true) 859 default: 860 wind.TheRow.Lk.Unlock() 861 goto Rescue 862 } 863 wind.TheRow.Lk.Unlock() 864 } 865 goto Out 866 867 Rescue: 868 err = Ebadevent 869 goto Out 870 871 Out: 872 if isfbuf { 873 bufs.FreeRunes(r) 874 } 875 n := len(x.fcall.Data) 876 if err != "" { 877 n = 0 878 } 879 var fc plan9.Fcall 880 fc.Count = uint32(n) 881 respond(x, &fc, err) 882 } 883 884 func strtoul(p []byte) (value, width int) { 885 i := 0 886 for i < len(p) && '0' <= p[i] && p[i] <= '9' { 887 value = value*10 + int(p[i]) - '0' 888 i++ 889 } 890 return value, i 891 } 892 893 func xfidutfread(x *Xfid, t *wind.Text, q1 int, qid int) { 894 w := t.W 895 wind.Wincommit(w, t) 896 off := int64(x.fcall.Offset) 897 r := bufs.AllocRunes() 898 b1 := make([]byte, bufs.Len) // fbufalloc() 899 n := 0 900 var q int 901 var boff int64 902 if qid == w.Utflastqid && off >= int64(w.Utflastboff) && w.Utflastq <= q1 { 903 boff = w.Utflastboff 904 q = w.Utflastq 905 } else { 906 // BUG: stupid code: scan from beginning 907 boff = 0 908 q = 0 909 } 910 w.Utflastqid = qid 911 for q < q1 && n < int(x.fcall.Count) { 912 /* 913 * Updating here avoids partial rune problem: we're always on a 914 * char boundary. The cost is we will usually do one more read 915 * than we really need, but that's better than being n^2. 916 */ 917 w.Utflastboff = boff 918 w.Utflastq = q 919 nr := q1 - q 920 if nr > bufs.Len/utf8.UTFMax { 921 nr = bufs.Len / utf8.UTFMax 922 } 923 t.File.Read(q, r[:nr]) 924 b := []byte(string(r[:nr])) 925 if boff >= off { 926 m := len(b) 927 if boff+int64(m) > off+int64(x.fcall.Count) { 928 m = int(off + int64(x.fcall.Count) - boff) 929 } 930 copy(b1[n:], b[:m]) 931 n += m 932 } else if boff+int64(len(b)) > off { 933 if n != 0 { 934 util.Fatal("bad count in utfrune") 935 } 936 m := int(int64(len(b)) - (off - boff)) 937 if m > int(x.fcall.Count) { 938 m = int(x.fcall.Count) 939 } 940 copy(b1[:m], b[off-boff:]) 941 n += m 942 } 943 boff += int64(len(b)) 944 q += nr 945 } 946 bufs.FreeRunes(r) 947 var fc plan9.Fcall 948 fc.Count = uint32(n) 949 fc.Data = b1[:n] 950 respond(x, &fc, "") 951 // TODO fbuffree(b1) 952 } 953 954 func xfidruneread(x *Xfid, t *wind.Text, q0 int, q1 int) int { 955 w := t.W 956 wind.Wincommit(w, t) 957 r := bufs.AllocRunes() 958 // b := fbufalloc() 959 b1 := make([]byte, bufs.Len) // fbufalloc() 960 n := 0 961 q := q0 962 boff := 0 963 for q < q1 && n < int(x.fcall.Count) { 964 nr := q1 - q 965 if nr > bufs.Len/utf8.UTFMax { 966 nr = bufs.Len / utf8.UTFMax 967 } 968 t.File.Read(q, r[:nr]) 969 b := []byte(string(r[:nr])) 970 nb := len(b) 971 m := nb 972 if boff+m > int(x.fcall.Count) { 973 i := int(x.fcall.Count) - boff 974 // copy whole runes only 975 m = 0 976 nr = 0 977 for m < i { 978 _, rw := utf8.DecodeRune(b[m:]) 979 if m+rw > i { 980 break 981 } 982 m += rw 983 nr++ 984 } 985 if m == 0 { 986 break 987 } 988 } 989 copy(b1[n:], b[:m]) 990 n += m 991 boff += nb 992 q += nr 993 } 994 bufs.FreeRunes(r) 995 var fc plan9.Fcall 996 fc.Count = uint32(n) 997 fc.Data = b1[:n] 998 respond(x, &fc, "") 999 return q - q0 1000 } 1001 1002 func xfideventread(x *Xfid, w *wind.Window) { 1003 x.flushed = false 1004 var fc plan9.Fcall 1005 if len(w.Events) == 0 { 1006 c := make(chan bool, 1) 1007 w.Eventtag = x.fcall.Tag 1008 w.Eventwait = c 1009 wind.Winunlock(w) 1010 bigUnlock() 1011 ok := <-w.Eventwait 1012 bigLock() 1013 wind.Winlock(w, 'F') 1014 if !ok { 1015 return 1016 } 1017 if len(w.Events) == 0 { 1018 respond(x, &fc, "window shut down") 1019 return 1020 } 1021 } 1022 1023 n := len(w.Events) 1024 if n > int(x.fcall.Count) { 1025 n = int(x.fcall.Count) 1026 } 1027 fc.Count = uint32(n) 1028 fc.Data = w.Events[:n] 1029 respond(x, &fc, "") 1030 m := copy(w.Events[n:], w.Events) 1031 w.Events = w.Events[:m] 1032 } 1033 1034 func xfidindexread(x *Xfid) { 1035 wind.TheRow.Lk.Lock() 1036 nmax := 0 1037 var i int 1038 var j int 1039 var w *wind.Window 1040 var c *wind.Column 1041 for j = 0; j < len(wind.TheRow.Col); j++ { 1042 c = wind.TheRow.Col[j] 1043 for i = 0; i < len(c.W); i++ { 1044 w = c.W[i] 1045 nmax += Ctlsize + w.Tag.Len()*utf8.UTFMax + 1 1046 } 1047 } 1048 nmax++ 1049 var buf bytes.Buffer 1050 r := bufs.AllocRunes() 1051 for j = 0; j < len(wind.TheRow.Col); j++ { 1052 c = wind.TheRow.Col[j] 1053 for i = 0; i < len(c.W); i++ { 1054 w = c.W[i] 1055 // only show the currently active window of a set 1056 if w.Body.File.Curtext != &w.Body { 1057 continue 1058 } 1059 buf.WriteString(wind.Winctlprint(w, false)) 1060 m := util.Min(bufs.RuneLen, w.Tag.Len()) 1061 w.Tag.File.Read(0, r[:m]) 1062 for i := 0; i < m && r[i] != '\n'; i++ { 1063 buf.WriteRune(r[i]) 1064 } 1065 buf.WriteRune('\n') 1066 } 1067 } 1068 bufs.FreeRunes(r) 1069 wind.TheRow.Lk.Unlock() 1070 off := int(x.fcall.Offset) 1071 cnt := int(x.fcall.Count) 1072 n := buf.Len() 1073 if off > n { 1074 off = n 1075 } 1076 if off+cnt > n { 1077 cnt = n - off 1078 } 1079 var fc plan9.Fcall 1080 fc.Count = uint32(cnt) 1081 fc.Data = buf.Bytes()[off : off+cnt] 1082 respond(x, &fc, "") 1083 } 1084 1085 type wq struct { 1086 w *wind.Window 1087 q int 1088 } 1089 1090 var nopen = make(map[wq]int)