home *** CD-ROM | disk | FTP | other *** search
- /* tmp.c */
-
- /* Author:
- * Steve Kirkendall
- * 14407 SW Teal Blvd. #C
- * Beaverton, OR 97005
- * kirkenda@cs.pdx.edu
- */
-
-
- /* This file contains functions which create & readback a TMPFILE */
-
-
- #include "config.h"
- #include "vi.h"
- #if TOS
- # include <stat.h>
- #else
- # if OSK
- # include "osk.h"
- # else
- # if AMIGA
- # include "amistat.h"
- # else
- # include <sys/stat.h>
- # endif
- # endif
- #endif
- #if TURBOC
- # include <process.h>
- #endif
-
- #ifndef NO_MODELINES
- static void do_modelines(l, stop)
- long l; /* line number to start at */
- long stop; /* line number to stop at */
- {
- char *str; /* used to scan through the line */
- char *start; /* points to the start of the line */
- char buf[80];
-
- /* if modelines are disabled, then do nothing */
- if (!*o_modelines)
- {
- return;
- }
-
- /* for each line... */
- for (; l <= stop; l++)
- {
- /* for each position in the line.. */
- for (str = fetchline(l); *str; str++)
- {
- /* if it is the start of a modeline command... */
- if ((str[0] == 'e' && str[1] == 'x'
- || str[0] == 'v' && str[1] == 'i')
- && str[2] == ':')
- {
- start = str += 3;
-
- /* find the end */
- for (str = start + strlen(start); *--str != ':'; )
- {
- }
-
- /* if it is a well-formed modeline, execute it */
- if (str > start && str - start < sizeof buf)
- {
- strncpy(buf, start, (int)(str - start));
- exstring(buf, str - start, '\\');
- break;
- }
- }
- }
- }
- }
- #endif
-
-
- /* The FAIL() macro prints an error message and then exits. */
- #define FAIL(why,arg) mode = MODE_EX; msg(why, arg); endwin(); exit(9)
-
- /* This is the name of the temp file */
- static char tmpname[80];
-
- /* This function creates the temp file and copies the original file into it.
- * Returns if successful, or stops execution if it fails.
- */
- int tmpstart(filename)
- char *filename; /* name of the original file */
- {
- int origfd; /* fd used for reading the original file */
- struct stat statb; /* stat buffer, used to examine inode */
- REG BLK *this; /* pointer to the current block buffer */
- REG BLK *next; /* pointer to the next block buffer */
- int inbuf; /* number of characters in a buffer */
- int nread; /* number of bytes read */
- REG int j, k;
- int i;
- long nbytes;
-
- /* switching to a different file certainly counts as a change */
- changes++;
- redraw(MARK_UNSET, FALSE);
-
- /* open the original file for reading */
- *origname = '\0';
- if (filename && *filename)
- {
- strcpy(origname, filename);
- origfd = open(origname, O_RDONLY);
- if (origfd < 0 && errno != ENOENT)
- {
- msg("Can't open \"%s\"", origname);
- return tmpstart("");
- }
- if (origfd >= 0)
- {
- if (stat(origname, &statb) < 0)
- {
- FAIL("Can't stat \"%s\"", origname);
- }
- #if TOS
- if (origfd >= 0 && (statb.st_mode & S_IJDIR))
- #else
- # if OSK
- if (origfd >= 0 && (statb.st_mode & S_IFDIR))
- # else
- if (origfd >= 0 && (statb.st_mode & S_IFMT) != S_IFREG)
- # endif
- #endif
- {
- msg("\"%s\" is not a regular file", origname);
- return tmpstart("");
- }
- }
- else
- {
- stat(".", &statb);
- }
- if (origfd >= 0)
- {
- origtime = statb.st_mtime;
- #if OSK
- if (*o_readonly || !(statb.st_mode &
- ((getuid() >> 16) == 0 ? S_IOWRITE | S_IWRITE :
- ((statb.st_gid != (getuid() >> 16) ? S_IOWRITE : S_IWRITE)))))
- #endif
- #if AMIGA || MSDOS || (TOS && defined(__GNUC__))
- if (*o_readonly || !(statb.st_mode & S_IWRITE))
- #endif
- #if TOS && !defined(__GNUC__)
- if (*o_readonly || (statb.st_mode & S_IJRON))
- #endif
- #if ANY_UNIX
- if (*o_readonly || !(statb.st_mode &
- ((geteuid() == 0) ? 0222 :
- ((statb.st_uid != geteuid() ? 0022 : 0200)))))
- #endif
- #if VMS
- if (*o_readonly)
- #endif
- {
- setflag(file, READONLY);
- }
- }
- else
- {
- origtime = 0L;
- }
- }
- else
- {
- setflag(file, NOFILE);
- origfd = -1;
- origtime = 0L;
- stat(".", &statb);
- }
-
- /* make a name for the tmp file */
- tmpnum++;
- #if MSDOS || TOS
- /* MS-Dos doesn't allow multiple slashes, but supports drives
- * with current directories.
- * This relies on TMPNAME beginning with "%s\\"!!!!
- */
- strcpy(tmpname, o_directory);
- if ((i = strlen(tmpname)) && !strchr(":/\\", tmpname[i-1]))
- tmpname[i++]=SLASH;
- sprintf(tmpname+i, TMPNAME+3, getpid(), tmpnum);
- #else
- sprintf(tmpname, TMPNAME, o_directory, getpid(), tmpnum);
- #endif
-
- /* make sure nobody else is editing the same file */
- if (access(tmpname, 0) == 0)
- {
- FAIL("Temp file \"%s\" already exists?", tmpname);
- }
-
- /* create the temp file */
- #if ANY_UNIX
- close(creat(tmpname, 0600)); /* only we can read it */
- #else
- close(creat(tmpname, FILEPERMS)); /* anybody body can read it, alas */
- #endif
- tmpfd = open(tmpname, O_RDWR | O_BINARY);
- if (tmpfd < 0)
- {
- FAIL("Can't create temp file... Does directory \"%s\" exist?", o_directory);
- return 1;
- }
-
- /* allocate space for the header in the file */
- write(tmpfd, hdr.c, (unsigned)BLKSIZE);
- write(tmpfd, tmpblk.c, (unsigned)BLKSIZE);
-
- #ifndef NO_RECYCLE
- /* initialize the block allocator */
- /* This must already be done here, before the first attempt
- * to write to the new file! GB */
- garbage();
- #endif
-
- /* initialize lnum[] */
- for (i = 1; i < MAXBLKS; i++)
- {
- lnum[i] = INFINITY;
- }
- lnum[0] = 0;
-
- /* if there is no original file, then create a 1-line file */
- if (origfd < 0)
- {
- hdr.n[0] = 0; /* invalid inode# denotes new file */
-
- this = blkget(1); /* get the new text block */
- strcpy(this->c, "\n"); /* put a line in it */
-
- lnum[1] = 1L; /* block 1 ends with line 1 */
- nlines = 1L; /* there is 1 line in the file */
- nbytes = 1L;
-
- if (*origname)
- {
- msg("\"%s\" [NEW FILE] 1 line, 1 char", origname);
- }
- else
- {
- msg("\"[NO FILE]\" 1 line, 1 char");
- }
- }
- else /* there is an original file -- read it in */
- {
- nbytes = nlines = 0;
-
- /* preallocate 1 "next" buffer */
- i = 1;
- next = blkget(i);
- inbuf = 0;
-
- /* loop, moving blocks from orig to tmp */
- for (;;)
- {
- /* "next" buffer becomes "this" buffer */
- this = next;
-
- /* read [more] text into this block */
- nread = tread(origfd, &this->c[inbuf], BLKSIZE - 1 - inbuf);
- if (nread < 0)
- {
- close(origfd);
- close(tmpfd);
- tmpfd = -1;
- unlink(tmpname);
- FAIL("Error reading \"%s\"", origname);
- }
-
- /* convert NUL characters to something else */
- for (j = k = inbuf; k < inbuf + nread; k++)
- {
- if (!this->c[k])
- {
- setflag(file, HADNUL);
- this->c[j++] = 0x80;
- }
- #ifndef CRUNCH
- else if (*o_beautify && this->c[k] < ' ' && this->c[k] > 0)
- {
- if (this->c[k] == '\t'
- || this->c[k] == '\n'
- || this->c[k] == '\f')
- {
- this->c[j++] = this->c[k];
- }
- else if (this->c[k] == '\b')
- {
- /* delete '\b', but complain */
- setflag(file, HADBS);
- }
- /* else silently delete control char */
- }
- #endif
- else
- {
- this->c[j++] = this->c[k];
- }
- }
- inbuf = j;
-
- /* if the buffer is empty, quit */
- if (inbuf == 0)
- {
- goto FoundEOF;
- }
-
- #if MSDOS || TOS
- /* BAH! MS text mode read fills inbuf, then compresses eliminating \r
- but leaving garbage at end of buf. The same is true for TURBOC. GB. */
-
- memset(this->c + inbuf, '\0', BLKSIZE - inbuf);
- #endif
-
- /* search backward for last newline */
- for (k = inbuf; --k >= 0 && this->c[k] != '\n';)
- {
- }
- if (k++ < 0)
- {
- if (inbuf >= BLKSIZE - 1)
- {
- k = 80;
- }
- else
- {
- k = inbuf;
- }
- }
-
- /* allocate next buffer */
- next = blkget(++i);
-
- /* move fragmentary last line to next buffer */
- inbuf -= k;
- for (j = 0; k < BLKSIZE; j++, k++)
- {
- next->c[j] = this->c[k];
- this->c[k] = 0;
- }
-
- /* if necessary, add a newline to this buf */
- for (k = BLKSIZE - inbuf; --k >= 0 && !this->c[k]; )
- {
- }
- if (this->c[k] != '\n')
- {
- setflag(file, ADDEDNL);
- this->c[k + 1] = '\n';
- }
-
- /* count the lines in this block */
- for (k = 0; k < BLKSIZE && this->c[k]; k++)
- {
- if (this->c[k] == '\n')
- {
- nlines++;
- }
- nbytes++;
- }
- lnum[i - 1] = nlines;
- }
- FoundEOF:
-
- /* if this is a zero-length file, add 1 line */
- if (nlines == 0)
- {
- this = blkget(1); /* get the new text block */
- strcpy(this->c, "\n"); /* put a line in it */
-
- lnum[1] = 1; /* block 1 ends with line 1 */
- nlines = 1; /* there is 1 line in the file */
- nbytes = 1;
- }
-
- #if MSDOS || TOS
- /* each line has an extra CR that we didn't count yet */
- nbytes += nlines;
- #endif
-
- /* report the number of lines in the file */
- msg("\"%s\" %s %ld line%s, %ld char%s",
- origname,
- (tstflag(file, READONLY) ? "[READONLY]" : ""),
- nlines,
- nlines == 1 ? "" : "s",
- nbytes,
- nbytes == 1 ? "" : "s");
- }
-
- /* initialize the cursor to start of line 1 */
- cursor = MARK_FIRST;
-
- /* close the original file */
- close(origfd);
-
- /* any other messages? */
- if (tstflag(file, HADNUL))
- {
- msg("This file contained NULs. They've been changed to \\x80 chars");
- }
- if (tstflag(file, ADDEDNL))
- {
- msg("Newline characters have been inserted to break up long lines");
- }
- #ifndef CRUNCH
- if (tstflag(file, HADBS))
- {
- msg("Backspace characters deleted due to ':set beautify'");
- }
- #endif
-
- storename(origname);
-
- #ifndef NO_MODELINES
- if (nlines > 10)
- {
- do_modelines(1L, 5L);
- do_modelines(nlines - 4L, nlines);
- }
- else
- {
- do_modelines(1L, nlines);
- }
- #endif
-
- /* force all blocks out onto the disk, to support file recovery */
- blksync();
-
- return 0;
- }
-
-
-
- /* This function copies the temp file back onto an original file.
- * Returns TRUE if successful, or FALSE if the file could NOT be saved.
- */
- int tmpsave(filename, bang)
- char *filename; /* the name to save it to */
- int bang; /* forced write? */
- {
- int fd; /* fd of the file we're writing to */
- REG int len; /* length of a text block */
- REG BLK *this; /* a text block */
- long bytes; /* byte counter */
- REG int i;
-
- /* if no filename is given, assume the original file name */
- if (!filename || !*filename)
- {
- filename = origname;
- }
-
- /* if still no file name, then fail */
- if (!*filename)
- {
- msg("Don't know a name for this file -- NOT WRITTEN");
- return FALSE;
- }
-
- /* can't rewrite a READONLY file */
- #if AMIGA
- if (!strcmp(filename, origname) && tstflag(file, READONLY) && !bang)
- #else
- if (!strcmp(filename, origname) && *o_readonly && !bang)
- #endif
- {
- msg("\"%s\" [READONLY] -- NOT WRITTEN", filename);
- return FALSE;
- }
-
- /* open the file */
- if (*filename == '>' && filename[1] == '>')
- {
- filename += 2;
- while (*filename == ' ' || *filename == '\t')
- {
- filename++;
- }
- #ifdef O_APPEND
- fd = open(filename, O_WRONLY|O_APPEND);
- #else
- fd = open(filename, O_WRONLY);
- lseek(fd, 0L, 2);
- #endif
- }
- else
- {
- /* either the file must not exist, or it must be the original
- * file, or we must have a bang, or "writeany" must be set.
- */
- if (strcmp(filename, origname) && access(filename, 0) == 0 && !bang
- #ifndef CRUNCH
- && !*o_writeany
- #endif
- )
- {
- msg("File already exists - Use :w! to overwrite");
- return FALSE;
- }
- #if VMS
- /* Create a new VMS version of this file. */
- {
- char *strrchr(), *ptr = strrchr(filename,';');
- if (ptr) *ptr = '\0'; /* Snip off any ;number in the name */
- }
- #endif
- fd = creat(filename, FILEPERMS);
- }
- if (fd < 0)
- {
- msg("Can't write to \"%s\" -- NOT WRITTEN", filename);
- return FALSE;
- }
-
- /* write each text block to the file */
- bytes = 0L;
- for (i = 1; i < MAXBLKS && (this = blkget(i)) && this->c[0]; i++)
- {
- for (len = 0; len < BLKSIZE && this->c[len]; len++)
- {
- }
- if (twrite(fd, this->c, len) < len)
- {
- msg("Trouble writing to \"%s\"", filename);
- if (!strcmp(filename, origname))
- {
- setflag(file, MODIFIED);
- }
- close(fd);
- return FALSE;
- }
- bytes += len;
- }
-
- /* reset the "modified" flag, but not the "undoable" flag */
- clrflag(file, MODIFIED);
- significant = FALSE;
-
- /* report lines & characters */
- #if MSDOS || TOS
- bytes += nlines; /* for the inserted carriage returns */
- #endif
- msg("Wrote \"%s\" %ld lines, %ld characters", filename, nlines, bytes);
-
- /* close the file */
- close(fd);
-
- return TRUE;
- }
-
-
- /* This function deletes the temporary file. If the file has been modified
- * and "bang" is FALSE, then it returns FALSE without doing anything; else
- * it returns TRUE.
- *
- * If the "autowrite" option is set, then instead of returning FALSE when
- * the file has been modified and "bang" is false, it will call tmpend().
- */
- int tmpabort(bang)
- int bang;
- {
- /* if there is no file, return successfully */
- if (tmpfd < 0)
- {
- return TRUE;
- }
-
- /* see if we must return FALSE -- can't quit */
- if (!bang && tstflag(file, MODIFIED))
- {
- /* if "autowrite" is set, then act like tmpend() */
- if (*o_autowrite)
- return tmpend(bang);
- else
- return FALSE;
- }
-
- /* delete the tmp file */
- cutswitch();
- strcpy(prevorig, origname);
- prevline = markline(cursor);
- *origname = '\0';
- origtime = 0L;
- blkinit();
- nlines = 0;
- initflags();
- return TRUE;
- }
-
- /* This function saves the file if it has been modified, and then deletes
- * the temporary file. Returns TRUE if successful, or FALSE if the file
- * needs to be saved but can't be. When it returns FALSE, it will not have
- * deleted the tmp file, either.
- */
- int tmpend(bang)
- int bang;
- {
- /* save the file if it has been modified */
- if (tstflag(file, MODIFIED) && !tmpsave((char *)0, FALSE) && !bang)
- {
- return FALSE;
- }
-
- /* delete the tmp file */
- tmpabort(TRUE);
-
- return TRUE;
- }
-
-
- /* If the tmp file has been changed, then this function will force those
- * changes to be written to the disk, so that the tmp file will survive a
- * system crash or power failure.
- */
- #if AMIGA || MSDOS || TOS
- sync()
- {
- /* MS-DOS and TOS don't flush their buffers until the file is closed,
- * so here we close the tmp file and then immediately reopen it.
- */
- close(tmpfd);
- tmpfd = open(tmpname, O_RDWR | O_BINARY);
- return 0;
- }
- #endif
-
-
- /* This function stores the file's name in the second block of the temp file.
- * SLEAZE ALERT! SLEAZE ALERT! The "tmpblk" buffer is probably being used
- * to store the arguments to a command, so we can't use it here. Instead,
- * we'll borrow the buffer that is used for "shift-U".
- */
- storename(name)
- char *name; /* the name of the file - normally origname */
- {
- #ifndef CRUNCH
- int len;
- char *ptr;
- #endif
-
- /* we're going to clobber the U_text buffer, so reset U_line */
- U_line = 0L;
-
- if (!name)
- {
- strncpy(U_text, "", BLKSIZE);
- U_text[1] = 127;
- }
- #ifndef CRUNCH
- else if (*name != SLASH)
- {
- /* get the directory name */
- ptr = getcwd(U_text, BLKSIZE);
- if (ptr != U_text)
- {
- strcpy(U_text, ptr);
- }
-
- /* append a slash to the directory name */
- len = strlen(U_text);
- U_text[len++] = SLASH;
-
- /* append the filename, padded with heaps o' NULs */
- strncpy(U_text + len, *name ? name : "foo", BLKSIZE - len);
- }
- #endif
- else
- {
- /* copy the filename into U_text */
- strncpy(U_text, *name ? name : "foo", BLKSIZE);
- }
-
- if (tmpfd >= 0)
- {
- /* write the name out to second block of the temp file */
- lseek(tmpfd, (long)BLKSIZE, 0);
- write(tmpfd, U_text, (unsigned)BLKSIZE);
- }
- return 0;
- }
-
-
-
- /* This function handles deadly signals. It restores sanity to the terminal
- * preserves the current temp file, and deletes any old temp files.
- */
- int deathtrap(sig)
- int sig; /* the deadly signal that we caught */
- {
- char *why;
-
- /* restore the terminal's sanity */
- endwin();
-
- #ifdef CRUNCH
- why = "-Elvis died";
- #else
- /* give a more specific description of how Elvis died */
- switch (sig)
- {
- # ifdef SIGHUP
- case SIGHUP: why = "-the modem lost its carrier"; break;
- # endif
- # ifndef DEBUG
- # ifdef SIGILL
- case SIGILL: why = "-Elvis hit an illegal instruction"; break;
- # endif
- # ifdef SIGBUS
- case SIGBUS: why = "-Elvis had a bus error"; break;
- # endif
- # if defined(SIGSEGV) && !defined(TOS)
- case SIGSEGV: why = "-Elvis had a segmentation violation"; break;
- # endif
- # ifdef SIGSYS
- case SIGSYS: why = "-Elvis munged a system call"; break;
- # endif
- # endif /* !DEBUG */
- # ifdef SIGPIPE
- case SIGPIPE: why = "-the pipe reader died"; break;
- # endif
- # ifdef SIGTERM
- case SIGTERM: why = "-Elvis was terminated"; break;
- # endif
- # if !MINIX
- # ifdef SIGUSR1
- case SIGUSR1: why = "-Elvis was killed via SIGUSR1"; break;
- # endif
- # ifdef SIGUSR2
- case SIGUSR2: why = "-Elvis was killed via SIGUSR2"; break;
- # endif
- # endif
- default: why = "-Elvis died"; break;
- }
- #endif
-
- /* if we had a temp file going, then preserve it */
- if (tmpnum > 0 && tmpfd >= 0)
- {
- close(tmpfd);
- sprintf(tmpblk.c, "%s \"%s\" %s", PRESERVE, why, tmpname);
- system(tmpblk.c);
- }
-
- /* delete any old temp files */
- cutend();
-
- /* exit with the proper exit status */
- exit(sig);
- }
-