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