1 module unde.command_line.run; 2 3 import unde.global_state; 4 import unde.lsblk; 5 import unde.path_mnt; 6 import unde.slash; 7 import unde.command_line.db; 8 import unde.font; 9 import unde.lib; 10 import unde.marks; 11 12 import std.stdio; 13 import std.conv; 14 import core.stdc.stdlib; 15 import std.math; 16 import berkeleydb.all; 17 import std.stdint; 18 import core.stdc.stdlib; 19 import std..string; 20 import std.path; 21 import std.algorithm.sorting; 22 import std.algorithm.searching; 23 import std.algorithm.comparison; 24 import std.range.primitives; 25 import std.utf; 26 import std.concurrency; 27 import core.time; 28 import core.exception; 29 import core.sys.posix.sys.select; 30 import core.sys.posix.fcntl; 31 import core.sys.posix.unistd; 32 import core.stdc..string; 33 import core.stdc.errno; 34 import core.thread; 35 import std.process; 36 import std.regex; 37 import std.datetime; 38 import std.algorithm.mutation; 39 import core.sys.posix.pty; //Hand-made module 40 import core.sys.posix.sys.ioctl; 41 import core.sys.posix.termios; 42 import core.sys.posix.sys.wait; 43 static import std.algorithm; 44 45 import derelict.sdl2.sdl; 46 47 import unde.command_line.delete_command; 48 import unde.command_line.lib; 49 import unde.marks; 50 import unde.lib; 51 52 import std.file; 53 54 version(Windows) 55 { 56 import core.sys.windows.windows; 57 import core.stdc.time; 58 alias ulong ulong_t; 59 } 60 61 enum Mode 62 { 63 Working, 64 Alternate 65 } 66 67 struct WorkMode 68 { 69 char[4096] buf; 70 ssize_t buf_r; 71 ssize_t max_r; 72 ushort cur_attr = Attr.Black<<4 | Attr.White; 73 ushort[] attrs; 74 ssize_t attrs_r; 75 ulong out_id1; 76 ssize_t saved_buf_r; 77 ushort saved_attr; 78 char[4096] prebuf; 79 ssize_t prebuf_r; 80 Tid tid; 81 bool select; 82 } 83 84 struct AltMode 85 { 86 dchar[] buffer; 87 ushort[] alt_attrs; 88 int cols = 80; 89 int rows = 25; 90 ssize_t y; 91 ssize_t x; 92 ulong alt_out_id1; 93 ushort alt_cur_attr = Attr.Black<<4 | Attr.White; 94 string control_input; 95 ssize_t scroll_from; 96 ssize_t scroll_to; 97 bool insert_mode; 98 bool cursor_mode; 99 100 void reset() 101 { 102 y = 0; 103 x = 0; 104 alt_out_id1 = 0; 105 alt_cur_attr = Attr.Black<<4 | Attr.White; 106 control_input = ""; 107 scroll_from = 0; 108 scroll_to = rows; 109 insert_mode = false; 110 } 111 } 112 113 private void 114 set_non_block_mode(int fd) 115 { 116 version(Posix) 117 { 118 int flags; 119 if (-1 == (flags = fcntl(fd, F_GETFL, 0))) 120 flags = 0; 121 int res = fcntl(fd, F_SETFL, flags | O_NONBLOCK); 122 if (res < 0) 123 { 124 throw new Exception("fcntl() error: " ~ fromStringz(strerror(errno)).idup()); 125 } 126 } 127 } 128 129 130 private ulong 131 get_max_id_of_cwd(T)(T cgs, string cwd) 132 if (is (T == CMDGlobalState) || is (T == GlobalState)) 133 { 134 Dbc cursor = cgs.db_commands.cursor(cgs.txn, 0); 135 scope(exit) cursor.close(); 136 137 Dbt key, data; 138 ulong id = find_prev_command(cursor, cwd, 0, key, data); 139 140 if (id != 0) 141 { 142 string key_string = key.to!(string); 143 command_key cmd_key; 144 parse_key_for_command(key_string, cmd_key); 145 if (cwd != cmd_key.cwd) 146 { 147 id = 0; 148 } 149 else 150 { 151 id = cmd_key.id; 152 } 153 } 154 155 return id; 156 } 157 158 private ulong 159 find_command_in_cwd(T)(T cgs, string cwd, string command) 160 if (is (T == CMDGlobalState) || is (T == GlobalState)) 161 { 162 Dbc cursor = cgs.db_commands.cursor(cgs.txn, 0); 163 scope(exit) cursor.close(); 164 165 Dbt key, data; 166 ulong id = 0; 167 string ks = get_key_for_command(command_key(cwd, 0)); 168 key = ks; 169 auto res = cursor.get(&key, &data, DB_SET_RANGE); 170 if (res == DB_NOTFOUND) 171 { 172 return 0; 173 } 174 175 do 176 { 177 string key_string = key.to!(string); 178 command_key cmd_key; 179 parse_key_for_command(key_string, cmd_key); 180 181 string data_string = data.to!(string); 182 command_data cmd_data; 183 parse_data_for_command(data_string, cmd_data); 184 185 if (cmd_key.cwd != cwd) break; 186 if (cmd_data.command == command) 187 { 188 id = cmd_key.id; 189 break; 190 } 191 192 } while (cursor.get(&key, &data, DB_NEXT) == 0); 193 194 return id; 195 } 196 197 enum StateEscape 198 { 199 WaitEscape, 200 WaitBracket, 201 WaitNumberR, 202 WaitText, 203 WaitCSI, 204 WaitCharSet, 205 WaitEight, 206 WaitG0CharSet, 207 WaitG1CharSet 208 } 209 210 /* Information about Escape sequence: man console_codes */ 211 private size_t 212 process_escape_sequences(CMDGlobalState cgs, 213 ref Mode mode, ref WorkMode workmode, ref AltMode altmode, 214 ref ssize_t r) 215 { 216 //process_escape_sequences: 1 sec, 403 ms of 3 s 217 with(workmode) 218 { 219 StateEscape state; 220 int substate; 221 string text; 222 int[] numbers; 223 ssize_t i; 224 ssize_t cur_chr_stride; 225 loop: 226 for (i = 0; i < r; i += cur_chr_stride) 227 { 228 //Stat processing: 570 ms, 203 μs of 3 s 229 //assert(buf[0..buf_r].walkLength == attrs_r, format("walkLength=%d, attrs_r=%d", buf[0..buf_r].walkLength, attrs_r)); 230 cur_chr_stride = prebuf.mystride(i, r); 231 232 char[] chr; 233 if (i+cur_chr_stride > r) 234 { 235 chr = prebuf[i..i+1]; 236 } 237 else 238 chr = prebuf[i..i+cur_chr_stride]; 239 240 if (i+chr.length >= r && chr.length == 1 && chr[0] & 0b1000_0000) 241 { 242 break; 243 } 244 245 with(altmode) 246 { 247 if (y < 0) y = 0; 248 if (y >= rows) y = rows-1; 249 if (x < 0) x = 0; 250 if (x >= cols) y = cols-1; 251 } 252 253 //writefln("Process '%c' - %X (%d)", prebuf[i], prebuf[i], prebuf[i]); 254 final switch (state) 255 { 256 case StateEscape.WaitEscape: 257 if (chr.length > 1) 258 { 259 //writef("+%s", chr); 260 final switch(mode) 261 { 262 case Mode.Working: 263 auto started2 = Clock.currTime(); 264 //Long character rpocessing: 455 ms, 532 μs of 3 s 265 /*writefln("%s", chr); 266 if (buf_r > 4) 267 writefln("buf=%s", buf[buf_r-4..buf_r+4]);*/ 268 if (max_r >= buf.length || buf_r >= buf.length) break loop; 269 auto chrlen = buf.mystride(buf_r); 270 271 if (altmode.insert_mode) 272 { 273 for (ssize_t j = max_r-1; j >= cast(ssize_t)(buf_r+chr.length); j--) 274 { 275 buf[j] = buf[j-chr.length]; 276 } 277 max_r += chr.length; 278 } 279 else if (chrlen != chr.length) 280 { 281 //if (max_r + chr.length-chrlen > buf.length) max_r = buf.length - (chr.length-chrlen); 282 if (max_r <= buf_r+chrlen) 283 { 284 buf[max_r..buf_r+chrlen] = ' '; 285 max_r = buf_r+chrlen; 286 } 287 if (max_r+chr.length-chrlen >= buf.length) break loop; 288 for (ssize_t j = max_r-1; j >= buf_r+chrlen; j--) 289 buf[j + chr.length-chrlen] = buf[j]; 290 //std.algorithm.mutation.copy(buf[buf_r+chrlen..max_r] , buf[buf_r+chr.length..max_r+chr.length-chrlen]); 291 max_r += chr.length-chrlen; 292 } 293 294 buf[buf_r..buf_r+chr.length] = chr; 295 /*if (buf_r > 4) 296 writefln("After buf=%s", buf[buf_r-4..buf_r+4]);*/ 297 buf_r+=chr.length; 298 //writefln("\nbuf_r=%d, buf=%s", buf_r, buf[0..buf_r]); 299 if (buf_r > max_r) max_r = buf_r; 300 if (attrs_r < 0) attrs_r = 0; 301 if (attrs_r >= attrs.length) attrs.length = attrs_r+1; 302 attrs[attrs_r] = cur_attr; 303 attrs_r++; 304 continue; 305 306 case Mode.Alternate: 307 with(altmode) 308 { 309 buffer[y*cols+x] = to!dchar(chr); 310 alt_attrs[y*cols+x] = alt_cur_attr; 311 x++; 312 if (x >= cols) 313 { 314 y++; 315 x = 0; 316 } 317 if (y >= scroll_to) 318 { 319 y--; 320 std.algorithm.mutation.copy(cast(ubyte[])(buffer[scroll_from*cols + cols..scroll_to*cols]), 321 cast(ubyte[])(buffer[scroll_from*cols..(scroll_to-1)*cols])); 322 std.algorithm.mutation.copy(alt_attrs[scroll_from*cols + cols..scroll_to*cols], 323 alt_attrs[scroll_from*cols..(scroll_to-1)*cols]); 324 buffer[(scroll_to-1)*cols..scroll_to*cols] = " "d[0]; 325 alt_attrs[(scroll_to-1)*cols..scroll_to*cols] = 0; 326 } 327 continue; 328 } 329 } 330 } 331 332 if (prebuf[i] == '\x07') //Bell 333 { 334 continue; 335 } 336 else if (prebuf[i] == '\x08') //BackSpace 337 { 338 final switch(mode) 339 { 340 case Mode.Working: 341 if (buf_r > buf.length) buf_r = buf.length-1; 342 if (buf_r > 0 && buf_r >= buf.mystrideBack(buf_r)) 343 buf_r -= buf.mystrideBack(buf_r); 344 attrs_r--; 345 break; 346 case Mode.Alternate: 347 with(altmode) 348 { 349 x--; 350 if (x < 0) x = 0; 351 } 352 break; 353 } 354 //writefln("BackSpace"); 355 //writefln("buf_r2=%s", buf[buf_r..max_r]); 356 } 357 else if (prebuf[i] == '\x09') //Tab 358 { 359 final switch(mode) 360 { 361 case Mode.Working: 362 ssize_t n = buf[0..buf_r].lastIndexOf("\n"); 363 if (n < 0) n = 0; 364 else n++; 365 366 ssize_t spaces = 8-(buf_r - n)%8; 367 368 for (ssize_t j=0; j < spaces; j++) 369 { 370 if (buf_r >= buf.length) break loop; 371 auto chrlen = buf.mystride(buf_r); 372 if (chrlen != 1) 373 { 374 if (max_r <= buf_r+chrlen) 375 { 376 buf[max_r..buf_r+chrlen] = ' '; 377 max_r = buf_r+chrlen; 378 } 379 std.algorithm.mutation.copy(cast(ubyte[])(buf[buf_r+chrlen..max_r]), cast(ubyte[])(buf[buf_r+1..max_r+1-chrlen])); 380 max_r += 1-chrlen; 381 } 382 buf[buf_r] = ' '; 383 /*if (buf_r > 4) 384 writefln("After buf=%s", buf[buf_r-4..buf_r+4]);*/ 385 buf_r++; 386 if (buf_r > max_r) max_r = buf_r; 387 if (attrs_r < 0) attrs_r = 0; 388 if (attrs_r >= attrs.length) attrs.length = attrs_r+1; 389 attrs[attrs_r] = cur_attr; 390 attrs_r++; 391 } 392 break; 393 394 case Mode.Alternate: 395 with(altmode) 396 { 397 ssize_t spaces = 8-x%8; 398 for (ssize_t j=0; j < spaces; j++) 399 { 400 buffer[y*cols+x] = " "d[0]; 401 alt_attrs[y*cols+x] = alt_cur_attr; 402 x++; 403 if (x >= cols) 404 { 405 y++; 406 x = 0; 407 } 408 if (y >= scroll_to) 409 { 410 y--; 411 std.algorithm.mutation.copy(cast(ubyte[])(buffer[scroll_from*cols + cols..scroll_to*cols]), 412 cast(ubyte[])(buffer[scroll_from*cols..(scroll_to-1)*cols])); 413 std.algorithm.mutation.copy(alt_attrs[scroll_from*cols + cols..scroll_to*cols], 414 alt_attrs[scroll_from*cols..(scroll_to-1)*cols]); 415 buffer[(scroll_to-1)*cols..scroll_to*cols] = " "d[0]; 416 alt_attrs[(scroll_to-1)*cols..scroll_to*cols] = 0; 417 } 418 } 419 } 420 break; 421 } 422 } 423 else if (prebuf[i] == '\r') 424 { 425 final switch(mode) 426 { 427 case Mode.Working: 428 if (prebuf[i+1] != '\n') 429 { 430 if (buf_r > buf.length) buf_r = buf.length; 431 ssize_t newline = buf[0..buf_r].lastIndexOf("\n"); 432 if (newline < 0) newline = 0; 433 else newline++; 434 ssize_t wl = buf[newline..buf_r].myWalkLength(); 435 436 //writefln("\\r wl = %d", wl); 437 while ( wl%altmode.cols != 0 ) 438 { 439 if (buf_r > 0) 440 { 441 buf_r -= buf.mystrideBack(buf_r); 442 attrs_r--; 443 } 444 wl--; 445 } 446 } 447 break; 448 449 case Mode.Alternate: 450 with(altmode) 451 { 452 x=0; 453 } 454 break; 455 456 } 457 } 458 else if (prebuf[i] == '\x1B') 459 { 460 state = StateEscape.WaitBracket; 461 /*auto till = prebuf[i+1..r].indexOf("\x1B"); 462 if (till < 0) till = r; 463 else till += i+1; 464 writefln("\n!ESC %s", prebuf[i+1..till]);*/ 465 } 466 else if (prebuf[i] == '\x9B') 467 { 468 state = StateEscape.WaitCSI; 469 text = ""; 470 numbers = []; 471 } 472 else 473 { 474 /*if ( (prebuf[i] < 0x20 || prebuf[i] == 0x7F) && prebuf[i] != '\n') 475 { 476 writef("*%X(%d)", prebuf[i], prebuf[i]); 477 } 478 else 479 { 480 writef("+%c", prebuf[i]); 481 }*/ 482 final switch(mode) 483 { 484 case Mode.Working: 485 /*writefln("%c", prebuf[i]); 486 if (buf_r > 4) 487 writefln("buf=%s", buf[buf_r-4..buf_r+4]);*/ 488 if (prebuf[i] == '\n' && buf_r < max_r && i+1 >= r && r > 1) 489 { 490 break loop; 491 } 492 if (prebuf[i] == '\n' && buf_r < max_r && i+1 < r && prebuf[i+1] == '\r') 493 { 494 if (buf_r > buf.length) buf_r = buf.length; 495 ssize_t newline = buf[0..buf_r].lastIndexOf("\n"); 496 if (newline < 0) newline = 0; 497 else newline++; 498 ssize_t wl = buf[newline..buf_r].myWalkLength(); 499 500 writefln("\\n wl = %d", wl); 501 while (wl%altmode.cols != 0) 502 { 503 if (buf_r < buf.length) 504 { 505 buf_r += buf.mystride(buf_r); 506 attrs_r++; 507 } 508 wl++; 509 } 510 if (buf_r > max_r) max_r = buf_r; 511 } 512 else 513 { 514 if (prebuf[i] == '\n') 515 { 516 if (altmode.scroll_to < altmode.rows) 517 { 518 if (buf_r > buf.length) buf_r = buf.length; 519 ssize_t newline = buf[0..buf_r].lastIndexOf("\n"); 520 if (newline < 0) newline = 0; 521 else newline++; 522 ssize_t wl = buf[newline..buf_r].myWalkLength(); 523 524 writefln("\\n wl = %d", wl); 525 while ( wl%altmode.cols != altmode.cols-1 ) 526 { 527 if (buf_r < buf.length) 528 { 529 buf_r += buf.mystride(buf_r); 530 if (buf[buf_r] == char.init) buf[buf_r] = ' '; 531 attrs_r++; 532 } 533 wl++; 534 } 535 if (buf_r > max_r) max_r = buf_r; 536 537 if (max_r > buf_r+1) 538 { 539 std.algorithm.mutation.copy(cast(ubyte[])(buf[buf_r+1..max_r]), cast(ubyte[])(buf[buf_r+1+altmode.cols..max_r+altmode.cols])); 540 attrs.length += altmode.cols; 541 std.algorithm.mutation.copy(attrs[attrs_r+1..$-altmode.cols], attrs[attrs_r+1+altmode.cols..$]); 542 buf[buf_r+1..max_r] = ' '; 543 attrs[attrs_r+1..$-altmode.cols] = Attr.Black<<4 | Attr.White; 544 max_r += altmode.cols; 545 } 546 } 547 else 548 { 549 buf_r = max_r; 550 attrs_r = attrs.length; 551 } 552 } 553 if (max_r >= buf.length || buf_r >= buf.length) break loop; 554 auto chrlen = buf.mystride(buf_r); 555 if (altmode.insert_mode) 556 { 557 for (ssize_t j = max_r-1; j >= buf_r+1; j--) 558 buf[j] = buf[j-1]; 559 max_r++; 560 } 561 else if (chrlen != 1) 562 { 563 if (max_r <= buf_r+chrlen) 564 { 565 buf[max_r..buf_r+chrlen] = ' '; 566 max_r = buf_r+chrlen; 567 } 568 std.algorithm.mutation.copy(cast(ubyte[])(buf[buf_r+chrlen..max_r]), cast(ubyte[])(buf[buf_r+1..max_r+1-chrlen])); 569 max_r += 1-chrlen; 570 } 571 buf[buf_r] = prebuf[i]; 572 /*if (buf_r > 4) 573 writefln("After buf=%s", buf[buf_r-4..buf_r+4]);*/ 574 buf_r++; 575 //writefln("\nbuf_r=%d, buf=%s", buf_r, buf[0..buf_r]); 576 if (buf_r > max_r) max_r = buf_r; 577 //writefln("ADD '%c': buf=%s", prebuf[i], buf[0..max_r]); 578 if (attrs_r < 0) attrs_r = 0; 579 if (attrs_r >= attrs.length) attrs.length = attrs_r+1; 580 attrs[attrs_r] = cur_attr; 581 //writefln("attrs[%d]=%X, buf_r=%d", attrs_r, cur_attr, buf_r); 582 attrs_r++; 583 } 584 break; 585 586 case Mode.Alternate: 587 with(altmode) 588 { 589 if (prebuf[i] == '\n') 590 { 591 y++; 592 x = 0; 593 } 594 else 595 { 596 { 597 if (y < 0) y = 0; 598 if (y >= rows) y = rows-1; 599 if (x < 0) x = 0; 600 if (x >= cols) y = cols-1; 601 } 602 buffer[y*cols+x] = to!dchar(prebuf[i]); 603 alt_attrs[y*cols+x] = alt_cur_attr; 604 x++; 605 if (x >= cols) 606 { 607 y++; 608 x = 0; 609 } 610 } 611 if (y >= scroll_to) 612 { 613 y--; 614 std.algorithm.mutation.copy(cast(ubyte[])(buffer[scroll_from*cols + cols..scroll_to*cols]), 615 cast(ubyte[])(buffer[scroll_from*cols..(scroll_to-1)*cols])); 616 std.algorithm.mutation.copy(alt_attrs[scroll_from*cols + cols..scroll_to*cols], 617 alt_attrs[scroll_from*cols..(scroll_to-1)*cols]); 618 buffer[(scroll_to-1)*cols..scroll_to*cols] = " "d[0]; 619 alt_attrs[(scroll_to-1)*cols..scroll_to*cols] = 0; 620 } 621 } 622 break; 623 } 624 } 625 break; 626 case StateEscape.WaitBracket: 627 switch (prebuf[i]) 628 { 629 case ']': 630 state = StateEscape.WaitNumberR; 631 break; 632 case '[': 633 state = StateEscape.WaitCSI; 634 text = ""; 635 numbers = []; 636 break; 637 case 'c': 638 /* Reset */ 639 state = StateEscape.WaitEscape; 640 break; 641 case 'D': 642 /* Linefeed */ 643 state = StateEscape.WaitEscape; 644 break; 645 case 'E': 646 /* Newline */ 647 state = StateEscape.WaitEscape; 648 break; 649 case 'H': 650 /* Set tab stop at current column. */ 651 state = StateEscape.WaitEscape; 652 break; 653 case 'M': 654 /* Reverse linefeed. */ 655 final switch(mode) 656 { 657 case Mode.Working: 658 break; 659 660 case Mode.Alternate: 661 with(altmode) 662 { 663 y--; 664 if (y < scroll_from) 665 { 666 y++; 667 //writefln("reverse linefeed"); 668 for (ssize_t R = scroll_to-2; R >= scroll_from; R--) 669 { 670 buffer[(R+1)*cols..(R+2)*cols] = buffer[R*cols..(R+1)*cols]; 671 alt_attrs[(R+1)*cols..(R+2)*cols] = alt_attrs[R*cols..(R+1)*cols]; 672 } 673 buffer[scroll_from*cols..scroll_from*cols+cols] = " "d[0]; 674 alt_attrs[scroll_from*cols..scroll_from*cols+cols] = 0; 675 } 676 } 677 break; 678 } 679 state = StateEscape.WaitEscape; 680 break; 681 case 'Z': 682 /* DEC private identification. The kernel returns the 683 string ESC [ ? 6 c, claiming that it is a VT102.. */ 684 state = StateEscape.WaitEscape; 685 break; 686 case '7': 687 /*Save current state (cursor coordinates, 688 attributes, character sets pointed at by G0, G1).*/ 689 final switch(mode) 690 { 691 case Mode.Working: 692 saved_buf_r = buf_r; 693 saved_attr = cur_attr; 694 break; 695 696 case Mode.Alternate: 697 with(altmode) 698 { 699 } 700 } 701 state = StateEscape.WaitEscape; 702 break; 703 case '8': 704 /*Restore state most recently saved by ESC 7.*/ 705 final switch(mode) 706 { 707 case Mode.Working: 708 buf_r = saved_buf_r; 709 while ( (buf[buf_r] & 0b1100_0000) == 0b1000_0000 ) 710 buf_r--; 711 attrs_r = buf[0..buf_r].myWalkLength(); 712 cur_attr = saved_attr; 713 break; 714 715 case Mode.Alternate: 716 with(altmode) 717 { 718 } 719 } 720 state = StateEscape.WaitEscape; 721 break; 722 case '%': 723 state = StateEscape.WaitCharSet; 724 break; 725 case '#': 726 state = StateEscape.WaitEight; 727 break; 728 case '(': 729 state = StateEscape.WaitG0CharSet; 730 break; 731 case ')': 732 state = StateEscape.WaitG1CharSet; 733 break; 734 case '>': 735 /*Set numeric keypad mode*/ 736 state = StateEscape.WaitEscape; 737 break; 738 case '=': 739 /*Set application keypad mode*/ 740 state = StateEscape.WaitEscape; 741 break; 742 default: 743 writefln("UNKNOWN Escape Sequence \"ESC %c\"", prebuf[i]); 744 state = StateEscape.WaitEscape; 745 break; 746 } 747 break; 748 case StateEscape.WaitNumberR: 749 if (r - i < 3) break; 750 if (prebuf[i..i+2] == "0;") 751 { 752 substate = 0; 753 state = StateEscape.WaitText; 754 text = ""; 755 i++; 756 } 757 else if (prebuf[i..i+2] == "1;") 758 { 759 substate = 1; 760 state = StateEscape.WaitText; 761 text = ""; 762 i++; 763 } 764 else if (prebuf[i..i+2] == "2;") 765 { 766 substate = 2; 767 state = StateEscape.WaitText; 768 text = ""; 769 i++; 770 } 771 else if (prebuf[i..i+2] == "4;") 772 { 773 substate = 4; 774 state = StateEscape.WaitText; 775 text = ""; 776 i++; 777 } 778 else if (prebuf[i..i+3] == "10;") 779 { 780 substate = 10; 781 state = StateEscape.WaitText; 782 text = ""; 783 i+=2; 784 } 785 else if (prebuf[i..i+3] == "11;") 786 { 787 substate = 11; 788 state = StateEscape.WaitText; 789 text = ""; 790 i+=2; 791 } 792 else if (prebuf[i..i+3] == "46;") 793 { 794 substate = 46; 795 state = StateEscape.WaitText; 796 text = ""; 797 i+=2; 798 } 799 else if (prebuf[i..i+3] == "50;") 800 { 801 substate = 50; 802 state = StateEscape.WaitText; 803 text = ""; 804 i+=2; 805 } 806 else 807 { 808 writefln("UNKNOWN Escape Sequence \"ESC ] %c%c\"", prebuf[i], prebuf[i+1]); 809 state = StateEscape.WaitEscape; 810 } 811 break; 812 case StateEscape.WaitText: 813 if (prebuf[i] == '\x07') 814 { 815 switch(substate) 816 { 817 case 0: 818 /* Set icon name and window title to txt. */ 819 break; 820 case 1: 821 /* Set icon name to txt. */ 822 break; 823 case 2: 824 /* Set window title to txt. */ 825 break; 826 case 4: 827 /* Set ANSI color num to txt. */ 828 break; 829 case 10: 830 /* Set dynamic text color to txt.. */ 831 break; 832 case 11: 833 break; 834 case 46: 835 /* Change log file to name */ 836 break; 837 case 50: 838 /* Set font to fn. */ 839 break; 840 default: 841 assert(0); 842 } 843 state = StateEscape.WaitEscape; 844 } 845 else 846 text ~= prebuf[i]; 847 break; 848 case StateEscape.WaitCSI: 849 switch (prebuf[i]) 850 { 851 case '@': 852 /*Insert the indicated # of blank characters.*/ 853 ssize_t num = 1; 854 if (text > "") 855 num = to!int(text); 856 final switch(mode) 857 { 858 case Mode.Working: 859 if (max_r+num > buf.length) num = buf.length-max_r; 860 for (ssize_t j = max_r-1; j >= buf_r; j--) 861 buf[j+num] = buf[j]; 862 attrs.length += num; 863 for (ssize_t j = attrs.length-num-1; j >= attrs_r; j--) 864 attrs[j+num] = attrs[j]; 865 buf[buf_r..buf_r+num] = ' '; 866 max_r+=num; 867 break; 868 case Mode.Alternate: 869 } 870 state = StateEscape.WaitEscape; 871 break; 872 case 'A': 873 /*Move cursor up the indicated # of rows.*/ 874 final switch(mode) 875 { 876 case Mode.Working: 877 878 ssize_t nl = buf[0..buf_r].lastIndexOf("\n"); 879 if (nl < 0) nl = 0; 880 else nl++; 881 882 ssize_t wl = buf[nl..buf_r].myWalkLength(); 883 writefln("UP: wl=%d", wl); 884 if (wl >= altmode.cols) 885 { 886 wl -= altmode.cols; 887 ssize_t pos = nl; 888 for (ssize_t j=0; j < wl; j++) 889 { 890 if (pos >= buf.length) break; 891 pos += buf.mystride(pos); 892 } 893 894 writefln("UP: buf_r=%d, pos=%d", buf_r, pos); 895 buf_r = pos; 896 attrs_r = buf[0..buf_r].myWalkLength(); 897 } 898 else if (nl > 2) 899 { 900 ssize_t nl2 = buf[0..nl-1].lastIndexOf("\n"); 901 if (nl2 < 0) nl2 = 0; 902 else nl2++; 903 904 ssize_t pos = nl2; 905 for (ssize_t j=0; j < wl; j++) 906 { 907 if (pos >= buf.length) break; 908 pos += buf.mystride(pos); 909 } 910 911 writefln("UP2: buf_r=%d, pos=%d", buf_r, pos); 912 writefln("UP2: buf=%s|%s", buf[0..pos], buf[pos..buf_r]); 913 buf_r = pos; 914 attrs_r = buf[0..buf_r].myWalkLength(); 915 } 916 917 break; 918 919 case Mode.Alternate: 920 with(altmode) 921 { 922 } 923 } 924 state = StateEscape.WaitEscape; 925 break; 926 case 'B': 927 /*Move cursor down the indicated # of rows.*/ 928 state = StateEscape.WaitEscape; 929 break; 930 case 'C': 931 /*Move cursor right the indicated # of columns.*/ 932 if (text > "") 933 numbers ~= to!int(text); 934 935 int num = 1; 936 if (numbers.length > 0) num = numbers[0]; 937 938 final switch(mode) 939 { 940 case Mode.Working: 941 //writefln("buf before Right: %s", buf[buf_r..max_r]); 942 while (num > 0) 943 { 944 if (buf_r >= buf.length) break; 945 buf_r += buf.mystride(buf_r); 946 if (buf_r > max_r) 947 { 948 buf[max_r..buf_r] = ' '; 949 max_r = buf_r; 950 } 951 //attrs_r++; 952 num--; 953 } 954 955 attrs_r = buf[0..buf_r].myWalkLength(); 956 //writefln("buf after Right: %s", buf[buf_r..max_r]); 957 break; 958 case Mode.Alternate: 959 with(altmode) 960 { 961 x += num; 962 if (x >= cols) 963 { 964 y++; 965 x = 0; 966 } 967 if (y >= rows) 968 { 969 y--; 970 std.algorithm.mutation.copy(cast(ubyte[])(buffer[cols..rows*cols]), cast(ubyte[])(buffer[0..(rows-1)*cols])); 971 std.algorithm.mutation.copy(alt_attrs[cols..rows*cols], alt_attrs[0..(rows-1)*cols]); 972 } 973 } 974 975 } 976 state = StateEscape.WaitEscape; 977 break; 978 case 'D': 979 /*Move cursor left the indicated # of columns.*/ 980 final switch(mode) 981 { 982 case Mode.Working: 983 if (buf_r > 0) 984 buf_r -= buf.mystrideBack(buf_r); 985 break; 986 case Mode.Alternate: 987 with(altmode) 988 { 989 x--; 990 if(x < 0) x = 0; 991 } 992 } 993 state = StateEscape.WaitEscape; 994 break; 995 case 'E': 996 /*Move cursor down the indicated # of rows, to column 1.*/ 997 state = StateEscape.WaitEscape; 998 break; 999 case 'F': 1000 /*Move cursor up the indicated # of rows, to column 1.*/ 1001 state = StateEscape.WaitEscape; 1002 break; 1003 case 'G': 1004 /*Move cursor to indicated column in current row.*/ 1005 ssize_t num = 1; 1006 if (text > "") 1007 num = to!int(text); 1008 1009 final switch(mode) 1010 { 1011 case Mode.Working: 1012 1013 if (buf_r > buf.length) buf_r = buf.length; 1014 ssize_t newline = buf[0..buf_r].lastIndexOf("\n"); 1015 if (newline < 0) newline = 0; 1016 else newline++; 1017 ssize_t wl = buf[newline..buf_r].myWalkLength(); 1018 1019 if ( (num-1) < wl%altmode.cols ) 1020 { 1021 while (wl%altmode.cols != (num-1)) 1022 { 1023 if (buf_r > 0) 1024 { 1025 buf_r -= buf.mystrideBack(buf_r); 1026 attrs_r--; 1027 } 1028 wl--; 1029 } 1030 } 1031 else 1032 { 1033 while (wl%altmode.cols != num-1) 1034 { 1035 if (buf_r < buf.length) 1036 { 1037 if (buf_r >= max_r) 1038 buf[buf_r] = ' '; 1039 buf_r += buf.mystride(buf_r); 1040 attrs_r++; 1041 } 1042 wl++; 1043 } 1044 } 1045 break; 1046 1047 case Mode.Alternate: 1048 with(altmode) 1049 { 1050 x = num-1; 1051 } 1052 break; 1053 } 1054 1055 state = StateEscape.WaitEscape; 1056 break; 1057 case 'H': 1058 /*Move cursor to the indicated row, column (origin at 1,1).*/ 1059 if (text > "") 1060 numbers ~= to!int(text); 1061 while (numbers.length < 2) numbers ~= 1; 1062 1063 if (numbers[0] < 1) numbers[0]=1; 1064 if (numbers[0] > altmode.rows) numbers[0]=altmode.rows; 1065 if (numbers[1] < 1) numbers[1]=1; 1066 if (numbers[1] > altmode.cols) numbers[1]=altmode.cols; 1067 final switch(mode) 1068 { 1069 case Mode.Working: 1070 if (buf_r > buf.length) buf_r = buf.length; 1071 ssize_t newline = buf[0..buf_r].lastIndexOf("\n"); 1072 if (newline < 0) newline = 0; 1073 else newline++; 1074 ssize_t wl = buf[newline..buf_r].myWalkLength(); 1075 1076 writefln("[H wl = %d", wl); 1077 if (numbers[0] > altmode.scroll_to) 1078 { 1079 do 1080 { 1081 if (buf_r < buf.length) 1082 { 1083 buf_r += buf.mystride(buf_r); 1084 if (buf[buf_r] == char.init) buf[buf_r] = ' '; 1085 attrs_r++; 1086 } 1087 wl++; 1088 } 1089 while ( wl%altmode.cols != numbers[1]-1 ); 1090 } 1091 else 1092 { 1093 while ( wl%altmode.cols != numbers[1]-1 ) 1094 { 1095 if (buf_r > 0) 1096 { 1097 buf_r -= buf.mystrideBack(buf_r); 1098 attrs_r--; 1099 } 1100 wl--; 1101 } 1102 } 1103 if (buf_r > max_r) max_r = buf_r; 1104 attrs_r = buf[0..buf_r].myWalkLength(); 1105 break; 1106 1107 case Mode.Alternate: 1108 with(altmode) 1109 { 1110 y = numbers[0]-1; 1111 x = numbers[1]-1; 1112 if (y < 0) y = 0; 1113 if (y >= rows) y = rows-1; 1114 if (x < 0) x = 0; 1115 if (x >= cols) y = cols-1; 1116 } 1117 break; 1118 } 1119 1120 state = StateEscape.WaitEscape; 1121 break; 1122 case 'J': 1123 /*Erase display (default: from cursor to end of display).*/ 1124 /* 1125 ESC [ 1 J: erase from start to cursor. 1126 ESC [ 2 J: erase whole display. 1127 ESC [ 3 J: erase whole display including scroll-back 1128 prebuffer (since Linux 3.0). 1129 */ 1130 size_t num = 0; 1131 if (text != "") num = to!int(text); 1132 1133 final switch(mode) 1134 { 1135 case Mode.Working: 1136 if (max_r > buf.length) max_r = buf.length; 1137 buf[buf_r..max_r] = ' '; 1138 attrs[attrs_r..$] = Attr.Black<<4 | Attr.White; 1139 break; 1140 case Mode.Alternate: 1141 with(altmode) 1142 { 1143 switch (num) 1144 { 1145 case 0: 1146 buffer[y*cols+x..$] = " "d[0]; 1147 alt_attrs[y*cols+x..$] = Attr.Black<<4 | Attr.White; 1148 break; 1149 case 1: 1150 buffer[0..y*cols+x] = " "d[0]; 1151 alt_attrs[0..y*cols+x] = Attr.Black<<4 | Attr.White; 1152 break; 1153 case 2: 1154 buffer[0..$] = " "d[0]; 1155 alt_attrs[0..$] = Attr.Black<<4 | Attr.White; 1156 break; 1157 case 3: 1158 buffer[0..$] = " "d[0]; 1159 alt_attrs[0..$] = Attr.Black<<4 | Attr.White; 1160 break; 1161 default: 1162 writefln("UNKNOWN sequence ESC [ %d J", num); 1163 break; 1164 } 1165 } 1166 break; 1167 } 1168 state = StateEscape.WaitEscape; 1169 break; 1170 case 'K': 1171 /*Erase line (default: from cursor to end of line).*/ 1172 /* 1173 ESC [ 1 K: erase from start of line to cursor. 1174 ESC [ 2 K: erase whole line. 1175 */ 1176 final switch(mode) 1177 { 1178 case Mode.Working: 1179 if (max_r > buf.length) max_r = buf.length; 1180 if (max_r <= buf_r) max_r = buf_r+1; 1181 buf[buf_r..max_r] = ' '; 1182 break; 1183 case Mode.Alternate: 1184 with(altmode) 1185 { 1186 buffer[y*cols+x..y*cols+cols] = " "d[0]; 1187 alt_attrs[y*cols+x..y*cols+cols] = Attr.Black<<4 | Attr.White; 1188 } 1189 break; 1190 } 1191 state = StateEscape.WaitEscape; 1192 break; 1193 case 'L': 1194 /*Insert the indicated # of blank lines.*/ 1195 size_t num = 1; 1196 if (text != "") num = to!int(text); 1197 1198 final switch(mode) 1199 { 1200 case Mode.Working: 1201 break; 1202 case Mode.Alternate: 1203 with(altmode) 1204 { 1205 for (ssize_t R = scroll_to-num-1; R >= y; R--) 1206 { 1207 buffer[(R+num)*cols..(R+num+1)*cols] = buffer[R*cols..(R+1)*cols]; 1208 alt_attrs[(R+num)*cols..(R+num+1)*cols] = alt_attrs[R*cols..(R+1)*cols]; 1209 } 1210 buffer[y*cols..(y+num)*cols] = " "d[0]; 1211 alt_attrs[y*cols..(y+num)*cols] = 0; 1212 } 1213 } 1214 1215 state = StateEscape.WaitEscape; 1216 break; 1217 case 'M': 1218 /*Delete the indicated # of lines.*/ 1219 size_t num = 1; 1220 if (text != "") num = to!int(text); 1221 1222 final switch(mode) 1223 { 1224 case Mode.Working: 1225 break; 1226 case Mode.Alternate: 1227 with(altmode) 1228 { 1229 std.algorithm.mutation.copy(cast(ubyte[])(buffer[(y+num)*cols..scroll_to*cols]), 1230 cast(ubyte[])(buffer[y*cols..(scroll_to-num)*cols])); 1231 std.algorithm.mutation.copy(alt_attrs[(y+num)*cols..scroll_to*cols], 1232 alt_attrs[y*cols..(scroll_to-num)*cols]); 1233 buffer[(scroll_to-num)*cols..scroll_to*cols] = " "d[0]; 1234 alt_attrs[(scroll_to-num)*cols..scroll_to*cols] = 0; 1235 } 1236 } 1237 1238 state = StateEscape.WaitEscape; 1239 break; 1240 case 'P': 1241 /*Delete the indicated # of characters on current line.*/ 1242 size_t num = 1; 1243 if (text != "") num = to!int(text); 1244 1245 final switch(mode) 1246 { 1247 case Mode.Working: 1248 //writefln("before buf='%s'", buf[buf_r..max_r]); 1249 size_t bytes = 0; 1250 for (auto j=0; j < num; j++) 1251 { 1252 bytes += buf.mystride(buf_r+bytes); 1253 } 1254 1255 ssize_t newline = buf[0..buf_r].lastIndexOf("\n"); 1256 if (newline < 0) newline = 0; 1257 else newline++; 1258 ssize_t wl = buf[newline..buf_r].myWalkLength(); 1259 1260 ssize_t eol = buf_r; 1261 ssize_t eol_attrs = attrs_r; 1262 do 1263 { 1264 if (eol >= max_r) break; 1265 eol += buf.mystride(eol); 1266 wl++; 1267 eol_attrs++; 1268 } 1269 while (wl%altmode.cols != 0); 1270 1271 bool eob, eoa; 1272 if (eol > max_r) 1273 { 1274 eob = true; 1275 eol = max_r; 1276 } 1277 if (eol_attrs > attrs.length) 1278 { 1279 eoa = true; 1280 eol_attrs = attrs.length; 1281 } 1282 1283 if (buf_r+bytes > eol) bytes = eol - buf_r; 1284 if (attrs_r+num > eol_attrs) num = eol_attrs - attrs_r; 1285 if (buf_r+bytes != eol) 1286 std.algorithm.mutation.copy(cast(ubyte[])(buf[buf_r+bytes..eol]), cast(ubyte[])(buf[buf_r..eol-bytes])); 1287 if (attrs_r+num != eol_attrs) 1288 std.algorithm.mutation.copy(attrs[attrs_r+num..eol_attrs], attrs[attrs_r..eol_attrs-num]); 1289 1290 buf[eol-bytes..eol] = ' '; 1291 attrs[eol_attrs-num..eol_attrs] = Attr.Black<<4 | Attr.White; 1292 1293 if (eob) 1294 max_r -= bytes; 1295 if (eoa) 1296 attrs.length -= num; 1297 //writefln("buf='%s'", buf[buf_r..max_r]); 1298 break; 1299 case Mode.Alternate: 1300 with(altmode) 1301 { 1302 std.algorithm.mutation.copy(cast(ubyte[])(buffer[y*cols+x+num..y*cols+cols]), cast(ubyte[])(buffer[y*cols+x..y*cols+cols-num])); 1303 std.algorithm.mutation.copy(alt_attrs[y*cols+x+num..y*cols+cols], alt_attrs[y*cols+x..y*cols+cols-num]); 1304 1305 buffer[y*cols+cols-num..y*cols+cols] = " "d[0]; 1306 alt_attrs[y*cols+cols-num..y*cols+cols] = Attr.Black<<4 | Attr.White; 1307 } 1308 } 1309 1310 state = StateEscape.WaitEscape; 1311 break; 1312 case 'X': 1313 /*Erase the indicated # of characters on current line.*/ 1314 state = StateEscape.WaitEscape; 1315 break; 1316 case 'a': 1317 /*Move cursor right the indicated # of columns.*/ 1318 state = StateEscape.WaitEscape; 1319 break; 1320 case 'c': 1321 /*Answer ESC [ ? 6 c: "I am a VT102".*/ 1322 state = StateEscape.WaitEscape; 1323 break; 1324 case 'd': 1325 /*Move cursor to the indicated row, current column.*/ 1326 ssize_t num = 1; 1327 if (text > "") 1328 num = to!int(text); 1329 1330 final switch(mode) 1331 { 1332 case Mode.Working: 1333 break; 1334 case Mode.Alternate: 1335 with(altmode) 1336 { 1337 y = num-1; 1338 } 1339 } 1340 state = StateEscape.WaitEscape; 1341 break; 1342 case 'e': 1343 /*Move cursor down the indicated # of rows.*/ 1344 state = StateEscape.WaitEscape; 1345 break; 1346 case 'f': 1347 /*Move cursor to the indicated row, column.*/ 1348 goto case 'H'; 1349 case 'g': 1350 /*Without parameter: clear tab stop at current position.*/ 1351 /* ESC [ 3 g: delete all tab stops. */ 1352 state = StateEscape.WaitEscape; 1353 break; 1354 case 'h': 1355 /*Set Mode.*/ 1356 /*More info on modes: https://conemu.github.io/blog/2015/11/09/Build-151109.html*/ 1357 /*http://www.vt100.net/docs/vt510-rm/chapter4.html*/ 1358 if (text > "") 1359 numbers ~= to!int(text); 1360 1361 if (numbers.length == 1) 1362 { 1363 switch(numbers[0]) 1364 { 1365 case 1: 1366 /*Turn on application cursor mode*/ 1367 altmode.cursor_mode = true; 1368 break; 1369 case 4: 1370 /*Turn on insert mode*/ 1371 altmode.insert_mode = true; 1372 break; 1373 case 25: 1374 /*Turn on cursor*/ 1375 break; 1376 case 12: 1377 /*Turn off local echo*/ 1378 break; 1379 case 1049: 1380 /* Save cursor position and activate xterm alternative buffer (no backscroll) */ 1381 mode = Mode.Alternate; 1382 altmode.reset(); 1383 altmode.buffer.length = altmode.cols*altmode.rows; 1384 altmode.alt_attrs.length = altmode.cols*altmode.rows; 1385 altmode.buffer[0..$] = " "d[0]; 1386 altmode.scroll_from = 0; 1387 altmode.scroll_to = altmode.rows; 1388 break; 1389 default: 1390 writefln("Unknown mode %d", numbers[0]); 1391 } 1392 } 1393 1394 state = StateEscape.WaitEscape; 1395 break; 1396 case 'l': 1397 /*Reset Mode (see below).*/ 1398 if (text > "") 1399 numbers ~= to!int(text); 1400 1401 if (numbers.length == 1) 1402 { 1403 switch(numbers[0]) 1404 { 1405 case 1: 1406 /*Turn off application cursor mode*/ 1407 altmode.cursor_mode = false; 1408 break; 1409 case 4: 1410 /*Turn off insert mode*/ 1411 altmode.insert_mode = false; 1412 break; 1413 case 25: 1414 /*Turn off cursor*/ 1415 break; 1416 case 12: 1417 /*Turn on local echo*/ 1418 break; 1419 case 1049: 1420 mode = Mode.Working; 1421 break; 1422 default: 1423 writefln("Unknown mode %d", numbers[0]); 1424 } 1425 } 1426 1427 state = StateEscape.WaitEscape; 1428 break; 1429 case 'm': 1430 /*Set attributes (see below).*/ 1431 if (text > "") 1432 numbers ~= to!int(text); 1433 1434 if (numbers.length == 0) numbers ~= 0; 1435 1436 ushort *pcur_attr; 1437 final switch(mode) 1438 { 1439 case Mode.Working: 1440 pcur_attr = &cur_attr; 1441 break; 1442 case Mode.Alternate: 1443 with(altmode) 1444 { 1445 pcur_attr = &alt_cur_attr; 1446 } 1447 } 1448 1449 numbers_loop: 1450 foreach(ni, n; numbers) 1451 { 1452 /* About mode: https://ru.wikipedia.org/wiki/%D0%A3%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D1%8F%D1%8E%D1%89%D0%B8%D0%B5_%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D0%B8_ANSI */ 1453 switch (n) 1454 { 1455 case 0: 1456 *pcur_attr = Attr.Black<<4 | Attr.White; 1457 break; 1458 case 1: 1459 *pcur_attr |= Attr.Bold; 1460 break; 1461 case 2: 1462 *pcur_attr |= Attr.HalfBright; 1463 break; 1464 case 4: 1465 *pcur_attr |= Attr.Underscore; 1466 break; 1467 case 5: 1468 *pcur_attr |= Attr.Blink; 1469 break; 1470 case 7: 1471 *pcur_attr = *pcur_attr & 0xFF00 | ((*pcur_attr & 0xF0) >> 4) | ((*pcur_attr & 0x0F) << 4); 1472 break; 1473 case 10: 1474 break; 1475 case 11: 1476 break; 1477 case 12: 1478 break; 1479 case 21: 1480 *pcur_attr &= ~Attr.HalfBright; 1481 break; 1482 case 22: 1483 *pcur_attr &= ~Attr.HalfBright; 1484 break; 1485 case 23: 1486 /* Не курсивный, не фрактура */ 1487 break; 1488 case 24: 1489 *pcur_attr &= ~Attr.Underscore; 1490 break; 1491 case 25: 1492 *pcur_attr &= ~Attr.Blink; 1493 break; 1494 case 27: 1495 *pcur_attr = *pcur_attr & 0xFF00 | ((*pcur_attr & 0xF0) >> 4) | ((*pcur_attr & 0x0F) << 4); 1496 break; 1497 case 30:..case 37: 1498 *pcur_attr = *pcur_attr & 0xFFF0 | ((n-30)&0xF); 1499 break; 1500 case 38: 1501 //*pcur_attr = *pcur_attr & 0xFFF0 | Attr.White | Attr.Underscore; 1502 if (numbers[ni+1] == 5) 1503 { 1504 if (numbers[ni+2]==32) numbers[ni+2]=1; 1505 *pcur_attr = *pcur_attr & 0xFFF0 | (numbers[ni+2] & 0xF); 1506 break numbers_loop; 1507 } 1508 break; 1509 case 39: 1510 *pcur_attr = (*pcur_attr & 0xFFF0 | Attr.White) & ~Attr.Underscore; 1511 break; 1512 case 40:..case 47: 1513 *pcur_attr = *pcur_attr & 0xFF0F | (((n-40)&0xF) << 4); 1514 break; 1515 case 48: 1516 if (numbers[ni+1] == 5) 1517 { 1518 *pcur_attr = *pcur_attr & 0xFF0F | ((numbers[ni+2] & 0xF) << 4); 1519 break numbers_loop; 1520 } 1521 break; 1522 case 49: 1523 *pcur_attr = *pcur_attr & 0xFF0F | (Attr.Black << 4); 1524 break; 1525 default: 1526 writefln("Unknown ECMA-48 SGR sequence %d", n); 1527 break; 1528 } 1529 } 1530 1531 state = StateEscape.WaitEscape; 1532 break; 1533 case 'n': 1534 /*Status report (see below).*/ 1535 if (text > "") 1536 numbers ~= to!int(text); 1537 1538 final switch(mode) 1539 { 1540 case Mode.Working: 1541 break; 1542 case Mode.Alternate: 1543 with(altmode) 1544 { 1545 switch (numbers[0]) 1546 { 1547 case 5: 1548 altmode.control_input ~= "\x1B[0n"; 1549 break; 1550 case 6: 1551 altmode.control_input ~= format("\x1B[%d;%dR", y+1, x+1); 1552 break; 1553 default: 1554 writefln("UNKNOWN sequence ESC [ %d n", numbers[0]); 1555 } 1556 } 1557 } 1558 1559 state = StateEscape.WaitEscape; 1560 break; 1561 case 'q': 1562 /*Set keyboard LEDs.*/ 1563 /*ESC [ 0 q: clear all LEDs 1564 ESC [ 1 q: set Scroll Lock LED 1565 ESC [ 2 q: set Num Lock LED 1566 ESC [ 3 q: set Caps Lock LED*/ 1567 state = StateEscape.WaitEscape; 1568 break; 1569 case 'r': 1570 /*Set scrolling region; parameters are top and bottom row.*/ 1571 if (text > "") 1572 numbers ~= to!int(text); 1573 1574 if (numbers.length == 0) numbers ~= 1; 1575 if (numbers.length == 1) numbers ~= altmode.rows; 1576 1577 final switch(mode) 1578 { 1579 case Mode.Working: 1580 with(altmode) 1581 { 1582 scroll_from = numbers[0]-1; 1583 scroll_to = numbers[1]; 1584 } 1585 break; 1586 case Mode.Alternate: 1587 with(altmode) 1588 { 1589 scroll_from = numbers[0]-1; 1590 scroll_to = numbers[1]; 1591 } 1592 } 1593 1594 state = StateEscape.WaitEscape; 1595 break; 1596 case 's': 1597 /*Save cursor location.*/ 1598 state = StateEscape.WaitEscape; 1599 break; 1600 case 'u': 1601 /*Restore cursor location.*/ 1602 state = StateEscape.WaitEscape; 1603 break; 1604 case '`': 1605 /*Move cursor to indicated column in current row.*/ 1606 state = StateEscape.WaitEscape; 1607 break; 1608 case '>': 1609 if (prebuf[i..i+1] == "c" || prebuf[i..i+2] == "0c") 1610 { 1611 /*Secondary Device Attributes*/ 1612 altmode.control_input ~= "\x1B[>85;95;0c"; 1613 } 1614 break; 1615 case '?': 1616 break; 1617 case '0':.. case '9': 1618 text ~= prebuf[i]; 1619 break; 1620 case ';': 1621 if (text == "") numbers ~= 0; 1622 else numbers ~= to!int(text); 1623 text = ""; 1624 break; 1625 default: 1626 writefln("UNKNOWN Escape Sequence \"ESC [ %c\"", prebuf[i]); 1627 state = StateEscape.WaitEscape; 1628 break; 1629 } 1630 break; 1631 case StateEscape.WaitCharSet: 1632 switch (prebuf[i]) 1633 { 1634 case '@': 1635 /*Select default (ISO 646 / ISO 8859-1)*/ 1636 break; 1637 case 'G': 1638 /*Select UTF-8*/ 1639 break; 1640 case '8': 1641 /*Select UTF-8 (obsolete)*/ 1642 break; 1643 default: 1644 writefln("UNKNOWN Escape Sequence \"ESC %% %c\"", prebuf[i]); 1645 break; 1646 } 1647 state = StateEscape.WaitEscape; 1648 break; 1649 case StateEscape.WaitEight: 1650 switch (prebuf[i]) 1651 { 1652 case '8': 1653 /*DEC screen alignment test - fill screen with E's.*/ 1654 break; 1655 default: 1656 writefln("UNKNOWN Escape Sequence \"ESC # %c\"", prebuf[i]); 1657 break; 1658 } 1659 state = StateEscape.WaitEscape; 1660 break; 1661 case StateEscape.WaitG0CharSet: 1662 switch (prebuf[i]) 1663 { 1664 case 'B': 1665 /*Select default (ISO 8859-1 mapping)*/ 1666 break; 1667 case '0': 1668 /*Select VT100 graphics mapping*/ 1669 break; 1670 case 'U': 1671 /*Select null mapping - straight to character ROM*/ 1672 break; 1673 case 'K': 1674 /*Select user mapping - the map that is loaded by 1675 the utility mapscrn(8).*/ 1676 break; 1677 default: 1678 writefln("UNKNOWN Escape Sequence \"ESC # %c\"", prebuf[i]); 1679 break; 1680 } 1681 state = StateEscape.WaitEscape; 1682 break; 1683 case StateEscape.WaitG1CharSet: 1684 switch (prebuf[i]) 1685 { 1686 case 'B': 1687 /*Select default (ISO 8859-1 mapping)*/ 1688 break; 1689 case '0': 1690 /*Select VT100 graphics mapping*/ 1691 break; 1692 case 'U': 1693 /*Select null mapping - straight to character ROM*/ 1694 break; 1695 case 'K': 1696 /*Select user mapping - the map that is loaded by 1697 the utility mapscrn(8).*/ 1698 break; 1699 default: 1700 writefln("UNKNOWN Escape Sequence \"ESC # %c\"", prebuf[i]); 1701 break; 1702 } 1703 state = StateEscape.WaitEscape; 1704 break; 1705 } 1706 } 1707 1708 if (i < r) 1709 { 1710 std.algorithm.mutation.copy(cast(ubyte[])(prebuf[i..r]), cast(ubyte[])(prebuf[0..r-i])); 1711 prebuf_r = r-i; 1712 } 1713 else 1714 prebuf_r = 0; 1715 1716 return max_r; 1717 } 1718 } 1719 1720 private int 1721 process_input(CMDGlobalState cgs, string cwd, ulong new_id, 1722 ref ulong out_id, ref Mode mode, 1723 ref WorkMode workmode, ref AltMode altmode, 1724 int fd, OutPipe pipe) 1725 { 1726 version(Posix) 1727 { 1728 with(workmode) 1729 { 1730 //writefln("max_r=%d", max_r); 1731 ssize_t r; 1732 //writefln("buf.length-buf_r = %d", buf.length-buf_r); 1733 auto r1 = read(fd, prebuf[prebuf_r..$].ptr, buf.length-prebuf_r-buf_r-16); 1734 auto buf_length_was = buf.length-buf_r; 1735 bool buffer_full = (r1 >= buf.length-prebuf_r-buf_r-16); 1736 1737 if(buf.length < buf_r) buf_r = buf.length-1; 1738 1739 if (r1 > 0) 1740 { 1741 r1 += prebuf_r; 1742 //writefln("READED: %s", prebuf[0..r1]); 1743 //writefln("r1=%d, buf.length-buf_r=%d", r1, buf.length-buf_r); 1744 1745 //writefln("read r=%d", r); 1746 buf[max_r..$] = ' '; 1747 r = process_escape_sequences(cgs, mode, workmode, altmode, r1); 1748 1749 //writefln("PROCESSED: r=%d buf=%s", r, buf[0..r]); 1750 1751 //writefln("r=%d buf=%s", r, buf[0..r]); 1752 //r = read(fd, buf[buf_r..$].ptr, buf[buf_r..$].length); 1753 Dbt key, data; 1754 string ks; 1755 final switch(mode) 1756 { 1757 case Mode.Working: 1758 if (out_id1 == 0) 1759 { 1760 out_id++; 1761 out_id1 = out_id; 1762 } 1763 //writefln("WRITED: out_id1 = %s", out_id1); 1764 //writefln("OUT: new_id=%s out_id=%s", new_id, out_id1); 1765 ks = get_key_for_command_out(command_out_key(cwd, new_id, out_id1)); 1766 key = ks; 1767 1768 ssize_t split_r; 1769 { 1770 ssize_t sym = buf[0..r].lastIndexOf("\n"); 1771 if (sym >= 0) 1772 { 1773 //writefln("Split_r by newline"); 1774 split_r = 0+sym+1; 1775 } 1776 else if (r1 < buf_length_was) split_r = r; 1777 else 1778 { 1779 sym = buf[r/2..r].lastIndexOf("."); 1780 if (sym >= 0) split_r = r/2+sym+1; 1781 else 1782 { 1783 sym = buf[r/2..r].lastIndexOf(" "); 1784 if (sym >= 0) split_r = r/2+sym+1; 1785 else 1786 { 1787 for (auto i = r-1; i >= 0; i--) 1788 { 1789 if ((buf[i] & 0b1000_0000) == 0 || 1790 (buf[i] & 0b1100_0000) == 0b1100_0000) 1791 { 1792 split_r = i; 1793 break; 1794 } 1795 } 1796 //writefln("Split_r by symbol"); 1797 } 1798 } 1799 } 1800 } 1801 1802 ssize_t count_n; 1803 ssize_t i; 1804 ssize_t a; 1805 ssize_t from = 0; 1806 ssize_t from_a = 0; 1807 for (i=0; i < split_r; i+=buf.mystride(i)) 1808 { 1809 char[] chr = buf[i..i + buf.mystride(i)]; 1810 if (chr == "\n") 1811 { 1812 count_n++; 1813 if (select) 1814 { 1815 string line = buf[from..i].idup(); 1816 1817 //writefln("line: %s", line); 1818 if ( exists(line) ) 1819 { 1820 line = buildNormalizedPath(absolutePath(expandTilde(line))); 1821 tid.send("select", line); 1822 } 1823 else 1824 { 1825 writefln("doesn't exist"); 1826 } 1827 1828 from = i+1; 1829 from_a = a+1; 1830 count_n = 0; 1831 } 1832 else 1833 { 1834 if (count_n >= 100) 1835 { 1836 //writefln("buf = %s", buf[from..i+1]); 1837 string ds = get_data_for_command_out( 1838 command_out_data(Clock.currTime().stdTime(), pipe, 1839 buf_r, 1840 buf[from..i+1].idup(), 1841 attrs[from_a..a+1])); 1842 data = ds; 1843 auto res = command_output_put(cgs, &key, &data); 1844 if (res != 0) 1845 { 1846 throw new Exception("DB command out not written"); 1847 } 1848 cgs.OIT++; 1849 out_id1 = 0; 1850 1851 if (out_id1 == 0) 1852 { 1853 out_id++; 1854 out_id1 = out_id; 1855 } 1856 ks = get_key_for_command_out(command_out_key(cwd, new_id, out_id1)); 1857 key = ks; 1858 from = i+1; 1859 from_a = a+1; 1860 count_n = 0; 1861 } 1862 } 1863 } 1864 a++; 1865 } 1866 1867 ssize_t attrs_split_r; 1868 if (select) 1869 { 1870 if (from < split_r) split_r = from-1; 1871 } 1872 else 1873 { 1874 if (i < split_r) split_r = i; 1875 1876 attrs_split_r = buf[0..split_r].myWalkLength(); 1877 if (attrs_split_r > attrs.length) attrs.length = attrs_split_r+1; 1878 1879 //writefln("Write to DB split_r=%d buf=%s, pos = %s, cmd_out=%s", split_r, buf[from..split_r], split_r > buf_r ? buf_r : split_r, out_id1); 1880 //writefln("attrs=%s", attrs[0..attrs_split_r]); 1881 string ds = get_data_for_command_out( 1882 command_out_data(Clock.currTime().stdTime(), pipe, 1883 split_r > buf_r ? buf_r : split_r, 1884 buf[from..split_r].idup(), 1885 attrs[from_a..attrs_split_r])); 1886 data = ds; 1887 auto res = command_output_put(cgs, &key, &data); 1888 if (res != 0) 1889 { 1890 throw new Exception("DB command out not written"); 1891 } 1892 cgs.OIT++; 1893 1894 if (!buffer_full) 1895 { 1896 cgs.commit(); 1897 cgs.recommit(); 1898 } 1899 } 1900 1901 //writefln("r=%d, split_r=%d, buf_r=%d", r, split_r, buf_r); 1902 /*writefln("%s: buf=%s r=%s", pipe, buf[0..split_r], split_r); 1903 if (pipe == OutPipe.STDERR) 1904 { 1905 for (auto i=0; i < split_r; i++) 1906 { 1907 writefln("char=%c code=%X (%d)", buf[i], buf[i], buf[i]); 1908 } 1909 }*/ 1910 /*if (split_r < r) 1911 { 1912 ssize_t a = ((split_r-50 >= 0) ? split_r-50 : 0); 1913 ssize_t b = ((split_r+50 < r) ? split_r+50 : r); 1914 writefln("OUT: split \"%s\"~\"%s\" r=%s", 1915 buf[a..split_r], 1916 buf[split_r..b], 1917 split_r); 1918 }*/ 1919 bool no_n = false; 1920 if (split_r < r) 1921 { 1922 std.algorithm.mutation.copy(cast(ubyte[])(buf[split_r..r]), cast(ubyte[])(buf[0..r - split_r])); 1923 max_r = r - split_r; 1924 1925 std.algorithm.mutation.copy(attrs[attrs_split_r..$], attrs[0..$ - attrs_split_r]); 1926 attrs.length -= attrs_split_r; 1927 1928 if (split_r > buf_r) 1929 { 1930 buf_r = 0; 1931 attrs_r = 0; 1932 } 1933 else 1934 { 1935 buf_r -= split_r; 1936 attrs_r -= attrs_split_r; 1937 } 1938 /*ssize_t c = ((50 < r - split_r) ? 50 : r - split_r); 1939 writefln("buf: \"%s\"", buf[0..c]);*/ 1940 } 1941 else 1942 { 1943 if (r < buf.length/2) 1944 { 1945 if (r > 0 && buf[r-1] != '\n') 1946 { 1947 no_n = true; 1948 1949 //writefln("%s: Not ended with \\n line found: %s", pipe, buf[0..split_r]); 1950 } 1951 } 1952 1953 if (!no_n) 1954 { 1955 buf_r = 0; 1956 max_r = 0; 1957 attrs_r = 0; 1958 attrs.length = 0; 1959 } 1960 } 1961 if (!no_n) 1962 { 1963 //writefln("%s: Ended with \\n line found: %s", pipe, buf[0..split_r]); 1964 out_id1 = 0; 1965 } 1966 //writefln("split_r = %d, r = %d, max_r = %d, buf_r=%d", split_r, r, max_r, buf_r); 1967 if (split_r < r && max_r > 0 && !select) 1968 { 1969 if (out_id1 == 0) 1970 { 1971 out_id++; 1972 out_id1 = out_id; 1973 } 1974 ks = get_key_for_command_out(command_out_key(cwd, new_id, out_id1)); 1975 key = ks; 1976 //writefln("REST buf = %s", buf[0..max_r]); 1977 string ds = get_data_for_command_out( 1978 command_out_data(Clock.currTime().stdTime(), pipe, 1979 buf_r, 1980 buf[0..max_r].idup(), 1981 attrs[0..$])); 1982 data = ds; 1983 auto res = command_output_put(cgs, &key, &data); 1984 if (res != 0) 1985 { 1986 throw new Exception("DB command out not written"); 1987 } 1988 cgs.OIT++; 1989 1990 if (!buffer_full) 1991 { 1992 cgs.commit(); 1993 cgs.recommit(); 1994 } 1995 } 1996 break; 1997 1998 case Mode.Alternate: 1999 with(altmode) 2000 { 2001 if (alt_out_id1 == 0) 2002 { 2003 alt_out_id1 = out_id+1; 2004 } 2005 ks = get_key_for_command_out(command_out_key(cwd, new_id, alt_out_id1)); 2006 key = ks; 2007 2008 string ds = get_data_for_command_out( 2009 command_out_data(Clock.currTime().stdTime(), pipe, 2010 y*cols+x, cols, rows, 2011 buffer, 2012 alt_attrs)); 2013 data = ds; 2014 auto res = command_output_put(cgs, &key, &data); 2015 if (res != 0) 2016 { 2017 throw new Exception("DB command out not written"); 2018 } 2019 cgs.OIT++; 2020 2021 if (!buffer_full) 2022 { 2023 cgs.commit(); 2024 cgs.recommit(); 2025 } 2026 } 2027 break; 2028 } 2029 } 2030 if (r1 < 0 && errno != EWOULDBLOCK) 2031 { 2032 //throw new Exception("read() error: " ~ fromStringz(strerror(errno)).idup()); 2033 return 1; 2034 } 2035 if (r1 == 0) return 1; 2036 return 0; 2037 } 2038 } 2039 else 2040 version (Windows) 2041 { 2042 return 0; 2043 } 2044 } 2045 2046 private int 2047 fork_command(CMDGlobalState cgs, string cwd, string full_cwd, string command, 2048 winsize ws, immutable string[] selection, Tid tid) 2049 { 2050 version (Posix) 2051 { 2052 cgs.recommit(); 2053 2054 /*db_commands 2055 cwd, id 2056 command, start, end, status*/ 2057 2058 ulong id = get_max_id_of_cwd(cgs, cwd); 2059 ulong new_id = id+1; 2060 if (id > 0) 2061 { 2062 new_id = id + 1000 - id%1000; 2063 //writefln("last_id=%s (%%1000=%s), new_id=%s", id, id%1000, new_id); 2064 } 2065 2066 if (command[0] != '*') 2067 { 2068 ulong replace_id = find_command_in_cwd(cgs, cwd, command); 2069 2070 Dbt key2, data2; 2071 string ks2 = get_key_for_command(command_key(cwd, replace_id)); 2072 key2 = ks2; 2073 2074 auto res = cgs.db_commands.get(cgs.txn, &key2, &data2); 2075 if (res == 0) 2076 { 2077 string data2_string = data2.to!(string); 2078 command_data cmd_data2; 2079 parse_data_for_command(data2_string, cmd_data2); 2080 if (cmd_data2.end > 0) 2081 { 2082 delete_command_out(cgs, cwd, replace_id); 2083 2084 cgs.commit; 2085 cgs.recommit; 2086 2087 string ks = get_key_for_command(command_key(cwd, replace_id)); 2088 Dbt key = ks; 2089 res = cgs.db_commands.del(cgs.txn, &key); 2090 2091 cgs.commit; 2092 cgs.recommit; 2093 } 2094 } 2095 } 2096 2097 tid.send(thisTid, "command_id", new_id); 2098 2099 cgs.commit(); 2100 cgs.recommit(); 2101 2102 Dbt key, data; 2103 string ks = get_key_for_command(command_key(cwd, new_id)); 2104 key = ks; 2105 command_data cmd_data = command_data(command, 0, 0, -1); 2106 string ds = get_data_for_command(cmd_data); 2107 data = ds; 2108 2109 auto res = cgs.db_commands.put(cgs.txn, &key, &data); 2110 if (res != 0) 2111 { 2112 throw new Exception("DB command not written"); 2113 } 2114 2115 if (command[0] == '+' && id > 0) 2116 { 2117 Dbt key2, data2; 2118 string ks2 = get_key_for_command(command_key(cwd, id)); 2119 key2 = ks2; 2120 2121 do 2122 { 2123 res = cgs.db_commands.get(cgs.txn, &key2, &data2); 2124 if (res == 0) 2125 { 2126 string data2_string = data2.to!(string); 2127 command_data cmd_data2; 2128 parse_data_for_command(data2_string, cmd_data2); 2129 if (cmd_data2.end > 0) 2130 { 2131 if (cmd_data2.status != 0) 2132 { 2133 cmd_data.end = Clock.currTime().stdTime(); 2134 ds = get_data_for_command(cmd_data); 2135 data = ds; 2136 2137 res = cgs.db_commands.put(cgs.txn, &key, &data); 2138 if (res != 0) 2139 { 2140 throw new Exception("DB command not written"); 2141 } 2142 cgs.OIT++; 2143 2144 return cmd_data2.status; 2145 } 2146 else 2147 { 2148 break; 2149 } 2150 } 2151 } 2152 else 2153 { 2154 writefln("Command with id %d not found", id); 2155 return -1; 2156 } 2157 2158 cgs.commit; 2159 Thread.sleep( 200.msecs() ); 2160 cgs.recommit; 2161 } 2162 while (true); 2163 } 2164 2165 if (command[0] == '*' || command[0] == '+') 2166 { 2167 command = command[1..$]; 2168 } 2169 2170 if (!full_cwd.isDir()) full_cwd = full_cwd[0..cwd.lastIndexOf("/")]; 2171 chdir(full_cwd); 2172 2173 bool select_files; 2174 version(WithoutTTY) 2175 { 2176 auto cmd_pipes = pipeProcess(["bash", "-c", command], Redirect.stdout | Redirect.stderr | Redirect.stdin); 2177 auto pid = cmd_pipes.pid; 2178 2179 auto fdstdout = cmd_pipes.stdout.fileno; 2180 auto fdstderr = cmd_pipes.stderr.fileno; 2181 auto fdstdin = cmd_pipes.stdin.fileno; 2182 auto fdmax = max(fdstdout, fdstderr, fdstdin); 2183 } 2184 else 2185 { 2186 if (!access("/bin/bash".toStringz(), X_OK) == 0) 2187 throw new Exception(text("Not an executable file: ", "/bin/bash")); 2188 2189 int master; 2190 2191 auto stderrPipe = pipe(); 2192 2193 if (command.indexOf("${SELECTED[@]}") >= 0 && selection.length > 0) 2194 { 2195 environment["SELECTED_FILES"] = join(selection, "\n"); 2196 command = q"{declare -a SELECTED 2197 OLD_IFS="$IFS" 2198 IFS=$'\n' 2199 i=0 2200 for s in $SELECTED_FILES; do 2201 SELECTED[$i]=$s 2202 i=$[$i+1] 2203 done 2204 IFS="$OLD_IFS" 2205 2206 }" ~ command; 2207 } 2208 2209 if ( command.endsWith("| select") ) 2210 { 2211 command = command[0..$-8]; 2212 select_files = true; 2213 } 2214 2215 environment["TERM"] = "rxvt-unicode"; 2216 environment["COLORTERM"] = "rxvt-xpm"; 2217 2218 immutable(char) *bash = "/bin/bash".toStringz(); 2219 immutable(char) *c_opt = "-c".toStringz(); 2220 immutable(char) *command_z = command.toStringz(); 2221 2222 auto pid = forkpty(&master, null, null, &ws); 2223 if (pid < 0) 2224 { 2225 throw new Exception("forkpty() error: " ~ fromStringz(strerror(errno)).idup()); 2226 } 2227 else if ( pid == 0 ) // Child 2228 { 2229 stderrPipe.readEnd.close(); 2230 dup2(stderrPipe.writeEnd.fileno, stderr.fileno); 2231 stderrPipe.writeEnd.close(); 2232 execl(bash, bash, c_opt, command_z, null); 2233 writefln("execl() error: " ~ fromStringz(strerror(errno)).idup()); 2234 assert(0); 2235 } 2236 //parent 2237 stderrPipe.writeEnd.close(); 2238 2239 if (command.indexOf("${SELECTED[@]}") >= 0 && selection.length > 0) 2240 { 2241 environment.remove("SELECTED_FILES"); 2242 } 2243 2244 File mfile; 2245 mfile.fdopen(master, "a+"); 2246 scope(exit) { 2247 mfile.close(); 2248 close(master); 2249 } 2250 2251 //auto pid = spawnProcess(["bash", "-c", command], sfile, sfile, stderrPipe.writeEnd); 2252 2253 auto fdstdout = master; 2254 auto fdstderr = stderrPipe.readEnd.fileno; 2255 auto fdstdin = master; 2256 auto fdmax = max(fdstdout, fdstderr, fdstdin); 2257 } 2258 2259 scope(success) { 2260 waitpid(pid, null, 0); 2261 } 2262 scope(failure) { 2263 kill(pid, SIGKILL); 2264 waitpid(pid, null, 0); 2265 } 2266 2267 cmd_data.start = Clock.currTime().stdTime(); 2268 ds = get_data_for_command(cmd_data); 2269 data = ds; 2270 2271 res = cgs.db_commands.put(cgs.txn, &key, &data); 2272 if (res != 0) 2273 { 2274 throw new Exception("DB command not written"); 2275 } 2276 2277 cgs.commit(); 2278 cgs.recommit(); 2279 2280 /*Make file descriptions NON_BLOCKING */ 2281 set_non_block_mode(fdstdout); 2282 set_non_block_mode(fdstderr); 2283 set_non_block_mode(fdstdin); 2284 2285 fd_set rfds; 2286 fd_set wfds; 2287 timeval tv; 2288 int retval; 2289 2290 /* Wait up to 100ms seconds. */ 2291 tv.tv_sec = 0; 2292 tv.tv_usec = 100_000; 2293 2294 bool select_zero; 2295 bool terminated; 2296 int result = -1; 2297 ulong out_id = 0; 2298 Mode mode; 2299 WorkMode workmode1; 2300 WorkMode workmode2; 2301 workmode1.tid = tid; 2302 workmode1.select = select_files; 2303 AltMode altmode; 2304 altmode.cols = ws.ws_col; 2305 altmode.rows = ws.ws_row; 2306 altmode.scroll_from = 0; 2307 altmode.scroll_to = altmode.rows; 2308 writefln("ALTMODE.WS: %dx%d", altmode.cols, altmode.rows); 2309 bool stdin_closed = false; 2310 while(!cgs.finish) 2311 { 2312 cgs.recommit(); 2313 if (select_zero) 2314 { 2315 int status; 2316 if ( waitpid(pid, &status, WNOHANG) ) 2317 { 2318 cgs.commit(); 2319 cgs.recommit(); 2320 cmd_data.end = Clock.currTime().stdTime(); 2321 cmd_data.status = status; 2322 result = status; 2323 ds = get_data_for_command(cmd_data); 2324 data = ds; 2325 2326 res = cgs.db_commands.put(cgs.txn, &key, &data); 2327 if (res != 0) 2328 { 2329 throw new Exception("DB command not written"); 2330 } 2331 cgs.OIT++; 2332 break; 2333 } 2334 } 2335 2336 select_zero = false; 2337 2338 FD_ZERO(&rfds); 2339 FD_SET(fdstdout, &rfds); 2340 FD_SET(fdstderr, &rfds); 2341 2342 if (!stdin_closed) 2343 { 2344 FD_ZERO(&wfds); 2345 FD_SET(fdstdin, &wfds); 2346 } 2347 2348 int eof = 0; 2349 bool pause = true; 2350 2351 retval = select(fdmax+1, &rfds, stdin_closed ? null : &wfds, null, &tv); 2352 if (retval < 0) 2353 { 2354 if (errno != EINTR) 2355 throw new Exception("select() error: " ~ fromStringz(strerror(errno)).idup()); 2356 } 2357 else if (retval > 0) 2358 { 2359 if (FD_ISSET(fdstdout, &rfds)) 2360 { 2361 pause = false; 2362 eof += process_input(cgs, cwd, new_id, 2363 out_id, mode, workmode1, altmode, 2364 fdstdout, OutPipe.STDOUT); 2365 tid.send(thisTid, "update terminal"); 2366 } 2367 if (FD_ISSET(fdstderr, &rfds)) 2368 { 2369 pause = false; 2370 eof += process_input(cgs, cwd, new_id, 2371 out_id, mode, workmode2, altmode, 2372 fdstderr, OutPipe.STDERR); 2373 tid.send(thisTid, "update terminal"); 2374 } 2375 if ( !stdin_closed && FD_ISSET(fdstdin, &wfds) ) 2376 { 2377 while( receiveTimeout( 0.seconds, 2378 (string input) { 2379 /*foreach (i; input) 2380 { 2381 writefln("INPUT: '%c' - %X (%d)", i, i, i); 2382 }*/ 2383 if (input == "\x04") // Ctrl+D 2384 { 2385 writefln("Ctrl+D"); 2386 version(WithoutTTY) 2387 { 2388 cmd_pipes.stdin.close(); 2389 stdin_closed = true; 2390 } 2391 else 2392 { 2393 core.sys.posix.unistd.write(fdstdin, input.ptr, input.length); 2394 } 2395 } 2396 else 2397 { 2398 if (altmode.cursor_mode) 2399 { 2400 if (input.startsWith("\x1B[") && 2401 input.length > 2 && 2402 "ABCD".indexOf(input[2]) >= 0) 2403 { 2404 input = input[0..1] ~ "O" ~ input[2..3]; 2405 } 2406 } 2407 core.sys.posix.unistd.write(fdstdin, input.ptr, input.length); 2408 } 2409 } 2410 ) ) 2411 { 2412 } 2413 } 2414 if (!stdin_closed && altmode.control_input.length > 0) 2415 { 2416 string input = altmode.control_input; 2417 core.sys.posix.unistd.write(fdstdin, input.ptr, input.length); 2418 altmode.control_input = ""; 2419 } 2420 } 2421 else 2422 { 2423 select_zero = true; 2424 } 2425 2426 if (eof >= 2) select_zero = true; 2427 2428 while ( receiveTimeout( 0.seconds, 2429 (OwnerTerminated ot) { 2430 writefln("Abort command due stopping parent"); 2431 kill(pid, SIGKILL); 2432 cgs.finish = true; 2433 2434 cmd_data.end = Clock.currTime().stdTime(); 2435 cmd_data.status = -1; 2436 string ds = get_data_for_command(cmd_data); 2437 data = ds; 2438 2439 auto res = cgs.db_commands.put(cgs.txn, &key, &data); 2440 if (res != 0) 2441 { 2442 throw new Exception("DB command not written"); 2443 } 2444 }, 2445 (string cmd, typeof(SIGTERM) signal) 2446 { 2447 if (cmd == "signal") 2448 { 2449 kill(pid, signal); 2450 } 2451 }, 2452 (winsize new_ws) 2453 { 2454 auto rc = ioctl(master, TIOCSWINSZ, &new_ws); 2455 if (rc < 0) 2456 { 2457 throw new Exception("ioctl(TIOCSWINSZ) error: " ~ fromStringz(strerror(errno)).idup()); 2458 } 2459 2460 altmode.cols = new_ws.ws_col; 2461 altmode.rows = new_ws.ws_row; 2462 writefln("CHANGE WS: %d, %d", altmode.cols, altmode.rows); 2463 2464 altmode.buffer.length = altmode.cols*altmode.rows; 2465 altmode.alt_attrs.length = altmode.cols*altmode.rows; 2466 altmode.buffer[0..$] = " "d[0]; 2467 altmode.scroll_from = 0; 2468 altmode.scroll_to = altmode.rows; 2469 } ) ) 2470 { 2471 } 2472 2473 if (pause) 2474 Thread.sleep(100.msecs); 2475 } 2476 return result; 2477 } 2478 version (Windows) 2479 { 2480 return 0; 2481 } 2482 } 2483 2484 private void 2485 command(string cwd, string full_cwd, string command, winsize ws, 2486 immutable string[] selection, Tid tid) 2487 { 2488 CMDGlobalState cgs = new CMDGlobalState(); 2489 try { 2490 scope(exit) 2491 { 2492 destroy(cgs); 2493 } 2494 fork_command(cgs, cwd, full_cwd, command, ws, selection, tid); 2495 cgs.commit(); 2496 } catch (shared(Throwable) exc) { 2497 send(tid, exc); 2498 } 2499 2500 writefln("Finish command %s", command); 2501 send(tid, thisTid); 2502 } 2503 2504 private auto 2505 get_arguments(bool Multiversion = true)(GlobalState gs, string command) 2506 { 2507 string full_current_path = gs.full_current_path; 2508 version (Windows) 2509 { 2510 if (full_current_path.length == 2 && full_current_path[1] == ':') 2511 full_current_path ~= SL; 2512 } 2513 2514 while (command > "" && command[0] == ' ' || command[0] == '\t') 2515 command = command[1..$]; 2516 2517 string argument = ""; 2518 static if (Multiversion) 2519 { 2520 string[] arguments; 2521 } 2522 2523 StringStatus status; 2524 2525 for (ssize_t i=0; i < command.length; i+= command.stride(i)) 2526 { 2527 string chr = command[i..i+command.stride(i)]; 2528 2529 if (chr == `\` && status != StringStatus.Quote) 2530 { 2531 i++; 2532 if (i >= command.length) continue; 2533 chr = command[i..i+command.stride(i)]; 2534 argument ~= chr; 2535 } 2536 else if (chr == `"` && status == StringStatus.Normal) 2537 { 2538 status = StringStatus.DblQuote; 2539 } 2540 else if (chr == `"` && status == StringStatus.DblQuote) 2541 { 2542 status = StringStatus.Normal; 2543 } 2544 else if (chr == `'` && status == StringStatus.Normal) 2545 { 2546 status = StringStatus.Quote; 2547 } 2548 else if (chr == `'` && status == StringStatus.Quote) 2549 { 2550 status = StringStatus.Normal; 2551 } 2552 else if (chr == ` ` && status == StringStatus.Normal) 2553 { 2554 static if (Multiversion) 2555 { 2556 arguments ~= buildNormalizedPath(absolutePath(expandTilde(argument), gs.full_current_path)); 2557 argument = ""; 2558 while (i+1 < command.length && command[i+1] == ' ') 2559 i++; 2560 } 2561 else 2562 { 2563 return [""]; 2564 } 2565 } 2566 else if (chr == `&` && status == StringStatus.Normal) 2567 { 2568 return [""]; 2569 } 2570 else if (chr == `|` && status == StringStatus.Normal) 2571 { 2572 return [""]; 2573 } 2574 else 2575 { 2576 argument ~= chr; 2577 } 2578 } 2579 2580 static if (Multiversion) 2581 { 2582 if (argument > "") 2583 arguments ~= buildNormalizedPath(absolutePath(expandTilde(argument), full_current_path)); 2584 return arguments; 2585 } 2586 else 2587 { 2588 return [buildNormalizedPath(absolutePath(expandTilde(argument), full_current_path))]; 2589 } 2590 } 2591 2592 private string 2593 get_argument(GlobalState gs, string command) 2594 { 2595 string[] args = get_arguments!false(gs, command); 2596 return args[0]; 2597 } 2598 2599 private bool 2600 write_command_and_response(GlobalState gs, string command, string error) 2601 { 2602 gs.txn = null; 2603 string cwd = gs.current_path; 2604 ulong id = get_max_id_of_cwd(gs, cwd); 2605 ulong new_id = id+1; 2606 if (id > 0) 2607 { 2608 new_id = id + 1000 - id%1000; 2609 //writefln("last_id=%s (%%1000=%s), new_id=%s", id, id%1000, new_id); 2610 } 2611 2612 { 2613 ulong replace_id = find_command_in_cwd(gs, cwd, command); 2614 2615 delete_command_out(gs, cwd, replace_id); 2616 2617 string ks = get_key_for_command(command_key(cwd, replace_id)); 2618 Dbt key = ks; 2619 auto res = gs.db_commands.del(gs.txn, &key); 2620 } 2621 2622 Dbt key, data; 2623 string ks = get_key_for_command(command_key(cwd, new_id)); 2624 key = ks; 2625 command_data cmd_data = command_data(command, Clock.currTime().stdTime(), Clock.currTime().stdTime(), error > ""?-1:0); 2626 string ds = get_data_for_command(cmd_data); 2627 data = ds; 2628 2629 auto res = gs.db_commands.put(null, &key, &data); 2630 if (res != 0) 2631 { 2632 throw new Exception("DB command not written"); 2633 } 2634 2635 if (error > "") 2636 { 2637 ulong out_id = 1; 2638 ks = get_key_for_command_out(command_out_key(cwd, new_id, out_id)); 2639 key = ks; 2640 2641 ds = get_data_for_command_out( 2642 command_out_data(Clock.currTime().stdTime(), OutPipe.STDERR, 2643 error.length, 2644 error)); 2645 data = ds; 2646 res = gs.db_command_output.put(null, &key, &data); 2647 if (res != 0) 2648 { 2649 throw new Exception("DB command out not written"); 2650 } 2651 } 2652 return true; 2653 } 2654 2655 private bool 2656 exec_builtin_command(GlobalState gs, string command) 2657 { 2658 string orig_command = command; 2659 while (command > "" && command[0] == ' ' || command[0] == '\t') 2660 command = command[1..$]; 2661 2662 while (command > "" && command[$-1] == ' ' || command[$-1] == '\t') 2663 command = command[0..$-1]; 2664 2665 if (command.startsWith("cd ")) 2666 { 2667 string argument = get_argument(gs, command[3..$]); 2668 if (argument == "") return write_command_and_response( 2669 gs, orig_command, "Wrong using built-in command cd"); 2670 2671 auto apply_rect = DRect(0, 0, 1024*1024, 1024*1024); 2672 auto drect = get_rectsize_for_path(gs, PathMnt(gs.lsblk, SL), argument, apply_rect); 2673 2674 if (!isNaN(drect.w)) 2675 { 2676 drect.rescale_screen(gs.screen, SDL_Rect(0, 0, gs.screen.w, gs.screen.h)); 2677 } 2678 else 2679 { 2680 writefln("Can't calculate DRect for %s", argument); 2681 } 2682 2683 gs.state = State.FileManager; 2684 write_command_and_response( 2685 gs, orig_command, ""); 2686 return true; 2687 } 2688 else if (command.startsWith("go ")) 2689 { 2690 string argument = get_argument(gs, command[3..$]); 2691 if (argument == "") return write_command_and_response( 2692 gs, orig_command, "Wrong using built-in command go"); 2693 2694 auto apply_rect = DRect(0, 0, 1024*1024, 1024*1024); 2695 auto drect = get_rectsize_for_path(gs, PathMnt(gs.lsblk, SL), argument, apply_rect); 2696 2697 if (!isNaN(drect.w)) 2698 { 2699 drect.rescale_screen(gs.screen, 2700 SDL_Rect(cast(int)((gs.screen.w - gs.screen.h*0.75)/2), 2701 cast(int)((gs.screen.h - cast(double)gs.screen.h/(gs.screen.w/gs.screen.h)*0.75)/2), 2702 cast(int)(gs.screen.h*0.75), 2703 cast(int)(cast(double)gs.screen.h/(gs.screen.w/gs.screen.h)*0.75))); 2704 } 2705 else 2706 { 2707 writefln("Can't calculate DRect for %s", argument); 2708 } 2709 2710 gs.state = State.FileManager; 2711 write_command_and_response( 2712 gs, orig_command, ""); 2713 return true; 2714 } 2715 else if (command.startsWith("open ")) 2716 { 2717 string argument = get_argument(gs, command[5..$]); 2718 if (argument == "") return write_command_and_response( 2719 gs, orig_command, "Wrong using built-in command open"); 2720 2721 openFile(gs, PathMnt(gs.lsblk, argument)); 2722 2723 write_command_and_response( 2724 gs, orig_command, ""); 2725 return true; 2726 } 2727 else if (command.startsWith("mopen ")) 2728 { 2729 string argument = get_argument(gs, command[6..$]); 2730 if (argument == "") return write_command_and_response( 2731 gs, orig_command, "Wrong using built-in command mopen"); 2732 2733 openFileByMime(gs, argument); 2734 2735 write_command_and_response( 2736 gs, orig_command, ""); 2737 return true; 2738 } 2739 else if (command.startsWith("select ")) 2740 { 2741 string[] arguments = get_arguments(gs, command[7..$]); 2742 if (arguments == []) return write_command_and_response( 2743 gs, orig_command, "Wrong using built-in command select"); 2744 2745 foreach (arg; arguments) 2746 { 2747 auto pathmnt = PathMnt(gs.lsblk, arg); 2748 auto apply_rect = DRect(0, 0, 1024*1024, 1024*1024); 2749 auto drect = get_rectsize_for_path(gs, PathMnt(gs.lsblk, SL), arg, apply_rect); 2750 gs.selection_hash[pathmnt] = drect; 2751 } 2752 2753 gs.dirty = true; 2754 2755 write_command_and_response( 2756 gs, orig_command, ""); 2757 return true; 2758 } 2759 2760 return false; 2761 } 2762 2763 public int 2764 run_command(GlobalState gs, string command) 2765 { 2766 if (command == "") return -1; 2767 2768 if (exec_builtin_command(gs, command)) return 0; 2769 2770 string[] selection; 2771 if (command.indexOf("${SELECTED[@]}") >= 0) 2772 selection = gs.selection_hash.keys; 2773 writefln("Start command %s", command); 2774 auto tid = spawn(&.command, gs.current_path, gs.full_current_path, command, 2775 gs.command_line.ws, selection.idup(), thisTid); 2776 gs.commands[tid] = command; 2777 return 0; 2778 } 2779