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 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 23 /* All Rights Reserved */ 24 25 26 /* Copyright (c) 1981 Regents of the University of California */ 27 28 /* 29 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 30 * Use is subject to license terms. 31 */ 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 42 /* 43 * File input/output, source, preserve and recover 44 */ 45 46 /* 47 * Following remember where . was in the previous file for return 48 * on file switching. 49 */ 50 int altdot; 51 int oldadot; 52 bool wasalt; 53 short isalt; 54 55 long cntch; /* Count of characters on unit io */ 56 #ifndef VMUNIX 57 short cntln; /* Count of lines " */ 58 #else 59 int cntln; 60 #endif 61 long cntnull; /* Count of nulls " */ 62 long cntodd; /* Count of non-ascii characters " */ 63 64 static void chkmdln(); 65 extern char getchar(); 66 67 /* 68 * Parse file name for command encoded by comm. 69 * If comm is E then command is doomed and we are 70 * parsing just so user won't have to retype the name. 71 */ 72 filename(comm) 73 int comm; 74 { 75 register int c = comm, d; 76 register int i; 77 78 d = getchar(); 79 if (endcmd(d)) { 80 if (savedfile[0] == 0 && comm != 'f') 81 error(value(vi_TERSE) ? gettext("No file") : 82 gettext("No current filename")); 83 CP(file, savedfile); 84 wasalt = (isalt > 0) ? isalt-1 : 0; 85 isalt = 0; 86 oldadot = altdot; 87 if (c == 'e' || c == 'E') 88 altdot = lineDOT(); 89 if (d == EOF) 90 ungetchar(d); 91 } else { 92 ungetchar(d); 93 getone(); 94 eol(); 95 if (savedfile[0] == 0 && c != 'E' && c != 'e') { 96 c = 'e'; 97 edited = 0; 98 } 99 wasalt = strcmp(file, altfile) == 0; 100 oldadot = altdot; 101 switch (c) { 102 103 case 'f': 104 edited = 0; 105 /* fall into ... */ 106 107 case 'e': 108 if (savedfile[0]) { 109 altdot = lineDOT(); 110 CP(altfile, savedfile); 111 } 112 CP(savedfile, file); 113 break; 114 115 default: 116 if (file[0]) { 117 if (c != 'E') 118 altdot = lineDOT(); 119 CP(altfile, file); 120 } 121 break; 122 } 123 } 124 if (hush && comm != 'f' || comm == 'E') 125 return; 126 if (file[0] != 0) { 127 lprintf("\"%s\"", file); 128 if (comm == 'f') { 129 if (value(vi_READONLY)) 130 printf(gettext(" [Read only]")); 131 if (!edited) 132 printf(gettext(" [Not edited]")); 133 if (tchng) 134 printf(gettext(" [Modified]")); 135 } 136 flush(); 137 } else 138 printf(gettext("No file ")); 139 if (comm == 'f') { 140 if (!(i = lineDOL())) 141 i++; 142 /* 143 * TRANSLATION_NOTE 144 * Reference order of arguments must not 145 * be changed using '%digit$', since vi's 146 * printf() does not support it. 147 */ 148 printf(gettext(" line %d of %d --%ld%%--"), lineDOT(), lineDOL(), 149 (long) 100 * lineDOT() / i); 150 } 151 } 152 153 /* 154 * Get the argument words for a command into genbuf 155 * expanding # and %. 156 */ 157 getargs() 158 { 159 register int c; 160 register 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 glob(gp) 259 struct glob *gp; 260 { 261 int pvec[2]; 262 register unsigned char **argv = gp->argv; 263 register unsigned char *cp = gp->argspac; 264 register int c; 265 unsigned char ch; 266 int nleft = NCARGS; 267 268 gp->argc0 = 0; 269 if (gscan() == 0) { 270 register 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(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 gscan() 340 { 341 register 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 getone() 358 { 359 register unsigned char *str; 360 361 if (getargs() == 0) 362 error(gettext("Missing filename")); 363 glob(&G); 364 if (G.argc0 > 1) 365 error(value(vi_TERSE) ? gettext("Ambiguous") : 366 gettext("Too many file names")); 367 if (G.argc0 < 1) 368 error(gettext("Missing filename")); 369 str = G.argv[G.argc0 - 1]; 370 if (strlen(str) > FNSIZE - 4) 371 error(gettext("Filename too long")); 372 samef: 373 CP(file, str); 374 } 375 376 /* 377 * Read a file from the world. 378 * C is command, 'e' if this really an edit (or a recover). 379 */ 380 rop(c) 381 int c; 382 { 383 register int i; 384 struct stat64 stbuf; 385 short magic; 386 static int ovro; /* old value(vi_READONLY) */ 387 static int denied; /* 1 if READONLY was set due to file permissions */ 388 389 io = open(file, 0); 390 if (io < 0) { 391 if (c == 'e' && errno == ENOENT) { 392 edited++; 393 /* 394 * If the user just did "ex foo" he is probably 395 * creating a new file. Don't be an error, since 396 * this is ugly, and it messes up the + option. 397 */ 398 if (!seenprompt) { 399 printf(gettext(" [New file]")); 400 noonl(); 401 return; 402 } 403 } 404 405 if (value(vi_READONLY) && denied) { 406 value(vi_READONLY) = ovro; 407 denied = 0; 408 } 409 syserror(0); 410 } 411 if (fstat64(io, &stbuf)) 412 syserror(0); 413 switch (FTYPE(stbuf) & S_IFMT) { 414 415 case S_IFBLK: 416 error(gettext(" Block special file")); 417 418 case S_IFCHR: 419 if (isatty(io)) 420 error(gettext(" Teletype")); 421 if (samei(&stbuf, "/dev/null")) 422 break; 423 error(gettext(" Character special file")); 424 425 case S_IFDIR: 426 error(gettext(" Directory")); 427 428 } 429 if (c != 'r') { 430 if (value(vi_READONLY) && denied) { 431 value(vi_READONLY) = ovro; 432 denied = 0; 433 } 434 if ((FMODE(stbuf) & 0222) == 0 || access(file, 2) < 0) { 435 ovro = value(vi_READONLY); 436 denied = 1; 437 value(vi_READONLY) = 1; 438 } 439 } 440 if (hush == 0 && value(vi_READONLY)) { 441 printf(gettext(" [Read only]")); 442 flush(); 443 } 444 if (c == 'r') 445 setdot(); 446 else 447 setall(); 448 449 /* If it is a read command, then we must set dot to addr1 450 * (value of N in :Nr ). In the default case, addr1 will 451 * already be set to dot. 452 * 453 * Next, it is necessary to mark the beginning (undap1) and 454 * ending (undap2) addresses affected (for undo). Note that 455 * rop2() and rop3() will adjust the value of undap2. 456 */ 457 if (FIXUNDO && inopen && c == 'r') { 458 dot = addr1; 459 undap1 = undap2 = dot + 1; 460 } 461 rop2(); 462 rop3(c); 463 } 464 465 rop2() 466 { 467 line *first, *last, *a; 468 469 deletenone(); 470 clrstats(); 471 first = addr2 + 1; 472 (void)append(getfile, addr2); 473 last = dot; 474 if (value(vi_MODELINES)) 475 for (a=first; a<=last; a++) { 476 if (a==first+5 && last-first > 10) 477 a = last - 4; 478 getline(*a); 479 chkmdln(linebuf); 480 } 481 } 482 483 rop3(c) 484 int c; 485 { 486 487 if (iostats() == 0 && c == 'e') 488 edited++; 489 if (c == 'e') { 490 if (wasalt || firstpat) { 491 register line *addr = zero + oldadot; 492 493 if (addr > dol) 494 addr = dol; 495 if (firstpat) { 496 globp = (*firstpat) ? firstpat : (unsigned char *)"$"; 497 commands(1,1); 498 firstpat = 0; 499 } else if (addr >= one) { 500 if (inopen) 501 dot = addr; 502 markpr(addr); 503 } else 504 goto other; 505 } else 506 other: 507 if (dol > zero) { 508 if (inopen) 509 dot = one; 510 markpr(one); 511 } 512 if(FIXUNDO) 513 undkind = UNDNONE; 514 if (inopen) { 515 vcline = 0; 516 vreplace(0, lines, lineDOL()); 517 } 518 } 519 if (laste) { 520 #ifdef VMUNIX 521 tlaste(); 522 #endif 523 laste = 0; 524 sync(); 525 } 526 } 527 528 /* 529 * Are these two really the same inode? 530 */ 531 samei(sp, cp) 532 struct stat64 *sp; 533 unsigned char *cp; 534 { 535 struct stat64 stb; 536 537 if (stat64((char *)cp, &stb) < 0) 538 return (0); 539 return (IDENTICAL((*sp), stb)); 540 } 541 542 /* Returns from edited() */ 543 #define EDF 0 /* Edited file */ 544 #define NOTEDF -1 /* Not edited file */ 545 #define PARTBUF 1 /* Write of partial buffer to Edited file */ 546 547 /* 548 * Write a file. 549 */ 550 wop(dofname) 551 bool dofname; /* if 1 call filename, else use savedfile */ 552 { 553 register int c, exclam, nonexist; 554 line *saddr1, *saddr2; 555 struct stat64 stbuf; 556 char *messagep; 557 558 c = 0; 559 exclam = 0; 560 if (dofname) { 561 if (peekchar() == '!') 562 exclam++, ignchar(); 563 (void)skipwh(); 564 while (peekchar() == '>') 565 ignchar(), c++, (void)skipwh(); 566 if (c != 0 && c != 2) 567 error(gettext("Write forms are 'w' and 'w>>'")); 568 filename('w'); 569 } else { 570 if (savedfile[0] == 0) 571 error(value(vi_TERSE) ? gettext("No file") : 572 gettext("No current filename")); 573 saddr1=addr1; 574 saddr2=addr2; 575 addr1=one; 576 addr2=dol; 577 CP(file, savedfile); 578 if (inopen) { 579 vclrech(0); 580 splitw++; 581 } 582 lprintf("\"%s\"", file); 583 } 584 nonexist = stat64((char *)file, &stbuf); 585 switch (c) { 586 587 case 0: 588 if (!exclam && (!value(vi_WRITEANY) || value(vi_READONLY))) 589 switch (edfile()) { 590 591 case NOTEDF: 592 if (nonexist) 593 break; 594 if (ISCHR(stbuf)) { 595 if (samei(&stbuf, "/dev/null")) 596 break; 597 if (samei(&stbuf, "/dev/tty")) 598 break; 599 } 600 io = open(file, 1); 601 if (io < 0) 602 syserror(0); 603 if (!isatty(io)) 604 serror(value(vi_TERSE) ? gettext(" File exists") : 605 gettext(" File exists - use \"w! %s\" to overwrite"), file); 606 close(io); 607 break; 608 609 case EDF: 610 if (value(vi_READONLY)) 611 error(gettext(" File is read only")); 612 break; 613 614 case PARTBUF: 615 if (value(vi_READONLY)) 616 error(gettext(" File is read only")); 617 error(gettext(" Use \"w!\" to write partial buffer")); 618 } 619 cre: 620 /* 621 synctmp(); 622 */ 623 io = creat(file, 0666); 624 if (io < 0) 625 syserror(0); 626 writing = 1; 627 if (hush == 0) 628 if (nonexist) 629 printf(gettext(" [New file]")); 630 else if (value(vi_WRITEANY) && edfile() != EDF) 631 printf(gettext(" [Existing file]")); 632 break; 633 634 case 2: 635 io = open(file, 1); 636 if (io < 0) { 637 if (exclam || value(vi_WRITEANY)) 638 goto cre; 639 syserror(0); 640 } 641 lseek(io, 0l, 2); 642 break; 643 } 644 if (write_quit && inopen && (argc == 0 || morargc == argc)) 645 setty(normf); 646 putfile(0); 647 if (fsync(io) < 0) { 648 /* For NFS files write in putfile doesn't return error, but fsync does. 649 So, catch it here. */ 650 messagep = (char *)gettext 651 ("\r\nYour file has been preserved\r\n"); 652 preserve(); 653 write(1, messagep, strlen(messagep)); 654 655 wrerror(); 656 } 657 (void)iostats(); 658 if (c != 2 && addr1 == one && addr2 == dol) { 659 if (eq(file, savedfile)) 660 edited = 1; 661 sync(); 662 } 663 if (!dofname) { 664 addr1 = saddr1; 665 addr2 = saddr2; 666 } 667 writing = 0; 668 } 669 670 /* 671 * Is file the edited file? 672 * Work here is that it is not considered edited 673 * if this is a partial buffer, and distinguish 674 * all cases. 675 */ 676 edfile() 677 { 678 679 if (!edited || !eq(file, savedfile)) 680 return (NOTEDF); 681 return (addr1 == one && addr2 == dol ? EDF : PARTBUF); 682 } 683 684 /* 685 * Extract the next line from the io stream. 686 */ 687 unsigned char *nextip; 688 689 getfile() 690 { 691 register short c; 692 register unsigned char *lp; 693 register unsigned char *fp; 694 695 lp = linebuf; 696 fp = nextip; 697 do { 698 if (--ninbuf < 0) { 699 ninbuf = read(io, genbuf, LBSIZE) - 1; 700 if (ninbuf < 0) { 701 if (lp != linebuf) { 702 lp++; 703 printf(gettext(" [Incomplete last line]")); 704 break; 705 } 706 return (EOF); 707 } 708 if(crflag == -1) { 709 if(isencrypt(genbuf, ninbuf + 1)) 710 crflag = 2; 711 else 712 crflag = -2; 713 } 714 if (crflag > 0 && run_crypt(cntch, genbuf, ninbuf+1, perm) == -1) { 715 smerror(gettext("Cannot decrypt block of text\n")); 716 break; 717 } 718 fp = genbuf; 719 cntch += ninbuf+1; 720 } 721 if (lp >= &linebuf[LBSIZE]) { 722 error(gettext(" Line too long")); 723 } 724 c = *fp++; 725 if (c == 0) { 726 cntnull++; 727 continue; 728 } 729 *lp++ = c; 730 } while (c != '\n'); 731 *--lp = 0; 732 nextip = fp; 733 cntln++; 734 return (0); 735 } 736 737 /* 738 * Write a range onto the io stream. 739 */ 740 putfile(int isfilter) 741 { 742 line *a1; 743 register unsigned char *lp; 744 register unsigned char *fp; 745 register int nib; 746 register bool ochng = chng; 747 char *messagep; 748 749 chng = 1; /* set to force file recovery procedures in */ 750 /* the event of an interrupt during write */ 751 a1 = addr1; 752 clrstats(); 753 cntln = addr2 - a1 + 1; 754 nib = BUFSIZE; 755 fp = genbuf; 756 do { 757 getline(*a1++); 758 lp = linebuf; 759 for (;;) { 760 if (--nib < 0) { 761 nib = fp - genbuf; 762 if(kflag && !isfilter) 763 if (run_crypt(cntch, genbuf, nib, perm) == -1) 764 wrerror(); 765 if (write(io, genbuf, nib) != nib) { 766 messagep = (char *)gettext 767 ("\r\nYour file has been preserved\r\n"); 768 preserve(); 769 write(1, messagep, strlen(messagep)); 770 771 if(!isfilter) 772 wrerror(); 773 return; 774 } 775 cntch += nib; 776 nib = BUFSIZE - 1; 777 fp = genbuf; 778 } 779 if ((*fp++ = *lp++) == 0) { 780 fp[-1] = '\n'; 781 break; 782 } 783 } 784 } while (a1 <= addr2); 785 nib = fp - genbuf; 786 if(kflag && !isfilter) 787 if (run_crypt(cntch, genbuf, nib, perm) == -1) 788 wrerror(); 789 if ((cntch == 0) && (nib == 1)) { 790 cntln = 0; 791 return; 792 } 793 if (write(io, genbuf, nib) != nib) { 794 messagep = (char *)gettext("\r\nYour file has been preserved\r\n"); 795 preserve(); 796 write(1, messagep, strlen(messagep)); 797 798 if(!isfilter) 799 wrerror(); 800 return; 801 } 802 cntch += nib; 803 chng = ochng; /* reset chng to original value */ 804 } 805 806 /* 807 * A write error has occurred; if the file being written was 808 * the edited file then we consider it to have changed since it is 809 * now likely scrambled. 810 */ 811 wrerror() 812 { 813 814 if (eq(file, savedfile) && edited) 815 change(); 816 syserror(1); 817 } 818 819 /* 820 * Source command, handles nested sources. 821 * Traps errors since it mungs unit 0 during the source. 822 */ 823 short slevel; 824 short ttyindes; 825 826 source(fil, okfail) 827 unsigned char *fil; 828 bool okfail; 829 { 830 jmp_buf osetexit; 831 register int saveinp, ointty, oerrno; 832 unsigned char *saveglobp; 833 short savepeekc; 834 835 signal(SIGINT, SIG_IGN); 836 saveinp = dup(0); 837 savepeekc = peekc; 838 saveglobp = globp; 839 peekc = 0; globp = 0; 840 if (saveinp < 0) 841 error(gettext("Too many nested sources")); 842 if (slevel <= 0) 843 ttyindes = saveinp; 844 close(0); 845 if (open(fil, 0) < 0) { 846 oerrno = errno; 847 setrupt(); 848 dup(saveinp); 849 close(saveinp); 850 errno = oerrno; 851 if (!okfail) 852 filioerr(fil); 853 return; 854 } 855 slevel++; 856 ointty = intty; 857 intty = isatty(0); 858 oprompt = value(vi_PROMPT); 859 value(vi_PROMPT) &= intty; 860 getexit(osetexit); 861 setrupt(); 862 if (setexit() == 0) 863 commands(1, 1); 864 else if (slevel > 1) { 865 close(0); 866 dup(saveinp); 867 close(saveinp); 868 slevel--; 869 resexit(osetexit); 870 reset(); 871 } 872 intty = ointty; 873 value(vi_PROMPT) = oprompt; 874 close(0); 875 dup(saveinp); 876 close(saveinp); 877 globp = saveglobp; 878 peekc = savepeekc; 879 slevel--; 880 resexit(osetexit); 881 } 882 883 /* 884 * Clear io statistics before a read or write. 885 */ 886 clrstats() 887 { 888 889 ninbuf = 0; 890 cntch = 0; 891 cntln = 0; 892 cntnull = 0; 893 cntodd = 0; 894 } 895 896 /* 897 * Io is finished, close the unit and print statistics. 898 */ 899 iostats() 900 { 901 902 close(io); 903 io = -1; 904 if (hush == 0) { 905 if (value(vi_TERSE)) 906 printf(" %d/%D", cntln, cntch); 907 else if (cntln == 1 && cntch == 1) { 908 printf(gettext(" 1 line, 1 character")); 909 } else if (cntln == 1 && cntch != 1) { 910 printf(gettext(" 1 line, %D characters"), cntch); 911 } else if (cntln != 1 && cntch != 1) { 912 /* 913 * TRANSLATION_NOTE 914 * Reference order of arguments must not 915 * be changed using '%digit$', since vi's 916 * printf() does not support it. 917 */ 918 printf(gettext(" %d lines, %D characters"), cntln, 919 cntch); 920 } else { 921 /* ridiculous */ 922 printf(gettext(" %d lines, 1 character"), cntln); 923 } 924 if (cntnull || cntodd) { 925 printf(" ("); 926 if (cntnull) { 927 printf(gettext("%D null"), cntnull); 928 if (cntodd) 929 printf(", "); 930 } 931 if (cntodd) 932 printf(gettext("%D non-ASCII"), cntodd); 933 putchar(')'); 934 } 935 noonl(); 936 flush(); 937 } 938 return (cntnull != 0 || cntodd != 0); 939 } 940 941 942 static void chkmdln(aline) 943 unsigned char *aline; 944 { 945 unsigned char *beg, *end; 946 unsigned char cmdbuf[1024]; 947 char *strchr(), *strrchr(); 948 bool savetty; 949 int savepeekc; 950 int savechng; 951 unsigned char *savefirstpat; 952 unsigned char *p; 953 int len; 954 955 beg = (unsigned char *)strchr((char *)aline, ':'); 956 if (beg == NULL) 957 return; 958 if ((len = beg - aline) < 2) 959 return; 960 961 if ((beg - aline) != 2) { 962 if ((p = beg - ((unsigned int)MB_CUR_MAX * 2) - 2) < aline) 963 p = aline; 964 for ( ; p < (beg - 2); p += len) { 965 if ((len = mblen((char *)p, MB_CUR_MAX)) <= 0) 966 len = 1; 967 } 968 if (p != (beg - 2)) 969 return; 970 } 971 972 if (!((beg[-2] == 'e' && p[-1] == 'x') 973 || (beg[-2] == 'v' && beg[-1] == 'i'))) 974 return; 975 976 strncpy(cmdbuf, beg+1, sizeof cmdbuf); 977 end = (unsigned char *)strrchr(cmdbuf, ':'); 978 if (end == NULL) 979 return; 980 *end = 0; 981 globp = cmdbuf; 982 savepeekc = peekc; 983 peekc = 0; 984 savetty = intty; 985 intty = 0; 986 savechng = chng; 987 savefirstpat = firstpat; 988 firstpat = (unsigned char *)""; 989 commands(1, 1); 990 peekc = savepeekc; 991 globp = 0; 992 intty = savetty; 993 /* chng being increased indicates that text was changed */ 994 if (savechng < chng) 995 laste = 0; 996 firstpat = savefirstpat; 997 } 998