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