1 /* $NetBSD: main.c,v 1.23 2011/05/23 23:13:10 joerg Exp $ */ 2 3 /* main.c: This file contains the main control and user-interface routines 4 for the ed line editor. */ 5 /*- 6 * Copyright (c) 1993 Andrew Moore, Talke Studio. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 #ifndef lint 33 __COPYRIGHT( 34 "@(#) Copyright (c) 1993 Andrew Moore, Talke Studio.\ 35 All rights reserved."); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char *rcsid = "@(#)main.c,v 1.1 1994/02/01 00:34:42 alm Exp"; 41 #else 42 __RCSID("$NetBSD: main.c,v 1.23 2011/05/23 23:13:10 joerg Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 /* 47 * CREDITS 48 * 49 * This program is based on the editor algorithm described in 50 * Brian W. Kernighan and P. J. Plauger's book "Software Tools 51 * in Pascal," Addison-Wesley, 1981. 52 * 53 * The buffering algorithm is attributed to Rodney Ruddock of 54 * the University of Guelph, Guelph, Ontario. 55 * 56 * The cbc.c encryption code is adapted from 57 * the bdes program by Matt Bishop of Dartmouth College, 58 * Hanover, NH. 59 * 60 */ 61 62 #include <sys/ioctl.h> 63 #include <sys/wait.h> 64 #include <termios.h> 65 #include <ctype.h> 66 #include <setjmp.h> 67 #include <pwd.h> 68 69 #include "ed.h" 70 71 72 #ifdef _POSIX_SOURCE 73 sigjmp_buf env; 74 #else 75 jmp_buf env; 76 #endif 77 78 /* static buffers */ 79 char stdinbuf[1]; /* stdin buffer */ 80 char *shcmd; /* shell command buffer */ 81 int shcmdsz; /* shell command buffer size */ 82 int shcmdi; /* shell command buffer index */ 83 char *ibuf; /* ed command-line buffer */ 84 int ibufsz; /* ed command-line buffer size */ 85 char *ibufp; /* pointer to ed command-line buffer */ 86 87 /* global flags */ 88 int des = 0; /* if set, use crypt(3) for i/o */ 89 int garrulous = 0; /* if set, print all error messages */ 90 int isbinary; /* if set, buffer contains ASCII NULs */ 91 int isglobal; /* if set, doing a global command */ 92 int modified; /* if set, buffer modified since last write */ 93 int mutex = 0; /* if set, signals set "sigflags" */ 94 int red = 0; /* if set, restrict shell/directory access */ 95 int ere = 0; /* if set, use extended regexes */ 96 int scripted = 0; /* if set, suppress diagnostics */ 97 int sigflags = 0; /* if set, signals received while mutex set */ 98 int sigactive = 0; /* if set, signal handlers are enabled */ 99 100 char old_filename[MAXPATHLEN + 1] = ""; /* default filename */ 101 long current_addr; /* current address in editor buffer */ 102 long addr_last; /* last address in editor buffer */ 103 int lineno; /* script line number */ 104 const char *prompt; /* command-line prompt */ 105 const char *dps = "*"; /* default command-line prompt */ 106 107 const char *usage = "usage: %s [-] [-sxE] [-p string] [name]\n"; 108 109 /* ed: line editor */ 110 int 111 main(int ac, char *av[]) 112 { 113 int c, n; 114 long status = 0; 115 volatile int argc = ac; 116 char ** volatile argv = av; 117 118 red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r'; 119 top: 120 while ((c = getopt(argc, argv, "p:sxE")) != -1) 121 switch(c) { 122 case 'p': /* set prompt */ 123 prompt = optarg; 124 break; 125 case 's': /* run script */ 126 scripted = 1; 127 break; 128 case 'x': /* use crypt */ 129 #ifdef DES 130 des = get_keyword(); 131 #else 132 fprintf(stderr, "crypt unavailable\n?\n"); 133 #endif 134 break; 135 136 case 'E': 137 ere = REG_EXTENDED; 138 break; 139 default: 140 fprintf(stderr, usage, argv[0]); 141 exit(1); 142 /* NOTREACHED */ 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)) != 0) 165 #else 166 if ((status = setjmp(env)) != 0) 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 strlcpy(old_filename, *argv, 179 sizeof(old_filename) - 2); 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 if (garrulous) { 204 fprintf(stderr, 205 "script, line %d: %s\n", 206 lineno, errmsg); 207 } 208 quit(2); 209 } 210 clearerr(stdin); 211 modified = 0; 212 status = EMOD; 213 continue; 214 } else 215 quit(0); 216 } else if (ibuf[n - 1] != '\n') { 217 /* discard line */ 218 sprintf(errmsg, "unexpected end-of-file"); 219 clearerr(stdin); 220 status = ERR; 221 continue; 222 } 223 isglobal = 0; 224 if ((status = extract_addr_range()) >= 0 && 225 (status = exec_command()) >= 0) 226 if (!status || (status && 227 (status = display_lines(current_addr, current_addr, 228 status))) >= 0) 229 continue; 230 switch (status) { 231 case EOF: 232 quit(0); 233 case EMOD: 234 modified = 0; 235 fputs("?\n", stderr); /* give warning */ 236 sprintf(errmsg, "warning: file modified"); 237 if (!isatty(0)) { 238 if (garrulous) { 239 fprintf(stderr, 240 "script, line %d: %s\n", 241 lineno, errmsg); 242 } 243 quit(2); 244 } 245 break; 246 case FATAL: 247 if (garrulous) { 248 if (!isatty(0)) { 249 fprintf(stderr, 250 "script, line %d: %s\n", 251 lineno, errmsg); 252 } else { 253 fprintf(stderr, "%s\n", errmsg); 254 } 255 } 256 quit(3); 257 default: 258 fputs("?\n", stderr); 259 if (!isatty(0)) { 260 if (garrulous) { 261 fprintf(stderr, "script, line %d: %s\n", 262 lineno, errmsg); 263 } 264 quit(2); 265 } 266 break; 267 } 268 } 269 /* NOTREACHED */ 270 } 271 272 long first_addr, second_addr, addr_cnt; 273 274 /* extract_addr_range: get line addresses from the command buffer until an 275 illegal address is seen; return status */ 276 int 277 extract_addr_range(void) 278 { 279 long addr; 280 281 addr_cnt = 0; 282 first_addr = second_addr = current_addr; 283 while ((addr = next_addr()) >= 0) { 284 addr_cnt++; 285 first_addr = second_addr; 286 second_addr = addr; 287 if (*ibufp != ',' && *ibufp != ';') 288 break; 289 else if (*ibufp++ == ';') 290 current_addr = addr; 291 } 292 if ((addr_cnt = min(addr_cnt, 2)) == 1 || second_addr != addr) 293 first_addr = second_addr; 294 return (addr == ERR) ? ERR : 0; 295 } 296 297 298 #define SKIP_BLANKS() while (isspace((unsigned char)*ibufp) && *ibufp != '\n') \ 299 ibufp++ 300 301 #define MUST_BE_FIRST() \ 302 if (!first) { sprintf(errmsg, "invalid address"); return ERR; } 303 304 /* next_addr: return the next line address in the command buffer */ 305 long 306 next_addr(void) 307 { 308 char *hd; 309 long addr = current_addr; 310 long n; 311 int first = 1; 312 int c; 313 314 SKIP_BLANKS(); 315 for (hd = ibufp;; first = 0) 316 switch (c = *ibufp) { 317 case '+': 318 case '\t': 319 case ' ': 320 case '-': 321 case '^': 322 ibufp++; 323 SKIP_BLANKS(); 324 if (isdigit((unsigned char)*ibufp)) { 325 STRTOL(n, ibufp); 326 addr += (c == '-' || c == '^') ? -n : n; 327 } else if (!isspace((unsigned char)c)) 328 addr += (c == '-' || c == '^') ? -1 : 1; 329 break; 330 case '0': case '1': case '2': 331 case '3': case '4': case '5': 332 case '6': case '7': case '8': case '9': 333 MUST_BE_FIRST(); 334 STRTOL(addr, ibufp); 335 break; 336 case '.': 337 case '$': 338 MUST_BE_FIRST(); 339 ibufp++; 340 addr = (c == '.') ? current_addr : addr_last; 341 break; 342 case '/': 343 case '?': 344 MUST_BE_FIRST(); 345 if ((addr = get_matching_node_addr( 346 get_compiled_pattern(), c == '/')) < 0) 347 return ERR; 348 else if (c == *ibufp) 349 ibufp++; 350 break; 351 case '\'': 352 MUST_BE_FIRST(); 353 ibufp++; 354 if ((addr = get_marked_node_addr((unsigned char)*ibufp++)) < 0) 355 return ERR; 356 break; 357 case '%': 358 case ',': 359 case ';': 360 if (first) { 361 ibufp++; 362 addr_cnt++; 363 second_addr = (c == ';') ? current_addr : 1; 364 addr = addr_last; 365 break; 366 } 367 /* FALL THROUGH */ 368 default: 369 if (ibufp == hd) 370 return EOF; 371 else if (addr < 0 || addr_last < addr) { 372 sprintf(errmsg, "invalid address"); 373 return ERR; 374 } else 375 return addr; 376 } 377 /* NOTREACHED */ 378 } 379 380 381 #ifdef BACKWARDS 382 /* GET_THIRD_ADDR: get a legal address from the command buffer */ 383 #define GET_THIRD_ADDR(addr) \ 384 { \ 385 long ol1, ol2; \ 386 \ 387 ol1 = first_addr, ol2 = second_addr; \ 388 if (extract_addr_range() < 0) \ 389 return ERR; \ 390 else if (addr_cnt == 0) { \ 391 sprintf(errmsg, "destination expected"); \ 392 return ERR; \ 393 } else if (second_addr < 0 || addr_last < second_addr) { \ 394 sprintf(errmsg, "invalid address"); \ 395 return ERR; \ 396 } \ 397 addr = second_addr; \ 398 first_addr = ol1, second_addr = ol2; \ 399 } 400 #else /* BACKWARDS */ 401 /* GET_THIRD_ADDR: get a legal address from the command buffer */ 402 #define GET_THIRD_ADDR(addr) \ 403 { \ 404 long ol1, ol2; \ 405 \ 406 ol1 = first_addr, ol2 = second_addr; \ 407 if (extract_addr_range() < 0) \ 408 return ERR; \ 409 if (second_addr < 0 || addr_last < second_addr) { \ 410 sprintf(errmsg, "invalid address"); \ 411 return ERR; \ 412 } \ 413 addr = second_addr; \ 414 first_addr = ol1, second_addr = ol2; \ 415 } 416 #endif 417 418 419 /* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */ 420 #define GET_COMMAND_SUFFIX() { \ 421 int done = 0; \ 422 do { \ 423 switch(*ibufp) { \ 424 case 'p': \ 425 gflag |= GPR, ibufp++; \ 426 break; \ 427 case 'l': \ 428 gflag |= GLS, ibufp++; \ 429 break; \ 430 case 'n': \ 431 gflag |= GNP, ibufp++; \ 432 break; \ 433 default: \ 434 done++; \ 435 } \ 436 } while (!done); \ 437 if (*ibufp++ != '\n') { \ 438 sprintf(errmsg, "invalid command suffix"); \ 439 return ERR; \ 440 } \ 441 } 442 443 444 /* sflags */ 445 #define SGG 001 /* complement previous global substitute suffix */ 446 #define SGP 002 /* complement previous print suffix */ 447 #define SGR 004 /* use last regex instead of last pat */ 448 #define SGF 010 /* repeat last substitution */ 449 450 int patlock = 0; /* if set, pattern not freed by get_compiled_pattern() */ 451 452 long rows = 22; /* scroll length: ws_row - 2 */ 453 454 /* exec_command: execute the next command in command buffer; return print 455 request, if any */ 456 int 457 exec_command(void) 458 { 459 static pattern_t *pat = NULL; 460 static int sgflag = 0; 461 static long sgnum = 0; 462 463 pattern_t *tpat; 464 char *fnp; 465 int gflag = 0; 466 int sflags = 0; 467 long addr = 0; 468 int n = 0; 469 int c; 470 471 SKIP_BLANKS(); 472 switch(c = *ibufp++) { 473 case 'a': 474 GET_COMMAND_SUFFIX(); 475 if (!isglobal) clear_undo_stack(); 476 if (append_lines(second_addr) < 0) 477 return ERR; 478 break; 479 case 'c': 480 if (check_addr_range(current_addr, current_addr) < 0) 481 return ERR; 482 GET_COMMAND_SUFFIX(); 483 if (!isglobal) clear_undo_stack(); 484 if (delete_lines(first_addr, second_addr) < 0 || 485 append_lines(current_addr) < 0) 486 return ERR; 487 break; 488 case 'd': 489 if (check_addr_range(current_addr, current_addr) < 0) 490 return ERR; 491 GET_COMMAND_SUFFIX(); 492 if (!isglobal) clear_undo_stack(); 493 if (delete_lines(first_addr, second_addr) < 0) 494 return ERR; 495 else if ((addr = INC_MOD(current_addr, addr_last)) != 0) 496 current_addr = addr; 497 break; 498 case 'e': 499 if (modified && !scripted) 500 return EMOD; 501 /* fall through */ 502 case 'E': 503 if (addr_cnt > 0) { 504 sprintf(errmsg, "unexpected address"); 505 return ERR; 506 } else if (!isspace((unsigned char)*ibufp)) { 507 sprintf(errmsg, "unexpected command suffix"); 508 return ERR; 509 } else if ((fnp = get_filename()) == NULL) 510 return ERR; 511 GET_COMMAND_SUFFIX(); 512 if (delete_lines(1, addr_last) < 0) 513 return ERR; 514 clear_undo_stack(); 515 if (close_sbuf() < 0) 516 return ERR; 517 else if (open_sbuf() < 0) 518 return FATAL; 519 if (*fnp && *fnp != '!') strlcpy(old_filename, fnp, 520 sizeof(old_filename) - 2); 521 #ifdef BACKWARDS 522 if (*fnp == '\0' && *old_filename == '\0') { 523 sprintf(errmsg, "no current filename"); 524 return ERR; 525 } 526 #endif 527 if (read_file(*fnp ? fnp : old_filename, 0) < 0) 528 return ERR; 529 clear_undo_stack(); 530 modified = 0; 531 u_current_addr = u_addr_last = -1; 532 break; 533 case 'f': 534 if (addr_cnt > 0) { 535 sprintf(errmsg, "unexpected address"); 536 return ERR; 537 } else if (!isspace((unsigned char)*ibufp)) { 538 sprintf(errmsg, "unexpected command suffix"); 539 return ERR; 540 } else if ((fnp = get_filename()) == NULL) 541 return ERR; 542 else if (*fnp == '!') { 543 sprintf(errmsg, "invalid redirection"); 544 return ERR; 545 } 546 GET_COMMAND_SUFFIX(); 547 if (*fnp) strlcpy(old_filename, fnp, sizeof(old_filename) - 2); 548 printf("%s\n", strip_escapes(old_filename)); 549 break; 550 case 'g': 551 case 'v': 552 case 'G': 553 case 'V': 554 if (isglobal) { 555 sprintf(errmsg, "cannot nest global commands"); 556 return ERR; 557 } else if (check_addr_range(1, addr_last) < 0) 558 return ERR; 559 else if (build_active_list(c == 'g' || c == 'G') < 0) 560 return ERR; 561 else if ((n = (c == 'G' || c == 'V')) != 0) 562 GET_COMMAND_SUFFIX(); 563 isglobal++; 564 if (exec_global(n, gflag) < 0) 565 return ERR; 566 break; 567 case 'h': 568 if (addr_cnt > 0) { 569 sprintf(errmsg, "unexpected address"); 570 return ERR; 571 } 572 GET_COMMAND_SUFFIX(); 573 if (*errmsg) fprintf(stderr, "%s\n", errmsg); 574 break; 575 case 'H': 576 if (addr_cnt > 0) { 577 sprintf(errmsg, "unexpected address"); 578 return ERR; 579 } 580 GET_COMMAND_SUFFIX(); 581 if ((garrulous = 1 - garrulous) && *errmsg) 582 fprintf(stderr, "%s\n", errmsg); 583 break; 584 case 'i': 585 if (second_addr == 0) { 586 sprintf(errmsg, "invalid address"); 587 return ERR; 588 } 589 GET_COMMAND_SUFFIX(); 590 if (!isglobal) clear_undo_stack(); 591 if (append_lines(second_addr - 1) < 0) 592 return ERR; 593 break; 594 case 'j': 595 if (check_addr_range(current_addr, current_addr + 1) < 0) 596 return ERR; 597 GET_COMMAND_SUFFIX(); 598 if (!isglobal) clear_undo_stack(); 599 if (first_addr != second_addr && 600 join_lines(first_addr, second_addr) < 0) 601 return ERR; 602 break; 603 case 'k': 604 c = *ibufp++; 605 if (second_addr == 0) { 606 sprintf(errmsg, "invalid address"); 607 return ERR; 608 } 609 GET_COMMAND_SUFFIX(); 610 if (mark_line_node(get_addressed_line_node(second_addr), (unsigned char)c) < 0) 611 return ERR; 612 break; 613 case 'l': 614 if (check_addr_range(current_addr, current_addr) < 0) 615 return ERR; 616 GET_COMMAND_SUFFIX(); 617 if (display_lines(first_addr, second_addr, gflag | GLS) < 0) 618 return ERR; 619 gflag = 0; 620 break; 621 case 'm': 622 if (check_addr_range(current_addr, current_addr) < 0) 623 return ERR; 624 GET_THIRD_ADDR(addr); 625 if (first_addr <= addr && addr < second_addr) { 626 sprintf(errmsg, "invalid destination"); 627 return ERR; 628 } 629 GET_COMMAND_SUFFIX(); 630 if (!isglobal) clear_undo_stack(); 631 if (move_lines(addr) < 0) 632 return ERR; 633 break; 634 case 'n': 635 if (check_addr_range(current_addr, current_addr) < 0) 636 return ERR; 637 GET_COMMAND_SUFFIX(); 638 if (display_lines(first_addr, second_addr, gflag | GNP) < 0) 639 return ERR; 640 gflag = 0; 641 break; 642 case 'p': 643 if (check_addr_range(current_addr, current_addr) < 0) 644 return ERR; 645 GET_COMMAND_SUFFIX(); 646 if (display_lines(first_addr, second_addr, gflag | GPR) < 0) 647 return ERR; 648 gflag = 0; 649 break; 650 case 'P': 651 if (addr_cnt > 0) { 652 sprintf(errmsg, "unexpected address"); 653 return ERR; 654 } 655 GET_COMMAND_SUFFIX(); 656 prompt = prompt ? NULL : optarg ? optarg : dps; 657 break; 658 case 'q': 659 case 'Q': 660 if (addr_cnt > 0) { 661 sprintf(errmsg, "unexpected address"); 662 return ERR; 663 } 664 GET_COMMAND_SUFFIX(); 665 gflag = (modified && !scripted && c == 'q') ? EMOD : EOF; 666 break; 667 case 'r': 668 if (!isspace((unsigned char)*ibufp)) { 669 sprintf(errmsg, "unexpected command suffix"); 670 return ERR; 671 } else if (addr_cnt == 0) 672 second_addr = addr_last; 673 if ((fnp = get_filename()) == NULL) 674 return ERR; 675 GET_COMMAND_SUFFIX(); 676 if (!isglobal) clear_undo_stack(); 677 if (*old_filename == '\0' && *fnp != '!') 678 strlcpy(old_filename, fnp, sizeof(old_filename) - 2); 679 #ifdef BACKWARDS 680 if (*fnp == '\0' && *old_filename == '\0') { 681 sprintf(errmsg, "no current filename"); 682 return ERR; 683 } 684 #endif 685 if ((addr = read_file(*fnp ? fnp : old_filename, second_addr)) < 0) 686 return ERR; 687 else if (addr && addr != addr_last) 688 modified = 1; 689 break; 690 case 's': 691 do { 692 switch(*ibufp) { 693 case '\n': 694 sflags |=SGF; 695 break; 696 case 'g': 697 sflags |= SGG; 698 ibufp++; 699 break; 700 case 'p': 701 sflags |= SGP; 702 ibufp++; 703 break; 704 case 'r': 705 sflags |= SGR; 706 ibufp++; 707 break; 708 case '0': case '1': case '2': case '3': case '4': 709 case '5': case '6': case '7': case '8': case '9': 710 STRTOL(sgnum, ibufp); 711 sflags |= SGF; 712 sgflag &= ~GSG; /* override GSG */ 713 break; 714 default: 715 if (sflags) { 716 sprintf(errmsg, "invalid command suffix"); 717 return ERR; 718 } 719 } 720 } while (sflags && *ibufp != '\n'); 721 if (sflags && !pat) { 722 sprintf(errmsg, "no previous substitution"); 723 return ERR; 724 } else if (sflags & SGG) 725 sgnum = 0; /* override numeric arg */ 726 if (*ibufp != '\n' && *(ibufp + 1) == '\n') { 727 sprintf(errmsg, "invalid pattern delimiter"); 728 return ERR; 729 } 730 tpat = pat; 731 SPL1(); 732 if ((!sflags || (sflags & SGR)) && 733 (tpat = get_compiled_pattern()) == NULL) { 734 SPL0(); 735 return ERR; 736 } else if (tpat != pat) { 737 if (pat) { 738 regfree(pat); 739 free(pat); 740 } 741 pat = tpat; 742 patlock = 1; /* reserve pattern */ 743 } 744 SPL0(); 745 if (!sflags && extract_subst_tail(&sgflag, &sgnum) < 0) 746 return ERR; 747 else if (isglobal) 748 sgflag |= GLB; 749 else 750 sgflag &= ~GLB; 751 if (sflags & SGG) 752 sgflag ^= GSG; 753 if (sflags & SGP) 754 sgflag ^= GPR, sgflag &= ~(GLS | GNP); 755 do { 756 switch(*ibufp) { 757 case 'p': 758 sgflag |= GPR, ibufp++; 759 break; 760 case 'l': 761 sgflag |= GLS, ibufp++; 762 break; 763 case 'n': 764 sgflag |= GNP, ibufp++; 765 break; 766 default: 767 n++; 768 } 769 } while (!n); 770 if (check_addr_range(current_addr, current_addr) < 0) 771 return ERR; 772 GET_COMMAND_SUFFIX(); 773 if (!isglobal) clear_undo_stack(); 774 if (search_and_replace(pat, sgflag, sgnum) < 0) 775 return ERR; 776 break; 777 case 't': 778 if (check_addr_range(current_addr, current_addr) < 0) 779 return ERR; 780 GET_THIRD_ADDR(addr); 781 GET_COMMAND_SUFFIX(); 782 if (!isglobal) clear_undo_stack(); 783 if (copy_lines(addr) < 0) 784 return ERR; 785 break; 786 case 'u': 787 if (addr_cnt > 0) { 788 sprintf(errmsg, "unexpected address"); 789 return ERR; 790 } 791 GET_COMMAND_SUFFIX(); 792 if (pop_undo_stack() < 0) 793 return ERR; 794 break; 795 case 'w': 796 case 'W': 797 if ((n = *ibufp) == 'q' || n == 'Q') { 798 gflag = EOF; 799 ibufp++; 800 } 801 if (!isspace((unsigned char)*ibufp)) { 802 sprintf(errmsg, "unexpected command suffix"); 803 return ERR; 804 } else if ((fnp = get_filename()) == NULL) 805 return ERR; 806 if (addr_cnt == 0 && !addr_last) 807 first_addr = second_addr = 0; 808 else if (check_addr_range(1, addr_last) < 0) 809 return ERR; 810 GET_COMMAND_SUFFIX(); 811 if (*old_filename == '\0' && *fnp != '!') 812 strlcpy(old_filename, fnp, sizeof(old_filename) - 2); 813 #ifdef BACKWARDS 814 if (*fnp == '\0' && *old_filename == '\0') { 815 sprintf(errmsg, "no current filename"); 816 return ERR; 817 } 818 #endif 819 if ((addr = write_file(*fnp ? fnp : old_filename, 820 (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0) 821 return ERR; 822 else if (addr == addr_last) 823 modified = 0; 824 else if (modified && !scripted && n == 'q') 825 gflag = EMOD; 826 break; 827 case 'x': 828 if (addr_cnt > 0) { 829 sprintf(errmsg, "unexpected address"); 830 return ERR; 831 } 832 GET_COMMAND_SUFFIX(); 833 #ifdef DES 834 des = get_keyword(); 835 #else 836 sprintf(errmsg, "crypt unavailable"); 837 return ERR; 838 #endif 839 break; 840 case 'z': 841 #ifdef BACKWARDS 842 if (check_addr_range(first_addr = 1, current_addr + 1) < 0) 843 #else 844 if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0) 845 #endif 846 return ERR; 847 else if ('0' < *ibufp && *ibufp <= '9') 848 STRTOL(rows, ibufp); 849 GET_COMMAND_SUFFIX(); 850 if (display_lines(second_addr, min(addr_last, 851 second_addr + rows), gflag) < 0) 852 return ERR; 853 gflag = 0; 854 break; 855 case '=': 856 GET_COMMAND_SUFFIX(); 857 printf("%ld\n", addr_cnt ? second_addr : addr_last); 858 break; 859 case '!': 860 if (addr_cnt > 0) { 861 sprintf(errmsg, "unexpected address"); 862 return ERR; 863 } else if ((sflags = get_shell_command()) < 0) 864 return ERR; 865 GET_COMMAND_SUFFIX(); 866 if (sflags) printf("%s\n", shcmd + 1); 867 system(shcmd + 1); 868 if (!scripted) printf("!\n"); 869 break; 870 case '\n': 871 #ifdef BACKWARDS 872 if (check_addr_range(first_addr = 1, current_addr + 1) < 0 873 #else 874 if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0 875 #endif 876 || display_lines(second_addr, second_addr, 0) < 0) 877 return ERR; 878 break; 879 default: 880 sprintf(errmsg, "unknown command"); 881 return ERR; 882 } 883 return gflag; 884 } 885 886 887 /* check_addr_range: return status of address range check */ 888 int 889 check_addr_range(long n, long m) 890 { 891 if (addr_cnt == 0) { 892 first_addr = n; 893 second_addr = m; 894 } 895 if (first_addr > second_addr || 1 > first_addr || 896 second_addr > addr_last) { 897 sprintf(errmsg, "invalid address"); 898 return ERR; 899 } 900 return 0; 901 } 902 903 904 /* get_matching_node_addr: return the address of the next line matching a 905 pattern in a given direction. wrap around begin/end of editor buffer if 906 necessary */ 907 long 908 get_matching_node_addr(pattern_t *pat, int dir) 909 { 910 char *s; 911 long n = current_addr; 912 line_t *lp; 913 914 if (!pat) return ERR; 915 do { 916 if ((n = dir ? INC_MOD(n, addr_last) : 917 DEC_MOD(n, addr_last)) != 0) { 918 lp = get_addressed_line_node(n); 919 if ((s = get_sbuf_line(lp)) == NULL) 920 return ERR; 921 if (isbinary) 922 NUL_TO_NEWLINE(s, lp->len); 923 if (!regexec(pat, s, 0, NULL, 0)) 924 return n; 925 } 926 } while (n != current_addr); 927 sprintf(errmsg, "no match"); 928 return ERR; 929 } 930 931 932 /* get_filename: return pointer to copy of filename in the command buffer */ 933 char * 934 get_filename(void) 935 { 936 static char *file = NULL; 937 static int filesz = 0; 938 939 int n; 940 941 if (*ibufp != '\n') { 942 SKIP_BLANKS(); 943 if (*ibufp == '\n') { 944 sprintf(errmsg, "invalid filename"); 945 return NULL; 946 } else if ((ibufp = get_extended_line(&n, 1)) == NULL) 947 return NULL; 948 else if (*ibufp == '!') { 949 ibufp++; 950 if ((n = get_shell_command()) < 0) 951 return NULL; 952 if (n) printf("%s\n", shcmd + 1); 953 return shcmd; 954 } else if (n - 1 > MAXPATHLEN) { 955 sprintf(errmsg, "filename too long"); 956 return NULL; 957 } 958 } 959 #ifndef BACKWARDS 960 else if (*old_filename == '\0') { 961 sprintf(errmsg, "no current filename"); 962 return NULL; 963 } 964 #endif 965 REALLOC(file, filesz, MAXPATHLEN + 1, NULL); 966 for (n = 0; *ibufp != '\n';) 967 file[n++] = *ibufp++; 968 file[n] = '\0'; 969 return is_legal_filename(file) ? file : NULL; 970 } 971 972 973 /* get_shell_command: read a shell command from stdin; return substitution 974 status */ 975 int 976 get_shell_command(void) 977 { 978 static char *buf = NULL; 979 static int n = 0; 980 981 char *s; /* substitution char pointer */ 982 int i = 0; 983 int j = 0; 984 985 if (red) { 986 sprintf(errmsg, "shell access restricted"); 987 return ERR; 988 } else if ((s = ibufp = get_extended_line(&j, 1)) == NULL) 989 return ERR; 990 REALLOC(buf, n, j + 1, ERR); 991 buf[i++] = '!'; /* prefix command w/ bang */ 992 while (*ibufp != '\n') 993 switch (*ibufp) { 994 default: 995 REALLOC(buf, n, i + 2, ERR); 996 buf[i++] = *ibufp; 997 if (*ibufp++ == '\\') 998 buf[i++] = *ibufp++; 999 break; 1000 case '!': 1001 if (s != ibufp) { 1002 REALLOC(buf, n, i + 1, ERR); 1003 buf[i++] = *ibufp++; 1004 } 1005 #ifdef BACKWARDS 1006 else if (shcmd == NULL || *(shcmd + 1) == '\0') 1007 #else 1008 else if (shcmd == NULL) 1009 #endif 1010 { 1011 sprintf(errmsg, "no previous command"); 1012 return ERR; 1013 } else { 1014 REALLOC(buf, n, i + shcmdi, ERR); 1015 for (s = shcmd + 1; s < shcmd + shcmdi;) 1016 buf[i++] = *s++; 1017 s = ibufp++; 1018 } 1019 break; 1020 case '%': 1021 if (*old_filename == '\0') { 1022 sprintf(errmsg, "no current filename"); 1023 return ERR; 1024 } 1025 j = strlen(s = strip_escapes(old_filename)); 1026 REALLOC(buf, n, i + j, ERR); 1027 while (j--) 1028 buf[i++] = *s++; 1029 s = ibufp++; 1030 break; 1031 } 1032 REALLOC(shcmd, shcmdsz, i + 1, ERR); 1033 memcpy(shcmd, buf, i); 1034 shcmd[shcmdi = i] = '\0'; 1035 return *s == '!' || *s == '%'; 1036 } 1037 1038 1039 /* append_lines: insert text from stdin to after line n; stop when either a 1040 single period is read or EOF; return status */ 1041 int 1042 append_lines(long n) 1043 { 1044 int l; 1045 char *lp = ibuf; 1046 char *eot; 1047 undo_t *up = NULL; 1048 1049 for (current_addr = n;;) { 1050 if (!isglobal) { 1051 if ((l = get_tty_line()) < 0) 1052 return ERR; 1053 else if (l == 0 || ibuf[l - 1] != '\n') { 1054 clearerr(stdin); 1055 return l ? EOF : 0; 1056 } 1057 lp = ibuf; 1058 } else if (*(lp = ibufp) == '\0') 1059 return 0; 1060 else { 1061 while (*ibufp++ != '\n') 1062 ; 1063 l = ibufp - lp; 1064 } 1065 if (l == 2 && lp[0] == '.' && lp[1] == '\n') { 1066 return 0; 1067 } 1068 eot = lp + l; 1069 SPL1(); 1070 do { 1071 if ((lp = put_sbuf_line(lp)) == NULL) { 1072 SPL0(); 1073 return ERR; 1074 } else if (up) 1075 up->t = get_addressed_line_node(current_addr); 1076 else if ((up = push_undo_stack(UADD, current_addr, 1077 current_addr)) == NULL) { 1078 SPL0(); 1079 return ERR; 1080 } 1081 } while (lp != eot); 1082 modified = 1; 1083 SPL0(); 1084 } 1085 /* NOTREACHED */ 1086 } 1087 1088 1089 /* join_lines: replace a range of lines with the joined text of those lines */ 1090 int 1091 join_lines(long from, long to) 1092 { 1093 static char *buf = NULL; 1094 static int n; 1095 1096 char *s; 1097 int size = 0; 1098 line_t *bp, *ep; 1099 1100 ep = get_addressed_line_node(INC_MOD(to, addr_last)); 1101 bp = get_addressed_line_node(from); 1102 for (; bp != ep; bp = bp->q_forw) { 1103 if ((s = get_sbuf_line(bp)) == NULL) 1104 return ERR; 1105 REALLOC(buf, n, size + bp->len, ERR); 1106 memcpy(buf + size, s, bp->len); 1107 size += bp->len; 1108 } 1109 REALLOC(buf, n, size + 2, ERR); 1110 memcpy(buf + size, "\n", 2); 1111 if (delete_lines(from, to) < 0) 1112 return ERR; 1113 current_addr = from - 1; 1114 SPL1(); 1115 if (put_sbuf_line(buf) == NULL || 1116 push_undo_stack(UADD, current_addr, current_addr) == NULL) { 1117 SPL0(); 1118 return ERR; 1119 } 1120 modified = 1; 1121 SPL0(); 1122 return 0; 1123 } 1124 1125 1126 /* move_lines: move a range of lines */ 1127 int 1128 move_lines(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 */ 1171 int 1172 copy_lines(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 */ 1207 int 1208 delete_lines(long from, long to) 1209 { 1210 line_t *n, *p; 1211 1212 SPL1(); 1213 if (push_undo_stack(UDEL, from, to) == NULL) { 1214 SPL0(); 1215 return ERR; 1216 } 1217 n = get_addressed_line_node(INC_MOD(to, addr_last)); 1218 p = get_addressed_line_node(from - 1); 1219 /* this get_addressed_line_node last! */ 1220 if (isglobal) 1221 unset_active_nodes(p->q_forw, n); 1222 REQUE(p, n); 1223 addr_last -= to - from + 1; 1224 current_addr = from - 1; 1225 modified = 1; 1226 SPL0(); 1227 return 0; 1228 } 1229 1230 1231 /* display_lines: print a range of lines to stdout */ 1232 int 1233 display_lines(long from, long to, int gflag) 1234 { 1235 line_t *bp; 1236 line_t *ep; 1237 char *s; 1238 1239 if (!from) { 1240 sprintf(errmsg, "invalid address"); 1241 return ERR; 1242 } 1243 ep = get_addressed_line_node(INC_MOD(to, addr_last)); 1244 bp = get_addressed_line_node(from); 1245 for (; bp != ep; bp = bp->q_forw) { 1246 if ((s = get_sbuf_line(bp)) == NULL) 1247 return ERR; 1248 if (put_tty_line(s, bp->len, current_addr = from++, gflag) < 0) 1249 return ERR; 1250 } 1251 return 0; 1252 } 1253 1254 1255 #define MAXMARK 26 /* max number of marks */ 1256 1257 line_t *mark[MAXMARK]; /* line markers */ 1258 int markno; /* line marker count */ 1259 1260 /* mark_line_node: set a line node mark */ 1261 int 1262 mark_line_node(line_t *lp, int n) 1263 { 1264 if (!islower(n)) { 1265 sprintf(errmsg, "invalid mark character"); 1266 return ERR; 1267 } else if (mark[n - 'a'] == NULL) 1268 markno++; 1269 mark[n - 'a'] = lp; 1270 return 0; 1271 } 1272 1273 1274 /* get_marked_node_addr: return address of a marked line */ 1275 long 1276 get_marked_node_addr(int n) 1277 { 1278 if (!islower(n)) { 1279 sprintf(errmsg, "invalid mark character"); 1280 return ERR; 1281 } 1282 return get_line_node_addr(mark[n - 'a']); 1283 } 1284 1285 1286 /* unmark_line_node: clear line node mark */ 1287 void 1288 unmark_line_node(line_t *lp) 1289 { 1290 int i; 1291 1292 for (i = 0; markno && i < MAXMARK; i++) 1293 if (mark[i] == lp) { 1294 mark[i] = NULL; 1295 markno--; 1296 } 1297 } 1298 1299 1300 /* dup_line_node: return a pointer to a copy of a line node */ 1301 line_t * 1302 dup_line_node(line_t *lp) 1303 { 1304 line_t *np; 1305 1306 if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) { 1307 fprintf(stderr, "%s\n", strerror(errno)); 1308 sprintf(errmsg, "out of memory"); 1309 return NULL; 1310 } 1311 np->seek = lp->seek; 1312 np->len = lp->len; 1313 return np; 1314 } 1315 1316 1317 /* has_trailing_escape: return the parity of escapes preceding a character 1318 in a string */ 1319 int 1320 has_trailing_escape(char *s, char *t) 1321 { 1322 return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape(s, t - 1); 1323 } 1324 1325 1326 /* strip_escapes: return copy of escaped string of at most length MAXPATHLEN */ 1327 char * 1328 strip_escapes(const char *s) 1329 { 1330 static char *file = NULL; 1331 static int filesz = 0; 1332 1333 int i = 0; 1334 1335 REALLOC(file, filesz, MAXPATHLEN + 1, NULL); 1336 while ((i < (filesz - 1)) && 1337 (file[i++] = (*s == '\\') != '\0' ? *++s : *s)) 1338 s++; 1339 file[filesz - 1] = '\0'; 1340 return file; 1341 } 1342 1343 1344 void 1345 signal_hup(int signo) 1346 { 1347 if (mutex) 1348 sigflags |= (1 << (signo - 1)); 1349 else handle_hup(signo); 1350 } 1351 1352 1353 void 1354 signal_int(int signo) 1355 { 1356 if (mutex) 1357 sigflags |= (1 << (signo - 1)); 1358 else handle_int(signo); 1359 } 1360 1361 1362 void 1363 handle_hup(int signo) 1364 { 1365 char *hup = NULL; /* hup filename */ 1366 char *s; 1367 int n; 1368 1369 if (!sigactive) 1370 quit(1); 1371 sigflags &= ~(1 << (signo - 1)); 1372 if (addr_last && write_file("ed.hup", "w", 1, addr_last) < 0 && 1373 (s = getenv("HOME")) != NULL && 1374 (n = strlen(s)) + 8 <= MAXPATHLEN && /* "ed.hup" + '/' */ 1375 (hup = (char *) malloc(n + 10)) != NULL) { 1376 strcpy(hup, s); 1377 if (hup[n - 1] != '/') 1378 hup[n] = '/', hup[n+1] = '\0'; 1379 strcat(hup, "ed.hup"); 1380 write_file(hup, "w", 1, addr_last); 1381 } 1382 quit(2); 1383 } 1384 1385 1386 void 1387 handle_int(int signo) 1388 { 1389 if (!sigactive) 1390 quit(1); 1391 sigflags &= ~(1 << (signo - 1)); 1392 #ifdef _POSIX_SOURCE 1393 siglongjmp(env, -1); 1394 #else 1395 longjmp(env, -1); 1396 #endif 1397 } 1398 1399 1400 int cols = 72; /* wrap column */ 1401 1402 void 1403 handle_winch(int signo) 1404 { 1405 struct winsize ws; /* window size structure */ 1406 1407 sigflags &= ~(1 << (signo - 1)); 1408 if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) { 1409 if (ws.ws_row > 2) rows = ws.ws_row - 2; 1410 if (ws.ws_col > 8) cols = ws.ws_col - 8; 1411 } 1412 } 1413 1414 1415 /* is_legal_filename: return a legal filename */ 1416 int 1417 is_legal_filename(char *s) 1418 { 1419 if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) { 1420 sprintf(errmsg, "shell access restricted"); 1421 return 0; 1422 } 1423 return 1; 1424 } 1425