1 /* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International
2 * This software is quasi-public; it may be used freely with
3 * like software, but may NOT be sold or made part of licensed
4 * products without permission of the author.
7 * EEFILE File reading/writing functions
11 #include <stdio.h> /* Use "standard" I/O package for writing */
13 #define BUFSIZ BUFSIZE /* Some places use wrong name in stdio.h */
29 #define ENOENT (2) /* Syscall error - no such file or dir */
32 #include <sys/types.h>
37 #include <sys/file.h> /* Get open mode bits */
40 extern char *strerror(); /* Return error string for errno */
41 extern struct buffer
*make_buf(), *find_buf();
43 char *fncons(), *last_fname();
45 int hoardfd
= -1; /* Retain a FD here to ensure we can always write */
47 /* Flags for iwritfile() */
48 #define WF_SMASK 07 /* Source Mask */
49 #define WF_SBUFF 0 /* source: Buffer */
50 #define WF_SREG 1 /* source: Region */
51 #define WF_SKILL 2 /* source: Last Kill */
52 #define WF_ASK 010 /* Ask for filename to write to */
53 static int iwritfile();
55 /* EFUN: "Find File" */
56 /* Ask user for a filename and do a find_file for it.
57 * If buffer exists for that filename, select that buffer.
58 * Else create a buffer for it, and read in the file if it exists.
63 hack_file("Visit file: ", find_file
);
65 hack_file("Find file: ", find_file
);
69 /* EFUN: "Read File" */
70 /* User read_file function, asks user for a filename and reads it
72 f_rfile() { u_r_file("Read file: "); }
74 /* EFUN: "Visit File" */
75 /* Same as Read File, with different prompt.
77 f_vfile() { u_r_file("Visit file: "); }
82 { register char *f_name
;
83 register struct buffer
*b
;
85 if((f_name
= ask (prompt
))==0) /* prompt user for filename */
86 return; /* user punted... */
90 ding("No default file name.");
91 else read_file(b
-> b_fn
);
93 else read_file(f_name
);
97 /* EFUN: "Insert File" */
98 /* Asks for a filename and inserts the file at current location.
99 * Point is left at beginning, and the mark at the end.
103 hack_file("Insert file: ", ins_file
);
106 /* EFUN: "Save File" */
107 /* Save current buffer to its default file name
110 { if(cur_buf
->b_flags
&B_MODIFIED
)
111 return(iwritfile(WF_SBUFF
)); /* Write buffer, don't ask */
113 { saynow("(No changes need to be written)");
118 #if FX_SAVEFILES || FX_WFEXIT
119 /* EFUN: "Save All Files" */
120 /* F_SAVEFILES - Offer to save all modified files.
121 * With argument, doesn't ask.
122 * Returns 0 if user aborts or if an error happened.
125 { register struct buffer
*b
, *savb
;
126 register int res
= 1;
130 for (b
= buf_head
; res
&& b
; b
= b
->b_next
)
131 if ((b
->b_flags
& B_MODIFIED
) && b
->b_fn
)
132 { if(exp_p
) /* If arg, */
133 { chg_buf(b
); /* just save, don't ask */
135 continue; /* Check next buffer */
137 /* Ask user whether to save */
138 ans
= ask("Buffer %s contains changes - write out? ",
141 { res
= 0; /* User aborted */
144 if (upcase(*ans
) == 'Y')
146 res
= f_sfile(); /* Save File */
153 #endif /*FX_SAVEFILES||FX_WFEXIT*/
155 /* EFUN: "Write File" */
156 /* Write out the buffer to an output file.
159 { return iwritfile(WF_ASK
|WF_SBUFF
);
162 /* EFUN: "Write Region" */
163 /* Write region out to a file
166 { return iwritfile(WF_ASK
|WF_SREG
); /* Ask, write region */
170 /* EFUN: "Write Last Kill" (not EMACS) */
171 /* Write current kill buffer out to a file.
172 ** This is mainly for MINIX.
174 extern int kill_ptr
; /* From EEF3 */
175 extern SBSTR
*kill_ring
[];
178 { return iwritfile(WF_ASK
|WF_SKILL
);
183 /* HACK_FILE - intermediate subroutine
185 hack_file(prompt
, rtn
)
188 { register char *f_name
;
190 if((f_name
= ask(prompt
)) == 0)
192 if (*f_name
!= '\0') /* Check for null answer */
198 * If there is a buffer whose fn == f_name, select that buffer.
199 * Else create one with name of the last section of f_name and
200 * read the file into that buffer.
203 register char *f_name
;
204 { register struct buffer
*b
;
210 char real_name
[128]; /* File name w/ expanded ~ and $ */
211 expand_file(real_name
, f_name
);
215 for (b
= buf_head
; b
; b
= b
-> b_next
)
216 if(b
->b_fn
&& (strcmp (b
-> b_fn
, f_name
) == 0))
218 if (b
) /* if we found one */
219 { sel_buf(b
); /* go there */
220 return; /* and we are done */
222 if((fd
= open(f_name
,0)) < 0) /* See if file exists */
223 { if(errno
!= ENOENT
) /* No, check reason */
224 { ferr_ropn(); /* Strange error, complain */
225 return; /* and do nothing else. */
228 else close(fd
); /* Found! Close FD, since the */
229 /* read_file rtn will re-open. */
231 lastn
= last_fname(f_name
); /* Find buffer name */
232 b
= find_buf(lastn
); /* Is there a buffer of that name? */
233 if (b
&& (ex_blen(b
) || b
->b_fn
))
234 { ans
= ask("Buffer %s contains %s, which buffer shall I use? ",
235 b
-> b_name
, b
->b_fn
? b
->b_fn
: "something");
236 if(ans
== 0) return; /* Aborted */
237 if (*ans
!= '\0') /* if null answer, use b */
238 b
= make_buf(ans
); /* else use ans */
244 if(fd
< 0) /* If file doesn't exist, */
245 { set_fn(f_name
); /* just say "new" and set filename */
246 return; /* and return right away. */
248 if (read_file(f_name
)==0) /* File exists, read it in! */
249 { if(b
->b_fn
) /* Failed... if filename, */
250 { chkfree(b
->b_fn
); /* flush the filename. */
257 * Reads file into current buffer, flushing any
258 * previous contents (if buffer modified, will ask about saving)
259 * Returns 0 if failed.
266 char real_name
[128]; /* File name w/ expanded ~ and $ */
269 if(!zap_buffer()) /* Flush the whole buffer */
270 return; /* Unless user aborts */
272 expand_file(real_name
, f_name
);
273 f_name
= real_name
; /* Hack, hack! */
276 if (ins_file(f_name
)==0)
278 f_bufnotmod(); /* Say not modified now */
280 stat(f_name
, &s
); /* Get file stat */
281 cur_buf
->b_mtime
= s
.st_mtime
; /* and pick out last-modified time */
287 * Inserts file named f_name into current buffer at current point
288 * Point is not moved; mark is set to end of inserted stuff.
289 * Returns 0 if failed, 1 if won.
295 chroff insdot
; /* To check for range of mods */
298 char real_name
[128]; /* File name w/ expanded ~ and $ */
299 expand_file(real_name
, f_name
);
303 if((ifd
= open(f_name
,0)) < 0)
305 if((ifd
= open(f_name
,O_RDONLY
|O_UNCONVERTED
)) < 0)
307 { ferr_ropn(); /* Can't open, complain */
308 return 0; /* no redisplay */
311 if((sd
= sb_fduse(ifd
)) == 0)
312 { if (ifd
>= SB_NFILES
)
313 dingtoo(" Cannot read - too many internal files");
316 else errbarf("SB rtn cannot read file?");
322 f_setmark(); /* Set mark at current ptr */
323 if(cur_dot
!= insdot
) /* If pointer was advanced, */
324 buf_tmat(insdot
); /* then stuff was inserted */
329 ferr_ropn() { ferr(" Cannot read"); }
330 ferr_wopn() { ferr(" Cannot write"); }
335 dingtoo(strerror(errno
));
339 /* IWRITFILE - auxiliary for writing files.
340 ** Returns 1 if write successful, 0 if not.
345 { register struct buffer
*b
;
346 register char *o_name
; /* output file name */
347 int styp
= flags
& WF_SMASK
; /* Source type, one of WF_Sxxx */
350 register FILE *o_file
; /* output file pointer */
354 int ofd
; /* output file FD */
356 char fname
[FNAMSIZ
]; /* To avoid chkfree hassle */
357 char newname
[FNAMSIZ
]; /* for robustness */
358 char oldname
[FNAMSIZ
]; /* ditto */
364 char real_name
[128]; /* File name w/ expanded ~ and $ */
366 res
= 1; /* Let's keep track of success */
368 /* Check for existence of source, and set prompt string */
372 prompt
= "Write File: ";
376 { dingtoo(" No Mark!");
379 prompt
= "Write Region: ";
383 if(!kill_ring
[kill_ptr
])
384 { dingtoo("No killed stuff");
387 prompt
= "Write Last Kill: ";
390 default: /* Internal error */
391 errbarf("bad iwritfile arg");
396 { if((o_name
= ask(prompt
))==0)
397 return(0); /* User punted. */
398 strcpy(&fname
[0], o_name
); /* Copy filename onto stack */
403 if (!(flags
&WF_ASK
) || (*o_name
== '\0'))
405 { ding("No default file name.");
408 strcpy(o_name
, b
->b_fn
);
412 expand_file(real_name
, o_name
);
413 o_name
= real_name
; /* Hack, hack */
416 statres
= stat(o_name
,&statb
); /* Get old file's info (if any) */
419 /* Now, make sure someone hasn't written the file behind our backs */
420 if ((styp
==WF_SBUFF
) && !(flags
&WF_ASK
)
421 && b
->b_fn
&& stat(b
->b_fn
, &s
) >= 0)
422 if (s
.st_mtime
!= b
->b_mtime
)
424 ans
= ask("Since you last read \"%s\", someone has changed it.\nDo you want to write it anyway (NOT RECOMMENDED!)? ",
426 if (ans
== 0 || upcase(*ans
) != 'Y')
428 ding("I suggest you either read it again, or\nwrite it to a temporary file, and merge the two versions manually.");
429 if (ans
) chkfree(ans
);
432 if (ans
) chkfree(ans
);
436 /* Try to get around major UNIX screw of smashing files.
437 * This still isn't perfect (screws up with long filenames) but...
438 * 1. Write out to <newname>
439 * 2. Rename <name> to <oldname> (may have to delete existing <oldname>)
440 * 3. Rename <newname> to <name>.
442 fncons(oldname
,ev_fno1
,o_name
,ev_fno2
); /* Set up "old" filename */
443 fncons(newname
,ev_fnn1
,o_name
,ev_fnn2
); /* Set up "new" filename */
444 unlink(newname
); /* Ensure we don't clobber */
445 unhoard(); /* Now give up saved FD */
446 #if !(V6) /* Standard V6 doesn't have access call */
447 if(statres
>= 0) /* If file exists, */
448 { if(access(o_name
, 2) != 0) /* check for write access */
450 res
= 0; /* Failure */
457 { if((o_file
= fopen(newname
, "w")) ==0) /* Create new output file */
459 res
= 0; /* Failure */
462 setbuf(o_file
,obuf
); /* Ensure always have buffer */
468 if((ofd
= creat(newname
,ev_filmod
)) < 0)
470 if((ofd
= open(newname
,O_WRONLY
|O_UNCONVERTED
)) < 0)
473 res
= 0; /* Failure */
478 set_fn(o_name
); /* Won, so set default fn for buff */
482 { case WF_SBUFF
: saytoo(b
->b_fn
); break;
483 case WF_SREG
: saytoo("region"); break;
485 case WF_SKILL
: saytoo("last kill"); break;
490 saynow("Writing...");
493 #if !(TOPS20) /* T20 does all this already */
494 if(statres
>= 0) /* Get old file's modes */
495 { /* Try to duplicate them */
496 /* Do chmod first since after changing owner we may not
497 ** have permission to change mode, at least on V6.
499 chmod(newname
,statb
.st_mode
& 07777);
501 chown(newname
, (statb
.st_gid
<<8)|(statb
.st_uid
&0377));
503 chown(newname
,statb
.st_uid
,statb
.st_gid
);
507 /* If no old file existed, and we are a V6 system, try to set
508 * the modes explicitly. On V7 we're OK because the user can
509 * diddle "umask" to get whatever is desired.
510 * On TOPS-20 of course everything is all peachy.
512 else chmod(newname
, ev_filmod
);
526 if((dotcnt
= mark_dot
- cur_dot
) < 0)
532 /* WF_SKILL not implemented here */
535 putc(sb_getc(((SBBUF
*)b
)), o_file
);
537 fflush(o_file
); /* Force everything out */
538 res
= ferror(o_file
); /* Save result of stuff */
539 fclose(o_file
); /* Now flush FD */
547 res
= sb_fsave((SBBUF
*)b
, ofd
);
551 sd
= e_copyn((chroff
)(mark_dot
- cur_dot
));
552 res
= sbx_aout(sd
, 2, ofd
);
557 res
= sbx_aout(kill_ring
[kill_ptr
], 2, ofd
);
564 { ferr(" Output error");
565 res
= 0; /* Failure */
569 res
= 1; /* Success so far */
571 f_bufnotmod(); /* Reset "buffer modified" flag */
573 /* Here we effect the screw-prevention steps explained earlier. */
574 /* TOPS-20, with generation numbers, need not worry about this. */
579 #if IMAGEN /* KLH -- This conditional bracketting is prone to lossage */
580 /* Only create the .BAK file once per editing session!! */
581 if ((styp
==WF_SBUFF
) || !(b
->b_flags
& B_BACKEDUP
))
582 { if (styp
==WF_SBUFF
)
583 b
->b_flags
|= B_BACKEDUP
;
585 unlink(oldname
); /* remove any existing "old" file */
586 if(link(o_name
,oldname
) == 0) /* Rename current to "old" */
588 /* Here is the critical point... if we stop here, there is no
589 * longer any file with the appropriate filename!!!
596 if(link(newname
,o_name
) == 0) /* Rename "new" to current */
605 { dingtoo("rename error!");
611 /* Update the last-modified time for the file in this buffer */
612 if ((styp
== WF_SBUFF
) && b
->b_fn
)
614 b
->b_mtime
= s
.st_mtime
;
619 hoard(); /* Get back a retained FD */
623 /* FNCONS(dest,pre,f_name,post)
624 * Specialized routine to cons up a filename string into "dest",
625 * given prefix and postfix strings to be added onto last component of
629 fncons(dest
, pre
, f_name
, post
)
630 char *dest
,*pre
,*f_name
,*post
;
631 { register char *cp
, *cp2
;
635 *cp
= 0; /* Make dest string null initially */
636 cp2
= last_fname(f_name
); /* Get pointer to beg of last name */
637 strncat(cp
,f_name
,cp2
-f_name
); /* Copy first part of filename */
638 if(pre
) strcat(cp
, pre
); /* If prefix exists, add it on */
639 cp
= last_fname(cp
); /* Recheck in case levels added */
640 strcat(cp
, cp2
); /* Now add last name */
641 if(cp2
= post
) /* If there's a postfix, must check */
642 { cp
[FNAMELEN
-strlen(cp2
)] = 0; /* and cut dest so postfix */
643 strcat(cp
, cp2
); /* will fit on end. */
648 /* LAST_FNAME(string)
649 * Get the last component of a file name. Returns pointer to
650 * start of component; does NOT copy string!
655 { register char *cp
, *p
;
658 p
= f_name
; /* pointer to last slash */
662 p
= cp
; /* point to after the slash */
667 * Set the default filename for current buffer to "string".
671 { register struct buffer
*b
;
680 str
= strdup(string
); /* Copy now in case copying self */
685 /* Do mode determination based on file name (HACK HACK) */
687 b
->b_flags
&= ~(B_CMODE
|B_TEXTMODE
);
689 { if (strcmp(&str
[len
- 5], "draft") == 0)
690 b
->b_flags
|= B_TEXTMODE
;
692 { cp
= &str
[len
- 4];
693 if (strcmp(cp
, ".txt") == 0 ||
694 strcmp(cp
, ".mss") == 0)
695 b
->b_flags
|= B_TEXTMODE
;
699 { cp
= &str
[len
- 2];
700 if (strcmp(cp
, ".h") == 0 || strcmp(cp
, ".c") == 0)
701 b
->b_flags
|= B_CMODE
;
707 /* SAVEWORLD - Attempt to save all changes user has made.
708 * Currently this amounts to writing out all modified buffers
709 * to the files $HOME/+buffername. If a buffer is given as argument,
710 * only that buffer is saved.
711 * This is only called from the error handling routines with
712 * the TTY either gone or in normal (non-edit) mode. The "grunt"
713 * flag says whether to output feedback during the saving process.
718 { register struct buffer
*b
;
720 char sfname
[FNAMSIZ
];
721 struct buffer
*sel_mbuf();
723 unhoard(); /* Ensure a FD is free for writing */
724 if(b
= bp
) goto once
;
725 while(!bp
&& (b
= sel_mbuf(b
)))
727 once
: strcat(strcat(strcpy(sfname
,homedir
),"/+"),b
->b_name
);
728 if(grunt
) printf("Saving %s...",sfname
);
730 if((wfd
= creat(sfname
, ev_filmod
)) < 0)
732 if((wfd
= open(sfname
,O_WRONLY
|O_UNCONVERTED
)) < 0)
735 printf(" error - %s\n", strerror(errno
));
738 { sb_fsave((SBBUF
*)b
, wfd
);
740 if(grunt
) printf("\n");
742 b
->b_flags
&= ~B_MODIFIED
;
747 /* HOARD, UNHOARD - Routines to save a FD for writing, to make sure
748 * that we can always write out a buffer no matter how many
749 * file descriptors we are currently using.
751 hoard() /* Stash away a FD */
754 hoardfd
= open("nul:", 1);
756 hoardfd
= open("/dev/null", 1);
759 unhoard() /* Give up our stashed FD so it can be re-used */
769 * expand_file: expand any ~user-name/ or $env-var/ prefixes in sfn,
770 * producing the full name in dfn
772 expand_file(dfn
, sfn
)
773 register char *dfn
, *sfn
;
775 register char *sp
, *tp
;
777 register struct passwd
*pw
;
780 /* HORRIBLE, GROSS, DISGUSTING HACK: if the destination and
781 * source strings are identical (same pointer), then do not
782 * do any expansion--this happens to work with the current
783 * structure very well, since multiple expansions may happen.
790 /* If have a leading $, then expand environment variable */
799 *--tp
= 0; /* Just in case */
800 strcpy(ts
, getenv(ts
)); /* MARGINAL!! */
802 /* If have leading ~, then expand login name (null means $HOME) */
803 else if (*sfn
== '~')
805 if (*sfn
== '/' || *sfn
== 0)
806 strcpy(ts
, getenv("HOME"));
809 while (*sfn
&& *sfn
!= '/')
812 pw
= (struct passwd
*)getpwnam(ts
);
816 strcpy(ts
, pw
->pw_dir
);
820 /* Now, ts is either empty or contains the expansion;
821 * sfn has been updated correctly.