ed.c (23648B)
1 /* See LICENSE file for copyright and license details. */ 2 3 /* 4 * TODO: Multi-line commands don't work in global commands: 5 * o g/^line/a \ 6 * line1 7 * . 8 * o Signal handling is broken 9 * o cat <<EOF | ed 10 * 0a 11 * int radix = 16; 12 * int Pflag; 13 * int Aflag; 14 * int vflag; 15 * int gflag; 16 * int uflag; 17 * int arflag; 18 * 19 * . 20 * ?radix?;/^$/-s/^/static / 21 * o cat <<EOF | ed 22 * 0a 23 * Line 24 * . 25 * s# *## 26 * o cat <<EOF | ed 27 * 0a 28 * line 29 * . 30 * 1g/^$/p 31 */ 32 33 #include <sys/stat.h> 34 #include <fcntl.h> 35 #include <regex.h> 36 #include <unistd.h> 37 38 #include <ctype.h> 39 #include <limits.h> 40 #include <setjmp.h> 41 #include <signal.h> 42 #include <stdint.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 47 #include "util.h" 48 49 #define REGEXSIZE 100 50 #define LINESIZE 80 51 #define NUMLINES 32 52 #define CACHESIZ 4096 53 #define AFTER 0 54 #define BEFORE 1 55 56 typedef struct { 57 char *str; 58 size_t cap; 59 size_t siz; 60 } String; 61 62 struct hline { 63 off_t seek; 64 char global; 65 int next, prev; 66 }; 67 68 struct undo { 69 int curln, lastln; 70 size_t nr, cap; 71 struct link { 72 int to1, from1; 73 int to2, from2; 74 } *vec; 75 }; 76 77 static char *prompt = "*"; 78 static regex_t *pattern; 79 static regmatch_t matchs[10]; 80 static String lastre; 81 82 static int optverbose, optprompt, exstatus, optdiag = 1; 83 static int marks['z' - 'a']; 84 static int nlines, line1, line2; 85 static int curln, lastln, ocurln, olastln; 86 static jmp_buf savesp; 87 static char *lasterr; 88 static size_t idxsize, lastidx; 89 static struct hline *zero; 90 static String text; 91 static char savfname[FILENAME_MAX]; 92 static char tmpname[FILENAME_MAX]; 93 static int scratch; 94 static int pflag, modflag, uflag, gflag; 95 static size_t csize; 96 static String cmdline; 97 static char *ocmdline; 98 static int repidx; 99 static char *rhs; 100 static char *lastmatch; 101 static struct undo udata; 102 static int newcmd; 103 int eol, bol; 104 105 static void 106 discard(void) 107 { 108 int c; 109 110 if (repidx >= 0) 111 return; 112 113 /* discard until the end of the line */ 114 if (cmdline.siz > 0 && cmdline.str[cmdline.siz-1] == '\n') 115 return; 116 117 while ((c = getchar()) != '\n' && c != EOF) 118 ; 119 } 120 121 static void undo(void); 122 123 static void 124 error(char *msg) 125 { 126 exstatus = 1; 127 lasterr = msg; 128 puts("?"); 129 130 if (optverbose) 131 puts(msg); 132 if (!newcmd) 133 undo(); 134 135 discard(); 136 curln = ocurln; 137 longjmp(savesp, 1); 138 } 139 140 static int 141 nextln(int line) 142 { 143 ++line; 144 return (line > lastln) ? 0 : line; 145 } 146 147 static int 148 prevln(int line) 149 { 150 --line; 151 return (line < 0) ? lastln : line; 152 } 153 154 static char * 155 addchar(char c, String *s) 156 { 157 size_t cap = s->cap, siz = s->siz; 158 char *t = s->str; 159 160 if (siz >= cap && 161 (cap > SIZE_MAX - LINESIZE || 162 (t = realloc(t, cap += LINESIZE)) == NULL)) 163 error("out of memory"); 164 t[siz++] = c; 165 s->siz = siz; 166 s->cap = cap; 167 s->str = t; 168 return t; 169 } 170 171 static int 172 input(void) 173 { 174 int c; 175 176 if (repidx >= 0) 177 return ocmdline[repidx++]; 178 179 if ((c = getchar()) != EOF) 180 addchar(c, &cmdline); 181 return c; 182 } 183 184 static int 185 back(int c) 186 { 187 if (repidx > 0) { 188 --repidx; 189 } else { 190 ungetc(c, stdin); 191 if (c != EOF) 192 --cmdline.siz; 193 } 194 return c; 195 } 196 197 static int 198 makeline(char *s, int *off) 199 { 200 struct hline *lp; 201 size_t len; 202 char c, *begin = s; 203 204 if (lastidx >= idxsize) { 205 lp = NULL; 206 if (idxsize <= SIZE_MAX - NUMLINES) 207 lp = realloc(zero, (idxsize + NUMLINES) * sizeof(*lp)); 208 if (!lp) 209 error("out of memory"); 210 idxsize += NUMLINES; 211 zero = lp; 212 } 213 lp = zero + lastidx; 214 215 if (!s) { 216 lp->seek = -1; 217 len = 0; 218 } else { 219 while ((c = *s++) != '\n') 220 /* nothing */; 221 len = s - begin; 222 if ((lp->seek = lseek(scratch, 0, SEEK_END)) < 0 || 223 write(scratch, begin, len) < 0) { 224 error("input/output error"); 225 } 226 } 227 if (off) 228 *off = len; 229 ++lastidx; 230 return lp - zero; 231 } 232 233 static int 234 getindex(int line) 235 { 236 struct hline *lp; 237 int n; 238 239 if (line == -1) 240 line = 0; 241 for (n = 0, lp = zero; n != line; n++) 242 lp = zero + lp->next; 243 244 return lp - zero; 245 } 246 247 static char * 248 gettxt(int line) 249 { 250 static char buf[CACHESIZ]; 251 static off_t lasto; 252 struct hline *lp; 253 off_t off, block; 254 ssize_t n; 255 char *p; 256 257 lp = zero + getindex(line); 258 text.siz = 0; 259 off = lp->seek; 260 261 if (off == (off_t) -1) 262 return addchar('\0', &text); 263 264 repeat: 265 if (!csize || off < lasto || off - lasto >= csize) { 266 block = off & ~(CACHESIZ-1); 267 if (lseek(scratch, block, SEEK_SET) < 0 || 268 (n = read(scratch, buf, CACHESIZ)) < 0) { 269 error("input/output error"); 270 } 271 csize = n; 272 lasto = block; 273 } 274 for (p = buf + off - lasto; p < buf + csize && *p != '\n'; ++p) { 275 ++off; 276 addchar(*p, &text); 277 } 278 if (csize && p == buf + csize) 279 goto repeat; 280 281 addchar('\n', &text); 282 addchar('\0', &text); 283 return text.str; 284 } 285 286 static void 287 setglobal(int i, int v) 288 { 289 zero[getindex(i)].global = v; 290 } 291 292 static void 293 clearundo(void) 294 { 295 free(udata.vec); 296 udata.vec = NULL; 297 newcmd = udata.nr = udata.cap = 0; 298 modflag = 0; 299 } 300 301 static void 302 newundo(int from1, int from2) 303 { 304 struct link *p; 305 306 if (newcmd) { 307 clearundo(); 308 udata.curln = ocurln; 309 udata.lastln = olastln; 310 } 311 if (udata.nr >= udata.cap) { 312 size_t siz = (udata.cap + 10) * sizeof(struct link); 313 if ((p = realloc(udata.vec, siz)) == NULL) 314 error("out of memory"); 315 udata.vec = p; 316 udata.cap = udata.cap + 10; 317 } 318 p = &udata.vec[udata.nr++]; 319 p->from1 = from1; 320 p->to1 = zero[from1].next; 321 p->from2 = from2; 322 p->to2 = zero[from2].prev; 323 } 324 325 /* 326 * relink: to1 <- from1 327 * from2 -> to2 328 */ 329 static void 330 relink(int to1, int from1, int from2, int to2) 331 { 332 newundo(from1, from2); 333 zero[from1].next = to1; 334 zero[from2].prev = to2; 335 modflag = 1; 336 } 337 338 static void 339 undo(void) 340 { 341 struct link *p; 342 343 if (udata.nr == 0) 344 return; 345 for (p = &udata.vec[udata.nr-1]; udata.nr > 0; --p) { 346 --udata.nr; 347 zero[p->from1].next = p->to1; 348 zero[p->from2].prev = p->to2; 349 } 350 free(udata.vec); 351 udata.vec = NULL; 352 udata.cap = 0; 353 curln = udata.curln; 354 lastln = udata.lastln; 355 } 356 357 static void 358 inject(char *s, int where) 359 { 360 int off, k, begin, end; 361 362 if (where == BEFORE) { 363 begin = getindex(curln-1); 364 end = getindex(nextln(curln-1)); 365 } else { 366 begin = getindex(curln); 367 end = getindex(nextln(curln)); 368 } 369 while (*s) { 370 k = makeline(s, &off); 371 s += off; 372 relink(k, begin, k, begin); 373 relink(end, k, end, k); 374 ++lastln; 375 ++curln; 376 begin = k; 377 } 378 } 379 380 static void 381 clearbuf(void) 382 { 383 if (scratch) 384 close(scratch); 385 remove(tmpname); 386 free(zero); 387 zero = NULL; 388 scratch = csize = idxsize = lastidx = curln = lastln = 0; 389 modflag = lastln = curln = 0; 390 } 391 392 static void 393 setscratch(void) 394 { 395 int r, k; 396 char *dir; 397 398 clearbuf(); 399 clearundo(); 400 if ((dir = getenv("TMPDIR")) == NULL) 401 dir = "/tmp"; 402 r = snprintf(tmpname, sizeof(tmpname), "%s/%s", 403 dir, "ed.XXXXXX"); 404 if (r < 0 || (size_t)r >= sizeof(tmpname)) 405 error("scratch filename too long"); 406 if ((scratch = mkstemp(tmpname)) < 0) 407 error("failed to create scratch file"); 408 if ((k = makeline(NULL, NULL))) 409 error("input/output error in scratch file"); 410 relink(k, k, k, k); 411 clearundo(); 412 } 413 414 static void 415 compile(int delim) 416 { 417 int n, ret, c,bracket; 418 static char buf[BUFSIZ]; 419 420 if (!isgraph(delim)) 421 error("invalid pattern delimiter"); 422 423 eol = bol = bracket = lastre.siz = 0; 424 for (n = 0;; ++n) { 425 if ((c = input()) == delim && !bracket) 426 break; 427 if (c == '^') { 428 bol = 1; 429 } else if (c == '$') { 430 eol = 1; 431 } else if (c == '\n' || c == EOF) { 432 back(c); 433 break; 434 } 435 436 if (c == '\\') { 437 addchar(c, &lastre); 438 c = input(); 439 } else if (c == '[') { 440 bracket = 1; 441 } else if (c == ']') { 442 bracket = 0; 443 } 444 addchar(c, &lastre); 445 } 446 if (n == 0) { 447 if (!pattern) 448 error("no previous pattern"); 449 return; 450 } 451 addchar('\0', &lastre); 452 453 if (pattern) 454 regfree(pattern); 455 if (!pattern && (!(pattern = malloc(sizeof(*pattern))))) 456 error("out of memory"); 457 if ((ret = regcomp(pattern, lastre.str, REG_NEWLINE))) { 458 regerror(ret, pattern, buf, sizeof(buf)); 459 error(buf); 460 } 461 } 462 463 static int 464 match(int num) 465 { 466 lastmatch = gettxt(num); 467 return !regexec(pattern, lastmatch, 10, matchs, 0); 468 } 469 470 static int 471 rematch(int num) 472 { 473 regoff_t off = matchs[0].rm_eo; 474 475 if (!regexec(pattern, lastmatch + off, 10, matchs, 0)) { 476 lastmatch += off; 477 return 1; 478 } 479 480 return 0; 481 } 482 483 static int 484 search(int way) 485 { 486 int i; 487 488 i = curln; 489 do { 490 i = (way == '?') ? prevln(i) : nextln(i); 491 if (i > 0 && match(i)) 492 return i; 493 } while (i != curln); 494 495 error("invalid address"); 496 return -1; /* not reached */ 497 } 498 499 static void 500 skipblank(void) 501 { 502 char c; 503 504 while ((c = input()) == ' ' || c == '\t') 505 /* nothing */; 506 back(c); 507 } 508 509 static int 510 getnum(void) 511 { 512 int ln, n, c; 513 514 for (ln = 0; isdigit(c = input()); ln += n) { 515 if (ln > INT_MAX/10) 516 goto invalid; 517 n = c - '0'; 518 ln *= 10; 519 if (INT_MAX - ln < n) 520 goto invalid; 521 } 522 back(c); 523 return ln; 524 525 invalid: 526 error("invalid address"); 527 return -1; /* not reached */ 528 } 529 530 static int 531 linenum(int *line) 532 { 533 int ln, c; 534 535 skipblank(); 536 537 switch (c = input()) { 538 case '.': 539 ln = curln; 540 break; 541 case '\'': 542 skipblank(); 543 if (!islower(c = input())) 544 error("invalid mark character"); 545 if (!(ln = marks[c - 'a'])) 546 error("invalid address"); 547 break; 548 case '$': 549 ln = lastln; 550 break; 551 case '?': 552 case '/': 553 compile(c); 554 ln = search(c); 555 break; 556 case '^': 557 case '-': 558 case '+': 559 ln = curln; 560 back(c); 561 break; 562 default: 563 back(c); 564 if (isdigit(c)) 565 ln = getnum(); 566 else 567 return 0; 568 break; 569 } 570 *line = ln; 571 return 1; 572 } 573 574 static int 575 address(int *line) 576 { 577 int ln, sign, c, num; 578 579 if (!linenum(&ln)) 580 return 0; 581 582 for (;;) { 583 skipblank(); 584 if ((c = input()) != '+' && c != '-' && c != '^') 585 break; 586 sign = c == '+' ? 1 : -1; 587 num = isdigit(back(input())) ? getnum() : 1; 588 num *= sign; 589 if (INT_MAX - ln < num) 590 goto invalid; 591 ln += num; 592 } 593 back(c); 594 595 if (ln < 0 || ln > lastln) 596 error("invalid address"); 597 *line = ln; 598 return 1; 599 600 invalid: 601 error("invalid address"); 602 return -1; /* not reached */ 603 } 604 605 static void 606 getlst(void) 607 { 608 int ln, c; 609 610 if ((c = input()) == ',') { 611 line1 = 1; 612 line2 = lastln; 613 nlines = lastln; 614 return; 615 } else if (c == ';') { 616 line1 = curln; 617 line2 = lastln; 618 nlines = lastln - curln + 1; 619 return; 620 } 621 back(c); 622 line2 = curln; 623 for (nlines = 0; address(&ln); ) { 624 line1 = line2; 625 line2 = ln; 626 ++nlines; 627 628 skipblank(); 629 if ((c = input()) != ',' && c != ';') { 630 back(c); 631 break; 632 } 633 if (c == ';') 634 curln = line2; 635 } 636 if (nlines > 2) 637 nlines = 2; 638 else if (nlines <= 1) 639 line1 = line2; 640 } 641 642 static void 643 deflines(int def1, int def2) 644 { 645 if (!nlines) { 646 line1 = def1; 647 line2 = def2; 648 } 649 if (line1 > line2 || line1 < 0 || line2 > lastln) 650 error("invalid address"); 651 } 652 653 static void 654 dowrite(const char *fname, int trunc) 655 { 656 FILE *fp; 657 int i, line; 658 659 if (!(fp = fopen(fname, (trunc) ? "w" : "a"))) 660 error("input/output error"); 661 662 line = curln; 663 for (i = line1; i <= line2; ++i) 664 fputs(gettxt(i), fp); 665 666 curln = line2; 667 if (fclose(fp)) 668 error("input/output error"); 669 strcpy(savfname, fname); 670 modflag = 0; 671 curln = line; 672 } 673 674 static void 675 doread(const char *fname) 676 { 677 size_t cnt; 678 ssize_t n; 679 char *p; 680 FILE *aux; 681 static size_t len; 682 static char *s; 683 static FILE *fp; 684 685 if (fp) 686 fclose(fp); 687 if ((fp = fopen(fname, "r")) == NULL) 688 error("cannot open input file"); 689 690 curln = line2; 691 for (cnt = 0; (n = getline(&s, &len, fp)) > 0; cnt += (size_t)n) { 692 if (s[n-1] != '\n') { 693 if (len == SIZE_MAX || !(p = realloc(s, ++len))) 694 error("out of memory"); 695 s = p; 696 s[n-1] = '\n'; 697 s[n] = '\0'; 698 } 699 inject(s, AFTER); 700 } 701 if (optdiag) 702 printf("%zu\n", cnt); 703 704 aux = fp; 705 fp = NULL; 706 if (fclose(aux)) 707 error("input/output error"); 708 } 709 710 static void 711 doprint(void) 712 { 713 int i, c; 714 char *s, *str; 715 716 if (line1 <= 0 || line2 > lastln) 717 error("incorrect address"); 718 for (i = line1; i <= line2; ++i) { 719 if (pflag == 'n') 720 printf("%d\t", i); 721 for (s = gettxt(i); (c = *s) != '\n'; ++s) { 722 if (pflag != 'l') 723 goto print_char; 724 switch (c) { 725 case '$': 726 str = "\\$"; 727 goto print_str; 728 case '\t': 729 str = "\\t"; 730 goto print_str; 731 case '\b': 732 str = "\\b"; 733 goto print_str; 734 case '\\': 735 str = "\\\\"; 736 goto print_str; 737 default: 738 if (!isprint(c)) { 739 printf("\\x%x", 0xFF & c); 740 break; 741 } 742 print_char: 743 putchar(c); 744 break; 745 print_str: 746 fputs(str, stdout); 747 break; 748 } 749 } 750 if (pflag == 'l') 751 fputs("$", stdout); 752 putc('\n', stdout); 753 } 754 curln = i - 1; 755 } 756 757 static void 758 dohelp(void) 759 { 760 if (lasterr) 761 puts(lasterr); 762 } 763 764 static void 765 chkprint(int flag) 766 { 767 char c; 768 769 if (flag) { 770 if ((c = input()) == 'p' || c == 'l' || c == 'n') 771 pflag = c; 772 else 773 back(c); 774 } 775 if (input() != '\n') 776 error("invalid command suffix"); 777 } 778 779 static char * 780 getfname(char comm) 781 { 782 int c; 783 char *bp; 784 static char fname[FILENAME_MAX]; 785 786 skipblank(); 787 for (bp = fname; bp < &fname[FILENAME_MAX]; *bp++ = c) { 788 if ((c = input()) == EOF || c == '\n') 789 break; 790 } 791 if (bp == fname) { 792 if (savfname[0] == '\0') 793 error("no current filename"); 794 return savfname; 795 } else if (bp == &fname[FILENAME_MAX]) { 796 error("file name too long"); 797 } else { 798 *bp = '\0'; 799 if (savfname[0] == '\0' || comm == 'e' || comm == 'f') 800 strcpy(savfname, fname); 801 return fname; 802 } 803 804 return NULL; /* not reached */ 805 } 806 807 static void 808 append(int num) 809 { 810 char *s = NULL; 811 size_t len = 0; 812 813 curln = num; 814 while (getline(&s, &len, stdin) > 0) { 815 if (*s == '.' && s[1] == '\n') 816 break; 817 inject(s, AFTER); 818 } 819 free(s); 820 } 821 822 static void 823 delete(int from, int to) 824 { 825 int lto, lfrom; 826 827 if (!from) 828 error("incorrect address"); 829 830 lfrom = getindex(prevln(from)); 831 lto = getindex(nextln(to)); 832 lastln -= to - from + 1; 833 curln = (from > lastln) ? lastln : from;; 834 relink(lto, lfrom, lto, lfrom); 835 } 836 837 static void 838 move(int where) 839 { 840 int before, after, lto, lfrom; 841 842 if (!line1 || (where >= line1 && where <= line2)) 843 error("incorrect address"); 844 845 before = getindex(prevln(line1)); 846 after = getindex(nextln(line2)); 847 lfrom = getindex(line1); 848 lto = getindex(line2); 849 relink(after, before, after, before); 850 851 if (where < line1) { 852 curln = where + line1 - line2 + 1; 853 } else { 854 curln = where; 855 where -= line1 - line2 + 1; 856 } 857 before = getindex(where); 858 after = getindex(nextln(where)); 859 relink(lfrom, before, lfrom, before); 860 relink(after, lto, after, lto); 861 } 862 863 static void 864 join(void) 865 { 866 int i; 867 char *t, c; 868 static String s; 869 870 free(s.str); 871 s.siz = s.cap = 0; 872 for (i = line1;; i = nextln(i)) { 873 for (t = gettxt(i); (c = *t) != '\n'; ++t) 874 addchar(*t, &s); 875 if (i == line2) 876 break; 877 } 878 879 addchar('\n', &s); 880 addchar('\0', &s); 881 delete(line1, line2); 882 inject(s.str, BEFORE); 883 free(s.str); 884 } 885 886 static void 887 scroll(int num) 888 { 889 int max, ln, cnt; 890 891 if (!line1 || line1 == lastln) 892 error("incorrect address"); 893 894 ln = line1; 895 max = line1 + num; 896 if (max > lastln) 897 max = lastln; 898 for (cnt = line1; cnt < max; cnt++) { 899 fputs(gettxt(ln), stdout); 900 ln = nextln(ln); 901 } 902 curln = ln; 903 } 904 905 static void 906 copy(int where) 907 { 908 909 if (!line1) 910 error("incorrect address"); 911 curln = where; 912 913 while (line1 <= line2) { 914 inject(gettxt(line1), AFTER); 915 if (line2 >= curln) 916 line2 = nextln(line2); 917 line1 = nextln(line1); 918 if (line1 >= curln) 919 line1 = nextln(line1); 920 } 921 } 922 923 static void 924 quit(void) 925 { 926 clearbuf(); 927 exit(exstatus); 928 } 929 930 static void 931 execsh(void) 932 { 933 static String cmd; 934 char *p; 935 int c, repl = 0; 936 937 skipblank(); 938 if ((c = input()) != '!') { 939 back(c); 940 cmd.siz = 0; 941 } else if (cmd.siz) { 942 --cmd.siz; 943 repl = 1; 944 } else { 945 error("no previous command"); 946 } 947 948 while ((c = input()) != EOF && c != '\n') { 949 if (c == '%' && (cmd.siz == 0 || cmd.str[cmd.siz - 1] != '\\')) { 950 if (savfname[0] == '\0') 951 error("no current filename"); 952 repl = 1; 953 for (p = savfname; *p; ++p) 954 addchar(*p, &cmd); 955 } else { 956 addchar(c, &cmd); 957 } 958 } 959 addchar('\0', &cmd); 960 961 if (repl) 962 puts(cmd.str); 963 system(cmd.str); 964 if (optdiag) 965 puts("!"); 966 } 967 968 static void 969 getrhs(int delim) 970 { 971 int c; 972 static String s; 973 974 free(s.str); 975 s.str = NULL; 976 s.siz = s.cap = 0; 977 while ((c = input()) != '\n' && c != EOF && c != delim) 978 addchar(c, &s); 979 addchar('\0', &s); 980 if (c == EOF) 981 error("invalid pattern delimiter"); 982 if (c == '\n') { 983 pflag = 'p'; 984 back(c); 985 } 986 987 if (!strcmp("%", s.str)) { 988 free(s.str); 989 if (!rhs) 990 error("no previous substitution"); 991 } else { 992 free(rhs); 993 rhs = s.str; 994 } 995 s.str = NULL; 996 } 997 998 static int 999 getnth(void) 1000 { 1001 int c; 1002 1003 if ((c = input()) == 'g') { 1004 return -1; 1005 } else if (isdigit(c)) { 1006 if (c == '0') 1007 return -1; 1008 return c - '0'; 1009 } else { 1010 back(c); 1011 return 1; 1012 } 1013 } 1014 1015 static void 1016 addpre(String *s) 1017 { 1018 char *p; 1019 1020 for (p = lastmatch; p < lastmatch + matchs[0].rm_so; ++p) 1021 addchar(*p, s); 1022 } 1023 1024 static void 1025 addpost(String *s) 1026 { 1027 char c, *p; 1028 1029 for (p = lastmatch + matchs[0].rm_eo; (c = *p); ++p) 1030 addchar(c, s); 1031 addchar('\0', s); 1032 } 1033 1034 static int 1035 addsub(String *s, int nth, int nmatch) 1036 { 1037 char *end, *q, *p, c; 1038 int sub; 1039 1040 if (nth != nmatch && nth != -1) { 1041 q = lastmatch + matchs[0].rm_so; 1042 end = lastmatch + matchs[0].rm_eo; 1043 while (q < end) 1044 addchar(*q++, s); 1045 return 0; 1046 } 1047 1048 for (p = rhs; (c = *p); ++p) { 1049 switch (c) { 1050 case '&': 1051 sub = 0; 1052 goto copy_match; 1053 case '\\': 1054 if ((c = *++p) == '\0') 1055 return 1; 1056 if (!isdigit(c)) 1057 goto copy_char; 1058 sub = c - '0'; 1059 copy_match: 1060 q = lastmatch + matchs[sub].rm_so; 1061 end = lastmatch + matchs[sub].rm_eo; 1062 while (q < end) 1063 addchar(*q++, s); 1064 break; 1065 default: 1066 copy_char: 1067 addchar(c, s); 1068 break; 1069 } 1070 } 1071 return 1; 1072 } 1073 1074 static void 1075 subline(int num, int nth) 1076 { 1077 int i, m, changed; 1078 static String s; 1079 1080 i = changed = s.siz = 0; 1081 for (m = match(num); m; m = rematch(num)) { 1082 addpre(&s); 1083 changed |= addsub(&s, nth, ++i); 1084 if (eol || bol) 1085 break; 1086 } 1087 if (!changed) 1088 return; 1089 addpost(&s); 1090 delete(num, num); 1091 curln = prevln(num); 1092 inject(s.str, AFTER); 1093 } 1094 1095 static void 1096 subst(int nth) 1097 { 1098 int i; 1099 1100 for (i = line1; i <= line2; ++i) 1101 subline(i, nth); 1102 } 1103 1104 static void 1105 docmd(void) 1106 { 1107 char cmd; 1108 int rep = 0, c, line3, num, trunc; 1109 1110 repeat: 1111 skipblank(); 1112 cmd = input(); 1113 trunc = pflag = 0; 1114 switch (cmd) { 1115 case '&': 1116 skipblank(); 1117 chkprint(0); 1118 if (!ocmdline) 1119 error("no previous command"); 1120 rep = 1; 1121 repidx = 0; 1122 getlst(); 1123 goto repeat; 1124 case '!': 1125 execsh(); 1126 break; 1127 case EOF: 1128 if (cmdline.siz == 0) 1129 quit(); 1130 case '\n': 1131 if (gflag && uflag) 1132 return; 1133 num = gflag ? curln : curln+1; 1134 deflines(num, num); 1135 pflag = 'p'; 1136 goto print; 1137 case 'l': 1138 case 'n': 1139 case 'p': 1140 back(cmd); 1141 chkprint(1); 1142 deflines(curln, curln); 1143 goto print; 1144 case 'g': 1145 case 'G': 1146 case 'v': 1147 case 'V': 1148 error("cannot nest global commands"); 1149 case 'H': 1150 if (nlines > 0) 1151 goto unexpected; 1152 chkprint(0); 1153 optverbose ^= 1; 1154 break; 1155 case 'h': 1156 if (nlines > 0) 1157 goto unexpected; 1158 chkprint(0); 1159 dohelp(); 1160 break; 1161 case 'w': 1162 trunc = 1; 1163 case 'W': 1164 deflines(nextln(0), lastln); 1165 dowrite(getfname(cmd), trunc); 1166 break; 1167 case 'r': 1168 if (nlines > 1) 1169 goto bad_address; 1170 deflines(lastln, lastln); 1171 doread(getfname(cmd)); 1172 break; 1173 case 'd': 1174 chkprint(1); 1175 deflines(curln, curln); 1176 delete(line1, line2); 1177 break; 1178 case '=': 1179 if (nlines > 1) 1180 goto bad_address; 1181 chkprint(1); 1182 deflines(lastln, lastln); 1183 printf("%d\n", line1); 1184 break; 1185 case 'u': 1186 if (nlines > 0) 1187 goto bad_address; 1188 chkprint(1); 1189 if (udata.nr == 0) 1190 error("nothing to undo"); 1191 undo(); 1192 break; 1193 case 's': 1194 deflines(curln, curln); 1195 c = input(); 1196 compile(c); 1197 getrhs(c); 1198 num = getnth(); 1199 chkprint(1); 1200 subst(num); 1201 break; 1202 case 'i': 1203 if (nlines > 1) 1204 goto bad_address; 1205 chkprint(1); 1206 deflines(curln, curln); 1207 if (!line1) 1208 line1++; 1209 append(prevln(line1)); 1210 break; 1211 case 'a': 1212 if (nlines > 1) 1213 goto bad_address; 1214 chkprint(1); 1215 deflines(curln, curln); 1216 append(line1); 1217 break; 1218 case 'm': 1219 deflines(curln, curln); 1220 if (!address(&line3)) 1221 line3 = curln; 1222 chkprint(1); 1223 move(line3); 1224 break; 1225 case 't': 1226 deflines(curln, curln); 1227 if (!address(&line3)) 1228 line3 = curln; 1229 chkprint(1); 1230 copy(line3); 1231 break; 1232 case 'c': 1233 chkprint(1); 1234 deflines(curln, curln); 1235 delete(line1, line2); 1236 append(prevln(line1)); 1237 break; 1238 case 'j': 1239 chkprint(1); 1240 deflines(curln, curln+1); 1241 if (line1 != line2 && curln != 0) 1242 join(); 1243 break; 1244 case 'z': 1245 if (nlines > 1) 1246 goto bad_address; 1247 if (isdigit(back(input()))) 1248 num = getnum(); 1249 else 1250 num = 24; 1251 chkprint(1); 1252 scroll(num); 1253 break; 1254 case 'k': 1255 if (nlines > 1) 1256 goto bad_address; 1257 if (!islower(c = input())) 1258 error("invalid mark character"); 1259 chkprint(1); 1260 deflines(curln, curln); 1261 marks[c - 'a'] = line1; 1262 break; 1263 case 'P': 1264 if (nlines > 0) 1265 goto unexpected; 1266 chkprint(1); 1267 optprompt ^= 1; 1268 break; 1269 case 'Q': 1270 modflag = 0; 1271 case 'q': 1272 if (nlines > 0) 1273 goto unexpected; 1274 if (modflag) 1275 goto modified; 1276 quit(); 1277 break; 1278 case 'f': 1279 if (nlines > 0) 1280 goto unexpected; 1281 if (back(input()) != '\n') 1282 getfname(cmd); 1283 else 1284 puts(savfname); 1285 chkprint(0); 1286 break; 1287 case 'E': 1288 modflag = 0; 1289 case 'e': 1290 if (nlines > 0) 1291 goto unexpected; 1292 if (modflag) 1293 goto modified; 1294 getfname(cmd); 1295 setscratch(); 1296 deflines(curln, curln); 1297 doread(savfname); 1298 clearundo(); 1299 break; 1300 default: 1301 error("unknown command"); 1302 bad_address: 1303 error("invalid address"); 1304 modified: 1305 modflag = 0; 1306 error("warning: file modified"); 1307 unexpected: 1308 error("unexpected address"); 1309 } 1310 1311 if (!pflag) 1312 goto save_last_cmd; 1313 1314 line1 = line2 = curln; 1315 print: 1316 doprint(); 1317 1318 save_last_cmd: 1319 if (!uflag) 1320 repidx = 0; 1321 if (rep) 1322 return; 1323 free(ocmdline); 1324 addchar('\0', &cmdline); 1325 if ((ocmdline = strdup(cmdline.str)) == NULL) 1326 error("out of memory"); 1327 } 1328 1329 static int 1330 chkglobal(void) 1331 { 1332 int delim, c, dir, i, v; 1333 1334 uflag = 1; 1335 gflag = 0; 1336 skipblank(); 1337 1338 switch (c = input()) { 1339 case 'g': 1340 uflag = 0; 1341 case 'G': 1342 dir = 1; 1343 break; 1344 case 'v': 1345 uflag = 0; 1346 case 'V': 1347 dir = 0; 1348 break; 1349 default: 1350 back(c); 1351 return 0; 1352 } 1353 gflag = 1; 1354 deflines(nextln(0), lastln); 1355 delim = input(); 1356 compile(delim); 1357 1358 for (i = 1; i <= lastln; ++i) { 1359 if (i >= line1 && i <= line2) 1360 v = match(i) == dir; 1361 else 1362 v = 0; 1363 setglobal(i, v); 1364 } 1365 1366 return 1; 1367 } 1368 1369 static void 1370 doglobal(void) 1371 { 1372 int cnt, ln, k; 1373 1374 skipblank(); 1375 cmdline.siz = 0; 1376 gflag = 1; 1377 if (uflag) 1378 chkprint(0); 1379 1380 ln = line1; 1381 for (cnt = 0; cnt < lastln; ) { 1382 k = getindex(ln); 1383 if (zero[k].global) { 1384 zero[k].global = 0; 1385 curln = ln; 1386 nlines = 0; 1387 if (uflag) { 1388 line1 = line2 = ln; 1389 pflag = 0; 1390 doprint(); 1391 } 1392 getlst(); 1393 docmd(); 1394 } else { 1395 cnt++; 1396 ln = nextln(ln); 1397 } 1398 } 1399 discard(); /* cover the case of not matching anything */ 1400 } 1401 1402 static void 1403 usage(void) 1404 { 1405 eprintf("usage: %s [-s] [-p] [file]\n", argv0); 1406 } 1407 1408 static void 1409 sigintr(int n) 1410 { 1411 signal(SIGINT, sigintr); 1412 error("interrupt"); 1413 } 1414 1415 static void 1416 sighup(int dummy) 1417 { 1418 int n; 1419 char *home = getenv("HOME"), fname[FILENAME_MAX]; 1420 1421 if (modflag) { 1422 line1 = nextln(0); 1423 line2 = lastln; 1424 if (!setjmp(savesp)) { 1425 dowrite("ed.hup", 1); 1426 } else if (home && !setjmp(savesp)) { 1427 n = snprintf(fname, 1428 sizeof(fname), "%s/%s", home, "ed.hup"); 1429 if (n < sizeof(fname) && n > 0) 1430 dowrite(fname, 1); 1431 } 1432 } 1433 exstatus = 1; 1434 quit(); 1435 } 1436 1437 static void 1438 edit(void) 1439 { 1440 for (;;) { 1441 newcmd = 1; 1442 ocurln = curln; 1443 olastln = lastln; 1444 cmdline.siz = 0; 1445 repidx = -1; 1446 if (optprompt) { 1447 fputs(prompt, stdout); 1448 fflush(stdout); 1449 } 1450 getlst(); 1451 chkglobal() ? doglobal() : docmd(); 1452 } 1453 } 1454 1455 static void 1456 init(char *fname) 1457 { 1458 size_t len; 1459 1460 setscratch(); 1461 if (!fname) 1462 return; 1463 if ((len = strlen(fname)) >= FILENAME_MAX || len == 0) 1464 error("incorrect filename"); 1465 memcpy(savfname, fname, len); 1466 doread(fname); 1467 clearundo(); 1468 } 1469 1470 int 1471 main(int argc, char *argv[]) 1472 { 1473 ARGBEGIN { 1474 case 'p': 1475 prompt = EARGF(usage()); 1476 optprompt = 1; 1477 break; 1478 case 's': 1479 optdiag = 0; 1480 break; 1481 default: 1482 usage(); 1483 } ARGEND 1484 1485 if (argc > 1) 1486 usage(); 1487 1488 if (!setjmp(savesp)) { 1489 signal(SIGINT, sigintr); 1490 signal(SIGHUP, sighup); 1491 signal(SIGQUIT, SIG_IGN); 1492 init(*argv); 1493 } 1494 edit(); 1495 1496 /* not reached */ 1497 return 0; 1498 }