|
|
1.1 root 1: /* tmpfile.c */
2:
3: /* Author:
4: * Steve Kirkendall
5: * 16820 SW Tallac Way
6: * Beaverton, OR 97006
7: * [email protected], or ...uunet!tektronix!psueea!jove!kirkenda
8: */
9:
10:
11: /* This file contains functions which create & readback a TMPFILE */
12:
13:
14: #include "config.h"
15: #include <ctype.h>
16: #include "vi.h"
17: #if TOS
18: # include <stat.h>
19: #else
20: # if OSK
21: # include "osk.h"
22: # else
23: # include <sys/stat.h>
24: # endif
25: #endif
26:
27:
28: #ifndef NO_MODELINE
29: static void do_modeline(l, stop)
30: long l; /* line number to start at */
31: long stop; /* line number to stop at */
32: {
33: char *str; /* used to scan through the line */
34: char *start; /* points to the start of the line */
35: char buf[80];
36:
37: /* if modelines are disabled, then do nothing */
38: if (!*o_modeline)
39: {
40: return;
41: }
42:
43: /* for each line... */
44: for (l = 1; l <= stop; l++)
45: {
46: /* for each position in the line.. */
47: for (str = fetchline(l); *str; str++)
48: {
49: /* if it is the start of a modeline command... */
50: if ((str[0] == 'e' && str[1] == 'x'
51: || str[0] == 'v' && str[1] == 'i')
52: && str[2] == ':')
53: {
54: start = str += 3;
55:
56: /* find the end */
57: while (*str && *str != ':')
58: {
59: str++;
60: }
61:
62: /* if it is a well-formed modeline, execute it */
63: if (*str && str - start < sizeof buf)
64: {
65: strncpy(buf, start, (int)(str - start));
66: buf[str - start] = '\0';
67: doexcmd(buf);
68: break;
69: }
70: }
71: }
72: }
73: }
74: #endif
75:
76:
77: /* The FAIL() macro prints an error message and then exits. */
78: #define FAIL(why,arg) mode = MODE_EX; msg(why, arg); endwin(); exit(9)
79:
80: /* This is the name of the temp file */
81: static char tmpname[80];
82:
83: /* This function creates the temp file and copies the original file into it.
84: * Returns if successful, or stops execution if it fails.
85: */
86: int tmpstart(filename)
87: char *filename; /* name of the original file */
88: {
89: int origfd; /* fd used for reading the original file */
90: struct stat statb; /* stat buffer, used to examine inode */
91: REG BLK *this; /* pointer to the current block buffer */
92: REG BLK *next; /* pointer to the next block buffer */
93: int inbuf; /* number of characters in a buffer */
94: int nread; /* number of bytes read */
95: REG int j, k;
96: int i;
97: int sum; /* used for calculating a checksum for this */
98: char *scan;
99: long nbytes;
100:
101: /* switching to a different file certainly counts as a change */
102: changes++;
103: redraw(MARK_UNSET, FALSE);
104:
105: /* open the original file for reading */
106: *origname = '\0';
107: if (filename && *filename)
108: {
109: strcpy(origname, filename);
110: origfd = open(origname, O_RDONLY);
111: if (origfd < 0 && errno != ENOENT)
112: {
113: msg("Can't open \"%s\"", origname);
114: return tmpstart("");
115: }
116: if (origfd >= 0)
117: {
118: if (stat(origname, &statb) < 0)
119: {
120: FAIL("Can't stat \"%s\"", origname);
121: }
122: #if TOS
123: if (origfd >= 0 && (statb.st_mode & S_IJDIR))
124: #else
125: # if OSK
126: if (origfd >= 0 && (statb.st_mode & S_IFDIR))
127: # else
128: if (origfd >= 0 && (statb.st_mode & S_IFMT) != S_IFREG)
129: # endif
130: #endif
131: {
132: msg("\"%s\" is not a regular file", origname);
133: return tmpstart("");
134: }
135: }
136: else
137: {
138: stat(".", &statb);
139: }
140: if (origfd >= 0)
141: {
142: origtime = statb.st_mtime;
143: #if MSDOS || OSK
144: if (*o_readonly || !(statb.st_mode & S_IWRITE))
145: #endif
146: #if TOS
147: if (*o_readonly || (statb.st_mode & S_IJRON))
148: #endif
149: #if ANY_UNIX
150: if (*o_readonly || !(statb.st_mode &
151: (statb.st_uid != geteuid() ? 0022 : 0200)))
152: #endif
153: {
154: setflag(file, READONLY);
155: }
156: }
157: else
158: {
159: origtime = 0L;
160: }
161: }
162: else
163: {
164: setflag(file, NOFILE);
165: origfd = -1;
166: origtime = 0L;
167: stat(".", &statb);
168: }
169:
170: /* generate a checksum from the file's name */
171: for (sum = 0, scan = origname + strlen(origname);
172: --scan >= origname && (isascii(*scan) && isalnum(*scan) || *scan == '.');
173: sum = sum + *scan)
174: {
175: }
176: sum &= 0xf;
177:
178: /* make a name for the tmp file */
179: #if MSDOS || TOS
180: /* MS-Dos doesn't allow multiple slashes, but supports drives
181: * with current directories.
182: * This relies on TMPNAME beginning with "%s\\"!!!!
183: */
184: strcpy(tmpname, o_directory);
185: if ((i = strlen(tmpname)) && !strchr(":/\\", tmpname[i-1]))
186: tmpname[i++]=SLASH;
187: sprintf(tmpname+i, TMPNAME+3, sum, statb.st_ino, statb.st_dev);
188: #else
189: sprintf(tmpname, TMPNAME, o_directory, sum, statb.st_ino, statb.st_dev);
190: #endif
191:
192: /* make sure nobody else is editing the same file */
193: if (access(tmpname, 0) == 0)
194: {
195: if (*origname)
196: {
197: msg("\"%s\" is busy", filename);
198: return tmpstart("");
199: }
200: FAIL("\"%s\" is busy", filename);
201: }
202:
203: /* create the temp file */
204: #if ANY_UNIX
205: close(creat(tmpname, 0600)); /* only we can read it */
206: #else
207: close(creat(tmpname, FILEPERMS)); /* anybody body can read it, alas */
208: #endif
209: tmpfd = open(tmpname, O_RDWR | O_BINARY);
210: if (tmpfd < 0)
211: {
212: FAIL("Can't create temporary file, errno=%d", errno);
213: return 1;
214: }
215:
216: /* allocate space for the header in the file */
217: write(tmpfd, hdr.c, (unsigned)BLKSIZE);
218:
219: #ifndef NO_RECYCLE
220: /* initialize the block allocator */
221: /* This must already be done here, before the first attempt
222: * to write to the new file! GB */
223: garbage();
224: #endif
225:
226: /* initialize lnum[] */
227: for (i = 1; i < MAXBLKS; i++)
228: {
229: lnum[i] = INFINITY;
230: }
231: lnum[0] = 0;
232:
233: /* if there is no original file, then create a 1-line file */
234: if (origfd < 0)
235: {
236: hdr.n[0] = 0; /* invalid inode# denotes new file */
237:
238: this = blkget(1); /* get the new text block */
239: strcpy(this->c, "\n"); /* put a line in it */
240:
241: lnum[1] = 1L; /* block 1 ends with line 1 */
242: nlines = 1L; /* there is 1 line in the file */
243: nbytes = 1L;
244:
245: if (*origname)
246: {
247: msg("\"%s\" [NEW FILE] 1 line, 1 char", origname);
248: }
249: else
250: {
251: msg("\"[NO FILE]\" 1 line, 1 char");
252: }
253: }
254: else /* there is an original file -- read it in */
255: {
256: hdr.n[0] = statb.st_ino;
257: nbytes = nlines = 0;
258:
259: /* preallocate 1 "next" buffer */
260: i = 1;
261: next = blkget(i);
262: inbuf = 0;
263:
264: /* loop, moving blocks from orig to tmp */
265: for (;;)
266: {
267: /* "next" buffer becomes "this" buffer */
268: this = next;
269:
270: /* read [more] text into this block */
271: nread = tread(origfd, &this->c[inbuf], BLKSIZE - 1 - inbuf);
272: if (nread < 0)
273: {
274: close(origfd);
275: close(tmpfd);
276: tmpfd = -1;
277: unlink(tmpname);
278: FAIL("Error reading \"%s\"", origname);
279: }
280:
281: /* convert NUL characters to something else */
282: for (k = inbuf; k < inbuf + nread; k++)
283: {
284: if (!this->c[k])
285: {
286: setflag(file, HADNUL);
287: this->c[k] = 0x80;
288: }
289: }
290: inbuf += nread;
291:
292: /* if the buffer is empty, quit */
293: if (inbuf == 0)
294: {
295: goto FoundEOF;
296: }
297:
298: #if MSDOS || TOS
299: /* BAH! MS text mode read fills inbuf, then compresses eliminating \r
300: but leaving garbage at end of buf. The same is true for TURBOC. GB. */
301:
302: memset(this->c + inbuf, '\0', BLKSIZE - inbuf);
303: #endif
304:
305: /* search backward for last newline */
306: for (k = inbuf; --k >= 0 && this->c[k] != '\n';)
307: {
308: }
309: if (k++ < 0)
310: {
311: if (inbuf >= BLKSIZE - 1)
312: {
313: k = 80;
314: }
315: else
316: {
317: k = inbuf;
318: }
319: }
320:
321: /* allocate next buffer */
322: next = blkget(++i);
323:
324: /* move fragmentary last line to next buffer */
325: inbuf -= k;
326: for (j = 0; k < BLKSIZE; j++, k++)
327: {
328: next->c[j] = this->c[k];
329: this->c[k] = 0;
330: }
331:
332: /* if necessary, add a newline to this buf */
333: for (k = BLKSIZE - inbuf; --k >= 0 && !this->c[k]; )
334: {
335: }
336: if (this->c[k] != '\n')
337: {
338: setflag(file, ADDEDNL);
339: this->c[k + 1] = '\n';
340: }
341:
342: /* count the lines in this block */
343: for (k = 0; k < BLKSIZE && this->c[k]; k++)
344: {
345: if (this->c[k] == '\n')
346: {
347: nlines++;
348: }
349: nbytes++;
350: }
351: lnum[i - 1] = nlines;
352: }
353: FoundEOF:
354:
355: /* if this is a zero-length file, add 1 line */
356: if (nlines == 0)
357: {
358: this = blkget(1); /* get the new text block */
359: strcpy(this->c, "\n"); /* put a line in it */
360:
361: lnum[1] = 1; /* block 1 ends with line 1 */
362: nlines = 1; /* there is 1 line in the file */
363: nbytes = 1;
364: }
365:
366: #if MSDOS || TOS
367: /* each line has an extra CR that we didn't count yet */
368: nbytes += nlines;
369: #endif
370:
371: /* report the number of lines in the file */
372: msg("\"%s\" %s %ld line%s, %ld char%s",
373: origname,
374: (tstflag(file, READONLY) ? "[READONLY]" : ""),
375: nlines,
376: nlines == 1 ? "" : "s",
377: nbytes,
378: nbytes == 1 ? "" : "s");
379: }
380:
381: /* initialize the cursor to start of line 1 */
382: cursor = MARK_FIRST;
383:
384: /* close the original file */
385: close(origfd);
386:
387: /* any other messages? */
388: if (tstflag(file, HADNUL))
389: {
390: msg("This file contained NULs. They've been changed to \\x80 chars");
391: }
392: if (tstflag(file, ADDEDNL))
393: {
394: msg("Newline characters have been inserted to break up long lines");
395: }
396:
397: #ifndef NO_MODELINE
398: if (nlines > 10)
399: {
400: do_modeline(1L, 5L);
401: do_modeline(nlines - 4L, nlines);
402: }
403: else
404: {
405: do_modeline(1L, nlines);
406: }
407: #endif
408: return 0;
409: }
410:
411:
412:
413: /* This function copies the temp file back onto an original file.
414: * Returns TRUE if successful, or FALSE if the file could NOT be saved.
415: */
416: int tmpsave(filename, bang)
417: char *filename; /* the name to save it to */
418: int bang; /* forced write? */
419: {
420: int fd; /* fd of the file we're writing to */
421: REG int len; /* length of a text block */
422: REG BLK *this; /* a text block */
423: long bytes; /* byte counter */
424: REG int i;
425:
426: /* if no filename is given, assume the original file name */
427: if (!filename || !*filename)
428: {
429: filename = origname;
430: }
431:
432: /* if still no file name, then fail */
433: if (!*filename)
434: {
435: msg("Don't know a name for this file -- NOT WRITTEN");
436: return FALSE;
437: }
438:
439: /* can't rewrite a READONLY file */
440: if (!strcmp(filename, origname) && *o_readonly && !bang)
441: {
442: msg("\"%s\" [READONLY] -- NOT WRITTEN", filename);
443: return FALSE;
444: }
445:
446: /* open the file */
447: if (*filename == '>' && filename[1] == '>')
448: {
449: filename += 2;
450: while (*filename == ' ' || *filename == '\t')
451: {
452: filename++;
453: }
454: #ifdef O_APPEND
455: fd = open(filename, O_WRONLY|O_APPEND);
456: #else
457: fd = open(filename, O_WRONLY);
458: lseek(fd, 0L, 2);
459: #endif
460: }
461: else
462: {
463: /* either the file must not exist, or it must be the original
464: * file, or we must have a bang
465: */
466: if (strcmp(filename, origname) && access(filename, 0) == 0 && !bang)
467: {
468: msg("File already exists - Use :w! to overwrite");
469: return FALSE;
470: }
471: fd = creat(filename, FILEPERMS);
472: }
473: if (fd < 0)
474: {
475: msg("Can't write to \"%s\" -- NOT WRITTEN", filename);
476: return FALSE;
477: }
478:
479: /* write each text block to the file */
480: bytes = 0L;
481: for (i = 1; i < MAXBLKS && (this = blkget(i)) && this->c[0]; i++)
482: {
483: for (len = 0; len < BLKSIZE && this->c[len]; len++)
484: {
485: }
486: twrite(fd, this->c, len);
487: bytes += len;
488: }
489:
490: /* reset the "modified" flag */
491: clrflag(file, MODIFIED);
492: significant = FALSE;
493:
494: /* report lines & characters */
495: #if MSDOS || TOS
496: bytes += nlines; /* for the inserted carriage returns */
497: #endif
498: if (strncmp(filename, o_directory, strlen(o_directory)))
499: {
500: msg("Wrote \"%s\" %ld lines, %ld characters", filename, nlines, bytes);
501: }
502:
503: /* close the file */
504: close(fd);
505:
506: return TRUE;
507: }
508:
509:
510: /* This function deletes the temporary file. If the file has been modified
511: * and "bang" is FALSE, then it returns FALSE without doing anything; else
512: * it returns TRUE.
513: *
514: * If the "autowrite" option is set, then instead of returning FALSE when
515: * the file has been modified and "bang" is false, it will call tmpend().
516: */
517: int tmpabort(bang)
518: int bang;
519: {
520: /* if there is no file, return successfully */
521: if (tmpfd < 0)
522: {
523: return TRUE;
524: }
525:
526: /* see if we must return FALSE -- can't quit */
527: if (!bang && tstflag(file, MODIFIED))
528: {
529: /* if "autowrite" is set, then act like tmpend() */
530: if (*o_autowrite)
531: return tmpend(bang);
532: else
533: return FALSE;
534: }
535:
536: /* delete the tmp file */
537: cutswitch(tmpname);
538: close(tmpfd);
539: tmpfd = -1;
540: unlink(tmpname);
541: strcpy(prevorig, origname);
542: prevline = markline(cursor);
543: *origname = '\0';
544: origtime = 0L;
545: blkinit();
546: nlines = 0;
547: initflags();
548: return TRUE;
549: }
550:
551: /* This function saves the file if it has been modified, and then deletes
552: * the temporary file. Returns TRUE if successful, or FALSE if the file
553: * needs to be saved but can't be. When it returns FALSE, it will not have
554: * deleted the tmp file, either.
555: */
556: int tmpend(bang)
557: int bang;
558: {
559: /* save the file if it has been modified */
560: if (tstflag(file, MODIFIED) && !tmpsave((char *)0, FALSE) && !bang)
561: {
562: return FALSE;
563: }
564:
565: /* delete the tmp file */
566: tmpabort(TRUE);
567:
568: return TRUE;
569: }
570:
571:
572: /* If the tmp file has been changed, then this function will force those
573: * changes to be written to the disk, so that the tmp file will survive a
574: * system crash or power failure.
575: */
576: #if MSDOS || TOS || OSK
577: sync()
578: {
579: # if OSK
580: /* OS9 doesn't need an explicit sync operation, but the linker
581: * demands something called sync(), so this is a dummy function.
582: */
583: #else
584: /* MS-DOS and TOS don't flush their buffers until the file is closed,
585: * so here we close the tmp file and then immediately reopen it.
586: */
587: close(tmpfd);
588: tmpfd = open(tmpname, O_RDWR | O_BINARY);
589: #endif
590: }
591: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.