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 /* fall into ... */ 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 /* fall into... */ 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 samef: 375 CP(file, str); 376 } 377 378 /* 379 * Read a file from the world. 380 * C is command, 'e' if this really an edit (or a recover). 381 */ 382 void 383 rop(int c) 384 { 385 int i; 386 struct stat64 stbuf; 387 short magic; 388 static int ovro; /* old value(vi_READONLY) */ 389 static int denied; /* 1 if READONLY was set due to file permissions */ 390 391 io = open(file, 0); 392 if (io < 0) { 393 if (c == 'e' && errno == ENOENT) { 394 edited++; 395 /* 396 * If the user just did "ex foo" they're probably 397 * creating a new file. Don't be an error, since 398 * this is ugly, and it messes up the + option. 399 */ 400 if (!seenprompt) { 401 viprintf(gettext(" [New file]")); 402 noonl(); 403 return; 404 } 405 } 406 407 if (value(vi_READONLY) && denied) { 408 value(vi_READONLY) = ovro; 409 denied = 0; 410 } 411 syserror(0); 412 } 413 if (fstat64(io, &stbuf)) 414 syserror(0); 415 switch (FTYPE(stbuf) & S_IFMT) { 416 417 case S_IFBLK: 418 error(gettext(" Block special file")); 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 427 case S_IFDIR: 428 error(gettext(" Directory")); 429 430 } 431 if (c != 'r') { 432 if (value(vi_READONLY) && denied) { 433 value(vi_READONLY) = ovro; 434 denied = 0; 435 } 436 if ((FMODE(stbuf) & 0222) == 0 || access((char *)file, 2) < 0) { 437 ovro = value(vi_READONLY); 438 denied = 1; 439 value(vi_READONLY) = 1; 440 } 441 } 442 if (hush == 0 && value(vi_READONLY)) { 443 viprintf(gettext(" [Read only]")); 444 flush(); 445 } 446 if (c == 'r') 447 setdot(); 448 else 449 setall(); 450 451 /* If it is a read command, then we must set dot to addr1 452 * (value of N in :Nr ). In the default case, addr1 will 453 * already be set to dot. 454 * 455 * Next, it is necessary to mark the beginning (undap1) and 456 * ending (undap2) addresses affected (for undo). Note that 457 * rop2() and rop3() will adjust the value of undap2. 458 */ 459 if (FIXUNDO && inopen && c == 'r') { 460 dot = addr1; 461 undap1 = undap2 = dot + 1; 462 } 463 rop2(); 464 rop3(c); 465 } 466 467 void 468 rop2(void) 469 { 470 line *first, *last, *a; 471 472 deletenone(); 473 clrstats(); 474 first = addr2 + 1; 475 (void)append(getfile, addr2); 476 last = dot; 477 if (value(vi_MODELINES)) 478 for (a=first; a<=last; a++) { 479 if (a==first+5 && last-first > 10) 480 a = last - 4; 481 getaline(*a); 482 chkmdln(linebuf); 483 } 484 } 485 486 void 487 rop3(int c) 488 { 489 490 if (iostats() == 0 && c == 'e') 491 edited++; 492 if (c == 'e') { 493 if (wasalt || firstpat) { 494 line *addr = zero + oldadot; 495 496 if (addr > dol) 497 addr = dol; 498 if (firstpat) { 499 globp = (*firstpat) ? firstpat : (unsigned char *)"$"; 500 commands(1,1); 501 firstpat = 0; 502 } else if (addr >= one) { 503 if (inopen) 504 dot = addr; 505 markpr(addr); 506 } else 507 goto other; 508 } else 509 other: 510 if (dol > zero) { 511 if (inopen) 512 dot = one; 513 markpr(one); 514 } 515 if(FIXUNDO) 516 undkind = UNDNONE; 517 if (inopen) { 518 vcline = 0; 519 vreplace(0, lines, lineDOL()); 520 } 521 } 522 if (laste) { 523 #ifdef VMUNIX 524 tlaste(); 525 #endif 526 laste = 0; 527 sync(); 528 } 529 } 530 531 /* 532 * Are these two really the same inode? 533 */ 534 int 535 samei(struct stat64 *sp, unsigned char *cp) 536 { 537 struct stat64 stb; 538 539 if (stat64((char *)cp, &stb) < 0) 540 return (0); 541 return (IDENTICAL((*sp), stb)); 542 } 543 544 /* Returns from edited() */ 545 #define EDF 0 /* Edited file */ 546 #define NOTEDF -1 /* Not edited file */ 547 #define PARTBUF 1 /* Write of partial buffer to Edited file */ 548 549 /* 550 * Write a file. 551 */ 552 void 553 wop(dofname) 554 bool dofname; /* if 1 call filename, else use savedfile */ 555 { 556 int c, exclam, nonexist; 557 line *saddr1, *saddr2; 558 struct stat64 stbuf; 559 char *messagep; 560 561 c = 0; 562 exclam = 0; 563 if (dofname) { 564 if (peekchar() == '!') 565 exclam++, ignchar(); 566 (void)skipwh(); 567 while (peekchar() == '>') 568 ignchar(), c++, (void)skipwh(); 569 if (c != 0 && c != 2) 570 error(gettext("Write forms are 'w' and 'w>>'")); 571 filename('w'); 572 } else { 573 if (savedfile[0] == 0) 574 error(value(vi_TERSE) ? gettext("No file") : 575 gettext("No current filename")); 576 saddr1=addr1; 577 saddr2=addr2; 578 addr1=one; 579 addr2=dol; 580 CP(file, savedfile); 581 if (inopen) { 582 vclrech(0); 583 splitw++; 584 } 585 lprintf("\"%s\"", file); 586 } 587 nonexist = stat64((char *)file, &stbuf); 588 switch (c) { 589 590 case 0: 591 if (!exclam && (!value(vi_WRITEANY) || value(vi_READONLY))) 592 switch (edfile()) { 593 594 case NOTEDF: 595 if (nonexist) 596 break; 597 if (ISCHR(stbuf)) { 598 if (samei(&stbuf, (unsigned char *)"/dev/null")) 599 break; 600 if (samei(&stbuf, (unsigned char *)"/dev/tty")) 601 break; 602 } 603 io = open(file, 1); 604 if (io < 0) 605 syserror(0); 606 if (!isatty(io)) 607 serror(value(vi_TERSE) ? 608 (unsigned char *)gettext(" File exists") : 609 (unsigned char *)gettext(" File exists - use \"w! %s\" to overwrite"), 610 file); 611 close(io); 612 break; 613 614 case EDF: 615 if (value(vi_READONLY)) 616 error(gettext(" File is read only")); 617 break; 618 619 case PARTBUF: 620 if (value(vi_READONLY)) 621 error(gettext(" File is read only")); 622 error(gettext(" Use \"w!\" to write partial buffer")); 623 } 624 cre: 625 /* 626 synctmp(); 627 */ 628 io = creat(file, 0666); 629 if (io < 0) 630 syserror(0); 631 writing = 1; 632 if (hush == 0) 633 if (nonexist) 634 viprintf(gettext(" [New file]")); 635 else if (value(vi_WRITEANY) && edfile() != EDF) 636 viprintf(gettext(" [Existing file]")); 637 break; 638 639 case 2: 640 io = open(file, 1); 641 if (io < 0) { 642 if (exclam || value(vi_WRITEANY)) 643 goto cre; 644 syserror(0); 645 } 646 lseek(io, 0l, 2); 647 break; 648 } 649 if (write_quit && inopen && (argc == 0 || morargc == argc)) 650 setty(normf); 651 putfile(0); 652 if (fsync(io) < 0) { 653 /* 654 * For NFS files write in putfile doesn't return error, but 655 * fsync does. So, catch it here. 656 */ 657 messagep = (char *)gettext( 658 "\r\nYour file has been preserved\r\n"); 659 (void) preserve(); 660 write(1, messagep, strlen(messagep)); 661 662 wrerror(); 663 } 664 (void)iostats(); 665 if (c != 2 && addr1 == one && addr2 == dol) { 666 if (eq(file, savedfile)) 667 edited = 1; 668 sync(); 669 } 670 if (!dofname) { 671 addr1 = saddr1; 672 addr2 = saddr2; 673 } 674 writing = 0; 675 } 676 677 /* 678 * Is file the edited file? 679 * Work here is that it is not considered edited 680 * if this is a partial buffer, and distinguish 681 * all cases. 682 */ 683 int 684 edfile(void) 685 { 686 687 if (!edited || !eq(file, savedfile)) 688 return (NOTEDF); 689 return (addr1 == one && addr2 == dol ? EDF : PARTBUF); 690 } 691 692 /* 693 * Extract the next line from the io stream. 694 */ 695 unsigned char *nextip; 696 697 int 698 getfile(void) 699 { 700 short c; 701 unsigned char *lp; 702 unsigned char *fp; 703 704 lp = linebuf; 705 fp = nextip; 706 do { 707 if (--ninbuf < 0) { 708 ninbuf = read(io, genbuf, LBSIZE) - 1; 709 if (ninbuf < 0) { 710 if (lp != linebuf) { 711 lp++; 712 viprintf( 713 gettext(" [Incomplete last line]")); 714 break; 715 } 716 return (EOF); 717 } 718 if(crflag == -1) { 719 if(isencrypt(genbuf, ninbuf + 1)) 720 crflag = 2; 721 else 722 crflag = -2; 723 } 724 if (crflag > 0 && run_crypt(cntch, genbuf, ninbuf+1, perm) == -1) { 725 smerror(gettext("Cannot decrypt block of text\n")); 726 break; 727 } 728 fp = genbuf; 729 cntch += ninbuf+1; 730 } 731 if (lp >= &linebuf[LBSIZE]) { 732 error(gettext(" Line too long")); 733 } 734 c = *fp++; 735 if (c == 0) { 736 cntnull++; 737 continue; 738 } 739 *lp++ = c; 740 } while (c != '\n'); 741 *--lp = 0; 742 nextip = fp; 743 cntln++; 744 return (0); 745 } 746 747 /* 748 * Write a range onto the io stream. 749 */ 750 void 751 putfile(int isfilter) 752 { 753 line *a1; 754 unsigned char *lp; 755 unsigned char *fp; 756 int nib; 757 bool ochng = chng; 758 char *messagep; 759 760 chng = 1; /* set to force file recovery procedures in */ 761 /* the event of an interrupt during write */ 762 a1 = addr1; 763 clrstats(); 764 cntln = addr2 - a1 + 1; 765 nib = BUFSIZE; 766 fp = genbuf; 767 do { 768 getaline(*a1++); 769 lp = linebuf; 770 for (;;) { 771 if (--nib < 0) { 772 nib = fp - genbuf; 773 if(kflag && !isfilter) 774 if (run_crypt(cntch, genbuf, nib, perm) == -1) 775 wrerror(); 776 if (write(io, genbuf, nib) != nib) { 777 messagep = (char *)gettext( 778 "\r\nYour file has been preserved\r\n"); 779 (void) preserve(); 780 write(1, messagep, strlen(messagep)); 781 782 if (!isfilter) 783 wrerror(); 784 return; 785 } 786 cntch += nib; 787 nib = BUFSIZE - 1; 788 fp = genbuf; 789 } 790 if ((*fp++ = *lp++) == 0) { 791 fp[-1] = '\n'; 792 break; 793 } 794 } 795 } while (a1 <= addr2); 796 nib = fp - genbuf; 797 if(kflag && !isfilter) 798 if (run_crypt(cntch, genbuf, nib, perm) == -1) 799 wrerror(); 800 if ((cntch == 0) && (nib == 1)) { 801 cntln = 0; 802 return; 803 } 804 if (write(io, genbuf, nib) != nib) { 805 messagep = (char *)gettext( 806 "\r\nYour file has been preserved\r\n"); 807 (void) preserve(); 808 write(1, messagep, strlen(messagep)); 809 810 if(!isfilter) 811 wrerror(); 812 return; 813 } 814 cntch += nib; 815 chng = ochng; /* reset chng to original value */ 816 } 817 818 /* 819 * A write error has occurred; if the file being written was 820 * the edited file then we consider it to have changed since it is 821 * now likely scrambled. 822 */ 823 void 824 wrerror(void) 825 { 826 827 if (eq(file, savedfile) && edited) 828 change(); 829 syserror(1); 830 } 831 832 /* 833 * Source command, handles nested sources. 834 * Traps errors since it mungs unit 0 during the source. 835 */ 836 short slevel; 837 short ttyindes; 838 839 void 840 source(fil, okfail) 841 unsigned char *fil; 842 bool okfail; 843 { 844 jmp_buf osetexit; 845 int saveinp, ointty, oerrno; 846 unsigned char *saveglobp; 847 short savepeekc; 848 849 signal(SIGINT, SIG_IGN); 850 saveinp = dup(0); 851 savepeekc = peekc; 852 saveglobp = globp; 853 peekc = 0; globp = 0; 854 if (saveinp < 0) 855 error(gettext("Too many nested sources")); 856 if (slevel <= 0) 857 ttyindes = saveinp; 858 close(0); 859 if (open(fil, 0) < 0) { 860 oerrno = errno; 861 setrupt(); 862 dup(saveinp); 863 close(saveinp); 864 errno = oerrno; 865 if (!okfail) 866 filioerr(fil); 867 return; 868 } 869 slevel++; 870 ointty = intty; 871 intty = isatty(0); 872 oprompt = value(vi_PROMPT); 873 value(vi_PROMPT) &= intty; 874 getexit(osetexit); 875 setrupt(); 876 if (setexit() == 0) 877 commands(1, 1); 878 else if (slevel > 1) { 879 close(0); 880 dup(saveinp); 881 close(saveinp); 882 slevel--; 883 resexit(osetexit); 884 reset(); 885 } 886 intty = ointty; 887 value(vi_PROMPT) = oprompt; 888 close(0); 889 dup(saveinp); 890 close(saveinp); 891 globp = saveglobp; 892 peekc = savepeekc; 893 slevel--; 894 resexit(osetexit); 895 } 896 897 /* 898 * Clear io statistics before a read or write. 899 */ 900 void 901 clrstats(void) 902 { 903 904 ninbuf = 0; 905 cntch = 0; 906 cntln = 0; 907 cntnull = 0; 908 cntodd = 0; 909 } 910 911 /* 912 * Io is finished, close the unit and print statistics. 913 */ 914 int 915 iostats(void) 916 { 917 918 close(io); 919 io = -1; 920 if (hush == 0) { 921 if (value(vi_TERSE)) 922 viprintf(" %d/%D", cntln, cntch); 923 else if (cntln == 1 && cntch == 1) { 924 viprintf(gettext(" 1 line, 1 character")); 925 } else if (cntln == 1 && cntch != 1) { 926 viprintf(gettext(" 1 line, %D characters"), cntch); 927 } else if (cntln != 1 && cntch != 1) { 928 /* 929 * TRANSLATION_NOTE 930 * Reference order of arguments must not 931 * be changed using '%digit$', since vi's 932 * viprintf() does not support it. 933 */ 934 viprintf(gettext(" %d lines, %D characters"), cntln, 935 cntch); 936 } else { 937 /* ridiculous */ 938 viprintf(gettext(" %d lines, 1 character"), cntln); 939 } 940 if (cntnull || cntodd) { 941 viprintf(" ("); 942 if (cntnull) { 943 viprintf(gettext("%D null"), cntnull); 944 if (cntodd) 945 viprintf(", "); 946 } 947 if (cntodd) 948 viprintf(gettext("%D non-ASCII"), cntodd); 949 putchar(')'); 950 } 951 noonl(); 952 flush(); 953 } 954 return (cntnull != 0 || cntodd != 0); 955 } 956 957 958 static void chkmdln(aline) 959 unsigned char *aline; 960 { 961 unsigned char *beg, *end; 962 unsigned char cmdbuf[1024]; 963 char *strchr(), *strrchr(); 964 bool savetty; 965 int savepeekc; 966 int savechng; 967 unsigned char *savefirstpat; 968 unsigned char *p; 969 int len; 970 971 beg = (unsigned char *)strchr((char *)aline, ':'); 972 if (beg == NULL) 973 return; 974 if ((len = beg - aline) < 2) 975 return; 976 977 if ((beg - aline) != 2) { 978 if ((p = beg - ((unsigned int)MB_CUR_MAX * 2) - 2) < aline) 979 p = aline; 980 for ( ; p < (beg - 2); p += len) { 981 if ((len = mblen((char *)p, MB_CUR_MAX)) <= 0) 982 len = 1; 983 } 984 if (p != (beg - 2)) 985 return; 986 } 987 988 if (!((beg[-2] == 'e' && p[-1] == 'x') 989 || (beg[-2] == 'v' && beg[-1] == 'i'))) 990 return; 991 992 strncpy(cmdbuf, beg+1, sizeof cmdbuf); 993 end = (unsigned char *)strrchr(cmdbuf, ':'); 994 if (end == NULL) 995 return; 996 *end = 0; 997 globp = cmdbuf; 998 savepeekc = peekc; 999 peekc = 0; 1000 savetty = intty; 1001 intty = 0; 1002 savechng = chng; 1003 savefirstpat = firstpat; 1004 firstpat = (unsigned char *)""; 1005 commands(1, 1); 1006 peekc = savepeekc; 1007 globp = 0; 1008 intty = savetty; 1009 /* chng being increased indicates that text was changed */ 1010 if (savechng < chng) 1011 laste = 0; 1012 firstpat = savefirstpat; 1013 } 1014