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