added depend label
[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
70b8dab5 8static char *sccsid = "@(#)ex_io.c 7.14 (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
5a6c967e 50 d = ex_getchar();
544cd46c
MH
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 100 if (value(READONLY))
5a6c967e 101 ex_printf(" [Read only]");
544cd46c 102 if (!edited)
5a6c967e 103 ex_printf(" [Not edited]");
544cd46c 104 if (tchng)
5a6c967e 105 ex_printf(" [Modified]");
544cd46c
MH
106 }
107 flush();
108 } else
5a6c967e 109 ex_printf("No file ");
544cd46c
MH
110 if (comm == 'f') {
111 if (!(i = lineDOL()))
112 i++;
5a6c967e 113 ex_printf(" line %d of %d --%ld%%--", lineDOT(), lineDOL(),
544cd46c
MH
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;;) {
5a6c967e 131 c = *cp++ = ex_getchar();
544cd46c
MH
132 if (cp >= &fpatbuf[sizeof(fpatbuf)])
133 error("Pattern too long");
134 if (c == '\\' && isspace(peekchar()))
5a6c967e 135 c = ex_getchar();
544cd46c
MH
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 (;;) {
5a6c967e 148 c = ex_getchar();
544cd46c
MH
149 if (endcmd(c)) {
150 ungetchar(c);
151 break;
152 }
153 switch (c) {
154
155 case '\\':
d266c416 156 if (any(peekchar(), "#%|"))
5a6c967e 157 c = ex_getchar();
544cd46c
MH
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");
5a6c967e 224 pid = vfork();
544cd46c
MH
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 236 close(2); /* so errors don't mess up the screen */
5a6c967e 237 ignore(open("/dev/null", 1));
544cd46c 238 execl(svalue(SHELL), "sh", "-c", genbuf, 0);
5a6c967e
CH
239 oerrno = errno;
240 close(1);
241 dup(2);
242 errno = oerrno;
544cd46c
MH
243 filioerr(svalue(SHELL));
244 }
245 close(pvec[1]);
246 do {
247 *argv = cp;
248 for (;;) {
249 if (read(io, &ch, 1) != 1) {
250 close(io);
251 c = -1;
252 } else
253 c = ch & TRIM;
254 if (c <= 0 || isspace(c))
255 break;
256 *cp++ = c;
257 if (--nleft <= 0)
258 error("Arg list too long");
259 }
260 if (cp != *argv) {
261 --nleft;
262 *cp++ = 0;
263 gp->argc0++;
264 if (gp->argc0 >= NARGS)
265 error("Arg list too long");
266 argv++;
267 }
268 } while (c >= 0);
269 waitfor();
270 if (gp->argc0 == 0)
887e3e0d 271 error("No match");
544cd46c
MH
272}
273
274/*
275 * Scan genbuf for shell metacharacters.
276 * Set is union of v7 shell and csh metas.
277 */
278gscan()
279{
70b8dab5 280#ifndef vms /* Never have meta-characters in vms */
544cd46c
MH
281 register char *cp;
282
283 for (cp = genbuf; *cp; cp++)
284 if (any(*cp, "~{[*?$`'\"\\"))
285 return (1);
5a6c967e 286#endif
70b8dab5 287 return (0);
544cd46c
MH
288}
289
290/*
291 * Parse one filename into file.
292 */
3c7b865a 293struct glob G;
544cd46c
MH
294getone()
295{
296 register char *str;
544cd46c
MH
297
298 if (getargs() == 0)
299 error("Missing filename");
300 glob(&G);
301 if (G.argc0 > 1)
302 error("Ambiguous|Too many file names");
303 str = G.argv[G.argc0 - 1];
304 if (strlen(str) > FNSIZE - 4)
305 error("Filename too long");
544cd46c
MH
306 CP(file, str);
307}
308
309/*
310 * Read a file from the world.
311 * C is command, 'e' if this really an edit (or a recover).
312 */
313rop(c)
314 int c;
315{
316 register int i;
317 struct stat stbuf;
318 short magic;
dcbc1067
MH
319 static int ovro; /* old value(READONLY) */
320 static int denied; /* 1 if READONLY was set due to file permissions */
544cd46c
MH
321
322 io = open(file, 0);
323 if (io < 0) {
04379bab 324 if (c == 'e' && errno == ENOENT) {
544cd46c 325 edited++;
04379bab
MH
326 /*
327 * If the user just did "ex foo" he is probably
328 * creating a new file. Don't be an error, since
329 * this is ugly, and it screws up the + option.
330 */
331 if (!seenprompt) {
5a6c967e 332 ex_printf(" [New file]");
04379bab
MH
333 noonl();
334 return;
335 }
336 }
544cd46c
MH
337 syserror();
338 }
339 if (fstat(io, &stbuf))
340 syserror();
341 switch (stbuf.st_mode & S_IFMT) {
342
343 case S_IFBLK:
344 error(" Block special file");
345
346 case S_IFCHR:
347 if (isatty(io))
348 error(" Teletype");
349 if (samei(&stbuf, "/dev/null"))
350 break;
351 error(" Character special file");
352
353 case S_IFDIR:
354 error(" Directory");
355
356 case S_IFREG:
357 i = read(io, (char *) &magic, sizeof(magic));
358 lseek(io, 0l, 0);
359 if (i != sizeof(magic))
360 break;
5a6c967e 361#ifndef vms
544cd46c
MH
362 switch (magic) {
363
299f2784 364 case 0405: /* data overlay on exec */
f0f2d980
MH
365 case 0407: /* unshared */
366 case 0410: /* shared text */
367 case 0411: /* separate I/D */
368 case 0413: /* VM/Unix demand paged */
369 case 0430: /* PDP-11 Overlay shared */
370 case 0431: /* PDP-11 Overlay sep I/D */
544cd46c
MH
371 error(" Executable");
372
f0f2d980
MH
373 /*
374 * We do not forbid the editing of portable archives
375 * because it is reasonable to edit them, especially
376 * if they are archives of text files. This is
377 * especially useful if you archive source files together
378 * and copy them to another system with ~%take, since
379 * the files sometimes show up munged and must be fixed.
380 */
544cd46c
MH
381 case 0177545:
382 case 0177555:
383 error(" Archive");
384
385 default:
52fd6c9c
MH
386#ifdef mbb
387 /* C/70 has a 10 bit byte */
388 if (magic & 03401600)
389#else
390 /* Everybody else has an 8 bit byte */
544cd46c 391 if (magic & 0100200)
52fd6c9c 392#endif
544cd46c
MH
393 error(" Non-ascii file");
394 break;
395 }
5a6c967e 396#endif
544cd46c 397 }
04379bab
MH
398 if (c != 'r') {
399 if (value(READONLY) && denied) {
400 value(READONLY) = ovro;
401 denied = 0;
402 }
403 if ((stbuf.st_mode & 0222) == 0 || access(file, 2) < 0) {
404 ovro = value(READONLY);
405 denied = 1;
406 value(READONLY) = 1;
407 }
dcbc1067 408 }
fdc664ec 409 if (value(READONLY)) {
5a6c967e 410 ex_printf(" [Read only]");
fdc664ec
MH
411 flush();
412 }
544cd46c
MH
413 if (c == 'r')
414 setdot();
415 else
416 setall();
887e3e0d 417 if (FIXUNDO && inopen && c == 'r')
544cd46c
MH
418 undap1 = undap2 = dot + 1;
419 rop2();
420 rop3(c);
421}
422
423rop2()
424{
299f2784 425 line *first, *last, *a;
4e3d2679 426 struct stat statb;
544cd46c
MH
427
428 deletenone();
429 clrstats();
299f2784 430 first = addr2 + 1;
4e3d2679
JB
431 if (fstat(io, &statb) < 0)
432 bsize = LBSIZE;
433 else {
434 bsize = statb.st_blksize;
435 if (bsize <= 0)
436 bsize = LBSIZE;
437 }
544cd46c 438 ignore(append(getfile, addr2));
299f2784 439 last = dot;
42a399ac
PK
440 /*
441 * if the modeline variable is set,
442 * check the first and last five lines of the file
443 * for a mode line.
444 */
445 if (value(MODELINE)) {
446 for (a=first; a<=last; a++) {
447 if (a==first+5 && last-first > 10)
448 a = last - 4;
449 getline(*a);
450 checkmodeline(linebuf);
451 }
299f2784 452 }
544cd46c
MH
453}
454
455rop3(c)
456 int c;
457{
458
459 if (iostats() == 0 && c == 'e')
460 edited++;
461 if (c == 'e') {
462 if (wasalt || firstpat) {
463 register line *addr = zero + oldadot;
464
465 if (addr > dol)
466 addr = dol;
467 if (firstpat) {
468 globp = (*firstpat) ? firstpat : "$";
469 commands(1,1);
470 firstpat = 0;
471 } else if (addr >= one) {
472 if (inopen)
473 dot = addr;
474 markpr(addr);
475 } else
476 goto other;
477 } else
478other:
479 if (dol > zero) {
480 if (inopen)
481 dot = one;
482 markpr(one);
483 }
887e3e0d
MH
484 if(FIXUNDO)
485 undkind = UNDNONE;
544cd46c
MH
486 if (inopen) {
487 vcline = 0;
488 vreplace(0, LINES, lineDOL());
489 }
490 }
544cd46c
MH
491}
492
493/*
494 * Are these two really the same inode?
495 */
496samei(sp, cp)
497 struct stat *sp;
498 char *cp;
499{
500 struct stat stb;
501
502 if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev)
503 return (0);
504 return (sp->st_ino == stb.st_ino);
505}
506
507/* Returns from edited() */
508#define EDF 0 /* Edited file */
509#define NOTEDF -1 /* Not edited file */
510#define PARTBUF 1 /* Write of partial buffer to Edited file */
511
512/*
513 * Write a file.
514 */
515wop(dofname)
516bool dofname; /* if 1 call filename, else use savedfile */
517{
518 register int c, exclam, nonexist;
519 line *saddr1, *saddr2;
520 struct stat stbuf;
521
522 c = 0;
523 exclam = 0;
524 if (dofname) {
525 if (peekchar() == '!')
526 exclam++, ignchar();
527 ignore(skipwh());
528 while (peekchar() == '>')
529 ignchar(), c++, ignore(skipwh());
530 if (c != 0 && c != 2)
531 error("Write forms are 'w' and 'w>>'");
532 filename('w');
533 } else {
887e3e0d
MH
534 if (savedfile[0] == 0)
535 error("No file|No current filename");
544cd46c
MH
536 saddr1=addr1;
537 saddr2=addr2;
538 addr1=one;
539 addr2=dol;
540 CP(file, savedfile);
541 if (inopen) {
542 vclrech(0);
543 splitw++;
544 }
545 lprintf("\"%s\"", file);
546 }
547 nonexist = stat(file, &stbuf);
548 switch (c) {
549
550 case 0:
d266c416
MH
551 if (!exclam && (!value(WRITEANY) || value(READONLY)))
552 switch (edfile()) {
544cd46c
MH
553
554 case NOTEDF:
555 if (nonexist)
556 break;
557 if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
558 if (samei(&stbuf, "/dev/null"))
559 break;
560 if (samei(&stbuf, "/dev/tty"))
561 break;
562 }
563 io = open(file, 1);
564 if (io < 0)
565 syserror();
566 if (!isatty(io))
567 serror(" File exists| File exists - use \"w! %s\" to overwrite", file);
568 close(io);
569 break;
570
d266c416
MH
571 case EDF:
572 if (value(READONLY))
573 error(" File is read only");
574 break;
575
544cd46c 576 case PARTBUF:
d266c416
MH
577 if (value(READONLY))
578 error(" File is read only");
544cd46c
MH
579 error(" Use \"w!\" to write partial buffer");
580 }
581cre:
44232d5b 582/*
544cd46c 583 synctmp();
44232d5b 584*/
544cd46c
MH
585#ifdef V6
586 io = creat(file, 0644);
587#else
588 io = creat(file, 0666);
589#endif
590 if (io < 0)
591 syserror();
04379bab 592 writing = 1;
544cd46c
MH
593 if (hush == 0)
594 if (nonexist)
5a6c967e 595 ex_printf(" [New file]");
544cd46c 596 else if (value(WRITEANY) && edfile() != EDF)
5a6c967e 597 ex_printf(" [Existing file]");
544cd46c
MH
598 break;
599
600 case 2:
601 io = open(file, 1);
602 if (io < 0) {
603 if (exclam || value(WRITEANY))
604 goto cre;
605 syserror();
606 }
607 lseek(io, 0l, 2);
608 break;
609 }
299f2784 610 putfile(0);
5a6c967e 611#ifndef vms
60242812 612 (void) fsync(io);
5a6c967e 613#endif
544cd46c
MH
614 ignore(iostats());
615 if (c != 2 && addr1 == one && addr2 == dol) {
616 if (eq(file, savedfile))
617 edited = 1;
5a6c967e 618 ex_sync();
544cd46c
MH
619 }
620 if (!dofname) {
621 addr1 = saddr1;
622 addr2 = saddr2;
623 }
04379bab 624 writing = 0;
544cd46c
MH
625}
626
627/*
628 * Is file the edited file?
629 * Work here is that it is not considered edited
630 * if this is a partial buffer, and distinguish
631 * all cases.
632 */
633edfile()
634{
635
636 if (!edited || !eq(file, savedfile))
637 return (NOTEDF);
638 return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
639}
640
544cd46c
MH
641/*
642 * Extract the next line from the io stream.
643 */
3c7b865a 644char *nextip;
544cd46c
MH
645
646getfile()
647{
648 register short c;
649 register char *lp, *fp;
650
651 lp = linebuf;
652 fp = nextip;
653 do {
654 if (--ninbuf < 0) {
5a6c967e 655 ninbuf = read(io, genbuf, (int) bsize) - 1;
544cd46c
MH
656 if (ninbuf < 0) {
657 if (lp != linebuf) {
d266c416 658 lp++;
5a6c967e 659 ex_printf(" [Incomplete last line]");
544cd46c
MH
660 break;
661 }
662 return (EOF);
663 }
d266c416
MH
664 fp = genbuf;
665 cntch += ninbuf+1;
544cd46c
MH
666 }
667 if (lp >= &linebuf[LBSIZE]) {
668 error(" Line too long");
669 }
670 c = *fp++;
671 if (c == 0) {
672 cntnull++;
673 continue;
674 }
675 if (c & QUOTE) {
676 cntodd++;
677 c &= TRIM;
678 if (c == 0)
679 continue;
680 }
681 *lp++ = c;
682 } while (c != '\n');
544cd46c
MH
683 *--lp = 0;
684 nextip = fp;
685 cntln++;
686 return (0);
687}
688
689/*
690 * Write a range onto the io stream.
691 */
5a6c967e 692/* ARGSUSED */
299f2784
MH
693putfile(isfilter)
694int isfilter;
544cd46c
MH
695{
696 line *a1;
697 register char *fp, *lp;
698 register int nib;
4e3d2679 699 struct stat statb;
544cd46c
MH
700
701 a1 = addr1;
702 clrstats();
703 cntln = addr2 - a1 + 1;
704 if (cntln == 0)
705 return;
4e3d2679
JB
706 if (fstat(io, &statb) < 0)
707 bsize = LBSIZE;
708 else {
709 bsize = statb.st_blksize;
710 if (bsize <= 0)
711 bsize = LBSIZE;
712 }
713 nib = bsize;
544cd46c
MH
714 fp = genbuf;
715 do {
716 getline(*a1++);
717 lp = linebuf;
718 for (;;) {
719 if (--nib < 0) {
720 nib = fp - genbuf;
721 if (write(io, genbuf, nib) != nib) {
722 wrerror();
723 }
724 cntch += nib;
4e3d2679 725 nib = bsize - 1;
544cd46c
MH
726 fp = genbuf;
727 }
728 if ((*fp++ = *lp++) == 0) {
729 fp[-1] = '\n';
730 break;
731 }
732 }
733 } while (a1 <= addr2);
734 nib = fp - genbuf;
735 if (write(io, genbuf, nib) != nib) {
736 wrerror();
737 }
738 cntch += nib;
739}
740
741/*
742 * A write error has occurred; if the file being written was
743 * the edited file then we consider it to have changed since it is
744 * now likely scrambled.
745 */
746wrerror()
747{
748
749 if (eq(file, savedfile) && edited)
750 change();
751 syserror();
752}
753
754/*
755 * Source command, handles nested sources.
756 * Traps errors since it mungs unit 0 during the source.
757 */
d266c416
MH
758short slevel;
759short ttyindes;
544cd46c
MH
760
761source(fil, okfail)
762 char *fil;
763 bool okfail;
764{
765 jmp_buf osetexit;
766 register int saveinp, ointty, oerrno;
b8c3ed58
MH
767 char *saveglobp;
768 short savepeekc;
544cd46c
MH
769
770 signal(SIGINT, SIG_IGN);
771 saveinp = dup(0);
04379bab
MH
772 savepeekc = peekc;
773 saveglobp = globp;
774 peekc = 0; globp = 0;
544cd46c
MH
775 if (saveinp < 0)
776 error("Too many nested sources");
d266c416
MH
777 if (slevel <= 0)
778 ttyindes = saveinp;
544cd46c
MH
779 close(0);
780 if (open(fil, 0) < 0) {
781 oerrno = errno;
782 setrupt();
783 dup(saveinp);
784 close(saveinp);
785 errno = oerrno;
786 if (!okfail)
787 filioerr(fil);
788 return;
789 }
790 slevel++;
791 ointty = intty;
792 intty = isatty(0);
793 oprompt = value(PROMPT);
794 value(PROMPT) &= intty;
795 getexit(osetexit);
796 setrupt();
797 if (setexit() == 0)
798 commands(1, 1);
799 else if (slevel > 1) {
800 close(0);
801 dup(saveinp);
802 close(saveinp);
803 slevel--;
804 resexit(osetexit);
805 reset();
806 }
807 intty = ointty;
808 value(PROMPT) = oprompt;
809 close(0);
810 dup(saveinp);
811 close(saveinp);
04379bab
MH
812 globp = saveglobp;
813 peekc = savepeekc;
544cd46c
MH
814 slevel--;
815 resexit(osetexit);
816}
817
818/*
819 * Clear io statistics before a read or write.
820 */
821clrstats()
822{
823
824 ninbuf = 0;
825 cntch = 0;
826 cntln = 0;
827 cntnull = 0;
828 cntodd = 0;
829}
830
831/*
832 * Io is finished, close the unit and print statistics.
833 */
834iostats()
835{
836
837 close(io);
838 io = -1;
839 if (hush == 0) {
840 if (value(TERSE))
5a6c967e 841 ex_printf(" %d/%D", cntln, cntch);
544cd46c 842 else
5a6c967e 843 ex_printf(" %d line%s, %D character%s", cntln, plural((long) cntln),
544cd46c
MH
844 cntch, plural(cntch));
845 if (cntnull || cntodd) {
5a6c967e 846 ex_printf(" (");
544cd46c 847 if (cntnull) {
5a6c967e 848 ex_printf("%D null", cntnull);
544cd46c 849 if (cntodd)
5a6c967e 850 ex_printf(", ");
544cd46c
MH
851 }
852 if (cntodd)
5a6c967e
CH
853 ex_printf("%D non-ASCII", cntodd);
854 ex_putchar(')');
544cd46c
MH
855 }
856 noonl();
857 flush();
858 }
859 return (cntnull != 0 || cntodd != 0);
860}
299f2784 861
5a6c967e
CH
862#ifdef USG
863# define index strchr
864# define rindex strrchr
865#endif
866#ifdef USG3TTY
867# define index strchr
868# define rindex strrchr
869#endif
870#ifdef vms
299f2784
MH
871# define index strchr
872# define rindex strrchr
873#endif
874
875checkmodeline(line)
876char *line;
877{
878 char *beg, *end;
879 char cmdbuf[1024];
5a6c967e 880 char *index(), *rindex(), *strncpy();
299f2784
MH
881
882 beg = index(line, ':');
883 if (beg == NULL)
884 return;
42a399ac
PK
885 if (&beg[-3] < line)
886 return;
887 if (!( ( (beg[-3] == ' ' || beg[-3] == '\t')
888 && beg[-2] == 'e'
889 && beg[-1] == 'x')
890 || ( (beg[-3] == ' ' || beg[-3] == '\t')
891 && beg[-2] == 'v'
892 && beg[-1] == 'i'))) return;
299f2784
MH
893 strncpy(cmdbuf, beg+1, sizeof cmdbuf);
894 end = rindex(cmdbuf, ':');
895 if (end == NULL)
896 return;
897 *end = 0;
898 globp = cmdbuf;
899 commands(1, 1);
900}