107/* ed: line editor */ 108int 109main(argc, argv) 110 int argc; 111 char **argv; 112{ 113 int c, n; 114 long status = 0; 115#if __GNUC__ 116 /* Avoid longjmp clobbering */ 117 (void) &argc; 118 (void) &argv; 119#endif 120 121 (void)setlocale(LC_ALL, ""); 122 123 red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r'; 124top: 125 while ((c = getopt(argc, argv, "p:sx")) != -1) 126 switch(c) { 127 case 'p': /* set prompt */ 128 prompt = optarg; 129 break; 130 case 's': /* run script */ 131 scripted = 1; 132 break; 133 case 'x': /* use crypt */ 134#ifdef DES 135 des = get_keyword(); 136#else 137 fprintf(stderr, "crypt unavailable\n?\n"); 138#endif 139 break; 140 141 default: 142 fprintf(stderr, usage, argv[0]); 143 exit(1); 144 } 145 argv += optind; 146 argc -= optind; 147 if (argc && **argv == '-') { 148 scripted = 1; 149 if (argc > 1) { 150 optind = 1; 151 goto top; 152 } 153 argv++; 154 argc--; 155 } 156 /* assert: reliable signals! */ 157#ifdef SIGWINCH 158 handle_winch(SIGWINCH); 159 if (isatty(0)) signal(SIGWINCH, handle_winch); 160#endif 161 signal(SIGHUP, signal_hup); 162 signal(SIGQUIT, SIG_IGN); 163 signal(SIGINT, signal_int); 164#ifdef _POSIX_SOURCE 165 if ((status = sigsetjmp(env, 1))) 166#else 167 if ((status = setjmp(env))) 168#endif 169 { 170 fputs("\n?\n", stderr); 171 sprintf(errmsg, "interrupt"); 172 } else { 173 init_buffers(); 174 sigactive = 1; /* enable signal handlers */ 175 if (argc && **argv && is_legal_filename(*argv)) { 176 if (read_file(*argv, 0) < 0 && !isatty(0)) 177 quit(2); 178 else if (**argv != '!') 179 strcpy(old_filename, *argv); 180 } else if (argc) { 181 fputs("?\n", stderr); 182 if (**argv == '\0') 183 sprintf(errmsg, "invalid filename"); 184 if (!isatty(0)) 185 quit(2); 186 } 187 } 188 for (;;) { 189 if (status < 0 && garrulous) 190 fprintf(stderr, "%s\n", errmsg); 191 if (prompt) { 192 printf("%s", prompt); 193 fflush(stdout); 194 } 195 if ((n = get_tty_line()) < 0) { 196 status = ERR; 197 continue; 198 } else if (n == 0) { 199 if (modified && !scripted) { 200 fputs("?\n", stderr); 201 sprintf(errmsg, "warning: file modified"); 202 if (!isatty(0)) { 203 fprintf(stderr, garrulous ? 204 "script, line %d: %s\n" : 205 "", lineno, errmsg); 206 quit(2); 207 } 208 clearerr(stdin); 209 modified = 0; 210 status = EMOD; 211 continue; 212 } else 213 quit(0); 214 } else if (ibuf[n - 1] != '\n') { 215 /* discard line */ 216 sprintf(errmsg, "unexpected end-of-file"); 217 clearerr(stdin); 218 status = ERR; 219 continue; 220 } 221 isglobal = 0; 222 if ((status = extract_addr_range()) >= 0 && 223 (status = exec_command()) >= 0) 224 if (!status || 225 (status = display_lines(current_addr, current_addr, 226 status)) >= 0) 227 continue; 228 switch (status) { 229 case EOF: 230 quit(0); 231 case EMOD: 232 modified = 0; 233 fputs("?\n", stderr); /* give warning */ 234 sprintf(errmsg, "warning: file modified"); 235 if (!isatty(0)) { 236 fprintf(stderr, garrulous ? 237 "script, line %d: %s\n" : 238 "", lineno, errmsg); 239 quit(2); 240 } 241 break; 242 case FATAL: 243 if (!isatty(0)) 244 fprintf(stderr, garrulous ? 245 "script, line %d: %s\n" : "", 246 lineno, errmsg); 247 else 248 fprintf(stderr, garrulous ? "%s\n" : "", 249 errmsg); 250 quit(3); 251 default: 252 fputs("?\n", stderr); 253 if (!isatty(0)) { 254 fprintf(stderr, garrulous ? 255 "script, line %d: %s\n" : "", 256 lineno, errmsg); 257 quit(2); 258 } 259 break; 260 } 261 } 262 /*NOTREACHED*/ 263} 264 265long first_addr, second_addr, addr_cnt; 266 267/* extract_addr_range: get line addresses from the command buffer until an 268 illegal address is seen; return status */ 269int 270extract_addr_range() 271{ 272 long addr; 273 274 addr_cnt = 0; 275 first_addr = second_addr = current_addr; 276 while ((addr = next_addr()) >= 0) { 277 addr_cnt++; 278 first_addr = second_addr; 279 second_addr = addr; 280 if (*ibufp != ',' && *ibufp != ';') 281 break; 282 else if (*ibufp++ == ';') 283 current_addr = addr; 284 } 285 if ((addr_cnt = min(addr_cnt, 2)) == 1 || second_addr != addr) 286 first_addr = second_addr; 287 return (addr == ERR) ? ERR : 0; 288} 289 290 291#define SKIP_BLANKS() while (isspace((unsigned char)*ibufp) && *ibufp != '\n') ibufp++ 292 293#define MUST_BE_FIRST() \ 294 if (!first) { sprintf(errmsg, "invalid address"); return ERR; } 295 296/* next_addr: return the next line address in the command buffer */ 297long 298next_addr() 299{ 300 char *hd; 301 long addr = current_addr; 302 long n; 303 int first = 1; 304 int c; 305 306 SKIP_BLANKS(); 307 for (hd = ibufp;; first = 0) 308 switch (c = *ibufp) { 309 case '+': 310 case '\t': 311 case ' ': 312 case '-': 313 case '^': 314 ibufp++; 315 SKIP_BLANKS(); 316 if (isdigit((unsigned char)*ibufp)) { 317 STRTOL(n, ibufp); 318 addr += (c == '-' || c == '^') ? -n : n; 319 } else if (!isspace((unsigned char)c)) 320 addr += (c == '-' || c == '^') ? -1 : 1; 321 break; 322 case '0': case '1': case '2': 323 case '3': case '4': case '5': 324 case '6': case '7': case '8': case '9': 325 MUST_BE_FIRST(); 326 STRTOL(addr, ibufp); 327 break; 328 case '.': 329 case '$': 330 MUST_BE_FIRST(); 331 ibufp++; 332 addr = (c == '.') ? current_addr : addr_last; 333 break; 334 case '/': 335 case '?': 336 MUST_BE_FIRST(); 337 if ((addr = get_matching_node_addr( 338 get_compiled_pattern(), c == '/')) < 0) 339 return ERR; 340 else if (c == *ibufp) 341 ibufp++; 342 break; 343 case '\'': 344 MUST_BE_FIRST(); 345 ibufp++; 346 if ((addr = get_marked_node_addr(*ibufp++)) < 0) 347 return ERR; 348 break; 349 case '%': 350 case ',': 351 case ';': 352 if (first) { 353 ibufp++; 354 addr_cnt++; 355 second_addr = (c == ';') ? current_addr : 1; 356 addr = addr_last; 357 break; 358 } 359 /* FALL THROUGH */ 360 default: 361 if (ibufp == hd) 362 return EOF; 363 else if (addr < 0 || addr_last < addr) { 364 sprintf(errmsg, "invalid address"); 365 return ERR; 366 } else 367 return addr; 368 } 369 /* NOTREACHED */ 370} 371 372 373#ifdef BACKWARDS 374/* GET_THIRD_ADDR: get a legal address from the command buffer */ 375#define GET_THIRD_ADDR(addr) \ 376{ \ 377 long ol1, ol2; \ 378\ 379 ol1 = first_addr, ol2 = second_addr; \ 380 if (extract_addr_range() < 0) \ 381 return ERR; \ 382 else if (addr_cnt == 0) { \ 383 sprintf(errmsg, "destination expected"); \ 384 return ERR; \ 385 } else if (second_addr < 0 || addr_last < second_addr) { \ 386 sprintf(errmsg, "invalid address"); \ 387 return ERR; \ 388 } \ 389 addr = second_addr; \ 390 first_addr = ol1, second_addr = ol2; \ 391} 392#else /* BACKWARDS */ 393/* GET_THIRD_ADDR: get a legal address from the command buffer */ 394#define GET_THIRD_ADDR(addr) \ 395{ \ 396 long ol1, ol2; \ 397\ 398 ol1 = first_addr, ol2 = second_addr; \ 399 if (extract_addr_range() < 0) \ 400 return ERR; \ 401 if (second_addr < 0 || addr_last < second_addr) { \ 402 sprintf(errmsg, "invalid address"); \ 403 return ERR; \ 404 } \ 405 addr = second_addr; \ 406 first_addr = ol1, second_addr = ol2; \ 407} 408#endif 409 410 411/* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */ 412#define GET_COMMAND_SUFFIX() { \ 413 int done = 0; \ 414 do { \ 415 switch(*ibufp) { \ 416 case 'p': \ 417 gflag |= GPR, ibufp++; \ 418 break; \ 419 case 'l': \ 420 gflag |= GLS, ibufp++; \ 421 break; \ 422 case 'n': \ 423 gflag |= GNP, ibufp++; \ 424 break; \ 425 default: \ 426 done++; \ 427 } \ 428 } while (!done); \ 429 if (*ibufp++ != '\n') { \ 430 sprintf(errmsg, "invalid command suffix"); \ 431 return ERR; \ 432 } \ 433} 434 435 436/* sflags */ 437#define SGG 001 /* complement previous global substitute suffix */ 438#define SGP 002 /* complement previous print suffix */ 439#define SGR 004 /* use last regex instead of last pat */ 440#define SGF 010 /* repeat last substitution */ 441 442int patlock = 0; /* if set, pattern not freed by get_compiled_pattern() */ 443 444long rows = 22; /* scroll length: ws_row - 2 */ 445 446/* exec_command: execute the next command in command buffer; return print 447 request, if any */ 448int 449exec_command() 450{ 451 extern long u_current_addr; 452 extern long u_addr_last; 453 454 static pattern_t *pat = NULL; 455 static int sgflag = 0; 456 static int sgnum = 0; 457 458 pattern_t *tpat; 459 char *fnp; 460 int gflag = 0; 461 int sflags = 0; 462 long addr = 0; 463 int n = 0; 464 int c; 465 466 SKIP_BLANKS(); 467 switch(c = *ibufp++) { 468 case 'a': 469 GET_COMMAND_SUFFIX(); 470 if (!isglobal) clear_undo_stack(); 471 if (append_lines(second_addr) < 0) 472 return ERR; 473 break; 474 case 'c': 475 if (check_addr_range(current_addr, current_addr) < 0) 476 return ERR; 477 GET_COMMAND_SUFFIX(); 478 if (!isglobal) clear_undo_stack(); 479 if (delete_lines(first_addr, second_addr) < 0 || 480 append_lines(current_addr) < 0) 481 return ERR; 482 break; 483 case 'd': 484 if (check_addr_range(current_addr, current_addr) < 0) 485 return ERR; 486 GET_COMMAND_SUFFIX(); 487 if (!isglobal) clear_undo_stack(); 488 if (delete_lines(first_addr, second_addr) < 0) 489 return ERR; 490 else if ((addr = INC_MOD(current_addr, addr_last)) != 0) 491 current_addr = addr; 492 break; 493 case 'e': 494 if (modified && !scripted) 495 return EMOD; 496 /* fall through */ 497 case 'E': 498 if (addr_cnt > 0) { 499 sprintf(errmsg, "unexpected address"); 500 return ERR; 501 } else if (!isspace((unsigned char)*ibufp)) { 502 sprintf(errmsg, "unexpected command suffix"); 503 return ERR; 504 } else if ((fnp = get_filename()) == NULL) 505 return ERR; 506 GET_COMMAND_SUFFIX(); 507 if (delete_lines(1, addr_last) < 0) 508 return ERR; 509 clear_undo_stack(); 510 if (close_sbuf() < 0) 511 return ERR; 512 else if (open_sbuf() < 0) 513 return FATAL; 514 if (*fnp && *fnp != '!') strcpy(old_filename, fnp); 515#ifdef BACKWARDS 516 if (*fnp == '\0' && *old_filename == '\0') { 517 sprintf(errmsg, "no current filename"); 518 return ERR; 519 } 520#endif 521 if (read_file(*fnp ? fnp : old_filename, 0) < 0) 522 return ERR; 523 clear_undo_stack(); 524 modified = 0; 525 u_current_addr = u_addr_last = -1; 526 break; 527 case 'f': 528 if (addr_cnt > 0) { 529 sprintf(errmsg, "unexpected address"); 530 return ERR; 531 } else if (!isspace((unsigned char)*ibufp)) { 532 sprintf(errmsg, "unexpected command suffix"); 533 return ERR; 534 } else if ((fnp = get_filename()) == NULL) 535 return ERR; 536 else if (*fnp == '!') { 537 sprintf(errmsg, "invalid redirection"); 538 return ERR; 539 } 540 GET_COMMAND_SUFFIX(); 541 if (*fnp) strcpy(old_filename, fnp); 542 printf("%s\n", strip_escapes(old_filename)); 543 break; 544 case 'g': 545 case 'v': 546 case 'G': 547 case 'V': 548 if (isglobal) { 549 sprintf(errmsg, "cannot nest global commands"); 550 return ERR; 551 } else if (check_addr_range(1, addr_last) < 0) 552 return ERR; 553 else if (build_active_list(c == 'g' || c == 'G') < 0) 554 return ERR; 555 else if ((n = (c == 'G' || c == 'V'))) 556 GET_COMMAND_SUFFIX(); 557 isglobal++; 558 if (exec_global(n, gflag) < 0) 559 return ERR; 560 break; 561 case 'h': 562 if (addr_cnt > 0) { 563 sprintf(errmsg, "unexpected address"); 564 return ERR; 565 } 566 GET_COMMAND_SUFFIX(); 567 if (*errmsg) fprintf(stderr, "%s\n", errmsg); 568 break; 569 case 'H': 570 if (addr_cnt > 0) { 571 sprintf(errmsg, "unexpected address"); 572 return ERR; 573 } 574 GET_COMMAND_SUFFIX(); 575 if ((garrulous = 1 - garrulous) && *errmsg) 576 fprintf(stderr, "%s\n", errmsg); 577 break; 578 case 'i': 579 if (second_addr == 0) { 580 sprintf(errmsg, "invalid address"); 581 return ERR; 582 } 583 GET_COMMAND_SUFFIX(); 584 if (!isglobal) clear_undo_stack(); 585 if (append_lines(second_addr - 1) < 0) 586 return ERR; 587 break; 588 case 'j': 589 if (check_addr_range(current_addr, current_addr + 1) < 0) 590 return ERR; 591 GET_COMMAND_SUFFIX(); 592 if (!isglobal) clear_undo_stack(); 593 if (first_addr != second_addr && 594 join_lines(first_addr, second_addr) < 0) 595 return ERR; 596 break; 597 case 'k': 598 c = *ibufp++; 599 if (second_addr == 0) { 600 sprintf(errmsg, "invalid address"); 601 return ERR; 602 } 603 GET_COMMAND_SUFFIX(); 604 if (mark_line_node(get_addressed_line_node(second_addr), c) < 0) 605 return ERR; 606 break; 607 case 'l': 608 if (check_addr_range(current_addr, current_addr) < 0) 609 return ERR; 610 GET_COMMAND_SUFFIX(); 611 if (display_lines(first_addr, second_addr, gflag | GLS) < 0) 612 return ERR; 613 gflag = 0; 614 break; 615 case 'm': 616 if (check_addr_range(current_addr, current_addr) < 0) 617 return ERR; 618 GET_THIRD_ADDR(addr); 619 if (first_addr <= addr && addr < second_addr) { 620 sprintf(errmsg, "invalid destination"); 621 return ERR; 622 } 623 GET_COMMAND_SUFFIX(); 624 if (!isglobal) clear_undo_stack(); 625 if (move_lines(addr) < 0) 626 return ERR; 627 break; 628 case 'n': 629 if (check_addr_range(current_addr, current_addr) < 0) 630 return ERR; 631 GET_COMMAND_SUFFIX(); 632 if (display_lines(first_addr, second_addr, gflag | GNP) < 0) 633 return ERR; 634 gflag = 0; 635 break; 636 case 'p': 637 if (check_addr_range(current_addr, current_addr) < 0) 638 return ERR; 639 GET_COMMAND_SUFFIX(); 640 if (display_lines(first_addr, second_addr, gflag | GPR) < 0) 641 return ERR; 642 gflag = 0; 643 break; 644 case 'P': 645 if (addr_cnt > 0) { 646 sprintf(errmsg, "unexpected address"); 647 return ERR; 648 } 649 GET_COMMAND_SUFFIX(); 650 prompt = prompt ? NULL : optarg ? optarg : dps; 651 break; 652 case 'q': 653 case 'Q': 654 if (addr_cnt > 0) { 655 sprintf(errmsg, "unexpected address"); 656 return ERR; 657 } 658 GET_COMMAND_SUFFIX(); 659 gflag = (modified && !scripted && c == 'q') ? EMOD : EOF; 660 break; 661 case 'r': 662 if (!isspace((unsigned char)*ibufp)) { 663 sprintf(errmsg, "unexpected command suffix"); 664 return ERR; 665 } else if (addr_cnt == 0) 666 second_addr = addr_last; 667 if ((fnp = get_filename()) == NULL) 668 return ERR; 669 GET_COMMAND_SUFFIX(); 670 if (!isglobal) clear_undo_stack(); 671 if (*old_filename == '\0' && *fnp != '!') 672 strcpy(old_filename, fnp); 673#ifdef BACKWARDS 674 if (*fnp == '\0' && *old_filename == '\0') { 675 sprintf(errmsg, "no current filename"); 676 return ERR; 677 } 678#endif 679 if ((addr = read_file(*fnp ? fnp : old_filename, second_addr)) < 0) 680 return ERR; 681 else if (addr && addr != addr_last) 682 modified = 1; 683 break; 684 case 's': 685 do { 686 switch(*ibufp) { 687 case '\n': 688 sflags |=SGF; 689 break; 690 case 'g': 691 sflags |= SGG; 692 ibufp++; 693 break; 694 case 'p': 695 sflags |= SGP; 696 ibufp++; 697 break; 698 case 'r': 699 sflags |= SGR; 700 ibufp++; 701 break; 702 case '0': case '1': case '2': case '3': case '4': 703 case '5': case '6': case '7': case '8': case '9': 704 STRTOL(sgnum, ibufp); 705 sflags |= SGF; 706 sgflag &= ~GSG; /* override GSG */ 707 break; 708 default: 709 if (sflags) { 710 sprintf(errmsg, "invalid command suffix"); 711 return ERR; 712 } 713 } 714 } while (sflags && *ibufp != '\n'); 715 if (sflags && !pat) { 716 sprintf(errmsg, "no previous substitution"); 717 return ERR; 718 } else if (sflags & SGG) 719 sgnum = 0; /* override numeric arg */ 720 if (*ibufp != '\n' && *(ibufp + 1) == '\n') { 721 sprintf(errmsg, "invalid pattern delimiter"); 722 return ERR; 723 } 724 tpat = pat; 725 SPL1(); 726 if ((!sflags || (sflags & SGR)) && 727 (tpat = get_compiled_pattern()) == NULL) { 728 SPL0(); 729 return ERR; 730 } else if (tpat != pat) { 731 if (pat) { 732 regfree(pat); 733 free(pat); 734 } 735 pat = tpat; 736 patlock = 1; /* reserve pattern */ 737 } 738 SPL0(); 739 if (!sflags && extract_subst_tail(&sgflag, &sgnum) < 0) 740 return ERR; 741 else if (isglobal) 742 sgflag |= GLB; 743 else 744 sgflag &= ~GLB; 745 if (sflags & SGG) 746 sgflag ^= GSG; 747 if (sflags & SGP) 748 sgflag ^= GPR, sgflag &= ~(GLS | GNP); 749 do { 750 switch(*ibufp) { 751 case 'p': 752 sgflag |= GPR, ibufp++; 753 break; 754 case 'l': 755 sgflag |= GLS, ibufp++; 756 break; 757 case 'n': 758 sgflag |= GNP, ibufp++; 759 break; 760 default: 761 n++; 762 } 763 } while (!n); 764 if (check_addr_range(current_addr, current_addr) < 0) 765 return ERR; 766 GET_COMMAND_SUFFIX(); 767 if (!isglobal) clear_undo_stack(); 768 if (search_and_replace(pat, sgflag, sgnum) < 0) 769 return ERR; 770 break; 771 case 't': 772 if (check_addr_range(current_addr, current_addr) < 0) 773 return ERR; 774 GET_THIRD_ADDR(addr); 775 GET_COMMAND_SUFFIX(); 776 if (!isglobal) clear_undo_stack(); 777 if (copy_lines(addr) < 0) 778 return ERR; 779 break; 780 case 'u': 781 if (addr_cnt > 0) { 782 sprintf(errmsg, "unexpected address"); 783 return ERR; 784 } 785 GET_COMMAND_SUFFIX(); 786 if (pop_undo_stack() < 0) 787 return ERR; 788 break; 789 case 'w': 790 case 'W': 791 if ((n = *ibufp) == 'q' || n == 'Q') { 792 gflag = EOF; 793 ibufp++; 794 } 795 if (!isspace((unsigned char)*ibufp)) { 796 sprintf(errmsg, "unexpected command suffix"); 797 return ERR; 798 } else if ((fnp = get_filename()) == NULL) 799 return ERR; 800 if (addr_cnt == 0 && !addr_last) 801 first_addr = second_addr = 0; 802 else if (check_addr_range(1, addr_last) < 0) 803 return ERR; 804 GET_COMMAND_SUFFIX(); 805 if (*old_filename == '\0' && *fnp != '!') 806 strcpy(old_filename, fnp); 807#ifdef BACKWARDS 808 if (*fnp == '\0' && *old_filename == '\0') { 809 sprintf(errmsg, "no current filename"); 810 return ERR; 811 } 812#endif 813 if ((addr = write_file(*fnp ? fnp : old_filename, 814 (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0) 815 return ERR; 816 else if (addr == addr_last) 817 modified = 0; 818 else if (modified && !scripted && n == 'q') 819 gflag = EMOD; 820 break; 821 case 'x': 822 if (addr_cnt > 0) { 823 sprintf(errmsg, "unexpected address"); 824 return ERR; 825 } 826 GET_COMMAND_SUFFIX(); 827#ifdef DES 828 des = get_keyword(); 829#else 830 sprintf(errmsg, "crypt unavailable"); 831 return ERR; 832#endif 833 break; 834 case 'z': 835#ifdef BACKWARDS 836 if (check_addr_range(first_addr = 1, current_addr + 1) < 0) 837#else 838 if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0) 839#endif 840 return ERR; 841 else if ('0' < *ibufp && *ibufp <= '9') 842 STRTOL(rows, ibufp); 843 GET_COMMAND_SUFFIX(); 844 if (display_lines(second_addr, min(addr_last, 845 second_addr + rows), gflag) < 0) 846 return ERR; 847 gflag = 0; 848 break; 849 case '=': 850 GET_COMMAND_SUFFIX(); 851 printf("%ld\n", addr_cnt ? second_addr : addr_last); 852 break; 853 case '!': 854 if (addr_cnt > 0) { 855 sprintf(errmsg, "unexpected address"); 856 return ERR; 857 } else if ((sflags = get_shell_command()) < 0) 858 return ERR; 859 GET_COMMAND_SUFFIX(); 860 if (sflags) printf("%s\n", shcmd + 1); 861 system(shcmd + 1); 862 if (!scripted) printf("!\n"); 863 break; 864 case '\n': 865#ifdef BACKWARDS 866 if (check_addr_range(first_addr = 1, current_addr + 1) < 0 867#else 868 if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0 869#endif 870 || display_lines(second_addr, second_addr, 0) < 0) 871 return ERR; 872 break; 873 default: 874 sprintf(errmsg, "unknown command"); 875 return ERR; 876 } 877 return gflag; 878} 879 880 881/* check_addr_range: return status of address range check */ 882int 883check_addr_range(n, m) 884 long n, m; 885{ 886 if (addr_cnt == 0) { 887 first_addr = n; 888 second_addr = m; 889 } 890 if (first_addr > second_addr || 1 > first_addr || 891 second_addr > addr_last) { 892 sprintf(errmsg, "invalid address"); 893 return ERR; 894 } 895 return 0; 896} 897 898 899/* get_matching_node_addr: return the address of the next line matching a 900 pattern in a given direction. wrap around begin/end of editor buffer if 901 necessary */ 902long 903get_matching_node_addr(pat, dir) 904 pattern_t *pat; 905 int dir; 906{ 907 char *s; 908 long n = current_addr; 909 line_t *lp; 910 911 if (!pat) return ERR; 912 do { 913 if ((n = dir ? INC_MOD(n, addr_last) : DEC_MOD(n, addr_last))) { 914 lp = get_addressed_line_node(n); 915 if ((s = get_sbuf_line(lp)) == NULL) 916 return ERR; 917 if (isbinary) 918 NUL_TO_NEWLINE(s, lp->len); 919 if (!regexec(pat, s, 0, NULL, 0)) 920 return n; 921 } 922 } while (n != current_addr); 923 sprintf(errmsg, "no match"); 924 return ERR; 925} 926 927 928/* get_filename: return pointer to copy of filename in the command buffer */ 929char * 930get_filename() 931{ 932 static char *file = NULL; 933 static int filesz = 0; 934 935 int n; 936 937 if (*ibufp != '\n') { 938 SKIP_BLANKS(); 939 if (*ibufp == '\n') { 940 sprintf(errmsg, "invalid filename"); 941 return NULL; 942 } else if ((ibufp = get_extended_line(&n, 1)) == NULL) 943 return NULL; 944 else if (*ibufp == '!') { 945 ibufp++; 946 if ((n = get_shell_command()) < 0) 947 return NULL; 948 if (n) printf("%s\n", shcmd + 1); 949 return shcmd; 950 } else if (n - 1 > MAXPATHLEN) { 951 sprintf(errmsg, "filename too long"); 952 return NULL; 953 } 954 } 955#ifndef BACKWARDS 956 else if (*old_filename == '\0') { 957 sprintf(errmsg, "no current filename"); 958 return NULL; 959 } 960#endif 961 REALLOC(file, filesz, MAXPATHLEN + 1, NULL); 962 for (n = 0; *ibufp != '\n';) 963 file[n++] = *ibufp++; 964 file[n] = '\0'; 965 return is_legal_filename(file) ? file : NULL; 966} 967 968 969/* get_shell_command: read a shell command from stdin; return substitution 970 status */ 971int 972get_shell_command() 973{ 974 static char *buf = NULL; 975 static int n = 0; 976 977 char *s; /* substitution char pointer */ 978 int i = 0; 979 int j = 0; 980 981 if (red) { 982 sprintf(errmsg, "shell access restricted"); 983 return ERR; 984 } else if ((s = ibufp = get_extended_line(&j, 1)) == NULL) 985 return ERR; 986 REALLOC(buf, n, j + 1, ERR); 987 buf[i++] = '!'; /* prefix command w/ bang */ 988 while (*ibufp != '\n') 989 switch (*ibufp) { 990 default: 991 REALLOC(buf, n, i + 2, ERR); 992 buf[i++] = *ibufp; 993 if (*ibufp++ == '\\') 994 buf[i++] = *ibufp++; 995 break; 996 case '!': 997 if (s != ibufp) { 998 REALLOC(buf, n, i + 1, ERR); 999 buf[i++] = *ibufp++; 1000 } 1001#ifdef BACKWARDS 1002 else if (shcmd == NULL || *(shcmd + 1) == '\0') 1003#else 1004 else if (shcmd == NULL) 1005#endif 1006 { 1007 sprintf(errmsg, "no previous command"); 1008 return ERR; 1009 } else { 1010 REALLOC(buf, n, i + shcmdi, ERR); 1011 for (s = shcmd + 1; s < shcmd + shcmdi;) 1012 buf[i++] = *s++; 1013 s = ibufp++; 1014 } 1015 break; 1016 case '%': 1017 if (*old_filename == '\0') { 1018 sprintf(errmsg, "no current filename"); 1019 return ERR; 1020 } 1021 j = strlen(s = strip_escapes(old_filename)); 1022 REALLOC(buf, n, i + j, ERR); 1023 while (j--) 1024 buf[i++] = *s++; 1025 s = ibufp++; 1026 break; 1027 } 1028 REALLOC(shcmd, shcmdsz, i + 1, ERR); 1029 memcpy(shcmd, buf, i); 1030 shcmd[shcmdi = i] = '\0'; 1031 return *s == '!' || *s == '%'; 1032} 1033 1034 1035/* append_lines: insert text from stdin to after line n; stop when either a 1036 single period is read or EOF; return status */ 1037int 1038append_lines(n) 1039 long n; 1040{ 1041 int l; 1042 char *lp = ibuf; 1043 char *eot; 1044 undo_t *up = NULL; 1045 1046 for (current_addr = n;;) { 1047 if (!isglobal) { 1048 if ((l = get_tty_line()) < 0) 1049 return ERR; 1050 else if (l == 0 || ibuf[l - 1] != '\n') { 1051 clearerr(stdin); 1052 return l ? EOF : 0; 1053 } 1054 lp = ibuf; 1055 } else if (*(lp = ibufp) == '\0') 1056 return 0; 1057 else { 1058 while (*ibufp++ != '\n') 1059 ; 1060 l = ibufp - lp; 1061 } 1062 if (l == 2 && lp[0] == '.' && lp[1] == '\n') { 1063 return 0; 1064 } 1065 eot = lp + l; 1066 SPL1(); 1067 do { 1068 if ((lp = put_sbuf_line(lp)) == NULL) { 1069 SPL0(); 1070 return ERR; 1071 } else if (up) 1072 up->t = get_addressed_line_node(current_addr); 1073 else if ((up = push_undo_stack(UADD, current_addr, 1074 current_addr)) == NULL) { 1075 SPL0(); 1076 return ERR; 1077 } 1078 } while (lp != eot); 1079 modified = 1; 1080 SPL0(); 1081 } 1082 /* NOTREACHED */ 1083} 1084 1085 1086/* join_lines: replace a range of lines with the joined text of those lines */ 1087int 1088join_lines(from, to) 1089 long from; 1090 long to; 1091{ 1092 static char *buf = NULL; 1093 static int n; 1094 1095 char *s; 1096 int size = 0; 1097 line_t *bp, *ep; 1098 1099 ep = get_addressed_line_node(INC_MOD(to, addr_last)); 1100 bp = get_addressed_line_node(from); 1101 for (; bp != ep; bp = bp->q_forw) { 1102 if ((s = get_sbuf_line(bp)) == NULL) 1103 return ERR; 1104 REALLOC(buf, n, size + bp->len, ERR); 1105 memcpy(buf + size, s, bp->len); 1106 size += bp->len; 1107 } 1108 REALLOC(buf, n, size + 2, ERR); 1109 memcpy(buf + size, "\n", 2); 1110 if (delete_lines(from, to) < 0) 1111 return ERR; 1112 current_addr = from - 1; 1113 SPL1(); 1114 if (put_sbuf_line(buf) == NULL || 1115 push_undo_stack(UADD, current_addr, current_addr) == NULL) { 1116 SPL0(); 1117 return ERR; 1118 } 1119 modified = 1; 1120 SPL0(); 1121 return 0; 1122} 1123 1124 1125/* move_lines: move a range of lines */ 1126int 1127move_lines(addr) 1128 long addr; 1129{ 1130 line_t *b1, *a1, *b2, *a2; 1131 long n = INC_MOD(second_addr, addr_last); 1132 long p = first_addr - 1; 1133 int done = (addr == first_addr - 1 || addr == second_addr); 1134 1135 SPL1(); 1136 if (done) { 1137 a2 = get_addressed_line_node(n); 1138 b2 = get_addressed_line_node(p); 1139 current_addr = second_addr; 1140 } else if (push_undo_stack(UMOV, p, n) == NULL || 1141 push_undo_stack(UMOV, addr, INC_MOD(addr, addr_last)) == NULL) { 1142 SPL0(); 1143 return ERR; 1144 } else { 1145 a1 = get_addressed_line_node(n); 1146 if (addr < first_addr) { 1147 b1 = get_addressed_line_node(p); 1148 b2 = get_addressed_line_node(addr); 1149 /* this get_addressed_line_node last! */ 1150 } else { 1151 b2 = get_addressed_line_node(addr); 1152 b1 = get_addressed_line_node(p); 1153 /* this get_addressed_line_node last! */ 1154 } 1155 a2 = b2->q_forw; 1156 REQUE(b2, b1->q_forw); 1157 REQUE(a1->q_back, a2); 1158 REQUE(b1, a1); 1159 current_addr = addr + ((addr < first_addr) ? 1160 second_addr - first_addr + 1 : 0); 1161 } 1162 if (isglobal) 1163 unset_active_nodes(b2->q_forw, a2); 1164 modified = 1; 1165 SPL0(); 1166 return 0; 1167} 1168 1169 1170/* copy_lines: copy a range of lines; return status */ 1171int 1172copy_lines(addr) 1173 long addr; 1174{ 1175 line_t *lp, *np = get_addressed_line_node(first_addr); 1176 undo_t *up = NULL; 1177 long n = second_addr - first_addr + 1; 1178 long m = 0; 1179 1180 current_addr = addr; 1181 if (first_addr <= addr && addr < second_addr) { 1182 n = addr - first_addr + 1; 1183 m = second_addr - addr; 1184 } 1185 for (; n > 0; n=m, m=0, np = get_addressed_line_node(current_addr + 1)) 1186 for (; n-- > 0; np = np->q_forw) { 1187 SPL1(); 1188 if ((lp = dup_line_node(np)) == NULL) { 1189 SPL0(); 1190 return ERR; 1191 } 1192 add_line_node(lp); 1193 if (up) 1194 up->t = lp; 1195 else if ((up = push_undo_stack(UADD, current_addr, 1196 current_addr)) == NULL) { 1197 SPL0(); 1198 return ERR; 1199 } 1200 modified = 1; 1201 SPL0(); 1202 } 1203 return 0; 1204} 1205 1206 1207/* delete_lines: delete a range of lines */ 1208int 1209delete_lines(from, to) 1210 long from, to; 1211{ 1212 line_t *n, *p; 1213 1214 SPL1(); 1215 if (push_undo_stack(UDEL, from, to) == NULL) { 1216 SPL0(); 1217 return ERR; 1218 } 1219 n = get_addressed_line_node(INC_MOD(to, addr_last)); 1220 p = get_addressed_line_node(from - 1); 1221 /* this get_addressed_line_node last! */ 1222 if (isglobal) 1223 unset_active_nodes(p->q_forw, n); 1224 REQUE(p, n); 1225 addr_last -= to - from + 1; 1226 current_addr = from - 1; 1227 modified = 1; 1228 SPL0(); 1229 return 0; 1230} 1231 1232 1233/* display_lines: print a range of lines to stdout */ 1234int 1235display_lines(from, to, gflag) 1236 long from; 1237 long to; 1238 int gflag; 1239{ 1240 line_t *bp; 1241 line_t *ep; 1242 char *s; 1243 1244 if (!from) { 1245 sprintf(errmsg, "invalid address"); 1246 return ERR; 1247 } 1248 ep = get_addressed_line_node(INC_MOD(to, addr_last)); 1249 bp = get_addressed_line_node(from); 1250 for (; bp != ep; bp = bp->q_forw) { 1251 if ((s = get_sbuf_line(bp)) == NULL) 1252 return ERR; 1253 if (put_tty_line(s, bp->len, current_addr = from++, gflag) < 0) 1254 return ERR; 1255 } 1256 return 0; 1257} 1258 1259 1260#define MAXMARK 26 /* max number of marks */ 1261 1262line_t *mark[MAXMARK]; /* line markers */ 1263int markno; /* line marker count */ 1264 1265/* mark_line_node: set a line node mark */ 1266int 1267mark_line_node(lp, n) 1268 line_t *lp; 1269 int n; 1270{ 1271 if (!islower((unsigned char)n)) { 1272 sprintf(errmsg, "invalid mark character"); 1273 return ERR; 1274 } else if (mark[n - 'a'] == NULL) 1275 markno++; 1276 mark[n - 'a'] = lp; 1277 return 0; 1278} 1279 1280 1281/* get_marked_node_addr: return address of a marked line */ 1282long 1283get_marked_node_addr(n) 1284 int n; 1285{ 1286 if (!islower((unsigned char)n)) { 1287 sprintf(errmsg, "invalid mark character"); 1288 return ERR; 1289 } 1290 return get_line_node_addr(mark[n - 'a']); 1291} 1292 1293 1294/* unmark_line_node: clear line node mark */ 1295void 1296unmark_line_node(lp) 1297 line_t *lp; 1298{ 1299 int i; 1300 1301 for (i = 0; markno && i < MAXMARK; i++) 1302 if (mark[i] == lp) { 1303 mark[i] = NULL; 1304 markno--; 1305 } 1306} 1307 1308 1309/* dup_line_node: return a pointer to a copy of a line node */ 1310line_t * 1311dup_line_node(lp) 1312 line_t *lp; 1313{ 1314 line_t *np; 1315 1316 if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) { 1317 fprintf(stderr, "%s\n", strerror(errno)); 1318 sprintf(errmsg, "out of memory"); 1319 return NULL; 1320 } 1321 np->seek = lp->seek; 1322 np->len = lp->len; 1323 return np; 1324} 1325 1326 1327/* has_trailing_escape: return the parity of escapes preceding a character 1328 in a string */ 1329int 1330has_trailing_escape(s, t) 1331 char *s; 1332 char *t; 1333{ 1334 return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape(s, t - 1); 1335} 1336 1337 1338/* strip_escapes: return copy of escaped string of at most length MAXPATHLEN */ 1339char * 1340strip_escapes(s) 1341 char *s; 1342{ 1343 static char *file = NULL; 1344 static int filesz = 0; 1345 1346 int i = 0; 1347 1348 REALLOC(file, filesz, MAXPATHLEN + 1, NULL); 1349 /* assert: no trailing escape */ 1350 while ((file[i++] = (*s == '\\') ? *++s : *s)) 1351 s++; 1352 return file; 1353} 1354 1355 1356void 1357signal_hup(signo) 1358 int signo; 1359{ 1360 if (mutex) 1361 sigflags |= (1 << (signo - 1)); 1362 else handle_hup(signo); 1363} 1364 1365 1366void 1367signal_int(signo) 1368 int signo; 1369{ 1370 if (mutex) 1371 sigflags |= (1 << (signo - 1)); 1372 else handle_int(signo); 1373} 1374 1375 1376void 1377handle_hup(signo) 1378 int signo; 1379{ 1380 char *hup = NULL; /* hup filename */ 1381 char *s; 1382 int n; 1383 1384 if (!sigactive) 1385 quit(1); 1386 sigflags &= ~(1 << (signo - 1)); 1387 if (addr_last && write_file("ed.hup", "w", 1, addr_last) < 0 && 1388 (s = getenv("HOME")) != NULL && 1389 (n = strlen(s)) + 8 <= MAXPATHLEN && /* "ed.hup" + '/' */ 1390 (hup = (char *) malloc(n + 10)) != NULL) { 1391 strcpy(hup, s); 1392 if (hup[n - 1] != '/') 1393 hup[n] = '/', hup[n+1] = '\0'; 1394 strcat(hup, "ed.hup"); 1395 write_file(hup, "w", 1, addr_last); 1396 } 1397 quit(2); 1398} 1399 1400 1401void 1402handle_int(signo) 1403 int signo; 1404{ 1405 if (!sigactive) 1406 quit(1); 1407 sigflags &= ~(1 << (signo - 1)); 1408#ifdef _POSIX_SOURCE 1409 siglongjmp(env, -1); 1410#else 1411 longjmp(env, -1); 1412#endif 1413} 1414 1415 1416int cols = 72; /* wrap column */ 1417 1418void 1419handle_winch(signo) 1420 int signo; 1421{ 1422 struct winsize ws; /* window size structure */ 1423 1424 sigflags &= ~(1 << (signo - 1)); 1425 if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) { 1426 if (ws.ws_row > 2) rows = ws.ws_row - 2; 1427 if (ws.ws_col > 8) cols = ws.ws_col - 8; 1428 } 1429} 1430 1431 1432/* is_legal_filename: return a legal filename */ 1433int 1434is_legal_filename(s) 1435 char *s; 1436{ 1437 if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) { 1438 sprintf(errmsg, "shell access restricted"); 1439 return 0; 1440 } 1441 return 1; 1442}
| 106/* ed: line editor */ 107int 108main(argc, argv) 109 int argc; 110 char **argv; 111{ 112 int c, n; 113 long status = 0; 114#if __GNUC__ 115 /* Avoid longjmp clobbering */ 116 (void) &argc; 117 (void) &argv; 118#endif 119 120 (void)setlocale(LC_ALL, ""); 121 122 red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r'; 123top: 124 while ((c = getopt(argc, argv, "p:sx")) != -1) 125 switch(c) { 126 case 'p': /* set prompt */ 127 prompt = optarg; 128 break; 129 case 's': /* run script */ 130 scripted = 1; 131 break; 132 case 'x': /* use crypt */ 133#ifdef DES 134 des = get_keyword(); 135#else 136 fprintf(stderr, "crypt unavailable\n?\n"); 137#endif 138 break; 139 140 default: 141 fprintf(stderr, usage, argv[0]); 142 exit(1); 143 } 144 argv += optind; 145 argc -= optind; 146 if (argc && **argv == '-') { 147 scripted = 1; 148 if (argc > 1) { 149 optind = 1; 150 goto top; 151 } 152 argv++; 153 argc--; 154 } 155 /* assert: reliable signals! */ 156#ifdef SIGWINCH 157 handle_winch(SIGWINCH); 158 if (isatty(0)) signal(SIGWINCH, handle_winch); 159#endif 160 signal(SIGHUP, signal_hup); 161 signal(SIGQUIT, SIG_IGN); 162 signal(SIGINT, signal_int); 163#ifdef _POSIX_SOURCE 164 if ((status = sigsetjmp(env, 1))) 165#else 166 if ((status = setjmp(env))) 167#endif 168 { 169 fputs("\n?\n", stderr); 170 sprintf(errmsg, "interrupt"); 171 } else { 172 init_buffers(); 173 sigactive = 1; /* enable signal handlers */ 174 if (argc && **argv && is_legal_filename(*argv)) { 175 if (read_file(*argv, 0) < 0 && !isatty(0)) 176 quit(2); 177 else if (**argv != '!') 178 strcpy(old_filename, *argv); 179 } else if (argc) { 180 fputs("?\n", stderr); 181 if (**argv == '\0') 182 sprintf(errmsg, "invalid filename"); 183 if (!isatty(0)) 184 quit(2); 185 } 186 } 187 for (;;) { 188 if (status < 0 && garrulous) 189 fprintf(stderr, "%s\n", errmsg); 190 if (prompt) { 191 printf("%s", prompt); 192 fflush(stdout); 193 } 194 if ((n = get_tty_line()) < 0) { 195 status = ERR; 196 continue; 197 } else if (n == 0) { 198 if (modified && !scripted) { 199 fputs("?\n", stderr); 200 sprintf(errmsg, "warning: file modified"); 201 if (!isatty(0)) { 202 fprintf(stderr, garrulous ? 203 "script, line %d: %s\n" : 204 "", lineno, errmsg); 205 quit(2); 206 } 207 clearerr(stdin); 208 modified = 0; 209 status = EMOD; 210 continue; 211 } else 212 quit(0); 213 } else if (ibuf[n - 1] != '\n') { 214 /* discard line */ 215 sprintf(errmsg, "unexpected end-of-file"); 216 clearerr(stdin); 217 status = ERR; 218 continue; 219 } 220 isglobal = 0; 221 if ((status = extract_addr_range()) >= 0 && 222 (status = exec_command()) >= 0) 223 if (!status || 224 (status = display_lines(current_addr, current_addr, 225 status)) >= 0) 226 continue; 227 switch (status) { 228 case EOF: 229 quit(0); 230 case EMOD: 231 modified = 0; 232 fputs("?\n", stderr); /* give warning */ 233 sprintf(errmsg, "warning: file modified"); 234 if (!isatty(0)) { 235 fprintf(stderr, garrulous ? 236 "script, line %d: %s\n" : 237 "", lineno, errmsg); 238 quit(2); 239 } 240 break; 241 case FATAL: 242 if (!isatty(0)) 243 fprintf(stderr, garrulous ? 244 "script, line %d: %s\n" : "", 245 lineno, errmsg); 246 else 247 fprintf(stderr, garrulous ? "%s\n" : "", 248 errmsg); 249 quit(3); 250 default: 251 fputs("?\n", stderr); 252 if (!isatty(0)) { 253 fprintf(stderr, garrulous ? 254 "script, line %d: %s\n" : "", 255 lineno, errmsg); 256 quit(2); 257 } 258 break; 259 } 260 } 261 /*NOTREACHED*/ 262} 263 264long first_addr, second_addr, addr_cnt; 265 266/* extract_addr_range: get line addresses from the command buffer until an 267 illegal address is seen; return status */ 268int 269extract_addr_range() 270{ 271 long addr; 272 273 addr_cnt = 0; 274 first_addr = second_addr = current_addr; 275 while ((addr = next_addr()) >= 0) { 276 addr_cnt++; 277 first_addr = second_addr; 278 second_addr = addr; 279 if (*ibufp != ',' && *ibufp != ';') 280 break; 281 else if (*ibufp++ == ';') 282 current_addr = addr; 283 } 284 if ((addr_cnt = min(addr_cnt, 2)) == 1 || second_addr != addr) 285 first_addr = second_addr; 286 return (addr == ERR) ? ERR : 0; 287} 288 289 290#define SKIP_BLANKS() while (isspace((unsigned char)*ibufp) && *ibufp != '\n') ibufp++ 291 292#define MUST_BE_FIRST() \ 293 if (!first) { sprintf(errmsg, "invalid address"); return ERR; } 294 295/* next_addr: return the next line address in the command buffer */ 296long 297next_addr() 298{ 299 char *hd; 300 long addr = current_addr; 301 long n; 302 int first = 1; 303 int c; 304 305 SKIP_BLANKS(); 306 for (hd = ibufp;; first = 0) 307 switch (c = *ibufp) { 308 case '+': 309 case '\t': 310 case ' ': 311 case '-': 312 case '^': 313 ibufp++; 314 SKIP_BLANKS(); 315 if (isdigit((unsigned char)*ibufp)) { 316 STRTOL(n, ibufp); 317 addr += (c == '-' || c == '^') ? -n : n; 318 } else if (!isspace((unsigned char)c)) 319 addr += (c == '-' || c == '^') ? -1 : 1; 320 break; 321 case '0': case '1': case '2': 322 case '3': case '4': case '5': 323 case '6': case '7': case '8': case '9': 324 MUST_BE_FIRST(); 325 STRTOL(addr, ibufp); 326 break; 327 case '.': 328 case '$': 329 MUST_BE_FIRST(); 330 ibufp++; 331 addr = (c == '.') ? current_addr : addr_last; 332 break; 333 case '/': 334 case '?': 335 MUST_BE_FIRST(); 336 if ((addr = get_matching_node_addr( 337 get_compiled_pattern(), c == '/')) < 0) 338 return ERR; 339 else if (c == *ibufp) 340 ibufp++; 341 break; 342 case '\'': 343 MUST_BE_FIRST(); 344 ibufp++; 345 if ((addr = get_marked_node_addr(*ibufp++)) < 0) 346 return ERR; 347 break; 348 case '%': 349 case ',': 350 case ';': 351 if (first) { 352 ibufp++; 353 addr_cnt++; 354 second_addr = (c == ';') ? current_addr : 1; 355 addr = addr_last; 356 break; 357 } 358 /* FALL THROUGH */ 359 default: 360 if (ibufp == hd) 361 return EOF; 362 else if (addr < 0 || addr_last < addr) { 363 sprintf(errmsg, "invalid address"); 364 return ERR; 365 } else 366 return addr; 367 } 368 /* NOTREACHED */ 369} 370 371 372#ifdef BACKWARDS 373/* GET_THIRD_ADDR: get a legal address from the command buffer */ 374#define GET_THIRD_ADDR(addr) \ 375{ \ 376 long ol1, ol2; \ 377\ 378 ol1 = first_addr, ol2 = second_addr; \ 379 if (extract_addr_range() < 0) \ 380 return ERR; \ 381 else if (addr_cnt == 0) { \ 382 sprintf(errmsg, "destination expected"); \ 383 return ERR; \ 384 } else if (second_addr < 0 || addr_last < second_addr) { \ 385 sprintf(errmsg, "invalid address"); \ 386 return ERR; \ 387 } \ 388 addr = second_addr; \ 389 first_addr = ol1, second_addr = ol2; \ 390} 391#else /* BACKWARDS */ 392/* GET_THIRD_ADDR: get a legal address from the command buffer */ 393#define GET_THIRD_ADDR(addr) \ 394{ \ 395 long ol1, ol2; \ 396\ 397 ol1 = first_addr, ol2 = second_addr; \ 398 if (extract_addr_range() < 0) \ 399 return ERR; \ 400 if (second_addr < 0 || addr_last < second_addr) { \ 401 sprintf(errmsg, "invalid address"); \ 402 return ERR; \ 403 } \ 404 addr = second_addr; \ 405 first_addr = ol1, second_addr = ol2; \ 406} 407#endif 408 409 410/* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */ 411#define GET_COMMAND_SUFFIX() { \ 412 int done = 0; \ 413 do { \ 414 switch(*ibufp) { \ 415 case 'p': \ 416 gflag |= GPR, ibufp++; \ 417 break; \ 418 case 'l': \ 419 gflag |= GLS, ibufp++; \ 420 break; \ 421 case 'n': \ 422 gflag |= GNP, ibufp++; \ 423 break; \ 424 default: \ 425 done++; \ 426 } \ 427 } while (!done); \ 428 if (*ibufp++ != '\n') { \ 429 sprintf(errmsg, "invalid command suffix"); \ 430 return ERR; \ 431 } \ 432} 433 434 435/* sflags */ 436#define SGG 001 /* complement previous global substitute suffix */ 437#define SGP 002 /* complement previous print suffix */ 438#define SGR 004 /* use last regex instead of last pat */ 439#define SGF 010 /* repeat last substitution */ 440 441int patlock = 0; /* if set, pattern not freed by get_compiled_pattern() */ 442 443long rows = 22; /* scroll length: ws_row - 2 */ 444 445/* exec_command: execute the next command in command buffer; return print 446 request, if any */ 447int 448exec_command() 449{ 450 extern long u_current_addr; 451 extern long u_addr_last; 452 453 static pattern_t *pat = NULL; 454 static int sgflag = 0; 455 static int sgnum = 0; 456 457 pattern_t *tpat; 458 char *fnp; 459 int gflag = 0; 460 int sflags = 0; 461 long addr = 0; 462 int n = 0; 463 int c; 464 465 SKIP_BLANKS(); 466 switch(c = *ibufp++) { 467 case 'a': 468 GET_COMMAND_SUFFIX(); 469 if (!isglobal) clear_undo_stack(); 470 if (append_lines(second_addr) < 0) 471 return ERR; 472 break; 473 case 'c': 474 if (check_addr_range(current_addr, current_addr) < 0) 475 return ERR; 476 GET_COMMAND_SUFFIX(); 477 if (!isglobal) clear_undo_stack(); 478 if (delete_lines(first_addr, second_addr) < 0 || 479 append_lines(current_addr) < 0) 480 return ERR; 481 break; 482 case 'd': 483 if (check_addr_range(current_addr, current_addr) < 0) 484 return ERR; 485 GET_COMMAND_SUFFIX(); 486 if (!isglobal) clear_undo_stack(); 487 if (delete_lines(first_addr, second_addr) < 0) 488 return ERR; 489 else if ((addr = INC_MOD(current_addr, addr_last)) != 0) 490 current_addr = addr; 491 break; 492 case 'e': 493 if (modified && !scripted) 494 return EMOD; 495 /* fall through */ 496 case 'E': 497 if (addr_cnt > 0) { 498 sprintf(errmsg, "unexpected address"); 499 return ERR; 500 } else if (!isspace((unsigned char)*ibufp)) { 501 sprintf(errmsg, "unexpected command suffix"); 502 return ERR; 503 } else if ((fnp = get_filename()) == NULL) 504 return ERR; 505 GET_COMMAND_SUFFIX(); 506 if (delete_lines(1, addr_last) < 0) 507 return ERR; 508 clear_undo_stack(); 509 if (close_sbuf() < 0) 510 return ERR; 511 else if (open_sbuf() < 0) 512 return FATAL; 513 if (*fnp && *fnp != '!') strcpy(old_filename, fnp); 514#ifdef BACKWARDS 515 if (*fnp == '\0' && *old_filename == '\0') { 516 sprintf(errmsg, "no current filename"); 517 return ERR; 518 } 519#endif 520 if (read_file(*fnp ? fnp : old_filename, 0) < 0) 521 return ERR; 522 clear_undo_stack(); 523 modified = 0; 524 u_current_addr = u_addr_last = -1; 525 break; 526 case 'f': 527 if (addr_cnt > 0) { 528 sprintf(errmsg, "unexpected address"); 529 return ERR; 530 } else if (!isspace((unsigned char)*ibufp)) { 531 sprintf(errmsg, "unexpected command suffix"); 532 return ERR; 533 } else if ((fnp = get_filename()) == NULL) 534 return ERR; 535 else if (*fnp == '!') { 536 sprintf(errmsg, "invalid redirection"); 537 return ERR; 538 } 539 GET_COMMAND_SUFFIX(); 540 if (*fnp) strcpy(old_filename, fnp); 541 printf("%s\n", strip_escapes(old_filename)); 542 break; 543 case 'g': 544 case 'v': 545 case 'G': 546 case 'V': 547 if (isglobal) { 548 sprintf(errmsg, "cannot nest global commands"); 549 return ERR; 550 } else if (check_addr_range(1, addr_last) < 0) 551 return ERR; 552 else if (build_active_list(c == 'g' || c == 'G') < 0) 553 return ERR; 554 else if ((n = (c == 'G' || c == 'V'))) 555 GET_COMMAND_SUFFIX(); 556 isglobal++; 557 if (exec_global(n, gflag) < 0) 558 return ERR; 559 break; 560 case 'h': 561 if (addr_cnt > 0) { 562 sprintf(errmsg, "unexpected address"); 563 return ERR; 564 } 565 GET_COMMAND_SUFFIX(); 566 if (*errmsg) fprintf(stderr, "%s\n", errmsg); 567 break; 568 case 'H': 569 if (addr_cnt > 0) { 570 sprintf(errmsg, "unexpected address"); 571 return ERR; 572 } 573 GET_COMMAND_SUFFIX(); 574 if ((garrulous = 1 - garrulous) && *errmsg) 575 fprintf(stderr, "%s\n", errmsg); 576 break; 577 case 'i': 578 if (second_addr == 0) { 579 sprintf(errmsg, "invalid address"); 580 return ERR; 581 } 582 GET_COMMAND_SUFFIX(); 583 if (!isglobal) clear_undo_stack(); 584 if (append_lines(second_addr - 1) < 0) 585 return ERR; 586 break; 587 case 'j': 588 if (check_addr_range(current_addr, current_addr + 1) < 0) 589 return ERR; 590 GET_COMMAND_SUFFIX(); 591 if (!isglobal) clear_undo_stack(); 592 if (first_addr != second_addr && 593 join_lines(first_addr, second_addr) < 0) 594 return ERR; 595 break; 596 case 'k': 597 c = *ibufp++; 598 if (second_addr == 0) { 599 sprintf(errmsg, "invalid address"); 600 return ERR; 601 } 602 GET_COMMAND_SUFFIX(); 603 if (mark_line_node(get_addressed_line_node(second_addr), c) < 0) 604 return ERR; 605 break; 606 case 'l': 607 if (check_addr_range(current_addr, current_addr) < 0) 608 return ERR; 609 GET_COMMAND_SUFFIX(); 610 if (display_lines(first_addr, second_addr, gflag | GLS) < 0) 611 return ERR; 612 gflag = 0; 613 break; 614 case 'm': 615 if (check_addr_range(current_addr, current_addr) < 0) 616 return ERR; 617 GET_THIRD_ADDR(addr); 618 if (first_addr <= addr && addr < second_addr) { 619 sprintf(errmsg, "invalid destination"); 620 return ERR; 621 } 622 GET_COMMAND_SUFFIX(); 623 if (!isglobal) clear_undo_stack(); 624 if (move_lines(addr) < 0) 625 return ERR; 626 break; 627 case 'n': 628 if (check_addr_range(current_addr, current_addr) < 0) 629 return ERR; 630 GET_COMMAND_SUFFIX(); 631 if (display_lines(first_addr, second_addr, gflag | GNP) < 0) 632 return ERR; 633 gflag = 0; 634 break; 635 case 'p': 636 if (check_addr_range(current_addr, current_addr) < 0) 637 return ERR; 638 GET_COMMAND_SUFFIX(); 639 if (display_lines(first_addr, second_addr, gflag | GPR) < 0) 640 return ERR; 641 gflag = 0; 642 break; 643 case 'P': 644 if (addr_cnt > 0) { 645 sprintf(errmsg, "unexpected address"); 646 return ERR; 647 } 648 GET_COMMAND_SUFFIX(); 649 prompt = prompt ? NULL : optarg ? optarg : dps; 650 break; 651 case 'q': 652 case 'Q': 653 if (addr_cnt > 0) { 654 sprintf(errmsg, "unexpected address"); 655 return ERR; 656 } 657 GET_COMMAND_SUFFIX(); 658 gflag = (modified && !scripted && c == 'q') ? EMOD : EOF; 659 break; 660 case 'r': 661 if (!isspace((unsigned char)*ibufp)) { 662 sprintf(errmsg, "unexpected command suffix"); 663 return ERR; 664 } else if (addr_cnt == 0) 665 second_addr = addr_last; 666 if ((fnp = get_filename()) == NULL) 667 return ERR; 668 GET_COMMAND_SUFFIX(); 669 if (!isglobal) clear_undo_stack(); 670 if (*old_filename == '\0' && *fnp != '!') 671 strcpy(old_filename, fnp); 672#ifdef BACKWARDS 673 if (*fnp == '\0' && *old_filename == '\0') { 674 sprintf(errmsg, "no current filename"); 675 return ERR; 676 } 677#endif 678 if ((addr = read_file(*fnp ? fnp : old_filename, second_addr)) < 0) 679 return ERR; 680 else if (addr && addr != addr_last) 681 modified = 1; 682 break; 683 case 's': 684 do { 685 switch(*ibufp) { 686 case '\n': 687 sflags |=SGF; 688 break; 689 case 'g': 690 sflags |= SGG; 691 ibufp++; 692 break; 693 case 'p': 694 sflags |= SGP; 695 ibufp++; 696 break; 697 case 'r': 698 sflags |= SGR; 699 ibufp++; 700 break; 701 case '0': case '1': case '2': case '3': case '4': 702 case '5': case '6': case '7': case '8': case '9': 703 STRTOL(sgnum, ibufp); 704 sflags |= SGF; 705 sgflag &= ~GSG; /* override GSG */ 706 break; 707 default: 708 if (sflags) { 709 sprintf(errmsg, "invalid command suffix"); 710 return ERR; 711 } 712 } 713 } while (sflags && *ibufp != '\n'); 714 if (sflags && !pat) { 715 sprintf(errmsg, "no previous substitution"); 716 return ERR; 717 } else if (sflags & SGG) 718 sgnum = 0; /* override numeric arg */ 719 if (*ibufp != '\n' && *(ibufp + 1) == '\n') { 720 sprintf(errmsg, "invalid pattern delimiter"); 721 return ERR; 722 } 723 tpat = pat; 724 SPL1(); 725 if ((!sflags || (sflags & SGR)) && 726 (tpat = get_compiled_pattern()) == NULL) { 727 SPL0(); 728 return ERR; 729 } else if (tpat != pat) { 730 if (pat) { 731 regfree(pat); 732 free(pat); 733 } 734 pat = tpat; 735 patlock = 1; /* reserve pattern */ 736 } 737 SPL0(); 738 if (!sflags && extract_subst_tail(&sgflag, &sgnum) < 0) 739 return ERR; 740 else if (isglobal) 741 sgflag |= GLB; 742 else 743 sgflag &= ~GLB; 744 if (sflags & SGG) 745 sgflag ^= GSG; 746 if (sflags & SGP) 747 sgflag ^= GPR, sgflag &= ~(GLS | GNP); 748 do { 749 switch(*ibufp) { 750 case 'p': 751 sgflag |= GPR, ibufp++; 752 break; 753 case 'l': 754 sgflag |= GLS, ibufp++; 755 break; 756 case 'n': 757 sgflag |= GNP, ibufp++; 758 break; 759 default: 760 n++; 761 } 762 } while (!n); 763 if (check_addr_range(current_addr, current_addr) < 0) 764 return ERR; 765 GET_COMMAND_SUFFIX(); 766 if (!isglobal) clear_undo_stack(); 767 if (search_and_replace(pat, sgflag, sgnum) < 0) 768 return ERR; 769 break; 770 case 't': 771 if (check_addr_range(current_addr, current_addr) < 0) 772 return ERR; 773 GET_THIRD_ADDR(addr); 774 GET_COMMAND_SUFFIX(); 775 if (!isglobal) clear_undo_stack(); 776 if (copy_lines(addr) < 0) 777 return ERR; 778 break; 779 case 'u': 780 if (addr_cnt > 0) { 781 sprintf(errmsg, "unexpected address"); 782 return ERR; 783 } 784 GET_COMMAND_SUFFIX(); 785 if (pop_undo_stack() < 0) 786 return ERR; 787 break; 788 case 'w': 789 case 'W': 790 if ((n = *ibufp) == 'q' || n == 'Q') { 791 gflag = EOF; 792 ibufp++; 793 } 794 if (!isspace((unsigned char)*ibufp)) { 795 sprintf(errmsg, "unexpected command suffix"); 796 return ERR; 797 } else if ((fnp = get_filename()) == NULL) 798 return ERR; 799 if (addr_cnt == 0 && !addr_last) 800 first_addr = second_addr = 0; 801 else if (check_addr_range(1, addr_last) < 0) 802 return ERR; 803 GET_COMMAND_SUFFIX(); 804 if (*old_filename == '\0' && *fnp != '!') 805 strcpy(old_filename, fnp); 806#ifdef BACKWARDS 807 if (*fnp == '\0' && *old_filename == '\0') { 808 sprintf(errmsg, "no current filename"); 809 return ERR; 810 } 811#endif 812 if ((addr = write_file(*fnp ? fnp : old_filename, 813 (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0) 814 return ERR; 815 else if (addr == addr_last) 816 modified = 0; 817 else if (modified && !scripted && n == 'q') 818 gflag = EMOD; 819 break; 820 case 'x': 821 if (addr_cnt > 0) { 822 sprintf(errmsg, "unexpected address"); 823 return ERR; 824 } 825 GET_COMMAND_SUFFIX(); 826#ifdef DES 827 des = get_keyword(); 828#else 829 sprintf(errmsg, "crypt unavailable"); 830 return ERR; 831#endif 832 break; 833 case 'z': 834#ifdef BACKWARDS 835 if (check_addr_range(first_addr = 1, current_addr + 1) < 0) 836#else 837 if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0) 838#endif 839 return ERR; 840 else if ('0' < *ibufp && *ibufp <= '9') 841 STRTOL(rows, ibufp); 842 GET_COMMAND_SUFFIX(); 843 if (display_lines(second_addr, min(addr_last, 844 second_addr + rows), gflag) < 0) 845 return ERR; 846 gflag = 0; 847 break; 848 case '=': 849 GET_COMMAND_SUFFIX(); 850 printf("%ld\n", addr_cnt ? second_addr : addr_last); 851 break; 852 case '!': 853 if (addr_cnt > 0) { 854 sprintf(errmsg, "unexpected address"); 855 return ERR; 856 } else if ((sflags = get_shell_command()) < 0) 857 return ERR; 858 GET_COMMAND_SUFFIX(); 859 if (sflags) printf("%s\n", shcmd + 1); 860 system(shcmd + 1); 861 if (!scripted) printf("!\n"); 862 break; 863 case '\n': 864#ifdef BACKWARDS 865 if (check_addr_range(first_addr = 1, current_addr + 1) < 0 866#else 867 if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0 868#endif 869 || display_lines(second_addr, second_addr, 0) < 0) 870 return ERR; 871 break; 872 default: 873 sprintf(errmsg, "unknown command"); 874 return ERR; 875 } 876 return gflag; 877} 878 879 880/* check_addr_range: return status of address range check */ 881int 882check_addr_range(n, m) 883 long n, m; 884{ 885 if (addr_cnt == 0) { 886 first_addr = n; 887 second_addr = m; 888 } 889 if (first_addr > second_addr || 1 > first_addr || 890 second_addr > addr_last) { 891 sprintf(errmsg, "invalid address"); 892 return ERR; 893 } 894 return 0; 895} 896 897 898/* get_matching_node_addr: return the address of the next line matching a 899 pattern in a given direction. wrap around begin/end of editor buffer if 900 necessary */ 901long 902get_matching_node_addr(pat, dir) 903 pattern_t *pat; 904 int dir; 905{ 906 char *s; 907 long n = current_addr; 908 line_t *lp; 909 910 if (!pat) return ERR; 911 do { 912 if ((n = dir ? INC_MOD(n, addr_last) : DEC_MOD(n, addr_last))) { 913 lp = get_addressed_line_node(n); 914 if ((s = get_sbuf_line(lp)) == NULL) 915 return ERR; 916 if (isbinary) 917 NUL_TO_NEWLINE(s, lp->len); 918 if (!regexec(pat, s, 0, NULL, 0)) 919 return n; 920 } 921 } while (n != current_addr); 922 sprintf(errmsg, "no match"); 923 return ERR; 924} 925 926 927/* get_filename: return pointer to copy of filename in the command buffer */ 928char * 929get_filename() 930{ 931 static char *file = NULL; 932 static int filesz = 0; 933 934 int n; 935 936 if (*ibufp != '\n') { 937 SKIP_BLANKS(); 938 if (*ibufp == '\n') { 939 sprintf(errmsg, "invalid filename"); 940 return NULL; 941 } else if ((ibufp = get_extended_line(&n, 1)) == NULL) 942 return NULL; 943 else if (*ibufp == '!') { 944 ibufp++; 945 if ((n = get_shell_command()) < 0) 946 return NULL; 947 if (n) printf("%s\n", shcmd + 1); 948 return shcmd; 949 } else if (n - 1 > MAXPATHLEN) { 950 sprintf(errmsg, "filename too long"); 951 return NULL; 952 } 953 } 954#ifndef BACKWARDS 955 else if (*old_filename == '\0') { 956 sprintf(errmsg, "no current filename"); 957 return NULL; 958 } 959#endif 960 REALLOC(file, filesz, MAXPATHLEN + 1, NULL); 961 for (n = 0; *ibufp != '\n';) 962 file[n++] = *ibufp++; 963 file[n] = '\0'; 964 return is_legal_filename(file) ? file : NULL; 965} 966 967 968/* get_shell_command: read a shell command from stdin; return substitution 969 status */ 970int 971get_shell_command() 972{ 973 static char *buf = NULL; 974 static int n = 0; 975 976 char *s; /* substitution char pointer */ 977 int i = 0; 978 int j = 0; 979 980 if (red) { 981 sprintf(errmsg, "shell access restricted"); 982 return ERR; 983 } else if ((s = ibufp = get_extended_line(&j, 1)) == NULL) 984 return ERR; 985 REALLOC(buf, n, j + 1, ERR); 986 buf[i++] = '!'; /* prefix command w/ bang */ 987 while (*ibufp != '\n') 988 switch (*ibufp) { 989 default: 990 REALLOC(buf, n, i + 2, ERR); 991 buf[i++] = *ibufp; 992 if (*ibufp++ == '\\') 993 buf[i++] = *ibufp++; 994 break; 995 case '!': 996 if (s != ibufp) { 997 REALLOC(buf, n, i + 1, ERR); 998 buf[i++] = *ibufp++; 999 } 1000#ifdef BACKWARDS 1001 else if (shcmd == NULL || *(shcmd + 1) == '\0') 1002#else 1003 else if (shcmd == NULL) 1004#endif 1005 { 1006 sprintf(errmsg, "no previous command"); 1007 return ERR; 1008 } else { 1009 REALLOC(buf, n, i + shcmdi, ERR); 1010 for (s = shcmd + 1; s < shcmd + shcmdi;) 1011 buf[i++] = *s++; 1012 s = ibufp++; 1013 } 1014 break; 1015 case '%': 1016 if (*old_filename == '\0') { 1017 sprintf(errmsg, "no current filename"); 1018 return ERR; 1019 } 1020 j = strlen(s = strip_escapes(old_filename)); 1021 REALLOC(buf, n, i + j, ERR); 1022 while (j--) 1023 buf[i++] = *s++; 1024 s = ibufp++; 1025 break; 1026 } 1027 REALLOC(shcmd, shcmdsz, i + 1, ERR); 1028 memcpy(shcmd, buf, i); 1029 shcmd[shcmdi = i] = '\0'; 1030 return *s == '!' || *s == '%'; 1031} 1032 1033 1034/* append_lines: insert text from stdin to after line n; stop when either a 1035 single period is read or EOF; return status */ 1036int 1037append_lines(n) 1038 long n; 1039{ 1040 int l; 1041 char *lp = ibuf; 1042 char *eot; 1043 undo_t *up = NULL; 1044 1045 for (current_addr = n;;) { 1046 if (!isglobal) { 1047 if ((l = get_tty_line()) < 0) 1048 return ERR; 1049 else if (l == 0 || ibuf[l - 1] != '\n') { 1050 clearerr(stdin); 1051 return l ? EOF : 0; 1052 } 1053 lp = ibuf; 1054 } else if (*(lp = ibufp) == '\0') 1055 return 0; 1056 else { 1057 while (*ibufp++ != '\n') 1058 ; 1059 l = ibufp - lp; 1060 } 1061 if (l == 2 && lp[0] == '.' && lp[1] == '\n') { 1062 return 0; 1063 } 1064 eot = lp + l; 1065 SPL1(); 1066 do { 1067 if ((lp = put_sbuf_line(lp)) == NULL) { 1068 SPL0(); 1069 return ERR; 1070 } else if (up) 1071 up->t = get_addressed_line_node(current_addr); 1072 else if ((up = push_undo_stack(UADD, current_addr, 1073 current_addr)) == NULL) { 1074 SPL0(); 1075 return ERR; 1076 } 1077 } while (lp != eot); 1078 modified = 1; 1079 SPL0(); 1080 } 1081 /* NOTREACHED */ 1082} 1083 1084 1085/* join_lines: replace a range of lines with the joined text of those lines */ 1086int 1087join_lines(from, to) 1088 long from; 1089 long to; 1090{ 1091 static char *buf = NULL; 1092 static int n; 1093 1094 char *s; 1095 int size = 0; 1096 line_t *bp, *ep; 1097 1098 ep = get_addressed_line_node(INC_MOD(to, addr_last)); 1099 bp = get_addressed_line_node(from); 1100 for (; bp != ep; bp = bp->q_forw) { 1101 if ((s = get_sbuf_line(bp)) == NULL) 1102 return ERR; 1103 REALLOC(buf, n, size + bp->len, ERR); 1104 memcpy(buf + size, s, bp->len); 1105 size += bp->len; 1106 } 1107 REALLOC(buf, n, size + 2, ERR); 1108 memcpy(buf + size, "\n", 2); 1109 if (delete_lines(from, to) < 0) 1110 return ERR; 1111 current_addr = from - 1; 1112 SPL1(); 1113 if (put_sbuf_line(buf) == NULL || 1114 push_undo_stack(UADD, current_addr, current_addr) == NULL) { 1115 SPL0(); 1116 return ERR; 1117 } 1118 modified = 1; 1119 SPL0(); 1120 return 0; 1121} 1122 1123 1124/* move_lines: move a range of lines */ 1125int 1126move_lines(addr) 1127 long addr; 1128{ 1129 line_t *b1, *a1, *b2, *a2; 1130 long n = INC_MOD(second_addr, addr_last); 1131 long p = first_addr - 1; 1132 int done = (addr == first_addr - 1 || addr == second_addr); 1133 1134 SPL1(); 1135 if (done) { 1136 a2 = get_addressed_line_node(n); 1137 b2 = get_addressed_line_node(p); 1138 current_addr = second_addr; 1139 } else if (push_undo_stack(UMOV, p, n) == NULL || 1140 push_undo_stack(UMOV, addr, INC_MOD(addr, addr_last)) == NULL) { 1141 SPL0(); 1142 return ERR; 1143 } else { 1144 a1 = get_addressed_line_node(n); 1145 if (addr < first_addr) { 1146 b1 = get_addressed_line_node(p); 1147 b2 = get_addressed_line_node(addr); 1148 /* this get_addressed_line_node last! */ 1149 } else { 1150 b2 = get_addressed_line_node(addr); 1151 b1 = get_addressed_line_node(p); 1152 /* this get_addressed_line_node last! */ 1153 } 1154 a2 = b2->q_forw; 1155 REQUE(b2, b1->q_forw); 1156 REQUE(a1->q_back, a2); 1157 REQUE(b1, a1); 1158 current_addr = addr + ((addr < first_addr) ? 1159 second_addr - first_addr + 1 : 0); 1160 } 1161 if (isglobal) 1162 unset_active_nodes(b2->q_forw, a2); 1163 modified = 1; 1164 SPL0(); 1165 return 0; 1166} 1167 1168 1169/* copy_lines: copy a range of lines; return status */ 1170int 1171copy_lines(addr) 1172 long addr; 1173{ 1174 line_t *lp, *np = get_addressed_line_node(first_addr); 1175 undo_t *up = NULL; 1176 long n = second_addr - first_addr + 1; 1177 long m = 0; 1178 1179 current_addr = addr; 1180 if (first_addr <= addr && addr < second_addr) { 1181 n = addr - first_addr + 1; 1182 m = second_addr - addr; 1183 } 1184 for (; n > 0; n=m, m=0, np = get_addressed_line_node(current_addr + 1)) 1185 for (; n-- > 0; np = np->q_forw) { 1186 SPL1(); 1187 if ((lp = dup_line_node(np)) == NULL) { 1188 SPL0(); 1189 return ERR; 1190 } 1191 add_line_node(lp); 1192 if (up) 1193 up->t = lp; 1194 else if ((up = push_undo_stack(UADD, current_addr, 1195 current_addr)) == NULL) { 1196 SPL0(); 1197 return ERR; 1198 } 1199 modified = 1; 1200 SPL0(); 1201 } 1202 return 0; 1203} 1204 1205 1206/* delete_lines: delete a range of lines */ 1207int 1208delete_lines(from, to) 1209 long from, to; 1210{ 1211 line_t *n, *p; 1212 1213 SPL1(); 1214 if (push_undo_stack(UDEL, from, to) == NULL) { 1215 SPL0(); 1216 return ERR; 1217 } 1218 n = get_addressed_line_node(INC_MOD(to, addr_last)); 1219 p = get_addressed_line_node(from - 1); 1220 /* this get_addressed_line_node last! */ 1221 if (isglobal) 1222 unset_active_nodes(p->q_forw, n); 1223 REQUE(p, n); 1224 addr_last -= to - from + 1; 1225 current_addr = from - 1; 1226 modified = 1; 1227 SPL0(); 1228 return 0; 1229} 1230 1231 1232/* display_lines: print a range of lines to stdout */ 1233int 1234display_lines(from, to, gflag) 1235 long from; 1236 long to; 1237 int gflag; 1238{ 1239 line_t *bp; 1240 line_t *ep; 1241 char *s; 1242 1243 if (!from) { 1244 sprintf(errmsg, "invalid address"); 1245 return ERR; 1246 } 1247 ep = get_addressed_line_node(INC_MOD(to, addr_last)); 1248 bp = get_addressed_line_node(from); 1249 for (; bp != ep; bp = bp->q_forw) { 1250 if ((s = get_sbuf_line(bp)) == NULL) 1251 return ERR; 1252 if (put_tty_line(s, bp->len, current_addr = from++, gflag) < 0) 1253 return ERR; 1254 } 1255 return 0; 1256} 1257 1258 1259#define MAXMARK 26 /* max number of marks */ 1260 1261line_t *mark[MAXMARK]; /* line markers */ 1262int markno; /* line marker count */ 1263 1264/* mark_line_node: set a line node mark */ 1265int 1266mark_line_node(lp, n) 1267 line_t *lp; 1268 int n; 1269{ 1270 if (!islower((unsigned char)n)) { 1271 sprintf(errmsg, "invalid mark character"); 1272 return ERR; 1273 } else if (mark[n - 'a'] == NULL) 1274 markno++; 1275 mark[n - 'a'] = lp; 1276 return 0; 1277} 1278 1279 1280/* get_marked_node_addr: return address of a marked line */ 1281long 1282get_marked_node_addr(n) 1283 int n; 1284{ 1285 if (!islower((unsigned char)n)) { 1286 sprintf(errmsg, "invalid mark character"); 1287 return ERR; 1288 } 1289 return get_line_node_addr(mark[n - 'a']); 1290} 1291 1292 1293/* unmark_line_node: clear line node mark */ 1294void 1295unmark_line_node(lp) 1296 line_t *lp; 1297{ 1298 int i; 1299 1300 for (i = 0; markno && i < MAXMARK; i++) 1301 if (mark[i] == lp) { 1302 mark[i] = NULL; 1303 markno--; 1304 } 1305} 1306 1307 1308/* dup_line_node: return a pointer to a copy of a line node */ 1309line_t * 1310dup_line_node(lp) 1311 line_t *lp; 1312{ 1313 line_t *np; 1314 1315 if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) { 1316 fprintf(stderr, "%s\n", strerror(errno)); 1317 sprintf(errmsg, "out of memory"); 1318 return NULL; 1319 } 1320 np->seek = lp->seek; 1321 np->len = lp->len; 1322 return np; 1323} 1324 1325 1326/* has_trailing_escape: return the parity of escapes preceding a character 1327 in a string */ 1328int 1329has_trailing_escape(s, t) 1330 char *s; 1331 char *t; 1332{ 1333 return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape(s, t - 1); 1334} 1335 1336 1337/* strip_escapes: return copy of escaped string of at most length MAXPATHLEN */ 1338char * 1339strip_escapes(s) 1340 char *s; 1341{ 1342 static char *file = NULL; 1343 static int filesz = 0; 1344 1345 int i = 0; 1346 1347 REALLOC(file, filesz, MAXPATHLEN + 1, NULL); 1348 /* assert: no trailing escape */ 1349 while ((file[i++] = (*s == '\\') ? *++s : *s)) 1350 s++; 1351 return file; 1352} 1353 1354 1355void 1356signal_hup(signo) 1357 int signo; 1358{ 1359 if (mutex) 1360 sigflags |= (1 << (signo - 1)); 1361 else handle_hup(signo); 1362} 1363 1364 1365void 1366signal_int(signo) 1367 int signo; 1368{ 1369 if (mutex) 1370 sigflags |= (1 << (signo - 1)); 1371 else handle_int(signo); 1372} 1373 1374 1375void 1376handle_hup(signo) 1377 int signo; 1378{ 1379 char *hup = NULL; /* hup filename */ 1380 char *s; 1381 int n; 1382 1383 if (!sigactive) 1384 quit(1); 1385 sigflags &= ~(1 << (signo - 1)); 1386 if (addr_last && write_file("ed.hup", "w", 1, addr_last) < 0 && 1387 (s = getenv("HOME")) != NULL && 1388 (n = strlen(s)) + 8 <= MAXPATHLEN && /* "ed.hup" + '/' */ 1389 (hup = (char *) malloc(n + 10)) != NULL) { 1390 strcpy(hup, s); 1391 if (hup[n - 1] != '/') 1392 hup[n] = '/', hup[n+1] = '\0'; 1393 strcat(hup, "ed.hup"); 1394 write_file(hup, "w", 1, addr_last); 1395 } 1396 quit(2); 1397} 1398 1399 1400void 1401handle_int(signo) 1402 int signo; 1403{ 1404 if (!sigactive) 1405 quit(1); 1406 sigflags &= ~(1 << (signo - 1)); 1407#ifdef _POSIX_SOURCE 1408 siglongjmp(env, -1); 1409#else 1410 longjmp(env, -1); 1411#endif 1412} 1413 1414 1415int cols = 72; /* wrap column */ 1416 1417void 1418handle_winch(signo) 1419 int signo; 1420{ 1421 struct winsize ws; /* window size structure */ 1422 1423 sigflags &= ~(1 << (signo - 1)); 1424 if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) { 1425 if (ws.ws_row > 2) rows = ws.ws_row - 2; 1426 if (ws.ws_col > 8) cols = ws.ws_col - 8; 1427 } 1428} 1429 1430 1431/* is_legal_filename: return a legal filename */ 1432int 1433is_legal_filename(s) 1434 char *s; 1435{ 1436 if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) { 1437 sprintf(errmsg, "shell access restricted"); 1438 return 0; 1439 } 1440 return 1; 1441}
|