improve behaviour under VPC, fixes from nicolas tittley.
[minix.git] / commands / elle / eefile.c
blobff4fe4aff640bdaedec6626d822a0a4f6bf6c773
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.
5 */
6 /*
7 * EEFILE File reading/writing functions
8 */
10 #include "elle.h"
11 #include <stdio.h> /* Use "standard" I/O package for writing */
12 #ifndef BUFSIZ
13 #define BUFSIZ BUFSIZE /* Some places use wrong name in stdio.h */
14 #endif /*-BUFSIZ*/
15 #if V6
16 struct stat {
17 int st_dev;
18 int st_ino;
19 char *st_mode;
20 char st_nlink;
21 char st_uid;
22 char st_gid;
23 char st_size0;
24 char st_size;
25 int st_addr[8];
26 long st_atime;
27 long st_mtime;
29 #define ENOENT (2) /* Syscall error - no such file or dir */
30 #else
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #endif /*-V6*/
36 #if TOPS20
37 #include <sys/file.h> /* Get open mode bits */
38 #endif
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.
60 f_ffile()
61 { int find_file();
62 #if IMAGEN
63 hack_file("Visit file: ", find_file);
64 #else
65 hack_file("Find file: ", find_file);
66 #endif /*-IMAGEN*/
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: "); }
80 u_r_file(prompt)
81 char *prompt;
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... */
87 b = cur_buf;
88 if(*f_name == '\0')
89 { if (b -> b_fn == 0)
90 ding("No default file name.");
91 else read_file(b -> b_fn);
93 else read_file(f_name);
94 chkfree(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.
101 f_ifile()
102 { int ins_file();
103 hack_file("Insert file: ", ins_file);
106 /* EFUN: "Save File" */
107 /* Save current buffer to its default file name
109 f_sfile()
110 { if(cur_buf->b_flags&B_MODIFIED)
111 return(iwritfile(WF_SBUFF)); /* Write buffer, don't ask */
112 else
113 { saynow("(No changes need to be written)");
114 return(1);
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.
124 f_savefiles()
125 { register struct buffer *b, *savb;
126 register int res = 1;
127 char *ans;
129 savb = cur_buf;
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 */
134 res = f_sfile();
135 continue; /* Check next buffer */
137 /* Ask user whether to save */
138 ans = ask("Buffer %s contains changes - write out? ",
139 b->b_name);
140 if(ans == 0)
141 { res = 0; /* User aborted */
142 break;
144 if (upcase(*ans) == 'Y')
145 { chg_buf(b);
146 res = f_sfile(); /* Save File */
148 chkfree(ans);
150 chg_buf(savb);
151 return(res);
153 #endif /*FX_SAVEFILES||FX_WFEXIT*/
155 /* EFUN: "Write File" */
156 /* Write out the buffer to an output file.
158 f_wfile()
159 { return iwritfile(WF_ASK|WF_SBUFF);
162 /* EFUN: "Write Region" */
163 /* Write region out to a file
165 f_wreg()
166 { return iwritfile(WF_ASK|WF_SREG); /* Ask, write region */
169 #if FX_WLASTKILL
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[];
177 f_wlastkill()
178 { return iwritfile(WF_ASK|WF_SKILL);
180 #endif
183 /* HACK_FILE - intermediate subroutine
185 hack_file(prompt, rtn)
186 char *prompt;
187 int (*rtn)();
188 { register char *f_name;
190 if((f_name = ask(prompt)) == 0)
191 return;
192 if (*f_name != '\0') /* Check for null answer */
193 (*rtn)(f_name);
194 chkfree(f_name);
197 /* FIND_FILE(f_name)
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.
202 find_file(f_name)
203 register char *f_name;
204 { register struct buffer *b;
205 register char *ans;
206 char *lastn;
207 int fd;
209 #if IMAGEN
210 char real_name[128]; /* File name w/ expanded ~ and $ */
211 expand_file(real_name, f_name);
212 f_name = real_name;
213 #endif /*IMAGEN*/
215 for (b = buf_head; b; b = b -> b_next)
216 if(b->b_fn && (strcmp (b -> b_fn, f_name) == 0))
217 break;
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 */
239 chkfree(ans);
241 else
242 b = make_buf(lastn);
243 sel_buf(b);
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. */
251 b->b_fn = 0;
256 /* READ_FILE(f_name)
257 * Reads file into current buffer, flushing any
258 * previous contents (if buffer modified, will ask about saving)
259 * Returns 0 if failed.
261 read_file(f_name)
262 char *f_name;
264 #if IMAGEN
265 struct stat s;
266 char real_name[128]; /* File name w/ expanded ~ and $ */
267 #endif /*IMAGEN*/
269 if(!zap_buffer()) /* Flush the whole buffer */
270 return; /* Unless user aborts */
271 #if IMAGEN
272 expand_file(real_name, f_name);
273 f_name = real_name; /* Hack, hack! */
274 #endif /*IMAGEN*/
275 set_fn(f_name);
276 if (ins_file(f_name)==0)
277 return 0;
278 f_bufnotmod(); /* Say not modified now */
279 #if IMAGEN
280 stat(f_name, &s); /* Get file stat */
281 cur_buf->b_mtime = s.st_mtime; /* and pick out last-modified time */
282 #endif /*IMAGEN*/
283 return 1;
286 /* INS_FILE(f_name)
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.
291 ins_file (f_name)
292 char *f_name;
293 { register int ifd;
294 register SBSTR *sd;
295 chroff insdot; /* To check for range of mods */
297 #if IMAGEN
298 char real_name[128]; /* File name w/ expanded ~ and $ */
299 expand_file(real_name, f_name);
300 f_name = real_name;
301 #endif /*IMAGEN*/
302 #if !(TOPS20)
303 if((ifd = open(f_name,0)) < 0)
304 #else
305 if((ifd = open(f_name,O_RDONLY|O_UNCONVERTED)) < 0)
306 #endif /*TOPS20*/
307 { ferr_ropn(); /* Can't open, complain */
308 return 0; /* no redisplay */
310 errno = 0;
311 if((sd = sb_fduse(ifd)) == 0)
312 { if (ifd >= SB_NFILES)
313 dingtoo(" Cannot read - too many internal files");
314 else if (errno)
315 ferr_ropn();
316 else errbarf("SB rtn cannot read file?");
317 close(ifd);
318 return 0;
320 sb_sins(cur_buf,sd);
321 insdot = e_dot();
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 */
325 e_gocur();
326 return 1;
329 ferr_ropn() { ferr(" Cannot read"); }
330 ferr_wopn() { ferr(" Cannot write"); }
331 ferr(str)
332 char *str;
333 { saytoo(str);
334 saytoo(" - ");
335 dingtoo(strerror(errno));
339 /* IWRITFILE - auxiliary for writing files.
340 ** Returns 1 if write successful, 0 if not.
342 static int
343 iwritfile(flags)
344 int flags;
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 */
348 char *prompt;
349 #ifdef STDWRITE
350 register FILE *o_file; /* output file pointer */
351 char obuf[BUFSIZ];
352 chroff dotcnt;
353 #endif /*STDWRITE*/
354 int ofd; /* output file FD */
355 SBSTR *sd;
356 char fname[FNAMSIZ]; /* To avoid chkfree hassle */
357 char newname[FNAMSIZ]; /* for robustness */
358 char oldname[FNAMSIZ]; /* ditto */
359 int res;
360 struct stat statb;
361 int statres;
362 #if IMAGEN
363 struct stat s;
364 char real_name[128]; /* File name w/ expanded ~ and $ */
365 #endif /*IMAGEN*/
366 res = 1; /* Let's keep track of success */
368 /* Check for existence of source, and set prompt string */
369 switch(styp)
371 case WF_SBUFF:
372 prompt = "Write File: ";
373 break;
374 case WF_SREG:
375 if(!mark_p)
376 { dingtoo(" No Mark!");
377 return(0);
379 prompt = "Write Region: ";
380 break;
381 #if FX_WLASTKILL
382 case WF_SKILL:
383 if(!kill_ring[kill_ptr])
384 { dingtoo("No killed stuff");
385 return(0);
387 prompt = "Write Last Kill: ";
388 break;
389 #endif
390 default: /* Internal error */
391 errbarf("bad iwritfile arg");
392 return 0;
395 if (flags&WF_ASK)
396 { if((o_name = ask(prompt))==0)
397 return(0); /* User punted. */
398 strcpy(&fname[0], o_name); /* Copy filename onto stack */
399 chkfree(o_name);
401 o_name = &fname[0];
402 b = cur_buf;
403 if (!(flags&WF_ASK) || (*o_name == '\0'))
404 { if (b->b_fn == 0)
405 { ding("No default file name.");
406 return(0);
408 strcpy(o_name, b->b_fn);
411 #if IMAGEN
412 expand_file(real_name, o_name);
413 o_name = real_name; /* Hack, hack */
414 #endif /*IMAGEN*/
416 statres = stat(o_name,&statb); /* Get old file's info (if any) */
418 #if IMAGEN
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)
423 { char *ans;
424 ans = ask("Since you last read \"%s\", someone has changed it.\nDo you want to write it anyway (NOT RECOMMENDED!)? ",
425 b->b_fn);
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);
430 return(0);
432 if (ans) chkfree(ans);
434 #endif /*IMAGEN*/
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 */
449 { ferr_wopn();
450 res = 0; /* Failure */
451 goto wdone;
454 #endif /*-V6*/
455 #ifdef STDWRITE
456 if(flags&WF_ASK)
457 { if((o_file = fopen(newname, "w")) ==0) /* Create new output file */
458 { ferr_wopn();
459 res = 0; /* Failure */
460 goto wdone;
462 setbuf(o_file,obuf); /* Ensure always have buffer */
464 else /* New stuff */
465 #endif /*STDWRITE*/
467 #if !(TOPS20)
468 if((ofd = creat(newname,ev_filmod)) < 0)
469 #else
470 if((ofd = open(newname,O_WRONLY|O_UNCONVERTED)) < 0)
471 #endif /*TOPS20*/
472 { ferr_wopn();
473 res = 0; /* Failure */
474 goto wdone;
477 if (styp==WF_SBUFF)
478 set_fn(o_name); /* Won, so set default fn for buff */
479 #if IMAGEN
480 saynow("Writing ");
481 switch(styp)
482 { case WF_SBUFF: saytoo(b->b_fn); break;
483 case WF_SREG: saytoo("region"); break;
484 #if FX_WLASTKILL
485 case WF_SKILL: saytoo("last kill"); break;
486 #endif
488 sayntoo("...");
489 #else
490 saynow("Writing...");
491 #endif /*-IMAGEN*/
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);
500 #if V6
501 chown(newname, (statb.st_gid<<8)|(statb.st_uid&0377));
502 #else
503 chown(newname,statb.st_uid,statb.st_gid);
504 #endif /*-V6*/
506 #if V6
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);
513 #endif /*V6*/
514 #endif /*TOPS20*/
517 #ifdef STDWRITE
518 if(flags&WF_ASK)
519 { switch(styp)
521 case WF_SBUFF:
522 dotcnt = e_blen();
523 e_gobob();
524 break;
525 case WF_SREG:
526 if((dotcnt = mark_dot - cur_dot) < 0)
527 { e_goff(dotcnt);
528 dotcnt = -dotcnt;
530 else e_gocur();
531 break;
532 /* WF_SKILL not implemented here */
534 while(--dotcnt >= 0)
535 putc(sb_getc(((SBBUF *)b)), o_file);
536 e_gocur();
537 fflush(o_file); /* Force everything out */
538 res = ferror(o_file); /* Save result of stuff */
539 fclose(o_file); /* Now flush FD */
541 else /* New stuff */
542 #endif /*STDWRITE*/
544 switch(styp)
546 case WF_SBUFF:
547 res = sb_fsave((SBBUF *)b, ofd);
548 break;
549 case WF_SREG:
550 e_gocur();
551 sd = e_copyn((chroff)(mark_dot - cur_dot));
552 res = sbx_aout(sd, 2, ofd);
553 sbs_del(sd);
554 break;
555 #if FX_WLASTKILL
556 case WF_SKILL:
557 res = sbx_aout(kill_ring[kill_ptr], 2, ofd);
558 break;
559 #endif
561 close(ofd);
563 if(errno = res)
564 { ferr(" Output error");
565 res = 0; /* Failure */
566 goto wdone;
568 else
569 res = 1; /* Success so far */
570 if(styp == WF_SBUFF)
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. */
575 #if TOPS20
576 saynow("Written");
578 #else /*-TOPS20*/
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;
584 #endif /*IMAGEN*/
585 unlink(oldname); /* remove any existing "old" file */
586 if(link(o_name,oldname) == 0) /* Rename current to "old" */
587 unlink(o_name);
588 /* Here is the critical point... if we stop here, there is no
589 * longer any file with the appropriate filename!!!
591 #if IMAGEN
593 else
594 unlink(o_name);
595 #endif /*IMAGEN*/
596 if(link(newname,o_name) == 0) /* Rename "new" to current */
597 { unlink(newname);
598 #if IMAGEN
599 sayntoo("OK");
600 #else
601 saynow("Written");
602 #endif /*-IMAGEN*/
604 else
605 { dingtoo("rename error!");
606 res = 0;
608 #endif /*-TOPS20*/
610 #if IMAGEN
611 /* Update the last-modified time for the file in this buffer */
612 if ((styp == WF_SBUFF) && b->b_fn)
613 { stat(b->b_fn, &s);
614 b->b_mtime = s.st_mtime;
616 #endif /*IMAGEN*/
618 wdone:
619 hoard(); /* Get back a retained FD */
620 return(res);
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
626 * filename.
628 char *
629 fncons(dest, pre, f_name, post)
630 char *dest,*pre,*f_name,*post;
631 { register char *cp, *cp2;
632 char *last_fname();
634 cp = dest;
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. */
645 return(dest);
648 /* LAST_FNAME(string)
649 * Get the last component of a file name. Returns pointer to
650 * start of component; does NOT copy string!
652 char *
653 last_fname(f_name)
654 char *f_name;
655 { register char *cp, *p;
656 register int c;
658 p = f_name; /* pointer to last slash */
659 cp = p;
660 while(c = *cp++)
661 if(c == '/')
662 p = cp; /* point to after the slash */
663 return(p);
666 /* SET_FN(string)
667 * Set the default filename for current buffer to "string".
669 set_fn (string)
670 char *string;
671 { register struct buffer *b;
672 register char *str;
673 #if IMAGEN
674 register char *cp;
675 register int len;
676 #endif /*IMAGEN*/
677 char *strdup();
679 b = cur_buf;
680 str = strdup(string); /* Copy now in case copying self */
681 if(b->b_fn)
682 chkfree(b->b_fn);
683 b -> b_fn = str;
684 #if IMAGEN
685 /* Do mode determination based on file name (HACK HACK) */
686 len = strlen(str);
687 b->b_flags &= ~(B_CMODE|B_TEXTMODE);
688 if (len > 4)
689 { if (strcmp(&str[len - 5], "draft") == 0)
690 b->b_flags |= B_TEXTMODE;
691 else
692 { cp = &str[len - 4];
693 if (strcmp(cp, ".txt") == 0 ||
694 strcmp(cp, ".mss") == 0)
695 b->b_flags |= B_TEXTMODE;
698 if (len > 2)
699 { cp = &str[len - 2];
700 if (strcmp(cp, ".h") == 0 || strcmp(cp, ".c") == 0)
701 b->b_flags |= B_CMODE;
703 #endif /*IMAGEN*/
704 redp(RD_MODE);
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.
715 saveworld(bp, grunt)
716 struct buffer *bp;
717 int grunt;
718 { register struct buffer *b;
719 register int wfd;
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);
729 #if !(TOPS20)
730 if((wfd = creat(sfname, ev_filmod)) < 0)
731 #else
732 if((wfd = open(sfname,O_WRONLY|O_UNCONVERTED)) < 0)
733 #endif /*TOPS20*/
734 { if(grunt)
735 printf(" error - %s\n", strerror(errno));
737 else
738 { sb_fsave((SBBUF *)b, wfd);
739 close(wfd);
740 if(grunt) printf("\n");
742 b->b_flags &= ~B_MODIFIED;
744 hoard();
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 */
752 { if(hoardfd <= 0)
753 #if !(TOPS20)
754 hoardfd = open("nul:", 1);
755 #else
756 hoardfd = open("/dev/null", 1);
757 #endif
759 unhoard() /* Give up our stashed FD so it can be re-used */
760 { close(hoardfd);
761 hoardfd = -1;
764 #if IMAGEN
765 #include <pwd.h>
766 #include <ctype.h>
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;
776 register int c;
777 register struct passwd *pw;
778 char ts[128];
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.
785 if (dfn == sfn)
786 return;
788 ts[0] = 0;
790 /* If have a leading $, then expand environment variable */
791 if (*sfn == '$')
792 { ++sfn;
793 tp = ts;
794 while (*tp++ = *sfn)
795 if (!isalnum(*sfn))
796 break;
797 else
798 ++sfn;
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 == '~')
804 { ++sfn;
805 if (*sfn == '/' || *sfn == 0)
806 strcpy(ts, getenv("HOME"));
807 else
808 { tp = ts;
809 while (*sfn && *sfn != '/')
810 *tp++ = *sfn++;
811 *tp = 0;
812 pw = (struct passwd *)getpwnam(ts);
813 if (! pw)
814 strcpy(ts, "???");
815 else
816 strcpy(ts, pw->pw_dir);
820 /* Now, ts is either empty or contains the expansion;
821 * sfn has been updated correctly.
823 strcpy(dfn, ts);
824 strcat(dfn, sfn);
826 #endif /*IMAGEN*/