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
filename(int comm)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
getargs(void)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
glob(struct glob * gp)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
gscan(void)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
getone(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
rop(int c)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
rop2(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
rop3(int c)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
samei(struct stat64 * sp,unsigned char * cp)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
wop(dofname)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
edfile(void)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
getfile(void)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
putfile(int isfilter)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
wrerror(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
source(fil,okfail)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
clrstats(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
iostats(void)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
chkmdln(aline)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