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