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