document distributed with 4.1BSD
[unix-history] / usr / src / usr.bin / ex / ex_io.c
CommitLineData
19d73a0e
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
70190965 8static char *sccsid = "@(#)ex_io.c 7.11 (Berkeley) %G%";
19d73a0e
DF
9#endif not lint
10
544cd46c
MH
11#include "ex.h"
12#include "ex_argv.h"
13#include "ex_temp.h"
14#include "ex_tty.h"
15#include "ex_vis.h"
16
17/*
d266c416 18 * File input/output, source, preserve and recover
544cd46c
MH
19 */
20
21/*
22 * Following remember where . was in the previous file for return
23 * on file switching.
24 */
44232d5b
MH
25int altdot;
26int oldadot;
544cd46c 27bool wasalt;
d266c416 28short isalt;
544cd46c
MH
29
30long cntch; /* Count of characters on unit io */
44232d5b 31#ifndef VMUNIX
544cd46c 32short cntln; /* Count of lines " */
44232d5b
MH
33#else
34int cntln;
35#endif
544cd46c
MH
36long cntnull; /* Count of nulls " */
37long cntodd; /* Count of non-ascii characters " */
38
39/*
40 * Parse file name for command encoded by comm.
41 * If comm is E then command is doomed and we are
42 * parsing just so user won't have to retype the name.
43 */
44filename(comm)
45 int comm;
46{
47 register int c = comm, d;
48 register int i;
49
50 d = getchar();
51 if (endcmd(d)) {
52 if (savedfile[0] == 0 && comm != 'f')
53 error("No file|No current filename");
54 CP(file, savedfile);
d266c416
MH
55 wasalt = (isalt > 0) ? isalt-1 : 0;
56 isalt = 0;
544cd46c 57 oldadot = altdot;
d266c416
MH
58 if (c == 'e' || c == 'E')
59 altdot = lineDOT();
544cd46c
MH
60 if (d == EOF)
61 ungetchar(d);
62 } else {
63 ungetchar(d);
64 getone();
65 eol();
66 if (savedfile[0] == 0 && c != 'E' && c != 'e') {
67 c = 'e';
68 edited = 0;
69 }
70 wasalt = strcmp(file, altfile) == 0;
71 oldadot = altdot;
72 switch (c) {
73
74 case 'f':
75 edited = 0;
76 /* fall into ... */
77
78 case 'e':
79 if (savedfile[0]) {
80 altdot = lineDOT();
81 CP(altfile, savedfile);
82 }
83 CP(savedfile, file);
84 break;
85
86 default:
87 if (file[0]) {
88 if (c != 'E')
89 altdot = lineDOT();
90 CP(altfile, file);
91 }
92 break;
93 }
94 }
95 if (hush && comm != 'f' || comm == 'E')
96 return;
97 if (file[0] != 0) {
98 lprintf("\"%s\"", file);
99 if (comm == 'f') {
d266c416
MH
100 if (value(READONLY))
101 printf(" [Read only]");
544cd46c
MH
102 if (!edited)
103 printf(" [Not edited]");
104 if (tchng)
105 printf(" [Modified]");
106 }
107 flush();
108 } else
109 printf("No file ");
110 if (comm == 'f') {
111 if (!(i = lineDOL()))
112 i++;
113 printf(" line %d of %d --%ld%%--", lineDOT(), lineDOL(),
114 (long) 100 * lineDOT() / i);
115 }
116}
117
118/*
119 * Get the argument words for a command into genbuf
120 * expanding # and %.
121 */
122getargs()
123{
124 register int c;
125 register char *cp, *fp;
126 static char fpatbuf[32]; /* hence limit on :next +/pat */
127
128 pastwh();
129 if (peekchar() == '+') {
130 for (cp = fpatbuf;;) {
131 c = *cp++ = getchar();
132 if (cp >= &fpatbuf[sizeof(fpatbuf)])
133 error("Pattern too long");
134 if (c == '\\' && isspace(peekchar()))
135 c = getchar();
136 if (c == EOF || isspace(c)) {
137 ungetchar(c);
138 *--cp = 0;
139 firstpat = &fpatbuf[1];
140 break;
141 }
142 }
143 }
144 if (skipend())
145 return (0);
146 CP(genbuf, "echo "); cp = &genbuf[5];
147 for (;;) {
148 c = getchar();
149 if (endcmd(c)) {
150 ungetchar(c);
151 break;
152 }
153 switch (c) {
154
155 case '\\':
d266c416 156 if (any(peekchar(), "#%|"))
544cd46c
MH
157 c = getchar();
158 /* fall into... */
159
160 default:
161 if (cp > &genbuf[LBSIZE - 2])
162flong:
163 error("Argument buffer overflow");
164 *cp++ = c;
165 break;
166
167 case '#':
168 fp = altfile;
169 if (*fp == 0)
170 error("No alternate filename@to substitute for #");
171 goto filexp;
172
173 case '%':
174 fp = savedfile;
175 if (*fp == 0)
176 error("No current filename@to substitute for %%");
177filexp:
178 while (*fp) {
179 if (cp > &genbuf[LBSIZE - 2])
180 goto flong;
181 *cp++ = *fp++;
182 }
183 break;
184 }
185 }
186 *cp = 0;
187 return (1);
188}
189
190/*
191 * Glob the argument words in genbuf, or if no globbing
192 * is implied, just split them up directly.
193 */
194glob(gp)
195 struct glob *gp;
196{
197 int pvec[2];
198 register char **argv = gp->argv;
199 register char *cp = gp->argspac;
200 register int c;
201 char ch;
202 int nleft = NCARGS;
203
204 gp->argc0 = 0;
205 if (gscan() == 0) {
206 register char *v = genbuf + 5; /* strlen("echo ") */
207
208 for (;;) {
209 while (isspace(*v))
210 v++;
211 if (!*v)
212 break;
213 *argv++ = cp;
214 while (*v && !isspace(*v))
215 *cp++ = *v++;
216 *cp++ = 0;
217 gp->argc0++;
218 }
219 *argv = 0;
220 return;
221 }
222 if (pipe(pvec) < 0)
223 error("Can't make pipe to glob");
224 pid = fork();
225 io = pvec[0];
226 if (pid < 0) {
227 close(pvec[1]);
228 error("Can't fork to do glob");
229 }
230 if (pid == 0) {
231 int oerrno;
232
233 close(1);
234 dup(pvec[1]);
235 close(pvec[0]);
887e3e0d
MH
236 close(2); /* so errors don't mess up the screen */
237 open("/dev/null", 1);
544cd46c
MH
238 execl(svalue(SHELL), "sh", "-c", genbuf, 0);
239 oerrno = errno; close(1); dup(2); errno = oerrno;
240 filioerr(svalue(SHELL));
241 }
242 close(pvec[1]);
243 do {
244 *argv = cp;
245 for (;;) {
246 if (read(io, &ch, 1) != 1) {
247 close(io);
248 c = -1;
249 } else
250 c = ch & TRIM;
251 if (c <= 0 || isspace(c))
252 break;
253 *cp++ = c;
254 if (--nleft <= 0)
255 error("Arg list too long");
256 }
257 if (cp != *argv) {
258 --nleft;
259 *cp++ = 0;
260 gp->argc0++;
261 if (gp->argc0 >= NARGS)
262 error("Arg list too long");
263 argv++;
264 }
265 } while (c >= 0);
266 waitfor();
267 if (gp->argc0 == 0)
887e3e0d 268 error("No match");
544cd46c
MH
269}
270
271/*
272 * Scan genbuf for shell metacharacters.
273 * Set is union of v7 shell and csh metas.
274 */
275gscan()
276{
277 register char *cp;
278
279 for (cp = genbuf; *cp; cp++)
280 if (any(*cp, "~{[*?$`'\"\\"))
281 return (1);
282 return (0);
283}
284
285/*
286 * Parse one filename into file.
287 */
3c7b865a 288struct glob G;
544cd46c
MH
289getone()
290{
291 register char *str;
544cd46c
MH
292
293 if (getargs() == 0)
294 error("Missing filename");
295 glob(&G);
296 if (G.argc0 > 1)
297 error("Ambiguous|Too many file names");
298 str = G.argv[G.argc0 - 1];
299 if (strlen(str) > FNSIZE - 4)
300 error("Filename too long");
301samef:
302 CP(file, str);
303}
304
305/*
306 * Read a file from the world.
307 * C is command, 'e' if this really an edit (or a recover).
308 */
309rop(c)
310 int c;
311{
312 register int i;
313 struct stat stbuf;
314 short magic;
dcbc1067
MH
315 static int ovro; /* old value(READONLY) */
316 static int denied; /* 1 if READONLY was set due to file permissions */
544cd46c
MH
317
318 io = open(file, 0);
319 if (io < 0) {
04379bab 320 if (c == 'e' && errno == ENOENT) {
544cd46c 321 edited++;
04379bab
MH
322 /*
323 * If the user just did "ex foo" he is probably
324 * creating a new file. Don't be an error, since
325 * this is ugly, and it screws up the + option.
326 */
327 if (!seenprompt) {
328 printf(" [New file]");
329 noonl();
330 return;
331 }
332 }
544cd46c
MH
333 syserror();
334 }
335 if (fstat(io, &stbuf))
336 syserror();
337 switch (stbuf.st_mode & S_IFMT) {
338
339 case S_IFBLK:
340 error(" Block special file");
341
342 case S_IFCHR:
343 if (isatty(io))
344 error(" Teletype");
345 if (samei(&stbuf, "/dev/null"))
346 break;
347 error(" Character special file");
348
349 case S_IFDIR:
350 error(" Directory");
351
352 case S_IFREG:
1807bf33 353#ifdef CRYPT
d266c416
MH
354 if (xflag)
355 break;
1807bf33 356#endif
544cd46c
MH
357 i = read(io, (char *) &magic, sizeof(magic));
358 lseek(io, 0l, 0);
359 if (i != sizeof(magic))
360 break;
361 switch (magic) {
362
299f2784 363 case 0405: /* data overlay on exec */
f0f2d980
MH
364 case 0407: /* unshared */
365 case 0410: /* shared text */
366 case 0411: /* separate I/D */
367 case 0413: /* VM/Unix demand paged */
368 case 0430: /* PDP-11 Overlay shared */
369 case 0431: /* PDP-11 Overlay sep I/D */
544cd46c
MH
370 error(" Executable");
371
f0f2d980
MH
372 /*
373 * We do not forbid the editing of portable archives
374 * because it is reasonable to edit them, especially
375 * if they are archives of text files. This is
376 * especially useful if you archive source files together
377 * and copy them to another system with ~%take, since
378 * the files sometimes show up munged and must be fixed.
379 */
544cd46c
MH
380 case 0177545:
381 case 0177555:
382 error(" Archive");
383
384 default:
52fd6c9c
MH
385#ifdef mbb
386 /* C/70 has a 10 bit byte */
387 if (magic & 03401600)
388#else
389 /* Everybody else has an 8 bit byte */
544cd46c 390 if (magic & 0100200)
52fd6c9c 391#endif
544cd46c
MH
392 error(" Non-ascii file");
393 break;
394 }
395 }
04379bab
MH
396 if (c != 'r') {
397 if (value(READONLY) && denied) {
398 value(READONLY) = ovro;
399 denied = 0;
400 }
401 if ((stbuf.st_mode & 0222) == 0 || access(file, 2) < 0) {
402 ovro = value(READONLY);
403 denied = 1;
404 value(READONLY) = 1;
405 }
dcbc1067 406 }
fdc664ec 407 if (value(READONLY)) {
718337ed 408 printf(" [Read only]");
fdc664ec
MH
409 flush();
410 }
544cd46c
MH
411 if (c == 'r')
412 setdot();
413 else
414 setall();
887e3e0d 415 if (FIXUNDO && inopen && c == 'r')
544cd46c
MH
416 undap1 = undap2 = dot + 1;
417 rop2();
418 rop3(c);
419}
420
421rop2()
422{
299f2784 423 line *first, *last, *a;
4e3d2679 424 struct stat statb;
544cd46c
MH
425
426 deletenone();
427 clrstats();
299f2784 428 first = addr2 + 1;
4e3d2679
JB
429 if (fstat(io, &statb) < 0)
430 bsize = LBSIZE;
431 else {
432 bsize = statb.st_blksize;
433 if (bsize <= 0)
434 bsize = LBSIZE;
435 }
544cd46c 436 ignore(append(getfile, addr2));
299f2784 437 last = dot;
42a399ac
PK
438 /*
439 * if the modeline variable is set,
440 * check the first and last five lines of the file
441 * for a mode line.
442 */
443 if (value(MODELINE)) {
444 for (a=first; a<=last; a++) {
445 if (a==first+5 && last-first > 10)
446 a = last - 4;
447 getline(*a);
448 checkmodeline(linebuf);
449 }
299f2784 450 }
544cd46c
MH
451}
452
453rop3(c)
454 int c;
455{
456
457 if (iostats() == 0 && c == 'e')
458 edited++;
459 if (c == 'e') {
460 if (wasalt || firstpat) {
461 register line *addr = zero + oldadot;
462
463 if (addr > dol)
464 addr = dol;
465 if (firstpat) {
466 globp = (*firstpat) ? firstpat : "$";
467 commands(1,1);
468 firstpat = 0;
469 } else if (addr >= one) {
470 if (inopen)
471 dot = addr;
472 markpr(addr);
473 } else
474 goto other;
475 } else
476other:
477 if (dol > zero) {
478 if (inopen)
479 dot = one;
480 markpr(one);
481 }
887e3e0d
MH
482 if(FIXUNDO)
483 undkind = UNDNONE;
544cd46c
MH
484 if (inopen) {
485 vcline = 0;
486 vreplace(0, LINES, lineDOL());
487 }
488 }
544cd46c
MH
489}
490
491/*
492 * Are these two really the same inode?
493 */
494samei(sp, cp)
495 struct stat *sp;
496 char *cp;
497{
498 struct stat stb;
499
500 if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev)
501 return (0);
502 return (sp->st_ino == stb.st_ino);
503}
504
505/* Returns from edited() */
506#define EDF 0 /* Edited file */
507#define NOTEDF -1 /* Not edited file */
508#define PARTBUF 1 /* Write of partial buffer to Edited file */
509
510/*
511 * Write a file.
512 */
513wop(dofname)
514bool dofname; /* if 1 call filename, else use savedfile */
515{
516 register int c, exclam, nonexist;
517 line *saddr1, *saddr2;
518 struct stat stbuf;
519
520 c = 0;
521 exclam = 0;
522 if (dofname) {
523 if (peekchar() == '!')
524 exclam++, ignchar();
525 ignore(skipwh());
526 while (peekchar() == '>')
527 ignchar(), c++, ignore(skipwh());
528 if (c != 0 && c != 2)
529 error("Write forms are 'w' and 'w>>'");
530 filename('w');
531 } else {
887e3e0d
MH
532 if (savedfile[0] == 0)
533 error("No file|No current filename");
544cd46c
MH
534 saddr1=addr1;
535 saddr2=addr2;
536 addr1=one;
537 addr2=dol;
538 CP(file, savedfile);
539 if (inopen) {
540 vclrech(0);
541 splitw++;
542 }
543 lprintf("\"%s\"", file);
544 }
545 nonexist = stat(file, &stbuf);
546 switch (c) {
547
548 case 0:
d266c416
MH
549 if (!exclam && (!value(WRITEANY) || value(READONLY)))
550 switch (edfile()) {
544cd46c
MH
551
552 case NOTEDF:
553 if (nonexist)
554 break;
555 if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
556 if (samei(&stbuf, "/dev/null"))
557 break;
558 if (samei(&stbuf, "/dev/tty"))
559 break;
560 }
561 io = open(file, 1);
562 if (io < 0)
563 syserror();
564 if (!isatty(io))
565 serror(" File exists| File exists - use \"w! %s\" to overwrite", file);
566 close(io);
567 break;
568
d266c416
MH
569 case EDF:
570 if (value(READONLY))
571 error(" File is read only");
572 break;
573
544cd46c 574 case PARTBUF:
d266c416
MH
575 if (value(READONLY))
576 error(" File is read only");
544cd46c
MH
577 error(" Use \"w!\" to write partial buffer");
578 }
579cre:
44232d5b 580/*
544cd46c 581 synctmp();
44232d5b 582*/
544cd46c
MH
583#ifdef V6
584 io = creat(file, 0644);
585#else
586 io = creat(file, 0666);
587#endif
588 if (io < 0)
589 syserror();
04379bab 590 writing = 1;
544cd46c
MH
591 if (hush == 0)
592 if (nonexist)
593 printf(" [New file]");
594 else if (value(WRITEANY) && edfile() != EDF)
595 printf(" [Existing file]");
596 break;
597
598 case 2:
599 io = open(file, 1);
600 if (io < 0) {
601 if (exclam || value(WRITEANY))
602 goto cre;
603 syserror();
604 }
605 lseek(io, 0l, 2);
606 break;
607 }
299f2784 608 putfile(0);
544cd46c
MH
609 ignore(iostats());
610 if (c != 2 && addr1 == one && addr2 == dol) {
611 if (eq(file, savedfile))
612 edited = 1;
613 sync();
614 }
615 if (!dofname) {
616 addr1 = saddr1;
617 addr2 = saddr2;
618 }
04379bab 619 writing = 0;
544cd46c
MH
620}
621
622/*
623 * Is file the edited file?
624 * Work here is that it is not considered edited
625 * if this is a partial buffer, and distinguish
626 * all cases.
627 */
628edfile()
629{
630
631 if (!edited || !eq(file, savedfile))
632 return (NOTEDF);
633 return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
634}
635
544cd46c
MH
636/*
637 * Extract the next line from the io stream.
638 */
3c7b865a 639char *nextip;
544cd46c
MH
640
641getfile()
642{
643 register short c;
644 register char *lp, *fp;
645
646 lp = linebuf;
647 fp = nextip;
648 do {
649 if (--ninbuf < 0) {
4e3d2679 650 ninbuf = read(io, genbuf, bsize) - 1;
544cd46c
MH
651 if (ninbuf < 0) {
652 if (lp != linebuf) {
d266c416 653 lp++;
544cd46c
MH
654 printf(" [Incomplete last line]");
655 break;
656 }
657 return (EOF);
658 }
1807bf33 659#ifdef CRYPT
55222bf6
RC
660 if (kflag) {
661 fp = genbuf;
662 while(fp < &genbuf[ninbuf]) {
663 if (*fp++ & 0200) {
d266c416 664 crblock(perm, genbuf, ninbuf+1,
55222bf6
RC
665 cntch);
666 break;
667 }
d266c416
MH
668 }
669 }
1807bf33 670#endif
d266c416
MH
671 fp = genbuf;
672 cntch += ninbuf+1;
544cd46c
MH
673 }
674 if (lp >= &linebuf[LBSIZE]) {
675 error(" Line too long");
676 }
677 c = *fp++;
678 if (c == 0) {
679 cntnull++;
680 continue;
681 }
682 if (c & QUOTE) {
683 cntodd++;
684 c &= TRIM;
685 if (c == 0)
686 continue;
687 }
688 *lp++ = c;
689 } while (c != '\n');
544cd46c
MH
690 *--lp = 0;
691 nextip = fp;
692 cntln++;
693 return (0);
694}
695
696/*
697 * Write a range onto the io stream.
698 */
299f2784
MH
699putfile(isfilter)
700int isfilter;
544cd46c
MH
701{
702 line *a1;
703 register char *fp, *lp;
704 register int nib;
4e3d2679 705 struct stat statb;
544cd46c
MH
706
707 a1 = addr1;
708 clrstats();
709 cntln = addr2 - a1 + 1;
710 if (cntln == 0)
711 return;
4e3d2679
JB
712 if (fstat(io, &statb) < 0)
713 bsize = LBSIZE;
714 else {
715 bsize = statb.st_blksize;
716 if (bsize <= 0)
717 bsize = LBSIZE;
718 }
719 nib = bsize;
544cd46c
MH
720 fp = genbuf;
721 do {
722 getline(*a1++);
723 lp = linebuf;
724 for (;;) {
725 if (--nib < 0) {
726 nib = fp - genbuf;
1807bf33 727#ifdef CRYPT
299f2784 728 if(kflag && !isfilter)
d266c416 729 crblock(perm, genbuf, nib, cntch);
1807bf33 730#endif
544cd46c
MH
731 if (write(io, genbuf, nib) != nib) {
732 wrerror();
733 }
734 cntch += nib;
4e3d2679 735 nib = bsize - 1;
544cd46c
MH
736 fp = genbuf;
737 }
738 if ((*fp++ = *lp++) == 0) {
739 fp[-1] = '\n';
740 break;
741 }
742 }
743 } while (a1 <= addr2);
744 nib = fp - genbuf;
1807bf33 745#ifdef CRYPT
299f2784 746 if(kflag && !isfilter)
d266c416 747 crblock(perm, genbuf, nib, cntch);
1807bf33 748#endif
544cd46c
MH
749 if (write(io, genbuf, nib) != nib) {
750 wrerror();
751 }
752 cntch += nib;
753}
754
755/*
756 * A write error has occurred; if the file being written was
757 * the edited file then we consider it to have changed since it is
758 * now likely scrambled.
759 */
760wrerror()
761{
762
763 if (eq(file, savedfile) && edited)
764 change();
765 syserror();
766}
767
768/*
769 * Source command, handles nested sources.
770 * Traps errors since it mungs unit 0 during the source.
771 */
d266c416
MH
772short slevel;
773short ttyindes;
544cd46c
MH
774
775source(fil, okfail)
776 char *fil;
777 bool okfail;
778{
779 jmp_buf osetexit;
780 register int saveinp, ointty, oerrno;
b8c3ed58
MH
781 char *saveglobp;
782 short savepeekc;
544cd46c
MH
783
784 signal(SIGINT, SIG_IGN);
785 saveinp = dup(0);
04379bab
MH
786 savepeekc = peekc;
787 saveglobp = globp;
788 peekc = 0; globp = 0;
544cd46c
MH
789 if (saveinp < 0)
790 error("Too many nested sources");
d266c416
MH
791 if (slevel <= 0)
792 ttyindes = saveinp;
544cd46c
MH
793 close(0);
794 if (open(fil, 0) < 0) {
795 oerrno = errno;
796 setrupt();
797 dup(saveinp);
798 close(saveinp);
799 errno = oerrno;
800 if (!okfail)
801 filioerr(fil);
802 return;
803 }
804 slevel++;
805 ointty = intty;
806 intty = isatty(0);
807 oprompt = value(PROMPT);
808 value(PROMPT) &= intty;
809 getexit(osetexit);
810 setrupt();
811 if (setexit() == 0)
812 commands(1, 1);
813 else if (slevel > 1) {
814 close(0);
815 dup(saveinp);
816 close(saveinp);
817 slevel--;
818 resexit(osetexit);
819 reset();
820 }
821 intty = ointty;
822 value(PROMPT) = oprompt;
823 close(0);
824 dup(saveinp);
825 close(saveinp);
04379bab
MH
826 globp = saveglobp;
827 peekc = savepeekc;
544cd46c
MH
828 slevel--;
829 resexit(osetexit);
830}
831
832/*
833 * Clear io statistics before a read or write.
834 */
835clrstats()
836{
837
838 ninbuf = 0;
839 cntch = 0;
840 cntln = 0;
841 cntnull = 0;
842 cntodd = 0;
843}
844
845/*
846 * Io is finished, close the unit and print statistics.
847 */
848iostats()
849{
850
2391e621 851 (void) fsync(io);
544cd46c
MH
852 close(io);
853 io = -1;
854 if (hush == 0) {
855 if (value(TERSE))
856 printf(" %d/%D", cntln, cntch);
857 else
858 printf(" %d line%s, %D character%s", cntln, plural((long) cntln),
859 cntch, plural(cntch));
860 if (cntnull || cntodd) {
861 printf(" (");
862 if (cntnull) {
863 printf("%D null", cntnull);
864 if (cntodd)
865 printf(", ");
866 }
867 if (cntodd)
868 printf("%D non-ASCII", cntodd);
869 putchar(')');
870 }
871 noonl();
872 flush();
873 }
874 return (cntnull != 0 || cntodd != 0);
875}
299f2784
MH
876
877#if USG | USG3TTY
878/* It's so wonderful how we all speak the same language... */
879# define index strchr
880# define rindex strrchr
881#endif
882
883checkmodeline(line)
884char *line;
885{
886 char *beg, *end;
887 char cmdbuf[1024];
888 char *index(), *rindex();
889
890 beg = index(line, ':');
891 if (beg == NULL)
892 return;
42a399ac
PK
893 if (&beg[-3] < line)
894 return;
895 if (!( ( (beg[-3] == ' ' || beg[-3] == '\t')
896 && beg[-2] == 'e'
897 && beg[-1] == 'x')
898 || ( (beg[-3] == ' ' || beg[-3] == '\t')
899 && beg[-2] == 'v'
900 && beg[-1] == 'i'))) return;
299f2784
MH
901 strncpy(cmdbuf, beg+1, sizeof cmdbuf);
902 end = rindex(cmdbuf, ':');
903 if (end == NULL)
904 return;
905 *end = 0;
906 globp = cmdbuf;
907 commands(1, 1);
908}