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