|
|
1.1 root 1: /* tmp.c */
2:
3: /* Author:
4: * Steve Kirkendall
5: * 14407 SW Teal Blvd. #C
6: * Beaverton, OR 97005
7: * [email protected]
8: */
9:
10:
11: /* This file contains functions which create & readback a TMPFILE */
12:
13:
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
32:
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 */
37: {
38: char *str; /* used to scan through the line */
39: char *start; /* points to the start of the line */
40: char buf[80];
41:
42: /* if modelines are disabled, then do nothing */
43: if (!*o_modelines)
44: {
45: return;
46: }
47:
48: /* for each line... */
49: for (; l <= stop; l++)
50: {
51: /* for each position in the line.. */
52: for (str = fetchline(l); *str; str++)
53: {
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] == ':')
58: {
59: start = str += 3;
60:
61: /* find the end */
62: for (str = start + strlen(start); *--str != ':'; )
63: {
64: }
65:
66: /* if it is a well-formed modeline, execute it */
67: if (str > start && str - start < sizeof buf)
68: {
69: strncpy(buf, start, (int)(str - start));
70: exstring(buf, str - start, '\\');
71: break;
72: }
73: }
74: }
75: }
76: }
77: #endif
78:
79:
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)
82:
83: /* This is the name of the temp file */
84: static char tmpname[80];
85:
86: /* This function creates the temp file and copies the original file into it.
87: * Returns if successful, or stops execution if it fails.
88: */
89: int tmpstart(filename)
90: char *filename; /* name of the original file */
91: {
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;
101:
102: /* switching to a different file certainly counts as a change */
103: changes++;
104: redraw(MARK_UNSET, FALSE);
105:
106: /* open the original file for reading */
107: *origname = '\0';
108: if (filename && *filename)
109: {
110: strcpy(origname, filename);
111: origfd = open(origname, O_RDONLY);
112: if (origfd < 0 && errno != ENOENT)
113: {
114: msg("Can't open \"%s\"", origname);
115: return tmpstart("");
116: }
117: if (origfd >= 0)
118: {
119: if (stat(origname, &statb) < 0)
120: {
121: FAIL("Can't stat \"%s\"", origname);
122: }
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
132: {
133: msg("\"%s\" is not a regular file", origname);
134: return tmpstart("");
135: }
136: }
137: else
138: {
139: stat(".", &statb);
140: }
141: if (origfd >= 0)
142: {
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
150: if (*o_readonly || !(statb.st_mode & S_IWRITE))
151: #endif
152: #if TOS
153: # ifdef __GNUC__
154: if (*o_readonly || !(statb.st_mode & S_IWRITE))
155: # else
156: if (*o_readonly || (statb.st_mode & S_IJRON))
157: # endif
158: #endif
159: #if ANY_UNIX
160: if (*o_readonly || !(statb.st_mode &
161: ((geteuid() == 0) ? 0222 :
162: ((statb.st_uid != geteuid() ? 0022 : 0200)))))
163: #endif
164: #if VMS
165: if (*o_readonly)
166: #endif
167: {
168: setflag(file, READONLY);
169: }
170: }
171: else
172: {
173: origtime = 0L;
174: }
175: }
176: else
177: {
178: setflag(file, NOFILE);
179: origfd = -1;
180: origtime = 0L;
181: stat(".", &statb);
182: }
183:
184: /* make a name for the tmp file */
185: tmpnum++;
186: #if MSDOS || TOS
187: /* MS-Dos doesn't allow multiple slashes, but supports drives
188: * with current directories.
189: * This relies on TMPNAME beginning with "%s\\"!!!!
190: */
191: strcpy(tmpname, o_directory);
192: if ((i = strlen(tmpname)) && !strchr(":/\\", tmpname[i-1]))
193: tmpname[i++]=SLASH;
194: sprintf(tmpname+i, TMPNAME+3, getpid(), tmpnum);
195: #else
196: sprintf(tmpname, TMPNAME, o_directory, getpid(), tmpnum);
197: #endif
198:
199: /* make sure nobody else is editing the same file */
200: if (access(tmpname, 0) == 0)
201: {
202: FAIL("Temp file \"%s\" already exists?", tmpname);
203: }
204:
205: /* create the temp file */
206: #if ANY_UNIX
207: close(creat(tmpname, 0600)); /* only we can read it */
208: #else
209: close(creat(tmpname, FILEPERMS)); /* anybody body can read it, alas */
210: #endif
211: tmpfd = open(tmpname, O_RDWR | O_BINARY);
212: if (tmpfd < 0)
213: {
214: FAIL("Can't create temp file... Does directory \"%s\" exist?", o_directory);
215: return 1;
216: }
217:
218: /* allocate space for the header in the file */
219: write(tmpfd, hdr.c, (unsigned)BLKSIZE);
220: write(tmpfd, tmpblk.c, (unsigned)BLKSIZE);
221:
222: #ifndef NO_RECYCLE
223: /* initialize the block allocator */
224: /* This must already be done here, before the first attempt
225: * to write to the new file! GB */
226: garbage();
227: #endif
228:
229: /* initialize lnum[] */
230: for (i = 1; i < MAXBLKS; i++)
231: {
232: lnum[i] = INFINITY;
233: }
234: lnum[0] = 0;
235:
236: /* if there is no original file, then create a 1-line file */
237: if (origfd < 0)
238: {
239: hdr.n[0] = 0; /* invalid inode# denotes new file */
240:
241: this = blkget(1); /* get the new text block */
242: strcpy(this->c, "\n"); /* put a line in it */
243:
244: lnum[1] = 1L; /* block 1 ends with line 1 */
245: nlines = 1L; /* there is 1 line in the file */
246: nbytes = 1L;
247:
248: if (*origname)
249: {
250: msg("\"%s\" [NEW FILE] 1 line, 1 char", origname);
251: }
252: else
253: {
254: msg("\"[NO FILE]\" 1 line, 1 char");
255: }
256: }
257: else /* there is an original file -- read it in */
258: {
259: nbytes = nlines = 0;
260:
261: /* preallocate 1 "next" buffer */
262: i = 1;
263: next = blkget(i);
264: inbuf = 0;
265:
266: /* loop, moving blocks from orig to tmp */
267: for (;;)
268: {
269: /* "next" buffer becomes "this" buffer */
270: this = next;
271:
272: /* read [more] text into this block */
273: nread = tread(origfd, &this->c[inbuf], BLKSIZE - 1 - inbuf);
274: if (nread < 0)
275: {
276: close(origfd);
277: close(tmpfd);
278: tmpfd = -1;
279: unlink(tmpname);
280: FAIL("Error reading \"%s\"", origname);
281: }
282:
283: /* convert NUL characters to something else */
284: for (j = k = inbuf; k < inbuf + nread; k++)
285: {
286: if (!this->c[k])
287: {
288: setflag(file, HADNUL);
289: this->c[j++] = 0x80;
290: }
291: #ifndef CRUNCH
292: else if (*o_beautify && this->c[k] < ' ' && this->c[k] >= 1)
293: {
294: if (this->c[k] == '\t'
295: || this->c[k] == '\n'
296: || this->c[k] == '\f')
297: {
298: this->c[j++] = this->c[k];
299: }
300: else if (this->c[k] == '\b')
301: {
302: /* delete '\b', but complain */
303: setflag(file, HADBS);
304: }
305: /* else silently delete control char */
306: }
307: #endif
308: else
309: {
310: this->c[j++] = this->c[k];
311: }
312: }
313: inbuf = j;
314:
315: /* if the buffer is empty, quit */
316: if (inbuf == 0)
317: {
318: goto FoundEOF;
319: }
320:
321: #if MSDOS || TOS
322: /* BAH! MS text mode read fills inbuf, then compresses eliminating \r
323: but leaving garbage at end of buf. The same is true for TURBOC. GB. */
324:
325: memset(this->c + inbuf, '\0', BLKSIZE - inbuf);
326: #endif
327:
328: /* search backward for last newline */
329: for (k = inbuf; --k >= 0 && this->c[k] != '\n';)
330: {
331: }
332: if (k++ < 0)
333: {
334: if (inbuf >= BLKSIZE - 1)
335: {
336: k = 80;
337: }
338: else
339: {
340: k = inbuf;
341: }
342: }
343:
344: /* allocate next buffer */
345: next = blkget(++i);
346:
347: /* move fragmentary last line to next buffer */
348: inbuf -= k;
349: for (j = 0; k < BLKSIZE; j++, k++)
350: {
351: next->c[j] = this->c[k];
352: this->c[k] = 0;
353: }
354:
355: /* if necessary, add a newline to this buf */
356: for (k = BLKSIZE - inbuf; --k >= 0 && !this->c[k]; )
357: {
358: }
359: if (this->c[k] != '\n')
360: {
361: setflag(file, ADDEDNL);
362: this->c[k + 1] = '\n';
363: }
364:
365: /* count the lines in this block */
366: for (k = 0; k < BLKSIZE && this->c[k]; k++)
367: {
368: if (this->c[k] == '\n')
369: {
370: nlines++;
371: }
372: nbytes++;
373: }
374: lnum[i - 1] = nlines;
375: }
376: FoundEOF:
377:
378: /* if this is a zero-length file, add 1 line */
379: if (nlines == 0)
380: {
381: this = blkget(1); /* get the new text block */
382: strcpy(this->c, "\n"); /* put a line in it */
383:
384: lnum[1] = 1; /* block 1 ends with line 1 */
385: nlines = 1; /* there is 1 line in the file */
386: nbytes = 1;
387: }
388:
389: #if MSDOS || TOS
390: /* each line has an extra CR that we didn't count yet */
391: nbytes += nlines;
392: #endif
393:
394: /* report the number of lines in the file */
395: msg("\"%s\" %s %ld line%s, %ld char%s",
396: origname,
397: (tstflag(file, READONLY) ? "[READONLY]" : ""),
398: nlines,
399: nlines == 1 ? "" : "s",
400: nbytes,
401: nbytes == 1 ? "" : "s");
402: }
403:
404: /* initialize the cursor to start of line 1 */
405: cursor = MARK_FIRST;
406:
407: /* close the original file */
408: close(origfd);
409:
410: /* any other messages? */
411: if (tstflag(file, HADNUL))
412: {
413: msg("This file contained NULs. They've been changed to \\x80 chars");
414: }
415: if (tstflag(file, ADDEDNL))
416: {
417: msg("Newline characters have been inserted to break up long lines");
418: }
419: #ifndef CRUNCH
420: if (tstflag(file, HADBS))
421: {
422: msg("Backspace characters deleted due to ':set beautify'");
423: }
424: #endif
425:
426: storename(origname);
427:
428: #ifndef NO_MODELINES
429: if (nlines > 10)
430: {
431: do_modelines(1L, 5L);
432: do_modelines(nlines - 4L, nlines);
433: }
434: else
435: {
436: do_modelines(1L, nlines);
437: }
438: #endif
439:
440: /* force all blocks out onto the disk, to support file recovery */
441: blksync();
442:
443: return 0;
444: }
445:
446:
447:
448: /* This function copies the temp file back onto an original file.
449: * Returns TRUE if successful, or FALSE if the file could NOT be saved.
450: */
451: int tmpsave(filename, bang)
452: char *filename; /* the name to save it to */
453: int bang; /* forced write? */
454: {
455: int fd; /* fd of the file we're writing to */
456: REG int len; /* length of a text block */
457: REG BLK *this; /* a text block */
458: long bytes; /* byte counter */
459: REG int i;
460:
461: /* if no filename is given, assume the original file name */
462: if (!filename || !*filename)
463: {
464: filename = origname;
465: }
466:
467: /* if still no file name, then fail */
468: if (!*filename)
469: {
470: msg("Don't know a name for this file -- NOT WRITTEN");
471: return FALSE;
472: }
473:
474: /* can't rewrite a READONLY file */
475: if (!strcmp(filename, origname) && tstflag(file, READONLY) && !bang)
476: {
477: msg("\"%s\" [READONLY] -- NOT WRITTEN", filename);
478: return FALSE;
479: }
480:
481: /* open the file */
482: if (*filename == '>' && filename[1] == '>')
483: {
484: filename += 2;
485: while (*filename == ' ' || *filename == '\t')
486: {
487: filename++;
488: }
489: #ifdef O_APPEND
490: fd = open(filename, O_WRONLY|O_APPEND);
491: #else
492: fd = open(filename, O_WRONLY);
493: lseek(fd, 0L, 2);
494: #endif
495: }
496: else
497: {
498: /* either the file must not exist, or it must be the original
499: * file, or we must have a bang, or "writeany" must be set.
500: */
501: if (strcmp(filename, origname) && access(filename, 0) == 0 && !bang
502: #ifndef CRUNCH
503: && !*o_writeany
504: #endif
505: )
506: {
507: msg("File already exists - Use :w! to overwrite");
508: return FALSE;
509: }
510: #if VMS
511: /* Create a new VMS version of this file. */
512: {
513: char *strrchr(), *ptr = strrchr(filename,';');
514: if (ptr) *ptr = '\0'; /* Snip off any ;number in the name */
515: }
516: #endif
517: fd = creat(filename, FILEPERMS);
518: }
519: if (fd < 0)
520: {
521: msg("Can't write to \"%s\" -- NOT WRITTEN", filename);
522: return FALSE;
523: }
524:
525: /* write each text block to the file */
526: bytes = 0L;
527: for (i = 1; i < MAXBLKS && (this = blkget(i)) && this->c[0]; i++)
528: {
529: for (len = 0; len < BLKSIZE && this->c[len]; len++)
530: {
531: }
532: if (twrite(fd, this->c, len) < len)
533: {
534: msg("Trouble writing to \"%s\"", filename);
535: if (!strcmp(filename, origname))
536: {
537: setflag(file, MODIFIED);
538: }
539: close(fd);
540: return FALSE;
541: }
542: bytes += len;
543: }
544:
545: /* reset the "modified" flag, but not the "undoable" flag */
546: clrflag(file, MODIFIED);
547: significant = FALSE;
548: if (!strcmp(origname, filename))
549: {
550: exitcode |= 1;
551: }
552:
553: /* report lines & characters */
554: #if MSDOS || TOS
555: bytes += nlines; /* for the inserted carriage returns */
556: #endif
557: msg("Wrote \"%s\" %ld lines, %ld characters", filename, nlines, bytes);
558:
559: /* close the file */
560: close(fd);
561:
562: return TRUE;
563: }
564:
565:
566: /* This function deletes the temporary file. If the file has been modified
567: * and "bang" is FALSE, then it returns FALSE without doing anything; else
568: * it returns TRUE.
569: *
570: * If the "autowrite" option is set, then instead of returning FALSE when
571: * the file has been modified and "bang" is false, it will call tmpend().
572: */
573: int tmpabort(bang)
574: int bang;
575: {
576: /* if there is no file, return successfully */
577: if (tmpfd < 0)
578: {
579: return TRUE;
580: }
581:
582: /* see if we must return FALSE -- can't quit */
583: if (!bang && tstflag(file, MODIFIED))
584: {
585: /* if "autowrite" is set, then act like tmpend() */
586: if (*o_autowrite)
587: return tmpend(bang);
588: else
589: return FALSE;
590: }
591:
592: /* delete the tmp file */
593: cutswitch();
594: strcpy(prevorig, origname);
595: prevline = markline(cursor);
596: *origname = '\0';
597: origtime = 0L;
598: blkinit();
599: nlines = 0;
600: initflags();
601: return TRUE;
602: }
603:
604: /* This function saves the file if it has been modified, and then deletes
605: * the temporary file. Returns TRUE if successful, or FALSE if the file
606: * needs to be saved but can't be. When it returns FALSE, it will not have
607: * deleted the tmp file, either.
608: */
609: int tmpend(bang)
610: int bang;
611: {
612: /* save the file if it has been modified */
613: if (tstflag(file, MODIFIED) && !tmpsave((char *)0, FALSE) && !bang)
614: {
615: return FALSE;
616: }
617:
618: /* delete the tmp file */
619: tmpabort(TRUE);
620:
621: return TRUE;
622: }
623:
624:
625: /* If the tmp file has been changed, then this function will force those
626: * changes to be written to the disk, so that the tmp file will survive a
627: * system crash or power failure.
628: */
629: #if AMIGA || MSDOS || TOS
630: sync()
631: {
632: /* MS-DOS and TOS don't flush their buffers until the file is closed,
633: * so here we close the tmp file and then immediately reopen it.
634: */
635: close(tmpfd);
636: tmpfd = open(tmpname, O_RDWR | O_BINARY);
637: return 0;
638: }
639: #endif
640:
641:
642: /* This function stores the file's name in the second block of the temp file.
643: * SLEAZE ALERT! SLEAZE ALERT! The "tmpblk" buffer is probably being used
644: * to store the arguments to a command, so we can't use it here. Instead,
645: * we'll borrow the buffer that is used for "shift-U".
646: */
647: int
648: storename(name)
649: char *name; /* the name of the file - normally origname */
650: {
651: #ifndef CRUNCH
652: int len;
653: char *ptr;
654: #endif
655:
656: /* we're going to clobber the U_text buffer, so reset U_line */
657: U_line = 0L;
658:
659: if (!name)
660: {
661: strncpy(U_text, "", BLKSIZE);
662: U_text[1] = 127;
663: }
664: #ifndef CRUNCH
665: else if (*name != SLASH)
666: {
667: /* get the directory name */
668: ptr = getcwd(U_text, BLKSIZE);
669: if (ptr != U_text)
670: {
671: strcpy(U_text, ptr);
672: }
673:
674: /* append a slash to the directory name */
675: len = strlen(U_text);
676: U_text[len++] = SLASH;
677:
678: /* append the filename, padded with heaps o' NULs */
679: strncpy(U_text + len, *name ? name : "foo", BLKSIZE - len);
680: }
681: #endif
682: else
683: {
684: /* copy the filename into U_text */
685: strncpy(U_text, *name ? name : "foo", BLKSIZE);
686: }
687:
688: if (tmpfd >= 0)
689: {
690: /* write the name out to second block of the temp file */
691: lseek(tmpfd, (long)BLKSIZE, 0);
692: write(tmpfd, U_text, (unsigned)BLKSIZE);
693: }
694: return 0;
695: }
696:
697:
698:
699: /* This function handles deadly signals. It restores sanity to the terminal
700: * preserves the current temp file, and deletes any old temp files.
701: */
702: int deathtrap(sig)
703: int sig; /* the deadly signal that we caught */
704: {
705: char *why;
706:
707: /* restore the terminal's sanity */
708: endwin();
709:
710: #ifdef CRUNCH
711: why = "-Elvis died";
712: #else
713: /* give a more specific description of how Elvis died */
714: switch (sig)
715: {
716: # ifdef SIGHUP
717: case SIGHUP: why = "-the modem lost its carrier"; break;
718: # endif
719: # ifndef DEBUG
720: # ifdef SIGILL
721: case SIGILL: why = "-Elvis hit an illegal instruction"; break;
722: # endif
723: # ifdef SIGBUS
724: case SIGBUS: why = "-Elvis had a bus error"; break;
725: # endif
726: # ifdef SIGSEGV
727: # if !TOS
728: case SIGSEGV: why = "-Elvis had a segmentation violation"; break;
729: # endif
730: # endif
731: # ifdef SIGSYS
732: case SIGSYS: why = "-Elvis munged a system call"; break;
733: # endif
734: # endif /* !DEBUG */
735: # ifdef SIGPIPE
736: case SIGPIPE: why = "-the pipe reader died"; break;
737: # endif
738: # ifdef SIGTERM
739: case SIGTERM: why = "-Elvis was terminated"; break;
740: # endif
741: # if !MINIX
742: # ifdef SIGUSR1
743: case SIGUSR1: why = "-Elvis was killed via SIGUSR1"; break;
744: # endif
745: # ifdef SIGUSR2
746: case SIGUSR2: why = "-Elvis was killed via SIGUSR2"; break;
747: # endif
748: # endif
749: default: why = "-Elvis died"; break;
750: }
751: #endif
752:
753: /* if we had a temp file going, then preserve it */
754: if (tmpnum > 0 && tmpfd >= 0)
755: {
756: close(tmpfd);
757: sprintf(tmpblk.c, "%s \"%s\" %s", PRESERVE, why, tmpname);
758: system(tmpblk.c);
759: }
760:
761: /* delete any old temp files */
762: cutend();
763:
764: /* exit with the proper exit status */
765: exit(sig);
766: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.