* New alpha version 2.24.1
[alpine.git] / pico / osdep / filesys.c
blob6ba669dab77468796ae054eec977e003ada2d5b3
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: filesys.c 770 2007-10-24 00:23:09Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2007 University of Washington
8 * Copyright 2013-2021 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include <system.h>
20 #include <general.h>
22 #if HAVE_PWD_H
23 # include <pwd.h>
24 #endif
26 #include "../../pith/osdep/temp_nam.h"
27 #include "../../pith/charconv/filesys.h"
29 #include "../estruct.h"
30 #include "../mode.h"
31 #include "../pico.h"
32 #include "../edef.h"
33 #include "../efunc.h"
34 #include "../keydefs.h"
36 #include "fsync.h"
37 #include "filesys.h"
40 #if HAVE_DIRENT_H
41 # include <dirent.h>
42 #else
43 # define dirent direct
44 # if HAVE_SYS_NDIR_H
45 # include <sys/ndir.h>
46 # endif
47 # if HAVE_SYS_DIR_H
48 # include <sys/dir.h>
49 # endif
50 # if HAVE_NDIR_H
51 # include <ndir.h>
52 # endif
53 #endif
57 * fexist - returns TRUE if the file exists with mode passed in m,
58 * FALSE otherwise. By side effect returns length of file in l
59 * File is assumed to be a UTF-8 string.
61 int
62 fexist(char *file,
63 char *m, /* files mode: r,w,rw,t or x */
64 off_t *l) /* t means use lstat */
66 #ifndef _WINDOWS
67 struct stat sbuf;
68 int rv;
69 int (*stat_f)() = (m && *m == 't') ? our_lstat : our_stat;
71 if(l)
72 *l = (off_t)0;
74 rv = (*stat_f)(file, &sbuf);
75 if(rv < 0){
76 switch(errno){
77 case ENOENT : /* File not found */
78 rv = FIOFNF;
79 break;
80 #ifdef ENAMETOOLONG
81 case ENAMETOOLONG : /* Name is too long */
82 rv = FIOLNG;
83 break;
84 #endif
85 case EACCES : /* File not found */
86 rv = FIOPER;
87 break;
88 default: /* Some other error */
89 rv = FIOERR;
90 break;
93 return(rv);
96 if(l)
97 *l = (off_t)sbuf.st_size;
99 if((sbuf.st_mode&S_IFMT) == S_IFDIR)
100 return(FIODIR);
101 else if(*m == 't'){
102 struct stat sbuf2;
105 * If it is a symbolic link pointing to a directory, treat
106 * it like it is a directory, not a link.
108 if((sbuf.st_mode&S_IFMT) == S_IFLNK){
109 rv = our_stat(file, &sbuf2);
110 if(rv < 0){
111 switch(errno){
112 case ENOENT : /* File not found */
113 rv = FIOSYM;
114 break;
115 #ifdef ENAMETOOLONG
116 case ENAMETOOLONG : /* Name is too long */
117 rv = FIOLNG;
118 break;
119 #endif
120 case EACCES : /* File not found */
121 rv = FIOPER;
122 break;
123 default: /* Some other error */
124 rv = FIOERR;
125 break;
128 return(rv);
131 if((sbuf2.st_mode&S_IFMT) == S_IFDIR)
132 return(FIODIR);
135 return(((sbuf.st_mode&S_IFMT) == S_IFLNK) ? FIOSYM : FIOSUC);
138 if(*m == 'r'){ /* read access? */
139 if(*(m+1) == 'w'){ /* and write access? */
140 rv = (can_access(file,READ_ACCESS)==0)
141 ? (can_access(file,WRITE_ACCESS)==0)
142 ? FIOSUC
143 : FIONWT
144 : FIONRD;
146 else if(!*(m+1)){ /* just read access? */
147 rv = (can_access(file,READ_ACCESS)==0) ? FIOSUC : FIONRD;
150 else if(*m == 'w' && !*(m+1)) /* write access? */
151 rv = (can_access(file,WRITE_ACCESS)==0) ? FIOSUC : FIONWT;
152 else if(*m == 'x' && !*(m+1)) /* execute access? */
153 rv = (can_access(file,EXECUTE_ACCESS)==0) ? FIOSUC : FIONEX;
154 else
155 rv = FIOERR; /* bad m arg */
157 return(rv);
158 #else /* _WINDOWS */
159 struct stat sbuf;
161 if(l != NULL)
162 *l = (off_t)0;
164 if(our_stat(file, &sbuf) < 0){
165 if(errno == ENOENT) /* File not found */
166 return(FIOFNF);
167 else
168 return(FIOERR);
171 if(l != NULL)
172 *l = (off_t)sbuf.st_size;
174 if(sbuf.st_mode & S_IFDIR)
175 return(FIODIR);
176 else if(*m == 't') /* no links, just say yes */
177 return(FIOSUC);
179 if(m[0] == 'r') /* read access? */
180 return((S_IREAD & sbuf.st_mode) ? FIOSUC : FIONRD);
181 else if(m[0] == 'w') /* write access? */
182 return((S_IWRITE & sbuf.st_mode) ? FIOSUC : FIONWT);
183 else if(m[0] == 'x') /* execute access? */
184 return((S_IEXEC & sbuf.st_mode) ? FIOSUC : FIONEX);
185 return(FIOERR); /* what? */
186 #endif /* _WINDOWS */
191 * isdir - returns true if fn is a readable directory, false otherwise
192 * silent on errors (we'll let someone else notice the problem;)).
195 isdir(char *fn, long *l, time_t *d)
197 struct stat sbuf;
199 if(l)
200 *l = 0;
202 if(our_stat(fn, &sbuf) < 0)
203 return(0);
205 if(l)
206 *l = sbuf.st_size;
208 if(d)
209 *d = sbuf.st_mtime;
211 return((sbuf.st_mode&S_IFMT) == S_IFDIR);
216 * gethomedir - returns the users home directory in UTF-8 string
217 * Note: home is malloc'd for life of pico
219 char *
220 gethomedir(int *l)
222 static char *home = NULL;
223 static short hlen = 0;
225 if(home == NULL){
226 char buf[NLINE];
228 #ifndef _WINDOWS
229 strncpy(buf, "~", sizeof(buf));
230 buf[sizeof(buf)-1] = '\0';
231 fixpath(buf, sizeof(buf)); /* let fixpath do the work! */
232 #else /* _WINDOWS */
233 if(Pmaster && Pmaster->home_dir)
234 snprintf(buf, sizeof(buf), "%s", Pmaster->home_dir);
235 else
236 snprintf(buf, sizeof(buf), "%c:\\", _getdrive() + 'A' - 1);
237 #endif /* _WINDOWS */
238 hlen = strlen(buf);
239 if((home = (char *)malloc((hlen + 1) * sizeof(char))) == NULL){
240 emlwrite("Problem allocating space for home dir", NULL);
241 return(0);
244 strncpy(home, buf, hlen);
245 home[hlen] = '\0';
248 if(l)
249 *l = hlen;
251 return(home);
256 * homeless - returns true if given file does not reside in the current
257 * user's home directory tree.
260 homeless(char *f)
262 char *home;
263 int len;
265 home = gethomedir(&len);
266 return(strncmp(home, f, len));
271 * getfnames - return all file names in the given directory in a single
272 * malloc'd string. n contains the number of names
274 char *
275 getfnames(char *dn, char *pat, int *n, char *e, size_t elen)
277 size_t l, avail, alloced, incr = 1024;
278 char *names, *np, *p;
279 struct stat sbuf;
280 #if defined(ct)
281 FILE *dirp;
282 char fn[DIRSIZ+1];
283 #else
284 #ifdef _WINDOWS
285 char buf[NLINE+1];
286 struct _finddata_t dbuf;
287 long findrv;
288 #else
289 DIR *dirp; /* opened directory */
290 #endif
291 #endif
292 #if defined(ct) || !defined(_WINDOWS)
293 struct dirent *dp;
294 #endif
296 *n = 0;
298 if(our_stat(dn, &sbuf) < 0){
299 switch(errno){
300 case ENOENT : /* File not found */
301 if(e)
302 snprintf(e, elen, _("File not found: \"%s\""), dn);
304 break;
305 #ifdef ENAMETOOLONG
306 case ENAMETOOLONG : /* Name is too long */
307 if(e)
308 snprintf(e, elen, _("File name too long: \"%s\""), dn);
310 break;
311 #endif
312 default: /* Some other error */
313 if(e)
314 snprintf(e, elen, _("Error getting file info: \"%s\""), dn);
316 break;
318 return(NULL);
320 else{
322 * We'd like to use 512 * st_blocks as an initial estimate but
323 * some systems have a stat struct with no st_blocks in it.
325 avail = alloced = MAX(sbuf.st_size, incr);
326 if((sbuf.st_mode&S_IFMT) != S_IFDIR){
327 if(e)
328 snprintf(e, elen, _("Not a directory: \"%s\""), dn);
330 return(NULL);
334 if((names=(char *)malloc(alloced * sizeof(char))) == NULL){
335 if(e)
336 snprintf(e, elen, _("Can't malloc space for file names"));
338 return(NULL);
341 #ifndef _WINDOWS
342 errno = 0;
343 if((dirp=opendir(fname_to_locale(dn))) == NULL){
344 if(e)
345 snprintf(e, elen, _("Can't open \"%s\": %s"), dn, errstr(errno));
347 free((char *)names);
348 return(NULL);
350 #endif
352 np = names;
354 #if defined(ct)
355 while(fread(&dp, sizeof(struct direct), 1, dirp) > 0) {
356 /* skip empty slots with inode of 0 */
357 if(dp.d_ino == 0)
358 continue;
359 (*n)++; /* count the number of active slots */
360 (void)strncpy(fn, dp.d_name, DIRSIZ);
361 fn[14] = '\0';
362 p = fname_to_utf8(fn);
363 while((*np++ = *p++) != '\0')
366 #else /* !defined(ct) */
367 #ifdef _WINDOWS
368 strncpy(buf, dn, sizeof(buf));
369 buf[sizeof(buf)-1] = '\0';
370 snprintf(buf, sizeof(buf), "%s%s%s*%s%s", dn,
371 (dn[strlen(dn)-1] == '\\') ? "" : "\\",
372 (pat && *pat) ? pat : "",
373 (pat && *pat && strchr(pat, '.')) ? "" : ".",
374 (pat && *pat && strchr(pat, '.')) ? "" : "*");
375 if((findrv = _findfirst(buf, &dbuf)) < 0){
376 if(e)
377 sprintf(e, "Can't find first file in \"%s\"", dn);
379 free((char *) names);
380 return(NULL);
383 do {
384 p = fname_to_utf8(dbuf.name);
385 #else /* UNIX */
386 while((dp = readdir(dirp)) != NULL){
387 p = fname_to_utf8(dp->d_name);
388 if(!pat || !*pat || !strncmp(p, pat, strlen(pat))){
389 #endif /* UNIX */
390 (*n)++;
391 l = strlen(p);
392 while(avail < l+1){
393 char *oldnames;
395 alloced += incr;
396 avail += incr;
397 oldnames = names;
398 if((names=(char *)realloc((void *)names, alloced * sizeof(char)))
399 == NULL){
400 if(e)
401 snprintf(e, elen, _("Can't malloc enough space for file names"));
403 return(NULL);
406 np = names + (np-oldnames);
409 avail -= (l+1);
411 while((*np++ = *p++) != '\0')
413 #ifdef _WINDOWS
415 while(_findnext(findrv, &dbuf) == 0);
416 #else /* UNIX */
419 #endif /* UNIX */
420 #endif /* !defined(ct) */
422 #ifdef _WINDOWS
423 _findclose(findrv);
424 #else
425 closedir(dirp); /* shut down */
426 #endif
427 return(names);
432 * fioperr - given the error number and file name, display error
434 void
435 fioperr(int e, char *f)
437 EML eml;
439 eml.s = f;
441 switch(e){
442 case FIOFNF: /* File not found */
443 emlwwrite(_("File \"%s\" not found"), &eml);
444 break;
445 case FIOEOF: /* end of file */
446 emlwwrite(_("End of file \"%s\" reached"), &eml);
447 break;
448 case FIOLNG: /* name too long */
449 emlwwrite(_("File name \"%s\" too long"), &eml);
450 break;
451 case FIODIR: /* file is a directory */
452 emlwwrite(_("File \"%s\" is a directory"), &eml);
453 break;
454 case FIONWT:
455 emlwwrite(_("Write permission denied: %s"), &eml);
456 break;
457 case FIONRD:
458 emlwwrite(_("Read permission denied: %s"), &eml);
459 break;
460 case FIOPER:
461 emlwwrite(_("Permission denied: %s"), &eml);
462 break;
463 case FIONEX:
464 emlwwrite(_("Execute permission denied: %s"), &eml);
465 break;
466 default:
467 emlwwrite(_("File I/O error: %s"), &eml);
474 * pfnexpand - pico's function to expand the given file name if there is
475 * a leading '~'. Converts the homedir from user's locale to UTF-8.
476 * Fn is assumed to already be UTF-8.
478 char *
479 pfnexpand(char *fn, size_t fnlen)
481 register char *x, *y, *z;
482 char *home = NULL;
483 #ifndef _WINDOWS
484 struct passwd *pw;
485 char name[50];
487 if(*fn == '~'){
488 for(x = fn+1, y = name;
489 *x != '/' && *x != '\0' && y-name < sizeof(name)-1;
490 *y++ = *x++)
493 *y = '\0';
494 if(x == fn + 1){ /* ~/ */
495 if (!(home = (char *) getenv("HOME")))
496 if ((pw = getpwuid(geteuid())) != NULL)
497 home = pw->pw_dir;
499 else if(*name){ /* ~username/ */
500 if((pw = getpwnam(name)) != NULL)
501 home = pw->pw_dir;
504 if(!home || (strlen(home) + strlen(fn) >= fnlen))
505 return(NULL);
506 #else /* _WINDOWS */
507 char name[_MAX_PATH];
509 if(*fn == '~' && *(x = fn + 1) == '\\') {
510 if(!(home = (char *) getenv("HOME"))
511 && getenv("HOMEDRIVE") && getenv("HOMEPATH"))
512 snprintf(home = name, sizeof(name), "%s%s",
513 (char *) getenv("HOMEDRIVE"), (char *) getenv("HOMEPATH"));
514 #endif /* _WINDOWS */
515 home = fname_to_utf8(home);
517 /* make room for expanded path */
518 for(z = x + strlen(x), y = fn + strlen(x) + strlen(home);
519 z >= x;
520 *y-- = *z--)
523 /* and insert the expanded address */
524 for(x = fn, y = home; *y != '\0' && x-fn < fnlen; *x++ = *y++)
528 fn[fnlen-1] = '\0';
530 return(fn);
535 * fixpath - make the given pathname into an absolute path, incoming
536 * name is UTF-8.
538 void
539 fixpath(char *name, size_t namelen)
541 #ifdef _WINDOWS
542 char file[_MAX_PATH];
543 int dr;
545 if(!namelen)
546 return;
548 /* return the full path of given file, so drive spec? */
549 if(name[1] == ':' && isalpha((unsigned char) name[0])){
550 if(name[2] != '\\'){ /* including path? */
551 dr = toupper((unsigned char)name[0]) - 'A' + 1;
552 if((void *)_getdcwd(dr, file, _MAX_PATH) != NULL){
553 if(file[strlen(file)-1] != '\\')
554 strncat(file, "\\", sizeof(file)-1-strlen(file));
556 /* add file name */
557 strncat(file, &name[2], sizeof(file)-1-strlen(file));
559 else
560 return;
562 else
563 return; /* fully qualified with drive and path! */
565 else if(name[0] == '\\' && name[1] != '\\') { /* no drive spec! */
566 sprintf(file, "%c:%.*s", _getdrive()+'A'-1, namelen-3, name);
568 else if(name[0] == '\\' && name[1] == '\\'){ /* Windows network drive */
569 return;
571 else{
572 if(Pmaster && !(gmode & MDCURDIR)){
573 strncpy(file, ((gmode & MDTREE) || opertree[0])
574 ? opertree : gethomedir(NULL), sizeof(file)-1);
575 file[sizeof(file)-1] = '\0';
577 else if(!_getcwd(file, sizeof(file))) /* no qualification */
578 return;
580 if(*name){ /* if name, append it */
581 if(*file && file[strlen(file)-1] != '\\')
582 strncat(file, "\\", sizeof(file)-1-strlen(file));
584 strncat(file, name, sizeof(file)-1-strlen(file));
588 strncpy(name, file, namelen-1); /* copy back to real buffer */
589 name[namelen-1] = '\0'; /* tie off just in case */
590 #else /* UNIX */
591 char *shft;
593 /* filenames relative to ~ */
594 if(!((name[0] == '/')
595 || (name[0] == '.'
596 && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))))){
597 if(Pmaster && !(gmode&MDCURDIR)
598 && (*name != '~' && strlen(name)+2 < namelen)){
600 if(gmode&MDTREE && strlen(name)+strlen(opertree)+1 < namelen){
601 int off = strlen(opertree);
603 for(shft = strchr(name, '\0'); shft >= name; shft--)
604 shft[off+1] = *shft;
606 strncpy(name, opertree, MIN(off,namelen-1));
607 name[MIN(off,namelen-1)] = '/';
609 else{
610 for(shft = strchr(name, '\0'); shft >= name; shft--)
611 shft[2] = *shft;
613 name[0] = '~';
614 name[1] = '/';
618 pfnexpand(name, namelen);
620 #endif /* UNIX */
625 * compresspath - given a base path and an additional directory, collapse
626 * ".." and "." elements and return absolute path (appending
627 * base if necessary).
629 * returns 1 if OK,
630 * 0 if there's a problem
631 * new path, by side effect, if things went OK
634 compresspath(char *base, char *path, size_t pathlen)
636 register int i;
637 int depth = 0;
638 char *p;
639 char *stack[32];
640 char pathbuf[NLINE];
642 #define PUSHD(X) (stack[depth++] = X)
643 #define POPD() ((depth > 0) ? stack[--depth] : "")
645 #ifndef _WINDOWS
646 if(*path == '~'){
647 fixpath(path, pathlen);
648 strncpy(pathbuf, path, sizeof(pathbuf));
649 pathbuf[sizeof(pathbuf)-1] = '\0';
651 else if(*path != C_FILESEP)
652 snprintf(pathbuf, sizeof(pathbuf), "%s%c%s", base, C_FILESEP, path);
653 else{
654 strncpy(pathbuf, path, sizeof(pathbuf));
655 pathbuf[sizeof(pathbuf)-1] = '\0';
657 #else /* _WINDOWS */
658 strncpy(pathbuf, path, sizeof(pathbuf));
659 pathbuf[sizeof(pathbuf)-1] = '\0';
660 fixpath(pathbuf, sizeof(pathbuf));
661 #endif /* _WINDOWS */
663 p = &pathbuf[0];
664 for(i=0; pathbuf[i] != '\0'; i++){ /* pass thru path name */
665 if(pathbuf[i] == C_FILESEP){
666 if(p != pathbuf)
667 PUSHD(p); /* push dir entry */
669 p = &pathbuf[i+1]; /* advance p */
670 pathbuf[i] = '\0'; /* cap old p off */
671 continue;
674 if(pathbuf[i] == '.'){ /* special cases! */
675 if(pathbuf[i+1] == '.' /* parent */
676 && (pathbuf[i+2] == C_FILESEP || pathbuf[i+2] == '\0')){
677 if(!strcmp(POPD(), "")) /* bad news! */
678 return(0);
680 i += 2;
681 p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
683 else if(pathbuf[i+1] == C_FILESEP || pathbuf[i+1] == '\0'){
684 i++;
685 p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
690 if(*p != '\0')
691 PUSHD(p); /* get last element */
693 path[0] = '\0';
694 for(i = 0; i < depth; i++){
695 strncat(path, S_FILESEP, pathlen-strlen(path)-1);
696 path[pathlen-1] = '\0';
697 strncat(path, stack[i], pathlen-strlen(path)-1);
698 path[pathlen-1] = '\0';
701 return(1); /* everything's ok */
707 * tmpname - return a temporary file name in the given buffer, the filename
708 * is in the directory dir unless dir is NULL. The file did not exist at
709 * the time of the temp_nam call, but was created by temp_nam.
711 void
712 tmpname(char *dir, char *name)
714 char *t;
715 #ifndef _WINDOWS
716 if((t = temp_nam((dir && *dir) ? dir : NULL, "pico.")) != NULL){
717 #else /* _WINDOWS */
718 char tmp[_MAX_PATH];
720 if(!((dir && *dir) ||
721 (dir = getenv("TMPDIR")) ||
722 (dir = getenv("TMP")) ||
723 (dir = getenv("TEMP"))))
724 if(!(getcwd(dir = tmp, _MAX_PATH)
725 && fexist(dir, "w", (off_t *) NULL) == FIOSUC))
726 dir = "c:\\";
728 if((t = temp_nam_ext(dir, "ae", "txt")) != NULL){
729 #endif /* _WINDOWS */
730 strncpy(name, t, NFILEN-1);
731 name[NFILEN-1] = '\0';
732 free((void *)t);
734 else {
735 emlwrite("Unable to construct temp file name", NULL);
736 name[0] = '\0';
742 * Take a file name, and from it
743 * fabricate a buffer name. This routine knows
744 * about the syntax of file names on the target system.
745 * I suppose that this information could be put in
746 * a better place than a line of code.
748 void
749 makename(char bname[], char fname[])
751 register char *cp1;
752 register char *cp2;
754 cp1 = &fname[0];
755 while (*cp1 != 0)
756 ++cp1;
758 while (cp1!=&fname[0] && cp1[-1]!=C_FILESEP)
759 --cp1;
761 cp2 = &bname[0];
762 while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
763 *cp2++ = *cp1++;
765 *cp2 = 0;
770 * copy - copy contents of file 'a' into a file named 'b'. Return error
771 * if either isn't accessible or is a directory
774 copy(char *a, char *b)
776 int in, out, n, rv = 0;
777 char *cb;
778 struct stat tsb, fsb;
779 EML eml;
780 extern int errno;
782 if(our_stat(a, &fsb) < 0){ /* get source file info */
783 eml.s = errstr(errno);
784 emlwrite("Can't Copy: %s", &eml);
785 return(-1);
788 if(!(fsb.st_mode&S_IREAD)){ /* can we read it? */
789 eml.s = a;
790 emlwwrite(_("Read permission denied: %s"), &eml);
791 return(-1);
794 if((fsb.st_mode&S_IFMT) == S_IFDIR){ /* is it a directory? */
795 eml.s = a;
796 emlwwrite(_("Can't copy: %s is a directory"), &eml);
797 return(-1);
800 if(our_stat(b, &tsb) < 0){ /* get dest file's mode */
801 switch(errno){
802 case ENOENT:
803 break; /* these are OK */
804 default:
805 eml.s = errstr(errno);
806 emlwwrite(_("Can't Copy: %s"), &eml);
807 return(-1);
810 else{
811 if(!(tsb.st_mode&S_IWRITE)){ /* can we write it? */
812 eml.s = b;
813 emlwwrite(_("Write permission denied: %s"), &eml);
814 return(-1);
817 if((tsb.st_mode&S_IFMT) == S_IFDIR){ /* is it directory? */
818 eml.s = b;
819 emlwwrite(_("Can't copy: %s is a directory"), &eml);
820 return(-1);
823 if(fsb.st_dev == tsb.st_dev && fsb.st_ino == tsb.st_ino){
824 emlwwrite(_("Identical files. File not copied"), NULL);
825 return(-1);
829 if((in = our_open(a, O_RDONLY|O_BINARY, 0600)) < 0){
830 eml.s = errstr(errno);
831 emlwrite("Copy Failed: %s", &eml);
832 return(-1);
835 if((out=our_creat(b, fsb.st_mode&0xfff)) < 0){
836 eml.s = errstr(errno);
837 emlwrite("Can't Copy: %s", &eml);
838 close(in);
839 return(-1);
842 if((cb = (char *)malloc(NLINE*sizeof(char))) == NULL){
843 emlwrite("Can't allocate space for copy buffer!", NULL);
844 close(in);
845 close(out);
846 return(-1);
849 while(1){ /* do the copy */
850 if((n = read(in, cb, NLINE)) < 0){
851 eml.s = errstr(errno);
852 emlwrite("Can't Read Copy: %s", &eml);
853 rv = -1;
854 break; /* get out now */
857 if(n == 0) /* done! */
858 break;
860 if(write(out, cb, n) != n){
861 eml.s = errstr(errno);
862 emlwrite("Can't Write Copy: %s", &eml);
863 rv = -1;
864 break;
868 free(cb);
869 close(in);
870 close(out);
871 return(rv);
876 * Open a file for writing. Return TRUE if all is well, and FALSE on error
877 * (cannot create).
880 ffwopen(char *fn, int readonly)
882 extern FIOINFO g_pico_fio;
883 #ifndef _WINDOWS
884 int fd;
885 EML eml;
886 #endif
887 #ifndef MODE_READONLY
888 #define MODE_READONLY (0600)
889 #endif
892 * Call open() by hand since we don't want O_TRUNC -- it'll
893 * screw over-quota users. The strategy is to overwrite the
894 * existing file's data and call ftruncate before close to lop
895 * off bytes
898 g_pico_fio.flags = FIOINFO_WRITE;
899 g_pico_fio.name = fn;
900 #ifndef _WINDOWS
901 if((fd = our_open(fn, O_CREAT|O_WRONLY, readonly ? MODE_READONLY : 0666)) >= 0
902 && (g_pico_fio.fp = fdopen(fd, "w")) != NULL
903 && fseek(g_pico_fio.fp, 0L, 0) == 0)
904 return (FIOSUC);
907 eml.s = errstr(errno);
908 emlwrite("Cannot open file for writing: %s", &eml);
909 return (FIOERR);
910 #else /* _WINDOWS */
911 if ((g_pico_fio.fp = our_fopen(fn, "w")) == NULL) {
912 emlwrite("Cannot open file for writing", NULL);
913 return (FIOERR);
916 #ifdef MODE_READONLY
917 if(readonly)
918 our_chmod(fn, MODE_READONLY); /* fix access rights */
919 #endif
921 return (FIOSUC);
922 #endif /* _WINDOWS */
927 * Close a file. Should look at the status in all systems.
930 ffclose(void)
932 extern FIOINFO g_pico_fio;
933 #ifndef _WINDOWS
934 EML eml;
936 errno = 0;
937 if((g_pico_fio.flags & FIOINFO_WRITE)
938 && (fflush(g_pico_fio.fp) == EOF
939 || ftruncate(fileno(g_pico_fio.fp),
940 (off_t) ftell(g_pico_fio.fp)) < 0)){
941 eml.s = errstr(errno);
942 emlwwrite(_("Error preparing to close file: %s"), &eml);
943 sleep(5);
946 if (fclose(g_pico_fio.fp) == EOF) {
947 eml.s = errstr(errno);
948 emlwwrite(_("Error closing file: %s"), &eml);
949 return(FIOERR);
951 #else /* _WINDOWS */
952 if (fclose(g_pico_fio.fp) != FALSE) {
953 emlwrite("Error closing file", NULL);
954 return(FIOERR);
956 #endif /* _WINDOWS */
958 return(FIOSUC);
963 #define EXTEND_BLOCK 1024
967 * ffelbowroom - make sure the destination's got enough room to receive
968 * what we're about to write...
971 ffelbowroom(void)
973 #ifndef _WINDOWS
974 register LINE *lp;
975 register long n;
976 int x;
977 char s[EXTEND_BLOCK], *errstring = NULL;
978 struct stat fsbuf;
979 extern FIOINFO g_pico_fio;
981 /* Figure out how much room do we need */
982 /* first, what's total */
983 for(n=0L, lp=lforw(curbp->b_linep); lp != curbp->b_linep; lp=lforw(lp))
984 n += (llength(lp) + 1);
986 errno = 0; /* make sure previous error are cleared */
988 if(fstat(fileno(g_pico_fio.fp), &fsbuf) == 0){
989 n -= fsbuf.st_size;
991 if(n > 0L){ /* must be growing file, extend it */
992 memset(s, 'U', EXTEND_BLOCK);
993 if(fseek(g_pico_fio.fp, fsbuf.st_size, 0) == 0){
994 for( ; n > 0L; n -= EXTEND_BLOCK){
995 x = (n < EXTEND_BLOCK) ? (int) n : EXTEND_BLOCK;
996 if(fwrite(s, x * sizeof(char), 1, g_pico_fio.fp) != 1){
997 errstring = errstr(errno);
998 break;
1002 if(!errstring
1003 && (fflush(g_pico_fio.fp) == EOF
1004 || fsync(fileno(g_pico_fio.fp)) < 0))
1005 errstring = errstr(errno);
1007 if(errstring) /* clean up */
1008 (void) ftruncate(fileno(g_pico_fio.fp), (off_t) fsbuf.st_size);
1009 else if(fseek(g_pico_fio.fp, 0L, 0) != 0)
1010 errstring = errstr(errno);
1012 else
1013 errstring = errstr(errno);
1016 else
1017 errstring = errstr(errno);
1019 if(errstring){
1020 snprintf(s, sizeof(s), "Error writing to %s: %s", g_pico_fio.name, errstring);
1021 emlwrite(s, NULL);
1022 (void) fclose(g_pico_fio.fp);
1023 return(FALSE);
1025 #endif /* UNIX */
1026 return(TRUE);