5 * 14407 SW Teal Blvd. #C
11 /* This file contains functions which create & readback a TMPFILE */
25 # include <sys/stat.h>
34 static void do_modelines(l
, stop
)
35 long l
; /* line number to start at */
36 long stop
; /* line number to stop at */
38 char *str
; /* used to scan through the line */
39 char *start
; /* points to the start of the line */
42 /* if modelines are disabled, then do nothing */
48 /* for each line... */
49 for (; l
<= stop
; l
++)
51 /* for each position in the line.. */
52 for (str
= fetchline(l
); *str
; str
++)
54 /* if it is the start of a modeline command... */
55 if ((str
[0] == 'e' && str
[1] == 'x'
56 || str
[0] == 'v' && str
[1] == 'i')
62 for (str
= start
+ strlen(start
); *--str
!= ':'; )
66 /* if it is a well-formed modeline, execute it */
67 if (str
> start
&& str
- start
< sizeof buf
)
69 strncpy(buf
, start
, (int)(str
- start
));
70 exstring(buf
, str
- start
, '\\');
80 /* The FAIL() macro prints an error message and then exits. */
81 #define FAIL(why,arg) mode = MODE_EX; msg(why, arg); endwin(); exit(9)
83 /* This is the name of the temp file */
84 static char tmpname
[80];
86 /* This function creates the temp file and copies the original file into it.
87 * Returns if successful, or stops execution if it fails.
89 int tmpstart(filename
)
90 char *filename
; /* name of the original file */
92 int origfd
; /* fd used for reading the original file */
93 struct stat statb
; /* stat buffer, used to examine inode */
94 REG BLK
*this; /* pointer to the current block buffer */
95 REG BLK
*next
; /* pointer to the next block buffer */
96 int inbuf
; /* number of characters in a buffer */
97 int nread
; /* number of bytes read */
102 /* switching to a different file certainly counts as a change */
104 redraw(MARK_UNSET
, FALSE
);
106 /* open the original file for reading */
108 if (filename
&& *filename
)
110 strcpy(origname
, filename
);
111 origfd
= open(origname
, O_RDONLY
);
112 if (origfd
< 0 && errno
!= ENOENT
)
114 msg("Can't open \"%s\"", origname
);
119 if (stat(origname
, &statb
) < 0)
121 FAIL("Can't stat \"%s\"", origname
);
124 if (origfd
>= 0 && (statb
.st_mode
& S_IJDIR
))
127 if (origfd
>= 0 && (statb
.st_mode
& S_IFDIR
))
129 if (origfd
>= 0 && (statb
.st_mode
& S_IFMT
) != S_IFREG
)
133 msg("\"%s\" is not a regular file", origname
);
143 origtime
= statb
.st_mtime
;
145 if (*o_readonly
|| !(statb
.st_mode
&
146 ((getuid() >> 16) == 0 ? S_IOWRITE
| S_IWRITE
:
147 ((statb
.st_gid
!= (getuid() >> 16) ? S_IOWRITE
: S_IWRITE
)))))
149 #if AMIGA || MSDOS || (TOS && defined(__GNUC__))
150 if (*o_readonly
|| !(statb
.st_mode
& S_IWRITE
))
152 #if TOS && !defined(__GNUC__)
153 if (*o_readonly
|| (statb
.st_mode
& S_IJRON
))
156 if (*o_readonly
|| !(statb
.st_mode
&
157 ((geteuid() == 0) ? 0222 :
158 ((statb
.st_uid
!= geteuid() ? 0022 : 0200)))))
164 setflag(file
, READONLY
);
174 setflag(file
, NOFILE
);
180 /* make a name for the tmp file */
183 /* MS-Dos doesn't allow multiple slashes, but supports drives
184 * with current directories.
185 * This relies on TMPNAME beginning with "%s\\"!!!!
187 strcpy(tmpname
, o_directory
);
188 if ((i
= strlen(tmpname
)) && !strchr(":/\\", tmpname
[i
-1]))
190 sprintf(tmpname
+i
, TMPNAME
+3, getpid(), tmpnum
);
192 sprintf(tmpname
, TMPNAME
, o_directory
, getpid(), tmpnum
);
195 /* make sure nobody else is editing the same file */
196 if (access(tmpname
, 0) == 0)
198 FAIL("Temp file \"%s\" already exists?", tmpname
);
201 /* create the temp file */
203 close(creat(tmpname
, 0600)); /* only we can read it */
205 close(creat(tmpname
, FILEPERMS
)); /* anybody body can read it, alas */
207 tmpfd
= open(tmpname
, O_RDWR
| O_BINARY
);
210 FAIL("Can't create temp file... Does directory \"%s\" exist?", o_directory
);
214 /* allocate space for the header in the file */
215 write(tmpfd
, hdr
.c
, (unsigned)BLKSIZE
);
216 write(tmpfd
, tmpblk
.c
, (unsigned)BLKSIZE
);
219 /* initialize the block allocator */
220 /* This must already be done here, before the first attempt
221 * to write to the new file! GB */
225 /* initialize lnum[] */
226 for (i
= 1; i
< MAXBLKS
; i
++)
232 /* if there is no original file, then create a 1-line file */
235 hdr
.n
[0] = 0; /* invalid inode# denotes new file */
237 this = blkget(1); /* get the new text block */
238 strcpy(this->c
, "\n"); /* put a line in it */
240 lnum
[1] = 1L; /* block 1 ends with line 1 */
241 nlines
= 1L; /* there is 1 line in the file */
246 msg("\"%s\" [NEW FILE] 1 line, 1 char", origname
);
250 msg("\"[NO FILE]\" 1 line, 1 char");
253 else /* there is an original file -- read it in */
257 /* preallocate 1 "next" buffer */
262 /* loop, moving blocks from orig to tmp */
265 /* "next" buffer becomes "this" buffer */
268 /* read [more] text into this block */
269 nread
= tread(origfd
, &this->c
[inbuf
], BLKSIZE
- 1 - inbuf
);
276 FAIL("Error reading \"%s\"", origname
);
279 /* convert NUL characters to something else */
280 for (j
= k
= inbuf
; k
< inbuf
+ nread
; k
++)
284 setflag(file
, HADNUL
);
288 else if (*o_beautify
&& this->c
[k
] < ' ' && this->c
[k
] > 0)
290 if (this->c
[k
] == '\t'
291 || this->c
[k
] == '\n'
292 || this->c
[k
] == '\f')
294 this->c
[j
++] = this->c
[k
];
296 else if (this->c
[k
] == '\b')
298 /* delete '\b', but complain */
299 setflag(file
, HADBS
);
301 /* else silently delete control char */
306 this->c
[j
++] = this->c
[k
];
311 /* if the buffer is empty, quit */
318 /* BAH! MS text mode read fills inbuf, then compresses eliminating \r
319 but leaving garbage at end of buf. The same is true for TURBOC. GB. */
321 memset(this->c
+ inbuf
, '\0', BLKSIZE
- inbuf
);
324 /* search backward for last newline */
325 for (k
= inbuf
; --k
>= 0 && this->c
[k
] != '\n';)
330 if (inbuf
>= BLKSIZE
- 1)
340 /* allocate next buffer */
343 /* move fragmentary last line to next buffer */
345 for (j
= 0; k
< BLKSIZE
; j
++, k
++)
347 next
->c
[j
] = this->c
[k
];
351 /* if necessary, add a newline to this buf */
352 for (k
= BLKSIZE
- inbuf
; --k
>= 0 && !this->c
[k
]; )
355 if (this->c
[k
] != '\n')
357 setflag(file
, ADDEDNL
);
358 this->c
[k
+ 1] = '\n';
361 /* count the lines in this block */
362 for (k
= 0; k
< BLKSIZE
&& this->c
[k
]; k
++)
364 if (this->c
[k
] == '\n')
371 FAIL("Too many blocks: %d.", i
);
373 lnum
[i
- 1] = nlines
;
377 /* if this is a zero-length file, add 1 line */
380 this = blkget(1); /* get the new text block */
381 strcpy(this->c
, "\n"); /* put a line in it */
383 lnum
[1] = 1; /* block 1 ends with line 1 */
384 nlines
= 1; /* there is 1 line in the file */
389 /* each line has an extra CR that we didn't count yet */
393 /* report the number of lines in the file */
394 msg("\"%s\" %s %ld line%s, %ld char%s",
396 (tstflag(file
, READONLY
) ? "[READONLY]" : ""),
398 nlines
== 1 ? "" : "s",
400 nbytes
== 1 ? "" : "s");
403 /* initialize the cursor to start of line 1 */
406 /* close the original file */
409 /* any other messages? */
410 if (tstflag(file
, HADNUL
))
412 msg("This file contained NULs. They've been changed to \\x80 chars");
414 if (tstflag(file
, ADDEDNL
))
416 msg("Newline characters have been inserted to break up long lines");
419 if (tstflag(file
, HADBS
))
421 msg("Backspace characters deleted due to ':set beautify'");
430 do_modelines(1L, 5L);
431 do_modelines(nlines
- 4L, nlines
);
435 do_modelines(1L, nlines
);
439 /* force all blocks out onto the disk, to support file recovery */
447 /* This function copies the temp file back onto an original file.
448 * Returns TRUE if successful, or FALSE if the file could NOT be saved.
450 int tmpsave(filename
, bang
)
451 char *filename
; /* the name to save it to */
452 int bang
; /* forced write? */
454 int fd
; /* fd of the file we're writing to */
455 REG
int len
; /* length of a text block */
456 REG BLK
*this; /* a text block */
457 long bytes
; /* byte counter */
460 /* if no filename is given, assume the original file name */
461 if (!filename
|| !*filename
)
466 /* if still no file name, then fail */
469 msg("Don't know a name for this file -- NOT WRITTEN");
473 /* can't rewrite a READONLY file */
475 if (!strcmp(filename
, origname
) && tstflag(file
, READONLY
) && !bang
)
477 if (!strcmp(filename
, origname
) && *o_readonly
&& !bang
)
480 msg("\"%s\" [READONLY] -- NOT WRITTEN", filename
);
485 if (*filename
== '>' && filename
[1] == '>')
488 while (*filename
== ' ' || *filename
== '\t')
493 fd
= open(filename
, O_WRONLY
|O_APPEND
);
495 fd
= open(filename
, O_WRONLY
);
501 /* either the file must not exist, or it must be the original
502 * file, or we must have a bang, or "writeany" must be set.
504 if (strcmp(filename
, origname
) && access(filename
, 0) == 0 && !bang
510 msg("File already exists - Use :w! to overwrite");
514 /* Create a new VMS version of this file. */
516 char *strrchr(), *ptr
= strrchr(filename
,';');
517 if (ptr
) *ptr
= '\0'; /* Snip off any ;number in the name */
520 fd
= creat(filename
, FILEPERMS
);
524 msg("Can't write to \"%s\" -- NOT WRITTEN", filename
);
528 /* write each text block to the file */
530 for (i
= 1; i
< MAXBLKS
&& (this = blkget(i
)) && this->c
[0]; i
++)
532 for (len
= 0; len
< BLKSIZE
&& this->c
[len
]; len
++)
535 if (twrite(fd
, this->c
, len
) < len
)
537 msg("Trouble writing to \"%s\"", filename
);
538 if (!strcmp(filename
, origname
))
540 setflag(file
, MODIFIED
);
548 /* reset the "modified" flag, but not the "undoable" flag */
549 clrflag(file
, MODIFIED
);
552 /* report lines & characters */
554 bytes
+= nlines
; /* for the inserted carriage returns */
556 msg("Wrote \"%s\" %ld lines, %ld characters", filename
, nlines
, bytes
);
565 /* This function deletes the temporary file. If the file has been modified
566 * and "bang" is FALSE, then it returns FALSE without doing anything; else
569 * If the "autowrite" option is set, then instead of returning FALSE when
570 * the file has been modified and "bang" is false, it will call tmpend().
575 /* if there is no file, return successfully */
581 /* see if we must return FALSE -- can't quit */
582 if (!bang
&& tstflag(file
, MODIFIED
))
584 /* if "autowrite" is set, then act like tmpend() */
591 /* delete the tmp file */
593 strcpy(prevorig
, origname
);
594 prevline
= markline(cursor
);
603 /* This function saves the file if it has been modified, and then deletes
604 * the temporary file. Returns TRUE if successful, or FALSE if the file
605 * needs to be saved but can't be. When it returns FALSE, it will not have
606 * deleted the tmp file, either.
611 /* save the file if it has been modified */
612 if (tstflag(file
, MODIFIED
) && !tmpsave((char *)0, FALSE
) && !bang
)
617 /* delete the tmp file */
624 /* If the tmp file has been changed, then this function will force those
625 * changes to be written to the disk, so that the tmp file will survive a
626 * system crash or power failure.
628 #if AMIGA || MSDOS || TOS
631 /* MS-DOS and TOS don't flush their buffers until the file is closed,
632 * so here we close the tmp file and then immediately reopen it.
635 tmpfd
= open(tmpname
, O_RDWR
| O_BINARY
);
641 /* This function stores the file's name in the second block of the temp file.
642 * SLEAZE ALERT! SLEAZE ALERT! The "tmpblk" buffer is probably being used
643 * to store the arguments to a command, so we can't use it here. Instead,
644 * we'll borrow the buffer that is used for "shift-U".
647 char *name
; /* the name of the file - normally origname */
654 /* we're going to clobber the U_text buffer, so reset U_line */
659 strncpy(U_text
, "", BLKSIZE
);
663 else if (*name
!= SLASH
)
665 /* get the directory name */
666 ptr
= getcwd(U_text
, BLKSIZE
);
672 /* append a slash to the directory name */
673 len
= strlen(U_text
);
674 U_text
[len
++] = SLASH
;
676 /* append the filename, padded with heaps o' NULs */
677 strncpy(U_text
+ len
, *name
? name
: "foo", BLKSIZE
- len
);
682 /* copy the filename into U_text */
683 strncpy(U_text
, *name
? name
: "foo", BLKSIZE
);
688 /* write the name out to second block of the temp file */
689 lseek(tmpfd
, (long)BLKSIZE
, 0);
690 write(tmpfd
, U_text
, (unsigned)BLKSIZE
);
697 /* This function handles deadly signals. It restores sanity to the terminal
698 * preserves the current temp file, and deletes any old temp files.
701 int sig
; /* the deadly signal that we caught */
705 /* restore the terminal's sanity */
711 /* give a more specific description of how Elvis died */
715 case SIGHUP
: why
= "-the modem lost its carrier"; break;
719 case SIGILL
: why
= "-Elvis hit an illegal instruction"; break;
722 case SIGBUS
: why
= "-Elvis had a bus error"; break;
724 # if defined(SIGSEGV) && !defined(TOS)
725 case SIGSEGV
: why
= "-Elvis had a segmentation violation"; break;
728 case SIGSYS
: why
= "-Elvis munged a system call"; break;
732 case SIGPIPE
: why
= "-the pipe reader died"; break;
735 case SIGTERM
: why
= "-Elvis was terminated"; break;
739 case SIGUSR1
: why
= "-Elvis was killed via SIGUSR1"; break;
742 case SIGUSR2
: why
= "-Elvis was killed via SIGUSR2"; break;
745 default: why
= "-Elvis died"; break;
749 /* if we had a temp file going, then preserve it */
750 if (tmpnum
> 0 && tmpfd
>= 0)
753 sprintf(tmpblk
.c
, "%s \"%s\" %s", PRESERVE
, why
, tmpname
);
757 /* delete any old temp files */
760 /* exit with the proper exit status */