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 | |
864e5380 | 8 | static 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 |
28 | int altdot; |
29 | int oldadot; | |
544cd46c | 30 | bool wasalt; |
d266c416 | 31 | short isalt; |
544cd46c MH |
32 | |
33 | long cntch; /* Count of characters on unit io */ | |
44232d5b | 34 | #ifndef VMUNIX |
544cd46c | 35 | short cntln; /* Count of lines " */ |
44232d5b MH |
36 | #else |
37 | int cntln; | |
38 | #endif | |
544cd46c MH |
39 | long cntnull; /* Count of nulls " */ |
40 | long 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 | */ | |
49 | int io_savedfile, io_altfile, io_curr ; | |
50 | int 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 | */ | |
58 | filename(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 | */ | |
176 | getargs() | |
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]) | |
216 | flong: | |
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 %%"); | |
231 | filexp: | |
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 | */ | |
248 | glob(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 | */ | |
332 | gscan() | |
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 | 347 | struct glob G; |
544cd46c MH |
348 | getone() |
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 | */ | |
367 | rop(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 | } | |
515 | fail_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 | ||
527 | rop2() | |
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 | ||
559 | rop3(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 | |
582 | other: | |
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 | */ | |
600 | samei(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 | */ | |
619 | wop(dofname) | |
620 | bool 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 | } | |
705 | cre: | |
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 | */ | |
791 | edfile() | |
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 | 802 | char *nextip; |
544cd46c MH |
803 | |
804 | getfile() | |
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 |
851 | putfile(isfilter) |
852 | int 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 | */ | |
904 | wrerror() | |
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 |
916 | short slevel; |
917 | short ttyindes; | |
544cd46c MH |
918 | |
919 | source(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 | */ | |
979 | clrstats() | |
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 | */ | |
992 | iostats() | |
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 | ||
1033 | checkmodeline(line) | |
1034 | char *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 | } |