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