pci: don't do sanity check for missing pci bus, the check can misfire.
[minix.git] / commands / elvis / tmp.c
blob9102169d8b324fb7be030840ef26403268718407
1 /* tmp.c */
3 /* Author:
4 * Steve Kirkendall
5 * 14407 SW Teal Blvd. #C
6 * Beaverton, OR 97005
7 * kirkenda@cs.pdx.edu
8 */
11 /* This file contains functions which create & readback a TMPFILE */
14 #include "config.h"
15 #include "vi.h"
16 #if TOS
17 # include <stat.h>
18 #else
19 # if OSK
20 # include "osk.h"
21 # else
22 # if AMIGA
23 # include "amistat.h"
24 # else
25 # include <sys/stat.h>
26 # endif
27 # endif
28 #endif
29 #if TURBOC
30 # include <process.h>
31 #endif
33 #ifndef NO_MODELINES
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 */
40 char buf[80];
42 /* if modelines are disabled, then do nothing */
43 if (!*o_modelines)
45 return;
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')
57 && str[2] == ':')
59 start = str += 3;
61 /* find the end */
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, '\\');
71 break;
77 #endif
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 */
98 REG int j, k;
99 int i;
100 long nbytes;
102 /* switching to a different file certainly counts as a change */
103 changes++;
104 redraw(MARK_UNSET, FALSE);
106 /* open the original file for reading */
107 *origname = '\0';
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);
115 return tmpstart("");
117 if (origfd >= 0)
119 if (stat(origname, &statb) < 0)
121 FAIL("Can't stat \"%s\"", origname);
123 #if TOS
124 if (origfd >= 0 && (statb.st_mode & S_IJDIR))
125 #else
126 # if OSK
127 if (origfd >= 0 && (statb.st_mode & S_IFDIR))
128 # else
129 if (origfd >= 0 && (statb.st_mode & S_IFMT) != S_IFREG)
130 # endif
131 #endif
133 msg("\"%s\" is not a regular file", origname);
134 return tmpstart("");
137 else
139 stat(".", &statb);
141 if (origfd >= 0)
143 origtime = statb.st_mtime;
144 #if OSK
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)))))
148 #endif
149 #if AMIGA || MSDOS || (TOS && defined(__GNUC__))
150 if (*o_readonly || !(statb.st_mode & S_IWRITE))
151 #endif
152 #if TOS && !defined(__GNUC__)
153 if (*o_readonly || (statb.st_mode & S_IJRON))
154 #endif
155 #if ANY_UNIX
156 if (*o_readonly || !(statb.st_mode &
157 ((geteuid() == 0) ? 0222 :
158 ((statb.st_uid != geteuid() ? 0022 : 0200)))))
159 #endif
160 #if VMS
161 if (*o_readonly)
162 #endif
164 setflag(file, READONLY);
167 else
169 origtime = 0L;
172 else
174 setflag(file, NOFILE);
175 origfd = -1;
176 origtime = 0L;
177 stat(".", &statb);
180 /* make a name for the tmp file */
181 tmpnum++;
182 #if MSDOS || TOS
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]))
189 tmpname[i++]=SLASH;
190 sprintf(tmpname+i, TMPNAME+3, getpid(), tmpnum);
191 #else
192 sprintf(tmpname, TMPNAME, o_directory, getpid(), tmpnum);
193 #endif
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 */
202 #if ANY_UNIX
203 close(creat(tmpname, 0600)); /* only we can read it */
204 #else
205 close(creat(tmpname, FILEPERMS)); /* anybody body can read it, alas */
206 #endif
207 tmpfd = open(tmpname, O_RDWR | O_BINARY);
208 if (tmpfd < 0)
210 FAIL("Can't create temp file... Does directory \"%s\" exist?", o_directory);
211 return 1;
214 /* allocate space for the header in the file */
215 write(tmpfd, hdr.c, (unsigned)BLKSIZE);
216 write(tmpfd, tmpblk.c, (unsigned)BLKSIZE);
218 #ifndef NO_RECYCLE
219 /* initialize the block allocator */
220 /* This must already be done here, before the first attempt
221 * to write to the new file! GB */
222 garbage();
223 #endif
225 /* initialize lnum[] */
226 for (i = 1; i < MAXBLKS; i++)
228 lnum[i] = INFINITY;
230 lnum[0] = 0;
232 /* if there is no original file, then create a 1-line file */
233 if (origfd < 0)
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 */
242 nbytes = 1L;
244 if (*origname)
246 msg("\"%s\" [NEW FILE] 1 line, 1 char", origname);
248 else
250 msg("\"[NO FILE]\" 1 line, 1 char");
253 else /* there is an original file -- read it in */
255 nbytes = nlines = 0;
257 /* preallocate 1 "next" buffer */
258 i = 1;
259 next = blkget(i);
260 inbuf = 0;
262 /* loop, moving blocks from orig to tmp */
263 for (;;)
265 /* "next" buffer becomes "this" buffer */
266 this = next;
268 /* read [more] text into this block */
269 nread = tread(origfd, &this->c[inbuf], BLKSIZE - 1 - inbuf);
270 if (nread < 0)
272 close(origfd);
273 close(tmpfd);
274 tmpfd = -1;
275 unlink(tmpname);
276 FAIL("Error reading \"%s\"", origname);
279 /* convert NUL characters to something else */
280 for (j = k = inbuf; k < inbuf + nread; k++)
282 if (!this->c[k])
284 setflag(file, HADNUL);
285 this->c[j++] = 0x80;
287 #ifndef CRUNCH
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 */
303 #endif
304 else
306 this->c[j++] = this->c[k];
309 inbuf = j;
311 /* if the buffer is empty, quit */
312 if (inbuf == 0)
314 goto FoundEOF;
317 #if MSDOS || TOS
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);
322 #endif
324 /* search backward for last newline */
325 for (k = inbuf; --k >= 0 && this->c[k] != '\n';)
328 if (k++ < 0)
330 if (inbuf >= BLKSIZE - 1)
332 k = 80;
334 else
336 k = inbuf;
340 /* allocate next buffer */
341 next = blkget(++i);
343 /* move fragmentary last line to next buffer */
344 inbuf -= k;
345 for (j = 0; k < BLKSIZE; j++, k++)
347 next->c[j] = this->c[k];
348 this->c[k] = 0;
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')
366 nlines++;
368 nbytes++;
370 if(i >= MAXBLKS) {
371 FAIL("Too many blocks: %d.", i);
373 lnum[i - 1] = nlines;
375 FoundEOF:
377 /* if this is a zero-length file, add 1 line */
378 if (nlines == 0)
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 */
385 nbytes = 1;
388 #if MSDOS || TOS
389 /* each line has an extra CR that we didn't count yet */
390 nbytes += nlines;
391 #endif
393 /* report the number of lines in the file */
394 msg("\"%s\" %s %ld line%s, %ld char%s",
395 origname,
396 (tstflag(file, READONLY) ? "[READONLY]" : ""),
397 nlines,
398 nlines == 1 ? "" : "s",
399 nbytes,
400 nbytes == 1 ? "" : "s");
403 /* initialize the cursor to start of line 1 */
404 cursor = MARK_FIRST;
406 /* close the original file */
407 close(origfd);
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");
418 #ifndef CRUNCH
419 if (tstflag(file, HADBS))
421 msg("Backspace characters deleted due to ':set beautify'");
423 #endif
425 storename(origname);
427 #ifndef NO_MODELINES
428 if (nlines > 10)
430 do_modelines(1L, 5L);
431 do_modelines(nlines - 4L, nlines);
433 else
435 do_modelines(1L, nlines);
437 #endif
439 /* force all blocks out onto the disk, to support file recovery */
440 blksync();
442 return 0;
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 */
458 REG int i;
460 /* if no filename is given, assume the original file name */
461 if (!filename || !*filename)
463 filename = origname;
466 /* if still no file name, then fail */
467 if (!*filename)
469 msg("Don't know a name for this file -- NOT WRITTEN");
470 return FALSE;
473 /* can't rewrite a READONLY file */
474 #if AMIGA
475 if (!strcmp(filename, origname) && tstflag(file, READONLY) && !bang)
476 #else
477 if (!strcmp(filename, origname) && *o_readonly && !bang)
478 #endif
480 msg("\"%s\" [READONLY] -- NOT WRITTEN", filename);
481 return FALSE;
484 /* open the file */
485 if (*filename == '>' && filename[1] == '>')
487 filename += 2;
488 while (*filename == ' ' || *filename == '\t')
490 filename++;
492 #ifdef O_APPEND
493 fd = open(filename, O_WRONLY|O_APPEND);
494 #else
495 fd = open(filename, O_WRONLY);
496 lseek(fd, 0L, 2);
497 #endif
499 else
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
505 #ifndef CRUNCH
506 && !*o_writeany
507 #endif
510 msg("File already exists - Use :w! to overwrite");
511 return FALSE;
513 #if VMS
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 */
519 #endif
520 fd = creat(filename, FILEPERMS);
522 if (fd < 0)
524 msg("Can't write to \"%s\" -- NOT WRITTEN", filename);
525 return FALSE;
528 /* write each text block to the file */
529 bytes = 0L;
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);
542 close(fd);
543 return FALSE;
545 bytes += len;
548 /* reset the "modified" flag, but not the "undoable" flag */
549 clrflag(file, MODIFIED);
550 significant = FALSE;
552 /* report lines & characters */
553 #if MSDOS || TOS
554 bytes += nlines; /* for the inserted carriage returns */
555 #endif
556 msg("Wrote \"%s\" %ld lines, %ld characters", filename, nlines, bytes);
558 /* close the file */
559 close(fd);
561 return TRUE;
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
567 * it returns TRUE.
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().
572 int tmpabort(bang)
573 int bang;
575 /* if there is no file, return successfully */
576 if (tmpfd < 0)
578 return TRUE;
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() */
585 if (*o_autowrite)
586 return tmpend(bang);
587 else
588 return FALSE;
591 /* delete the tmp file */
592 cutswitch();
593 strcpy(prevorig, origname);
594 prevline = markline(cursor);
595 *origname = '\0';
596 origtime = 0L;
597 blkinit();
598 nlines = 0;
599 initflags();
600 return TRUE;
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.
608 int tmpend(bang)
609 int bang;
611 /* save the file if it has been modified */
612 if (tstflag(file, MODIFIED) && !tmpsave((char *)0, FALSE) && !bang)
614 return FALSE;
617 /* delete the tmp file */
618 tmpabort(TRUE);
620 return TRUE;
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
629 sync()
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.
634 close(tmpfd);
635 tmpfd = open(tmpname, O_RDWR | O_BINARY);
636 return 0;
638 #endif
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".
646 storename(name)
647 char *name; /* the name of the file - normally origname */
649 #ifndef CRUNCH
650 int len;
651 char *ptr;
652 #endif
654 /* we're going to clobber the U_text buffer, so reset U_line */
655 U_line = 0L;
657 if (!name)
659 strncpy(U_text, "", BLKSIZE);
660 U_text[1] = 127;
662 #ifndef CRUNCH
663 else if (*name != SLASH)
665 /* get the directory name */
666 ptr = getcwd(U_text, BLKSIZE);
667 if (ptr != U_text)
669 strcpy(U_text, ptr);
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);
679 #endif
680 else
682 /* copy the filename into U_text */
683 strncpy(U_text, *name ? name : "foo", BLKSIZE);
686 if (tmpfd >= 0)
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);
692 return 0;
697 /* This function handles deadly signals. It restores sanity to the terminal
698 * preserves the current temp file, and deletes any old temp files.
700 int deathtrap(sig)
701 int sig; /* the deadly signal that we caught */
703 char *why;
705 /* restore the terminal's sanity */
706 endwin();
708 #ifdef CRUNCH
709 why = "-Elvis died";
710 #else
711 /* give a more specific description of how Elvis died */
712 switch (sig)
714 # ifdef SIGHUP
715 case SIGHUP: why = "-the modem lost its carrier"; break;
716 # endif
717 # ifndef DEBUG
718 # ifdef SIGILL
719 case SIGILL: why = "-Elvis hit an illegal instruction"; break;
720 # endif
721 # ifdef SIGBUS
722 case SIGBUS: why = "-Elvis had a bus error"; break;
723 # endif
724 # if defined(SIGSEGV) && !defined(TOS)
725 case SIGSEGV: why = "-Elvis had a segmentation violation"; break;
726 # endif
727 # ifdef SIGSYS
728 case SIGSYS: why = "-Elvis munged a system call"; break;
729 # endif
730 # endif /* !DEBUG */
731 # ifdef SIGPIPE
732 case SIGPIPE: why = "-the pipe reader died"; break;
733 # endif
734 # ifdef SIGTERM
735 case SIGTERM: why = "-Elvis was terminated"; break;
736 # endif
737 # if !MINIX
738 # ifdef SIGUSR1
739 case SIGUSR1: why = "-Elvis was killed via SIGUSR1"; break;
740 # endif
741 # ifdef SIGUSR2
742 case SIGUSR2: why = "-Elvis was killed via SIGUSR2"; break;
743 # endif
744 # endif
745 default: why = "-Elvis died"; break;
747 #endif
749 /* if we had a temp file going, then preserve it */
750 if (tmpnum > 0 && tmpfd >= 0)
752 close(tmpfd);
753 sprintf(tmpblk.c, "%s \"%s\" %s", PRESERVE, why, tmpname);
754 system(tmpblk.c);
757 /* delete any old temp files */
758 cutend();
760 /* exit with the proper exit status */
761 exit(sig);