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