9fans.net/go@v0.0.7/cmd/sam/cmd.go (about) 1 // #include "sam.h" 2 // #include "parse.h" 3 4 package main 5 6 import ( 7 "fmt" 8 "os" 9 "strings" 10 "unicode/utf8" 11 ) 12 13 var linex = "\n" 14 var wordx = " \t\n" 15 16 var cmdtab []Cmdtab = nil 17 18 func init() { cmdtab = cmdtab1 } // break init loop 19 20 var cmdtab1 = []Cmdtab{ 21 /* cmdc text regexp addr defcmd defaddr count token fn */ 22 {'\n', false, false, false, 0, aDot, 0, "", nl_cmd}, 23 {'a', true, false, false, 0, aDot, 0, "", a_cmd}, 24 {'b', false, false, false, 0, aNo, 0, linex, b_cmd}, 25 {'B', false, false, false, 0, aNo, 0, linex, b_cmd}, 26 {'c', true, false, false, 0, aDot, 0, "", c_cmd}, 27 {'d', false, false, false, 0, aDot, 0, "", d_cmd}, 28 {'D', false, false, false, 0, aNo, 0, linex, D_cmd}, 29 {'e', false, false, false, 0, aNo, 0, wordx, e_cmd}, 30 {'f', false, false, false, 0, aNo, 0, wordx, f_cmd}, 31 {'g', false, true, false, 'p', aDot, 0, "", g_cmd}, 32 {'i', true, false, false, 0, aDot, 0, "", i_cmd}, 33 {'k', false, false, false, 0, aDot, 0, "", k_cmd}, 34 {'m', false, false, true, 0, aDot, 0, "", m_cmd}, 35 {'n', false, false, false, 0, aNo, 0, "", n_cmd}, 36 {'p', false, false, false, 0, aDot, 0, "", p_cmd}, 37 {'q', false, false, false, 0, aNo, 0, "", q_cmd}, 38 {'r', false, false, false, 0, aDot, 0, wordx, e_cmd}, 39 {'s', false, true, false, 0, aDot, 1, "", s_cmd}, 40 {'t', false, false, true, 0, aDot, 0, "", m_cmd}, 41 {'u', false, false, false, 0, aNo, 2, "", u_cmd}, 42 {'v', false, true, false, 'p', aDot, 0, "", g_cmd}, 43 {'w', false, false, false, 0, aAll, 0, wordx, w_cmd}, 44 {'x', false, true, false, 'p', aDot, 0, "", x_cmd}, 45 {'y', false, true, false, 'p', aDot, 0, "", x_cmd}, 46 {'X', false, true, false, 'f', aNo, 0, "", X_cmd}, 47 {'Y', false, true, false, 'f', aNo, 0, "", X_cmd}, 48 {'!', false, false, false, 0, aNo, 0, linex, plan9_cmd}, 49 {'>', false, false, false, 0, aDot, 0, linex, plan9_cmd}, 50 {'<', false, false, false, 0, aDot, 0, linex, plan9_cmd}, 51 {'|', false, false, false, 0, aDot, 0, linex, plan9_cmd}, 52 {'=', false, false, false, 0, aDot, 0, linex, eq_cmd}, 53 {'c' | 0x100, false, false, false, 0, aNo, 0, wordx, cd_cmd}, 54 } 55 56 var line = make([]rune, 0, BLOCKSIZE) 57 var linep int // index in line 58 59 var termline [BLOCKSIZE]rune 60 var terminp int // write index in termline 61 var termoutp int // read index in termline 62 63 var cmdlist []*Cmd 64 var addrlist []*Addr 65 var relist []*String 66 var stringlist []*String 67 68 var eof bool 69 70 func resetcmd() { 71 linep = 0 72 line = line[:0] 73 termoutp = 0 74 terminp = 0 75 freecmd() 76 } 77 78 func inputc() rune { 79 Again: 80 nbuf := 0 81 var r rune 82 if downloaded { 83 for termoutp == terminp { 84 cmdupdate() 85 if patset { 86 tellpat() 87 } 88 for termlocked > 0 { 89 outT0(Hunlock) 90 termlocked-- 91 } 92 if !rcv() { 93 return -1 94 } 95 } 96 r = termline[termoutp] 97 termoutp++ 98 if termoutp == terminp { 99 termoutp = 0 100 terminp = 0 101 } 102 } else { 103 var buf [utf8.UTFMax]byte 104 for { 105 n, err := os.Stdin.Read(buf[nbuf : nbuf+1]) 106 if err != nil || n <= 0 { 107 return -1 108 } 109 nbuf += n 110 if utf8.FullRune(buf[:nbuf]) { 111 break 112 } 113 } 114 r, _ = utf8.DecodeRune(buf[:]) 115 } 116 if r == 0 { 117 warn(Wnulls) 118 goto Again 119 } 120 return r 121 } 122 123 var Dflag bool 124 125 func debug(format string, args ...interface{}) { 126 if !Dflag { 127 return 128 } 129 s := fmt.Sprintf(format, args...) 130 if !strings.HasSuffix(s, "\n") { 131 s += "\n" 132 } 133 os.Stderr.WriteString(s) 134 } 135 136 func inputline() int { 137 /* 138 * Could set linep = line and i = 0 here and just 139 * error(Etoolong) below, but this way we keep 140 * old input buffer history around for a while. 141 * This is useful only for debugging. 142 */ 143 i := linep 144 for { 145 c := inputc() 146 if c <= 0 { 147 return -1 148 } 149 if i >= cap(line) { 150 if linep == 0 { 151 error_(Etoolong) 152 } 153 i = copy(line[0:], line[linep:]) 154 linep = 0 155 } 156 line = append(line, c) 157 if c == '\n' { 158 break 159 } 160 } 161 debug("input: %q\n", string(line[linep:])) 162 return 1 163 } 164 165 func getch() rune { 166 if eof { 167 return -1 168 } 169 if linep == len(line) && inputline() < 0 { 170 eof = true 171 return -1 172 } 173 r := line[linep] 174 debug("getch %d %q\n", linep, r) 175 linep++ 176 return r 177 } 178 179 func nextc() rune { 180 if linep >= len(line) { 181 return -1 182 } 183 return line[linep] 184 } 185 186 func ungetch() { 187 linep-- 188 if linep < 0 { 189 panic_("ungetch") 190 } 191 // debug("ungetch %d %q\n", linep, line[linep]) 192 } 193 194 func getnum(signok int) Posn { 195 n := 0 196 sign := 1 197 if signok > 1 && nextc() == '-' { 198 sign = -1 199 getch() 200 } 201 c := nextc() 202 if c < '0' || '9' < c { /* no number defaults to 1 */ 203 return sign 204 } 205 for { 206 c = getch() 207 if !('0' <= c) || !(c <= '9') { 208 break 209 } 210 n = n*10 + int(c-'0') 211 } 212 ungetch() 213 return sign * n 214 } 215 216 func skipbl() rune { 217 var c rune 218 for { 219 c = getch() 220 if !(c == ' ') && !(c == '\t') { 221 break 222 } 223 } 224 if c >= 0 { 225 ungetch() 226 } 227 return c 228 } 229 230 func termcommand() { 231 for p := cmdpt; p < cmd.b.nc; p++ { 232 if terminp >= len(termline) { 233 cmdpt = cmd.b.nc 234 error_(Etoolong) 235 } 236 termline[terminp] = filereadc(cmd, p) 237 terminp++ 238 } 239 cmdpt = cmd.b.nc 240 } 241 242 func cmdloop() { 243 for { 244 if !downloaded && curfile != nil && curfile.unread { 245 load(curfile) 246 } 247 cmdp := parsecmd(0) 248 if cmdp == nil { 249 if downloaded { 250 rescue() 251 os.Exit(1) // "eof" 252 } 253 break 254 } 255 ocurfile := curfile 256 loaded := curfile != nil && !curfile.unread 257 if !cmdexec(curfile, cmdp) { 258 break 259 } 260 freecmd() 261 cmdupdate() 262 update() 263 if downloaded && curfile != nil && (ocurfile != curfile || (!loaded && !curfile.unread)) { 264 outTs(Hcurrent, curfile.tag) 265 } 266 /* don't allow type ahead on files that aren't bound */ 267 if downloaded && curfile != nil && curfile.rasp == nil { 268 terminp = termoutp 269 } 270 } 271 } 272 273 func newcmd() *Cmd { 274 p := new(Cmd) 275 cmdlist = append(cmdlist, p) 276 return p 277 } 278 279 func newaddr() *Addr { 280 p := new(Addr) 281 addrlist = append(addrlist, p) 282 return p 283 } 284 285 func newre() *String { 286 p := new(String) 287 relist = append(relist, p) 288 return p 289 } 290 291 func newstring() *String { 292 p := new(String) 293 stringlist = append(stringlist, p) 294 return p 295 } 296 297 func freecmd() { 298 for _, c := range cmdlist { 299 // free(c) 300 _ = c 301 } 302 cmdlist = cmdlist[:0] 303 for _, a := range addrlist { 304 // free(a) 305 _ = a 306 } 307 addrlist = addrlist[:0] 308 for _, s := range relist { 309 Strclose(s) 310 // free(s) 311 } 312 relist = relist[:0] 313 for _, s := range stringlist { 314 Strclose(s) 315 // free(s) 316 } 317 stringlist = stringlist[:0] 318 } 319 320 func lookup(c rune) int { 321 for i := range cmdtab { 322 if cmdtab[i].cmdc == c { 323 return i 324 } 325 } 326 return -1 327 } 328 329 func okdelim(c rune) { 330 if c == '\\' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') { 331 error_c(Edelim, c) 332 } 333 } 334 335 func atnl() { 336 skipbl() 337 if c := getch(); c != '\n' { 338 error_(Enewline) 339 } 340 } 341 342 func getrhs(s *String, delim rune, cmd int) { 343 for { 344 c := getch() 345 if !(c > 0 && c != delim) || !(c != '\n') { 346 break 347 } 348 if c == '\\' { 349 c = getch() 350 if c <= 0 { 351 error_(Ebadrhs) 352 } 353 if c == '\n' { 354 ungetch() 355 c = '\\' 356 } else if c == 'n' { 357 c = '\n' 358 } else if c != delim && (cmd == 's' || c != '\\') { /* s does its own */ 359 Straddc(s, '\\') 360 } 361 } 362 Straddc(s, c) 363 } 364 ungetch() /* let client read whether delimeter, '\n' or whatever */ 365 } 366 367 func collecttoken(end string) *String { 368 s := newstring() 369 var c rune 370 for { 371 c = nextc() 372 if !(c == ' ') && !(c == '\t') { 373 break 374 } 375 Straddc(s, getch()) /* blanks significant for getname() */ 376 } 377 for { 378 c = getch() 379 if c <= 0 || strings.ContainsRune(end, c) { 380 break 381 } 382 Straddc(s, c) 383 } 384 if c != '\n' { 385 atnl() 386 } 387 return s 388 } 389 390 func collecttext() *String { 391 s := newstring() 392 if skipbl() == '\n' { 393 getch() 394 i := 0 395 for { 396 begline := i 397 var c rune 398 for { 399 c = getch() 400 if !(c > 0) || !(c != '\n') { 401 break 402 } 403 i++ 404 Straddc(s, c) 405 } 406 i++ 407 Straddc(s, '\n') 408 if c < 0 { 409 goto Return 410 } 411 if !(s.s[begline] != '.') && !(s.s[begline+1] != '\n') { 412 break 413 } 414 } 415 Strdelete(s, len(s.s)-2, len(s.s)) 416 } else { 417 delim := getch() 418 okdelim(delim) 419 getrhs(s, delim, 'a') 420 if nextc() == delim { 421 getch() 422 } 423 atnl() 424 } 425 Return: 426 return s 427 } 428 429 func parsecmd(nest int) *Cmd { 430 var cmd Cmd 431 cmd.ccmd = nil 432 cmd.next = cmd.ccmd 433 cmd.re = nil 434 cmd.num = 0 435 cmd.flag = false 436 cmd.addr = compoundaddr() 437 if skipbl() == -1 { 438 return nil 439 } 440 c := getch() 441 if c == -1 { 442 return nil 443 } 444 cmd.cmdc = c 445 if cmd.cmdc == 'c' && nextc() == 'd' { /* sleazy two-character case */ 446 getch() /* the 'd' */ 447 cmd.cmdc = 'c' | 0x100 448 } 449 i := lookup(cmd.cmdc) 450 var cp *Cmd 451 if i >= 0 { 452 if cmd.cmdc == '\n' { 453 goto Return /* let nl_cmd work it all out */ 454 } 455 ct := &cmdtab[i] 456 if ct.defaddr == aNo && cmd.addr != nil { 457 error_(Enoaddr) 458 } 459 if ct.count != 0 { 460 cmd.num = getnum(int(ct.count)) 461 } 462 if ct.regexp { 463 /* x without pattern -> .*\n, indicated by cmd.re==0 */ 464 /* X without pattern is all files */ 465 if (ct.cmdc != 'x' && ct.cmdc != 'X') || func() bool { c = nextc(); return c != ' ' && c != '\t' && c != '\n' }() { 466 skipbl() 467 c = getch() 468 if c == '\n' || c < 0 { 469 error_(Enopattern) 470 } 471 okdelim(c) 472 cmd.re = getregexp(c) 473 if ct.cmdc == 's' { 474 cmd.ctext = newstring() 475 getrhs(cmd.ctext, c, 's') 476 if nextc() == c { 477 getch() 478 if nextc() == 'g' { 479 getch() 480 cmd.flag = true 481 } 482 } 483 484 } 485 } 486 } 487 if ct.addr { 488 cmd.caddr = simpleaddr() 489 if cmd.caddr == nil { 490 error_(Eaddress) 491 } 492 } 493 if ct.defcmd != 0 { 494 if skipbl() == '\n' { 495 getch() 496 cmd.ccmd = newcmd() 497 cmd.ccmd.cmdc = ct.defcmd 498 } else { 499 cmd.ccmd = parsecmd(nest) 500 if cmd.ccmd == nil { 501 panic_("defcmd") 502 } 503 } 504 } else if ct.text { 505 cmd.ctext = collecttext() 506 } else if ct.token != "" { 507 cmd.ctext = collecttoken(ct.token) 508 } else { 509 atnl() 510 } 511 } else { 512 var ncp *Cmd 513 switch cmd.cmdc { 514 case '{': 515 cp = nil 516 for { 517 if skipbl() == '\n' { 518 getch() 519 } 520 ncp = parsecmd(nest + 1) 521 if cp != nil { 522 cp.next = ncp 523 } else { 524 cmd.ccmd = ncp 525 } 526 cp = ncp 527 if cp == nil { 528 break 529 } 530 } 531 case '}': 532 atnl() 533 if nest == 0 { 534 error_(Enolbrace) 535 } 536 return nil 537 default: 538 error_c(Eunk, cmd.cmdc) 539 } 540 } 541 Return: 542 cp = newcmd() 543 *cp = cmd 544 return cp /* BUGGERED */ 545 } 546 547 func getregexp(delim rune) *String { 548 r := newre() 549 var c rune 550 for Strzero(&genstr); ; Straddc(&genstr, c) { 551 c = getch() 552 if c == '\\' { 553 if nextc() == delim { 554 c = getch() 555 } else if nextc() == '\\' { 556 Straddc(&genstr, c) 557 c = getch() 558 } 559 } else if c == delim || c == '\n' { 560 break 561 } 562 } 563 if c != delim && c != 0 { 564 ungetch() 565 } 566 if len(genstr.s) > 0 { 567 patset = true 568 Strduplstr(&lastpat, &genstr) 569 } 570 if len(lastpat.s) <= 0 { 571 error_(Epattern) 572 } 573 Strduplstr(r, &lastpat) 574 return r 575 } 576 577 func simpleaddr() *Addr { 578 var addr Addr 579 addr.next = nil 580 addr.left = nil 581 addr.num = 0 582 switch skipbl() { 583 case '#': 584 addr.type_ = getch() 585 addr.num = getnum(1) 586 case '0', 587 '1', 588 '2', 589 '3', 590 '4', 591 '5', 592 '6', 593 '7', 594 '8', 595 '9': 596 addr.num = getnum(1) 597 addr.type_ = 'l' 598 case '/', 599 '?', 600 '"': 601 addr.type_ = getch() 602 addr.are = getregexp(addr.type_) 603 case '.', 604 '$', 605 '+', 606 '-', 607 '\'': 608 addr.type_ = getch() 609 default: 610 return nil 611 } 612 addr.next = simpleaddr() 613 if addr.next != nil { 614 var nap *Addr 615 switch addr.next.type_ { 616 case '.', 617 '$', 618 '\'': 619 if addr.type_ == '"' { 620 break 621 } 622 fallthrough 623 /* fall through */ 624 case '"': 625 error_(Eaddress) 626 case 'l', 627 '#': 628 if addr.type_ == '"' { 629 break 630 } 631 fallthrough 632 /* fall through */ 633 case '/', 634 '?': 635 if addr.type_ != '+' && addr.type_ != '-' { 636 /* insert the missing '+' */ 637 nap = newaddr() 638 nap.type_ = '+' 639 nap.next = addr.next 640 addr.next = nap 641 } 642 case '+', 643 '-': 644 break 645 default: 646 panic_("simpleaddr") 647 } 648 } 649 ap := newaddr() 650 *ap = addr 651 return ap 652 } 653 654 func compoundaddr() *Addr { 655 var addr Addr 656 addr.left = simpleaddr() 657 addr.type_ = skipbl() 658 if addr.type_ != ',' && addr.type_ != ';' { 659 return addr.left 660 } 661 getch() 662 addr.next = compoundaddr() 663 next := addr.next 664 if next != nil && (next.type_ == ',' || next.type_ == ';') && next.left == nil { 665 error_(Eaddress) 666 } 667 ap := newaddr() 668 *ap = addr 669 return ap 670 }