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