Commit | Line | Data |
---|---|---|
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 | 8 | static 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 |
27 | int altdot; |
28 | int oldadot; | |
544cd46c | 29 | bool wasalt; |
d266c416 | 30 | short isalt; |
544cd46c MH |
31 | |
32 | long cntch; /* Count of characters on unit io */ | |
44232d5b | 33 | #ifndef VMUNIX |
544cd46c | 34 | short cntln; /* Count of lines " */ |
44232d5b MH |
35 | #else |
36 | int cntln; | |
37 | #endif | |
544cd46c MH |
38 | long cntnull; /* Count of nulls " */ |
39 | long 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 | */ | |
48 | int io_savedfile, io_altfile, io_curr ; | |
49 | int 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 | */ | |
57 | filename(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 | */ | |
175 | getargs() | |
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]) | |
215 | flong: | |
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 %%"); | |
230 | filexp: | |
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 | */ | |
247 | glob(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 | */ | |
331 | gscan() | |
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 | 346 | struct glob G; |
544cd46c MH |
347 | getone() |
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 | */ | |
366 | rop(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 | } | |
517 | fail_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 | ||
529 | rop2() | |
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 | ||
561 | rop3(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 | |
584 | other: | |
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 | */ | |
602 | samei(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 | */ | |
621 | wop(dofname) | |
622 | bool 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 | } | |
707 | cre: | |
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 | */ | |
797 | edfile() | |
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 | 808 | char *nextip; |
544cd46c MH |
809 | |
810 | getfile() | |
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 |
857 | putfile(isfilter) |
858 | int 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 | */ | |
910 | wrerror() | |
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 |
922 | short slevel; |
923 | short ttyindes; | |
544cd46c MH |
924 | |
925 | source(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 | */ | |
985 | clrstats() | |
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 | */ | |
998 | iostats() | |
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 |
1039 | checkmodeline(l) |
1040 | char *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 | } |