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