source: trunk/minix/commands/elvis/tmp.c@ 12

Last change on this file since 12 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 16.2 KB
Line 
1/* tmp.c */
2
3/* Author:
4 * Steve Kirkendall
5 * 14407 SW Teal Blvd. #C
6 * Beaverton, OR 97005
7 * kirkenda@cs.pdx.edu
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
34static 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 */
84static 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 */
89int 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 || (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
163 {
164 setflag(file, READONLY);
165 }
166 }
167 else
168 {
169 origtime = 0L;
170 }
171 }
172 else
173 {
174 setflag(file, NOFILE);
175 origfd = -1;
176 origtime = 0L;
177 stat(".", &statb);
178 }
179
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\\"!!!!
186 */
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
194
195 /* make sure nobody else is editing the same file */
196 if (access(tmpname, 0) == 0)
197 {
198 FAIL("Temp file \"%s\" already exists?", tmpname);
199 }
200
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)
209 {
210 FAIL("Can't create temp file... Does directory \"%s\" exist?", o_directory);
211 return 1;
212 }
213
214 /* allocate space for the header in the file */
215 write(tmpfd, hdr.c, (unsigned)BLKSIZE);
216 write(tmpfd, tmpblk.c, (unsigned)BLKSIZE);
217
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
224
225 /* initialize lnum[] */
226 for (i = 1; i < MAXBLKS; i++)
227 {
228 lnum[i] = INFINITY;
229 }
230 lnum[0] = 0;
231
232 /* if there is no original file, then create a 1-line file */
233 if (origfd < 0)
234 {
235 hdr.n[0] = 0; /* invalid inode# denotes new file */
236
237 this = blkget(1); /* get the new text block */
238 strcpy(this->c, "\n"); /* put a line in it */
239
240 lnum[1] = 1L; /* block 1 ends with line 1 */
241 nlines = 1L; /* there is 1 line in the file */
242 nbytes = 1L;
243
244 if (*origname)
245 {
246 msg("\"%s\" [NEW FILE] 1 line, 1 char", origname);
247 }
248 else
249 {
250 msg("\"[NO FILE]\" 1 line, 1 char");
251 }
252 }
253 else /* there is an original file -- read it in */
254 {
255 nbytes = nlines = 0;
256
257 /* preallocate 1 "next" buffer */
258 i = 1;
259 next = blkget(i);
260 inbuf = 0;
261
262 /* loop, moving blocks from orig to tmp */
263 for (;;)
264 {
265 /* "next" buffer becomes "this" buffer */
266 this = next;
267
268 /* read [more] text into this block */
269 nread = tread(origfd, &this->c[inbuf], BLKSIZE - 1 - inbuf);
270 if (nread < 0)
271 {
272 close(origfd);
273 close(tmpfd);
274 tmpfd = -1;
275 unlink(tmpname);
276 FAIL("Error reading \"%s\"", origname);
277 }
278
279 /* convert NUL characters to something else */
280 for (j = k = inbuf; k < inbuf + nread; k++)
281 {
282 if (!this->c[k])
283 {
284 setflag(file, HADNUL);
285 this->c[j++] = 0x80;
286 }
287#ifndef CRUNCH
288 else if (*o_beautify && this->c[k] < ' ' && this->c[k] > 0)
289 {
290 if (this->c[k] == '\t'
291 || this->c[k] == '\n'
292 || this->c[k] == '\f')
293 {
294 this->c[j++] = this->c[k];
295 }
296 else if (this->c[k] == '\b')
297 {
298 /* delete '\b', but complain */
299 setflag(file, HADBS);
300 }
301 /* else silently delete control char */
302 }
303#endif
304 else
305 {
306 this->c[j++] = this->c[k];
307 }
308 }
309 inbuf = j;
310
311 /* if the buffer is empty, quit */
312 if (inbuf == 0)
313 {
314 goto FoundEOF;
315 }
316
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. */
320
321 memset(this->c + inbuf, '\0', BLKSIZE - inbuf);
322#endif
323
324 /* search backward for last newline */
325 for (k = inbuf; --k >= 0 && this->c[k] != '\n';)
326 {
327 }
328 if (k++ < 0)
329 {
330 if (inbuf >= BLKSIZE - 1)
331 {
332 k = 80;
333 }
334 else
335 {
336 k = inbuf;
337 }
338 }
339
340 /* allocate next buffer */
341 next = blkget(++i);
342
343 /* move fragmentary last line to next buffer */
344 inbuf -= k;
345 for (j = 0; k < BLKSIZE; j++, k++)
346 {
347 next->c[j] = this->c[k];
348 this->c[k] = 0;
349 }
350
351 /* if necessary, add a newline to this buf */
352 for (k = BLKSIZE - inbuf; --k >= 0 && !this->c[k]; )
353 {
354 }
355 if (this->c[k] != '\n')
356 {
357 setflag(file, ADDEDNL);
358 this->c[k + 1] = '\n';
359 }
360
361 /* count the lines in this block */
362 for (k = 0; k < BLKSIZE && this->c[k]; k++)
363 {
364 if (this->c[k] == '\n')
365 {
366 nlines++;
367 }
368 nbytes++;
369 }
370 lnum[i - 1] = nlines;
371 }
372FoundEOF:
373
374 /* if this is a zero-length file, add 1 line */
375 if (nlines == 0)
376 {
377 this = blkget(1); /* get the new text block */
378 strcpy(this->c, "\n"); /* put a line in it */
379
380 lnum[1] = 1; /* block 1 ends with line 1 */
381 nlines = 1; /* there is 1 line in the file */
382 nbytes = 1;
383 }
384
385#if MSDOS || TOS
386 /* each line has an extra CR that we didn't count yet */
387 nbytes += nlines;
388#endif
389
390 /* report the number of lines in the file */
391 msg("\"%s\" %s %ld line%s, %ld char%s",
392 origname,
393 (tstflag(file, READONLY) ? "[READONLY]" : ""),
394 nlines,
395 nlines == 1 ? "" : "s",
396 nbytes,
397 nbytes == 1 ? "" : "s");
398 }
399
400 /* initialize the cursor to start of line 1 */
401 cursor = MARK_FIRST;
402
403 /* close the original file */
404 close(origfd);
405
406 /* any other messages? */
407 if (tstflag(file, HADNUL))
408 {
409 msg("This file contained NULs. They've been changed to \\x80 chars");
410 }
411 if (tstflag(file, ADDEDNL))
412 {
413 msg("Newline characters have been inserted to break up long lines");
414 }
415#ifndef CRUNCH
416 if (tstflag(file, HADBS))
417 {
418 msg("Backspace characters deleted due to ':set beautify'");
419 }
420#endif
421
422 storename(origname);
423
424#ifndef NO_MODELINES
425 if (nlines > 10)
426 {
427 do_modelines(1L, 5L);
428 do_modelines(nlines - 4L, nlines);
429 }
430 else
431 {
432 do_modelines(1L, nlines);
433 }
434#endif
435
436 /* force all blocks out onto the disk, to support file recovery */
437 blksync();
438
439 return 0;
440}
441
442
443
444/* This function copies the temp file back onto an original file.
445 * Returns TRUE if successful, or FALSE if the file could NOT be saved.
446 */
447int tmpsave(filename, bang)
448 char *filename; /* the name to save it to */
449 int bang; /* forced write? */
450{
451 int fd; /* fd of the file we're writing to */
452 REG int len; /* length of a text block */
453 REG BLK *this; /* a text block */
454 long bytes; /* byte counter */
455 REG int i;
456
457 /* if no filename is given, assume the original file name */
458 if (!filename || !*filename)
459 {
460 filename = origname;
461 }
462
463 /* if still no file name, then fail */
464 if (!*filename)
465 {
466 msg("Don't know a name for this file -- NOT WRITTEN");
467 return FALSE;
468 }
469
470 /* can't rewrite a READONLY file */
471#if AMIGA
472 if (!strcmp(filename, origname) && tstflag(file, READONLY) && !bang)
473#else
474 if (!strcmp(filename, origname) && *o_readonly && !bang)
475#endif
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
549 /* report lines & characters */
550#if MSDOS || TOS
551 bytes += nlines; /* for the inserted carriage returns */
552#endif
553 msg("Wrote \"%s\" %ld lines, %ld characters", filename, nlines, bytes);
554
555 /* close the file */
556 close(fd);
557
558 return TRUE;
559}
560
561
562/* This function deletes the temporary file. If the file has been modified
563 * and "bang" is FALSE, then it returns FALSE without doing anything; else
564 * it returns TRUE.
565 *
566 * If the "autowrite" option is set, then instead of returning FALSE when
567 * the file has been modified and "bang" is false, it will call tmpend().
568 */
569int tmpabort(bang)
570 int bang;
571{
572 /* if there is no file, return successfully */
573 if (tmpfd < 0)
574 {
575 return TRUE;
576 }
577
578 /* see if we must return FALSE -- can't quit */
579 if (!bang && tstflag(file, MODIFIED))
580 {
581 /* if "autowrite" is set, then act like tmpend() */
582 if (*o_autowrite)
583 return tmpend(bang);
584 else
585 return FALSE;
586 }
587
588 /* delete the tmp file */
589 cutswitch();
590 strcpy(prevorig, origname);
591 prevline = markline(cursor);
592 *origname = '\0';
593 origtime = 0L;
594 blkinit();
595 nlines = 0;
596 initflags();
597 return TRUE;
598}
599
600/* This function saves the file if it has been modified, and then deletes
601 * the temporary file. Returns TRUE if successful, or FALSE if the file
602 * needs to be saved but can't be. When it returns FALSE, it will not have
603 * deleted the tmp file, either.
604 */
605int tmpend(bang)
606 int bang;
607{
608 /* save the file if it has been modified */
609 if (tstflag(file, MODIFIED) && !tmpsave((char *)0, FALSE) && !bang)
610 {
611 return FALSE;
612 }
613
614 /* delete the tmp file */
615 tmpabort(TRUE);
616
617 return TRUE;
618}
619
620
621/* If the tmp file has been changed, then this function will force those
622 * changes to be written to the disk, so that the tmp file will survive a
623 * system crash or power failure.
624 */
625#if AMIGA || MSDOS || TOS
626sync()
627{
628 /* MS-DOS and TOS don't flush their buffers until the file is closed,
629 * so here we close the tmp file and then immediately reopen it.
630 */
631 close(tmpfd);
632 tmpfd = open(tmpname, O_RDWR | O_BINARY);
633 return 0;
634}
635#endif
636
637
638/* This function stores the file's name in the second block of the temp file.
639 * SLEAZE ALERT! SLEAZE ALERT! The "tmpblk" buffer is probably being used
640 * to store the arguments to a command, so we can't use it here. Instead,
641 * we'll borrow the buffer that is used for "shift-U".
642 */
643storename(name)
644 char *name; /* the name of the file - normally origname */
645{
646#ifndef CRUNCH
647 int len;
648 char *ptr;
649#endif
650
651 /* we're going to clobber the U_text buffer, so reset U_line */
652 U_line = 0L;
653
654 if (!name)
655 {
656 strncpy(U_text, "", BLKSIZE);
657 U_text[1] = 127;
658 }
659#ifndef CRUNCH
660 else if (*name != SLASH)
661 {
662 /* get the directory name */
663 ptr = getcwd(U_text, BLKSIZE);
664 if (ptr != U_text)
665 {
666 strcpy(U_text, ptr);
667 }
668
669 /* append a slash to the directory name */
670 len = strlen(U_text);
671 U_text[len++] = SLASH;
672
673 /* append the filename, padded with heaps o' NULs */
674 strncpy(U_text + len, *name ? name : "foo", BLKSIZE - len);
675 }
676#endif
677 else
678 {
679 /* copy the filename into U_text */
680 strncpy(U_text, *name ? name : "foo", BLKSIZE);
681 }
682
683 if (tmpfd >= 0)
684 {
685 /* write the name out to second block of the temp file */
686 lseek(tmpfd, (long)BLKSIZE, 0);
687 write(tmpfd, U_text, (unsigned)BLKSIZE);
688 }
689 return 0;
690}
691
692
693
694/* This function handles deadly signals. It restores sanity to the terminal
695 * preserves the current temp file, and deletes any old temp files.
696 */
697int deathtrap(sig)
698 int sig; /* the deadly signal that we caught */
699{
700 char *why;
701
702 /* restore the terminal's sanity */
703 endwin();
704
705#ifdef CRUNCH
706 why = "-Elvis died";
707#else
708 /* give a more specific description of how Elvis died */
709 switch (sig)
710 {
711# ifdef SIGHUP
712 case SIGHUP: why = "-the modem lost its carrier"; break;
713# endif
714# ifndef DEBUG
715# ifdef SIGILL
716 case SIGILL: why = "-Elvis hit an illegal instruction"; break;
717# endif
718# ifdef SIGBUS
719 case SIGBUS: why = "-Elvis had a bus error"; break;
720# endif
721# if defined(SIGSEGV) && !defined(TOS)
722 case SIGSEGV: why = "-Elvis had a segmentation violation"; break;
723# endif
724# ifdef SIGSYS
725 case SIGSYS: why = "-Elvis munged a system call"; break;
726# endif
727# endif /* !DEBUG */
728# ifdef SIGPIPE
729 case SIGPIPE: why = "-the pipe reader died"; break;
730# endif
731# ifdef SIGTERM
732 case SIGTERM: why = "-Elvis was terminated"; break;
733# endif
734# if !MINIX
735# ifdef SIGUSR1
736 case SIGUSR1: why = "-Elvis was killed via SIGUSR1"; break;
737# endif
738# ifdef SIGUSR2
739 case SIGUSR2: why = "-Elvis was killed via SIGUSR2"; break;
740# endif
741# endif
742 default: why = "-Elvis died"; break;
743 }
744#endif
745
746 /* if we had a temp file going, then preserve it */
747 if (tmpnum > 0 && tmpfd >= 0)
748 {
749 close(tmpfd);
750 sprintf(tmpblk.c, "%s \"%s\" %s", PRESERVE, why, tmpname);
751 system(tmpblk.c);
752 }
753
754 /* delete any old temp files */
755 cutend();
756
757 /* exit with the proper exit status */
758 exit(sig);
759}
Note: See TracBrowser for help on using the repository browser.