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