1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright (c) 2016 by Delphix. All rights reserved. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 /* Copyright (c) 1981 Regents of the University of California */ 32 33 #include "ex.h" 34 #include "ex_argv.h" 35 #include "ex_temp.h" 36 #include "ex_tty.h" 37 #include "ex_vis.h" 38 #include <stdlib.h> 39 #include <unistd.h> 40 41 /* 42 * File input/output, source, preserve and recover 43 */ 44 45 /* 46 * Following remember where . was in the previous file for return 47 * on file switching. 48 */ 49 int altdot; 50 int oldadot; 51 bool wasalt; 52 short isalt; 53 54 long cntch; /* Count of characters on unit io */ 55 #ifndef VMUNIX 56 short cntln; /* Count of lines " */ 57 #else 58 int cntln; 59 #endif 60 long cntnull; /* Count of nulls " */ 61 long cntodd; /* Count of non-ascii characters " */ 62 63 static void chkmdln(); 64 extern int getchar(); 65 66 /* 67 * Parse file name for command encoded by comm. 68 * If comm is E then command is doomed and we are 69 * parsing just so user won't have to retype the name. 70 */ 71 void 72 filename(int comm) 73 { 74 int c = comm, d; 75 int i; 76 77 d = getchar(); 78 if (endcmd(d)) { 79 if (savedfile[0] == 0 && comm != 'f') 80 error(value(vi_TERSE) ? gettext("No file") : 81 gettext("No current filename")); 82 CP(file, savedfile); 83 wasalt = (isalt > 0) ? isalt-1 : 0; 84 isalt = 0; 85 oldadot = altdot; 86 if (c == 'e' || c == 'E') 87 altdot = lineDOT(); 88 if (d == EOF) 89 ungetchar(d); 90 } else { 91 ungetchar(d); 92 getone(); 93 eol(); 94 if (savedfile[0] == 0 && c != 'E' && c != 'e') { 95 c = 'e'; 96 edited = 0; 97 } 98 wasalt = strcmp(file, altfile) == 0; 99 oldadot = altdot; 100 switch (c) { 101 102 case 'f': 103 edited = 0; 104 /* FALLTHROUGH */ 105 106 case 'e': 107 if (savedfile[0]) { 108 altdot = lineDOT(); 109 CP(altfile, savedfile); 110 } 111 CP(savedfile, file); 112 break; 113 114 default: 115 if (file[0]) { 116 if (c != 'E') 117 altdot = lineDOT(); 118 CP(altfile, file); 119 } 120 break; 121 } 122 } 123 if (hush && comm != 'f' || comm == 'E') 124 return; 125 if (file[0] != 0) { 126 lprintf("\"%s\"", file); 127 if (comm == 'f') { 128 if (value(vi_READONLY)) 129 viprintf(gettext(" [Read only]")); 130 if (!edited) 131 viprintf(gettext(" [Not edited]")); 132 if (tchng) 133 viprintf(gettext(" [Modified]")); 134 } 135 flush(); 136 } else 137 viprintf(gettext("No file ")); 138 if (comm == 'f') { 139 if (!(i = lineDOL())) 140 i++; 141 /* 142 * TRANSLATION_NOTE 143 * Reference order of arguments must not 144 * be changed using '%digit$', since vi's 145 * viprintf() does not support it. 146 */ 147 viprintf(gettext(" line %d of %d --%ld%%--"), lineDOT(), 148 lineDOL(), (long)(100 * lineDOT() / i)); 149 } 150 } 151 152 /* 153 * Get the argument words for a command into genbuf 154 * expanding # and %. 155 */ 156 int 157 getargs(void) 158 { 159 int c; 160 unsigned char *cp, *fp; 161 static unsigned char fpatbuf[32]; /* hence limit on :next +/pat */ 162 char multic[MB_LEN_MAX + 1]; 163 int len; 164 wchar_t wc; 165 166 pastwh(); 167 if (peekchar() == '+') { 168 for (cp = fpatbuf;;) { 169 if (!isascii(c = peekchar()) && (c != EOF)) { 170 if ((len = _mbftowc(multic, &wc, getchar, &peekc)) > 0) { 171 if ((cp + len) >= &fpatbuf[sizeof(fpatbuf)]) 172 error(gettext("Pattern too long")); 173 strncpy(cp, multic, len); 174 cp += len; 175 continue; 176 } 177 } 178 179 c = getchar(); 180 *cp++ = c; 181 if (cp >= &fpatbuf[sizeof(fpatbuf)]) 182 error(gettext("Pattern too long")); 183 if (c == '\\' && isspace(peekchar())) 184 c = getchar(); 185 if (c == EOF || isspace(c)) { 186 ungetchar(c); 187 *--cp = 0; 188 firstpat = &fpatbuf[1]; 189 break; 190 } 191 } 192 } 193 if (skipend()) 194 return (0); 195 CP(genbuf, "echo "); cp = &genbuf[5]; 196 for (;;) { 197 if (!isascii(c = peekchar())) { 198 if (endcmd(c) && c != '"') 199 break; 200 if ((len = _mbftowc(multic, &wc, getchar, &peekc)) > 0) { 201 if ((cp + len) > &genbuf[LBSIZE - 2]) 202 error(gettext("Argument buffer overflow")); 203 strncpy(cp, multic, len); 204 cp += len; 205 continue; 206 } 207 } 208 209 if (endcmd(c) && c != '"') 210 break; 211 212 c = getchar(); 213 switch (c) { 214 215 case '\\': 216 if (any(peekchar(), "#%|")) 217 c = getchar(); 218 /* FALLTHROUGH */ 219 220 default: 221 if (cp > &genbuf[LBSIZE - 2]) 222 flong: 223 error(gettext("Argument buffer overflow")); 224 *cp++ = c; 225 break; 226 227 case '#': 228 fp = (unsigned char *)altfile; 229 if (*fp == 0) 230 error(value(vi_TERSE) ? 231 gettext("No alternate filename") : 232 gettext("No alternate filename to substitute for #")); 233 goto filexp; 234 235 case '%': 236 fp = savedfile; 237 if (*fp == 0) 238 error(value(vi_TERSE) ? 239 gettext("No current filename") : 240 gettext("No current filename to substitute for %%")); 241 filexp: 242 while (*fp) { 243 if (cp > &genbuf[LBSIZE - 2]) 244 goto flong; 245 *cp++ = *fp++; 246 } 247 break; 248 } 249 } 250 *cp = 0; 251 return (1); 252 } 253 254 /* 255 * Glob the argument words in genbuf, or if no globbing 256 * is implied, just split them up directly. 257 */ 258 void 259 glob(struct glob *gp) 260 { 261 int pvec[2]; 262 unsigned char **argv = gp->argv; 263 unsigned char *cp = gp->argspac; 264 int c; 265 unsigned char ch; 266 int nleft = NCARGS; 267 268 gp->argc0 = 0; 269 if (gscan() == 0) { 270 unsigned char *v = genbuf + 5; /* strlen("echo ") */ 271 272 for (;;) { 273 while (isspace(*v)) 274 v++; 275 if (!*v) 276 break; 277 *argv++ = cp; 278 while (*v && !isspace(*v)) 279 *cp++ = *v++; 280 *cp++ = 0; 281 gp->argc0++; 282 } 283 *argv = 0; 284 return; 285 } 286 if (pipe(pvec) < 0) 287 error(gettext("Can't make pipe to glob")); 288 pid = fork(); 289 io = pvec[0]; 290 if (pid < 0) { 291 close(pvec[1]); 292 error(gettext("Can't fork to do glob")); 293 } 294 if (pid == 0) { 295 int oerrno; 296 297 close(1); 298 dup(pvec[1]); 299 close(pvec[0]); 300 close(2); /* so errors don't mess up the screen */ 301 open("/dev/null", 1); 302 execlp((char *)svalue(vi_SHELL), "sh", "-c", genbuf, (char *)0); 303 oerrno = errno; close(1); dup(2); errno = oerrno; 304 filioerr(svalue(vi_SHELL)); 305 } 306 close(pvec[1]); 307 do { 308 *argv = cp; 309 for (;;) { 310 if (read(io, &ch, 1) != 1) { 311 close(io); 312 c = -1; 313 } else 314 c = ch; 315 if (c <= 0 || isspace(c)) 316 break; 317 *cp++ = c; 318 if (--nleft <= 0) 319 error(gettext("Arg list too long")); 320 } 321 if (cp != *argv) { 322 --nleft; 323 *cp++ = 0; 324 gp->argc0++; 325 if (gp->argc0 >= NARGS) 326 error(gettext("Arg list too long")); 327 argv++; 328 } 329 } while (c >= 0); 330 waitfor(); 331 if (gp->argc0 == 0) 332 error(gettext("No match")); 333 } 334 335 /* 336 * Scan genbuf for shell metacharacters. 337 * Set is union of v7 shell and csh metas. 338 */ 339 int 340 gscan(void) 341 { 342 unsigned char *cp; 343 int len; 344 345 for (cp = genbuf; *cp; cp += len) { 346 if (any(*cp, "~{[*?$`'\"\\")) 347 return (1); 348 if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0) 349 len = 1; 350 } 351 return (0); 352 } 353 354 /* 355 * Parse one filename into file. 356 */ 357 struct glob G; 358 void 359 getone(void) 360 { 361 unsigned char *str; 362 363 if (getargs() == 0) 364 error(gettext("Missing filename")); 365 glob(&G); 366 if (G.argc0 > 1) 367 error(value(vi_TERSE) ? gettext("Ambiguous") : 368 gettext("Too many file names")); 369 if (G.argc0 < 1) 370 error(gettext("Missing filename")); 371 str = G.argv[G.argc0 - 1]; 372 if (strlen(str) > FNSIZE - 4) 373 error(gettext("Filename too long")); 374 CP(file, str); 375 } 376 377 /* 378 * Read a file from the world. 379 * C is command, 'e' if this really an edit (or a recover). 380 */ 381 void 382 rop(int c) 383 { 384 int i; 385 struct stat64 stbuf; 386 short magic; 387 static int ovro; /* old value(vi_READONLY) */ 388 static int denied; /* 1 if READONLY was set due to file permissions */ 389 390 io = open(file, 0); 391 if (io < 0) { 392 if (c == 'e' && errno == ENOENT) { 393 edited++; 394 /* 395 * If the user just did "ex foo" they're probably 396 * creating a new file. Don't be an error, since 397 * this is ugly, and it messes up the + option. 398 */ 399 if (!seenprompt) { 400 viprintf(gettext(" [New file]")); 401 noonl(); 402 return; 403 } 404 } 405 406 if (value(vi_READONLY) && denied) { 407 value(vi_READONLY) = ovro; 408 denied = 0; 409 } 410 syserror(0); 411 } 412 if (fstat64(io, &stbuf)) 413 syserror(0); 414 switch (FTYPE(stbuf) & S_IFMT) { 415 416 case S_IFBLK: 417 error(gettext(" Block special file")); 418 /* FALLTHROUGH */ 419 420 case S_IFCHR: 421 if (isatty(io)) 422 error(gettext(" Teletype")); 423 if (samei(&stbuf, "/dev/null")) 424 break; 425 error(gettext(" Character special file")); 426 /* FALLTHROUGH */ 427 428 case S_IFDIR: 429 error(gettext(" Directory")); 430 431 } 432 if (c != 'r') { 433 if (value(vi_READONLY) && denied) { 434 value(vi_READONLY) = ovro; 435 denied = 0; 436 } 437 if ((FMODE(stbuf) & 0222) == 0 || access((char *)file, 2) < 0) { 438 ovro = value(vi_READONLY); 439 denied = 1; 440 value(vi_READONLY) = 1; 441 } 442 } 443 if (hush == 0 && value(vi_READONLY)) { 444 viprintf(gettext(" [Read only]")); 445 flush(); 446 } 447 if (c == 'r') 448 setdot(); 449 else 450 setall(); 451 452 /* If it is a read command, then we must set dot to addr1 453 * (value of N in :Nr ). In the default case, addr1 will 454 * already be set to dot. 455 * 456 * Next, it is necessary to mark the beginning (undap1) and 457 * ending (undap2) addresses affected (for undo). Note that 458 * rop2() and rop3() will adjust the value of undap2. 459 */ 460 if (FIXUNDO && inopen && c == 'r') { 461 dot = addr1; 462 undap1 = undap2 = dot + 1; 463 } 464 rop2(); 465 rop3(c); 466 } 467 468 void 469 rop2(void) 470 { 471 line *first, *last, *a; 472 473 deletenone(); 474 clrstats(); 475 first = addr2 + 1; 476 (void)append(getfile, addr2); 477 last = dot; 478 if (value(vi_MODELINES)) 479 for (a=first; a<=last; a++) { 480 if (a==first+5 && last-first > 10) 481 a = last - 4; 482 getaline(*a); 483 chkmdln(linebuf); 484 } 485 } 486 487 void 488 rop3(int c) 489 { 490 491 if (iostats() == 0 && c == 'e') 492 edited++; 493 if (c == 'e') { 494 if (wasalt || firstpat) { 495 line *addr = zero + oldadot; 496 497 if (addr > dol) 498 addr = dol; 499 if (firstpat) { 500 globp = (*firstpat) ? firstpat : (unsigned char *)"$"; 501 commands(1,1); 502 firstpat = 0; 503 } else if (addr >= one) { 504 if (inopen) 505 dot = addr; 506 markpr(addr); 507 } else 508 goto other; 509 } else 510 other: 511 if (dol > zero) { 512 if (inopen) 513 dot = one; 514 markpr(one); 515 } 516 if(FIXUNDO) 517 undkind = UNDNONE; 518 if (inopen) { 519 vcline = 0; 520 vreplace(0, lines, lineDOL()); 521 } 522 } 523 if (laste) { 524 #ifdef VMUNIX 525 tlaste(); 526 #endif 527 laste = 0; 528 sync(); 529 } 530 } 531 532 /* 533 * Are these two really the same inode? 534 */ 535 int 536 samei(struct stat64 *sp, unsigned char *cp) 537 { 538 struct stat64 stb; 539 540 if (stat64((char *)cp, &stb) < 0) 541 return (0); 542 return (IDENTICAL((*sp), stb)); 543 } 544 545 /* Returns from edited() */ 546 #define EDF 0 /* Edited file */ 547 #define NOTEDF -1 /* Not edited file */ 548 #define PARTBUF 1 /* Write of partial buffer to Edited file */ 549 550 /* 551 * Write a file. 552 */ 553 void 554 wop(dofname) 555 bool dofname; /* if 1 call filename, else use savedfile */ 556 { 557 int c, exclam, nonexist; 558 line *saddr1, *saddr2; 559 struct stat64 stbuf; 560 char *messagep; 561 562 c = 0; 563 exclam = 0; 564 if (dofname) { 565 if (peekchar() == '!') 566 exclam++, ignchar(); 567 (void)skipwh(); 568 while (peekchar() == '>') 569 ignchar(), c++, (void)skipwh(); 570 if (c != 0 && c != 2) 571 error(gettext("Write forms are 'w' and 'w>>'")); 572 filename('w'); 573 } else { 574 if (savedfile[0] == 0) 575 error(value(vi_TERSE) ? gettext("No file") : 576 gettext("No current filename")); 577 saddr1=addr1; 578 saddr2=addr2; 579 addr1=one; 580 addr2=dol; 581 CP(file, savedfile); 582 if (inopen) { 583 vclrech(0); 584 splitw++; 585 } 586 lprintf("\"%s\"", file); 587 } 588 nonexist = stat64((char *)file, &stbuf); 589 switch (c) { 590 591 case 0: 592 if (!exclam && (!value(vi_WRITEANY) || value(vi_READONLY))) 593 switch (edfile()) { 594 595 case NOTEDF: 596 if (nonexist) 597 break; 598 if (ISCHR(stbuf)) { 599 if (samei(&stbuf, (unsigned char *)"/dev/null")) 600 break; 601 if (samei(&stbuf, (unsigned char *)"/dev/tty")) 602 break; 603 } 604 io = open(file, 1); 605 if (io < 0) 606 syserror(0); 607 if (!isatty(io)) 608 serror(value(vi_TERSE) ? 609 (unsigned char *)gettext(" File exists") : 610 (unsigned char *)gettext(" File exists - use \"w! %s\" to overwrite"), 611 file); 612 close(io); 613 break; 614 615 case EDF: 616 if (value(vi_READONLY)) 617 error(gettext(" File is read only")); 618 break; 619 620 case PARTBUF: 621 if (value(vi_READONLY)) 622 error(gettext(" File is read only")); 623 error(gettext(" Use \"w!\" to write partial buffer")); 624 } 625 cre: 626 /* 627 synctmp(); 628 */ 629 io = creat(file, 0666); 630 if (io < 0) 631 syserror(0); 632 writing = 1; 633 if (hush == 0) 634 if (nonexist) 635 viprintf(gettext(" [New file]")); 636 else if (value(vi_WRITEANY) && edfile() != EDF) 637 viprintf(gettext(" [Existing file]")); 638 break; 639 640 case 2: 641 io = open(file, 1); 642 if (io < 0) { 643 if (exclam || value(vi_WRITEANY)) 644 goto cre; 645 syserror(0); 646 } 647 lseek(io, 0l, 2); 648 break; 649 } 650 if (write_quit && inopen && (argc == 0 || morargc == argc)) 651 setty(normf); 652 putfile(0); 653 if (fsync(io) < 0) { 654 /* 655 * For NFS files write in putfile doesn't return error, but 656 * fsync does. So, catch it here. 657 */ 658 messagep = (char *)gettext( 659 "\r\nYour file has been preserved\r\n"); 660 (void) preserve(); 661 write(1, messagep, strlen(messagep)); 662 663 wrerror(); 664 } 665 (void)iostats(); 666 if (c != 2 && addr1 == one && addr2 == dol) { 667 if (eq(file, savedfile)) 668 edited = 1; 669 sync(); 670 } 671 if (!dofname) { 672 addr1 = saddr1; 673 addr2 = saddr2; 674 } 675 writing = 0; 676 } 677 678 /* 679 * Is file the edited file? 680 * Work here is that it is not considered edited 681 * if this is a partial buffer, and distinguish 682 * all cases. 683 */ 684 int 685 edfile(void) 686 { 687 688 if (!edited || !eq(file, savedfile)) 689 return (NOTEDF); 690 return (addr1 == one && addr2 == dol ? EDF : PARTBUF); 691 } 692 693 /* 694 * Extract the next line from the io stream. 695 */ 696 unsigned char *nextip; 697 698 int 699 getfile(void) 700 { 701 short c; 702 unsigned char *lp; 703 unsigned char *fp; 704 705 lp = linebuf; 706 fp = nextip; 707 do { 708 if (--ninbuf < 0) { 709 ninbuf = read(io, genbuf, LBSIZE) - 1; 710 if (ninbuf < 0) { 711 if (lp != linebuf) { 712 lp++; 713 viprintf( 714 gettext(" [Incomplete last line]")); 715 break; 716 } 717 return (EOF); 718 } 719 if(crflag == -1) { 720 if(isencrypt(genbuf, ninbuf + 1)) 721 crflag = 2; 722 else 723 crflag = -2; 724 } 725 if (crflag > 0 && run_crypt(cntch, genbuf, ninbuf+1, perm) == -1) { 726 smerror(gettext("Cannot decrypt block of text\n")); 727 break; 728 } 729 fp = genbuf; 730 cntch += ninbuf+1; 731 } 732 if (lp >= &linebuf[LBSIZE]) { 733 error(gettext(" Line too long")); 734 } 735 c = *fp++; 736 if (c == 0) { 737 cntnull++; 738 continue; 739 } 740 *lp++ = c; 741 } while (c != '\n'); 742 *--lp = 0; 743 nextip = fp; 744 cntln++; 745 return (0); 746 } 747 748 /* 749 * Write a range onto the io stream. 750 */ 751 void 752 putfile(int isfilter) 753 { 754 line *a1; 755 unsigned char *lp; 756 unsigned char *fp; 757 int nib; 758 bool ochng = chng; 759 char *messagep; 760 761 chng = 1; /* set to force file recovery procedures in */ 762 /* the event of an interrupt during write */ 763 a1 = addr1; 764 clrstats(); 765 cntln = addr2 - a1 + 1; 766 nib = BUFSIZE; 767 fp = genbuf; 768 do { 769 getaline(*a1++); 770 lp = linebuf; 771 for (;;) { 772 if (--nib < 0) { 773 nib = fp - genbuf; 774 if(kflag && !isfilter) 775 if (run_crypt(cntch, genbuf, nib, perm) == -1) 776 wrerror(); 777 if (write(io, genbuf, nib) != nib) { 778 messagep = (char *)gettext( 779 "\r\nYour file has been preserved\r\n"); 780 (void) preserve(); 781 write(1, messagep, strlen(messagep)); 782 783 if (!isfilter) 784 wrerror(); 785 return; 786 } 787 cntch += nib; 788 nib = BUFSIZE - 1; 789 fp = genbuf; 790 } 791 if ((*fp++ = *lp++) == 0) { 792 fp[-1] = '\n'; 793 break; 794 } 795 } 796 } while (a1 <= addr2); 797 nib = fp - genbuf; 798 if(kflag && !isfilter) 799 if (run_crypt(cntch, genbuf, nib, perm) == -1) 800 wrerror(); 801 if ((cntch == 0) && (nib == 1)) { 802 cntln = 0; 803 return; 804 } 805 if (write(io, genbuf, nib) != nib) { 806 messagep = (char *)gettext( 807 "\r\nYour file has been preserved\r\n"); 808 (void) preserve(); 809 write(1, messagep, strlen(messagep)); 810 811 if(!isfilter) 812 wrerror(); 813 return; 814 } 815 cntch += nib; 816 chng = ochng; /* reset chng to original value */ 817 } 818 819 /* 820 * A write error has occurred; if the file being written was 821 * the edited file then we consider it to have changed since it is 822 * now likely scrambled. 823 */ 824 void 825 wrerror(void) 826 { 827 828 if (eq(file, savedfile) && edited) 829 change(); 830 syserror(1); 831 } 832 833 /* 834 * Source command, handles nested sources. 835 * Traps errors since it mungs unit 0 during the source. 836 */ 837 short slevel; 838 short ttyindes; 839 840 void 841 source(fil, okfail) 842 unsigned char *fil; 843 bool okfail; 844 { 845 jmp_buf osetexit; 846 int saveinp, ointty, oerrno; 847 unsigned char *saveglobp; 848 short savepeekc; 849 850 signal(SIGINT, SIG_IGN); 851 saveinp = dup(0); 852 savepeekc = peekc; 853 saveglobp = globp; 854 peekc = 0; globp = 0; 855 if (saveinp < 0) 856 error(gettext("Too many nested sources")); 857 if (slevel <= 0) 858 ttyindes = saveinp; 859 close(0); 860 if (open(fil, 0) < 0) { 861 oerrno = errno; 862 setrupt(); 863 dup(saveinp); 864 close(saveinp); 865 errno = oerrno; 866 if (!okfail) 867 filioerr(fil); 868 return; 869 } 870 slevel++; 871 ointty = intty; 872 intty = isatty(0); 873 oprompt = value(vi_PROMPT); 874 value(vi_PROMPT) &= intty; 875 getexit(osetexit); 876 setrupt(); 877 if (setexit() == 0) 878 commands(1, 1); 879 else if (slevel > 1) { 880 close(0); 881 dup(saveinp); 882 close(saveinp); 883 slevel--; 884 resexit(osetexit); 885 reset(); 886 } 887 intty = ointty; 888 value(vi_PROMPT) = oprompt; 889 close(0); 890 dup(saveinp); 891 close(saveinp); 892 globp = saveglobp; 893 peekc = savepeekc; 894 slevel--; 895 resexit(osetexit); 896 } 897 898 /* 899 * Clear io statistics before a read or write. 900 */ 901 void 902 clrstats(void) 903 { 904 905 ninbuf = 0; 906 cntch = 0; 907 cntln = 0; 908 cntnull = 0; 909 cntodd = 0; 910 } 911 912 /* 913 * Io is finished, close the unit and print statistics. 914 */ 915 int 916 iostats(void) 917 { 918 919 close(io); 920 io = -1; 921 if (hush == 0) { 922 if (value(vi_TERSE)) 923 viprintf(" %d/%D", cntln, cntch); 924 else if (cntln == 1 && cntch == 1) { 925 viprintf(gettext(" 1 line, 1 character")); 926 } else if (cntln == 1 && cntch != 1) { 927 viprintf(gettext(" 1 line, %D characters"), cntch); 928 } else if (cntln != 1 && cntch != 1) { 929 /* 930 * TRANSLATION_NOTE 931 * Reference order of arguments must not 932 * be changed using '%digit$', since vi's 933 * viprintf() does not support it. 934 */ 935 viprintf(gettext(" %d lines, %D characters"), cntln, 936 cntch); 937 } else { 938 /* ridiculous */ 939 viprintf(gettext(" %d lines, 1 character"), cntln); 940 } 941 if (cntnull || cntodd) { 942 viprintf(" ("); 943 if (cntnull) { 944 viprintf(gettext("%D null"), cntnull); 945 if (cntodd) 946 viprintf(", "); 947 } 948 if (cntodd) 949 viprintf(gettext("%D non-ASCII"), cntodd); 950 putchar(')'); 951 } 952 noonl(); 953 flush(); 954 } 955 return (cntnull != 0 || cntodd != 0); 956 } 957 958 959 static void chkmdln(aline) 960 unsigned char *aline; 961 { 962 unsigned char *beg, *end; 963 unsigned char cmdbuf[1024]; 964 char *strchr(), *strrchr(); 965 bool savetty; 966 int savepeekc; 967 int savechng; 968 unsigned char *savefirstpat; 969 unsigned char *p; 970 int len; 971 972 beg = (unsigned char *)strchr((char *)aline, ':'); 973 if (beg == NULL) 974 return; 975 if ((len = beg - aline) < 2) 976 return; 977 978 if ((beg - aline) != 2) { 979 if ((p = beg - ((unsigned int)MB_CUR_MAX * 2) - 2) < aline) 980 p = aline; 981 for ( ; p < (beg - 2); p += len) { 982 if ((len = mblen((char *)p, MB_CUR_MAX)) <= 0) 983 len = 1; 984 } 985 if (p != (beg - 2)) 986 return; 987 } 988 989 if (!((beg[-2] == 'e' && p[-1] == 'x') 990 || (beg[-2] == 'v' && beg[-1] == 'i'))) 991 return; 992 993 strncpy(cmdbuf, beg+1, sizeof cmdbuf); 994 end = (unsigned char *)strrchr(cmdbuf, ':'); 995 if (end == NULL) 996 return; 997 *end = 0; 998 globp = cmdbuf; 999 savepeekc = peekc; 1000 peekc = 0; 1001 savetty = intty; 1002 intty = 0; 1003 savechng = chng; 1004 savefirstpat = firstpat; 1005 firstpat = (unsigned char *)""; 1006 commands(1, 1); 1007 peekc = savepeekc; 1008 globp = 0; 1009 intty = savetty; 1010 /* chng being increased indicates that text was changed */ 1011 if (savechng < chng) 1012 laste = 0; 1013 firstpat = savefirstpat; 1014 } 1015