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