Add support for tab-completion when selecting by rule
[alpine.git] / pico / osdep / filesys.c
blob75cb91ff5a6ae33efe1534d83ba29a35d4277123
1 /*
2 * ========================================================================
3 * Copyright 2006-2007 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include <system.h>
16 #include <general.h>
18 #if HAVE_PWD_H
19 # include <pwd.h>
20 #endif
22 #include "../../pith/osdep/temp_nam.h"
23 #include "../../pith/charconv/filesys.h"
25 #include "../estruct.h"
26 #include "../mode.h"
27 #include "../pico.h"
28 #include "../edef.h"
29 #include "../efunc.h"
30 #include "../keydefs.h"
32 #include "fsync.h"
33 #include "filesys.h"
36 #if HAVE_DIRENT_H
37 # include <dirent.h>
38 #else
39 # define dirent direct
40 # if HAVE_SYS_NDIR_H
41 # include <sys/ndir.h>
42 # endif
43 # if HAVE_SYS_DIR_H
44 # include <sys/dir.h>
45 # endif
46 # if HAVE_NDIR_H
47 # include <ndir.h>
48 # endif
49 #endif
53 * fexist - returns TRUE if the file exists with mode passed in m,
54 * FALSE otherwise. By side effect returns length of file in l
55 * File is assumed to be a UTF-8 string.
57 int
58 fexist(char *file,
59 char *m, /* files mode: r,w,rw,t or x */
60 off_t *l) /* t means use lstat */
62 #ifndef _WINDOWS
63 struct stat sbuf;
64 int rv;
65 int (*stat_f)() = (m && *m == 't') ? our_lstat : our_stat;
67 if(l)
68 *l = (off_t)0;
70 rv = (*stat_f)(file, &sbuf);
71 if(rv < 0){
72 switch(errno){
73 case ENOENT : /* File not found */
74 rv = FIOFNF;
75 break;
76 #ifdef ENAMETOOLONG
77 case ENAMETOOLONG : /* Name is too long */
78 rv = FIOLNG;
79 break;
80 #endif
81 case EACCES : /* File not found */
82 rv = FIOPER;
83 break;
84 default: /* Some other error */
85 rv = FIOERR;
86 break;
89 return(rv);
92 if(l)
93 *l = (off_t)sbuf.st_size;
95 if((sbuf.st_mode&S_IFMT) == S_IFDIR)
96 return(FIODIR);
97 else if(*m == 't'){
98 struct stat sbuf2;
101 * If it is a symbolic link pointing to a directory, treat
102 * it like it is a directory, not a link.
104 if((sbuf.st_mode&S_IFMT) == S_IFLNK){
105 rv = our_stat(file, &sbuf2);
106 if(rv < 0){
107 switch(errno){
108 case ENOENT : /* File not found */
109 rv = FIOSYM;
110 break;
111 #ifdef ENAMETOOLONG
112 case ENAMETOOLONG : /* Name is too long */
113 rv = FIOLNG;
114 break;
115 #endif
116 case EACCES : /* File not found */
117 rv = FIOPER;
118 break;
119 default: /* Some other error */
120 rv = FIOERR;
121 break;
124 return(rv);
127 if((sbuf2.st_mode&S_IFMT) == S_IFDIR)
128 return(FIODIR);
131 return(((sbuf.st_mode&S_IFMT) == S_IFLNK) ? FIOSYM : FIOSUC);
134 if(*m == 'r'){ /* read access? */
135 if(*(m+1) == 'w'){ /* and write access? */
136 rv = (can_access(file,READ_ACCESS)==0)
137 ? (can_access(file,WRITE_ACCESS)==0)
138 ? FIOSUC
139 : FIONWT
140 : FIONRD;
142 else if(!*(m+1)){ /* just read access? */
143 rv = (can_access(file,READ_ACCESS)==0) ? FIOSUC : FIONRD;
146 else if(*m == 'w' && !*(m+1)) /* write access? */
147 rv = (can_access(file,WRITE_ACCESS)==0) ? FIOSUC : FIONWT;
148 else if(*m == 'x' && !*(m+1)) /* execute access? */
149 rv = (can_access(file,EXECUTE_ACCESS)==0) ? FIOSUC : FIONEX;
150 else
151 rv = FIOERR; /* bad m arg */
153 return(rv);
154 #else /* _WINDOWS */
155 struct stat sbuf;
157 if(l != NULL)
158 *l = (off_t)0;
160 if(our_stat(file, &sbuf) < 0){
161 if(errno == ENOENT) /* File not found */
162 return(FIOFNF);
163 else
164 return(FIOERR);
167 if(l != NULL)
168 *l = (off_t)sbuf.st_size;
170 if(sbuf.st_mode & S_IFDIR)
171 return(FIODIR);
172 else if(*m == 't') /* no links, just say yes */
173 return(FIOSUC);
175 if(m[0] == 'r') /* read access? */
176 return((S_IREAD & sbuf.st_mode) ? FIOSUC : FIONRD);
177 else if(m[0] == 'w') /* write access? */
178 return((S_IWRITE & sbuf.st_mode) ? FIOSUC : FIONWT);
179 else if(m[0] == 'x') /* execute access? */
180 return((S_IEXEC & sbuf.st_mode) ? FIOSUC : FIONEX);
181 return(FIOERR); /* what? */
182 #endif /* _WINDOWS */
187 * isdir - returns true if fn is a readable directory, false otherwise
188 * silent on errors (we'll let someone else notice the problem;)).
191 isdir(char *fn, long *l, time_t *d)
193 struct stat sbuf;
195 if(l)
196 *l = 0;
198 if(our_stat(fn, &sbuf) < 0)
199 return(0);
201 if(l)
202 *l = sbuf.st_size;
204 if(d)
205 *d = sbuf.st_mtime;
207 return((sbuf.st_mode&S_IFMT) == S_IFDIR);
212 * gethomedir - returns the users home directory in UTF-8 string
213 * Note: home is malloc'd for life of pico
215 char *
216 gethomedir(int *l)
218 static char *home = NULL;
219 static short hlen = 0;
221 if(home == NULL){
222 char buf[NLINE];
224 #ifndef _WINDOWS
225 strncpy(buf, "~", sizeof(buf));
226 buf[sizeof(buf)-1] = '\0';
227 fixpath(buf, sizeof(buf)); /* let fixpath do the work! */
228 #else /* _WINDOWS */
229 if(Pmaster && Pmaster->home_dir)
230 snprintf(buf, sizeof(buf), "%s", Pmaster->home_dir);
231 else
232 snprintf(buf, sizeof(buf), "%c:\\", _getdrive() + 'A' - 1);
233 #endif /* _WINDOWS */
234 hlen = strlen(buf);
235 if((home = (char *)malloc((hlen + 1) * sizeof(char))) == NULL){
236 emlwrite("Problem allocating space for home dir", NULL);
237 return(0);
240 strncpy(home, buf, hlen);
241 home[hlen] = '\0';
244 if(l)
245 *l = hlen;
247 return(home);
252 * homeless - returns true if given file does not reside in the current
253 * user's home directory tree.
256 homeless(char *f)
258 char *home;
259 int len;
261 home = gethomedir(&len);
262 return(strncmp(home, f, len));
267 * getfnames - return all file names in the given directory in a single
268 * malloc'd string. n contains the number of names
270 char *
271 getfnames(char *dn, char *pat, int *n, char *e, size_t elen)
273 size_t l, avail, alloced, incr = 1024;
274 char *names, *np, *p;
275 struct stat sbuf;
276 #if defined(ct)
277 FILE *dirp;
278 char fn[DIRSIZ+1];
279 #else
280 #ifdef _WINDOWS
281 char buf[NLINE+1];
282 struct _finddata_t dbuf;
283 long findrv;
284 #else
285 DIR *dirp; /* opened directory */
286 #endif
287 #endif
288 #if defined(ct) || !defined(_WINDOWS)
289 struct dirent *dp;
290 #endif
292 *n = 0;
294 if(our_stat(dn, &sbuf) < 0){
295 switch(errno){
296 case ENOENT : /* File not found */
297 if(e)
298 snprintf(e, elen, _("File not found: \"%s\""), dn);
300 break;
301 #ifdef ENAMETOOLONG
302 case ENAMETOOLONG : /* Name is too long */
303 if(e)
304 snprintf(e, elen, _("File name too long: \"%s\""), dn);
306 break;
307 #endif
308 default: /* Some other error */
309 if(e)
310 snprintf(e, elen, _("Error getting file info: \"%s\""), dn);
312 break;
314 return(NULL);
316 else{
318 * We'd like to use 512 * st_blocks as an initial estimate but
319 * some systems have a stat struct with no st_blocks in it.
321 * In some systems the size of a directory is the sum of the
322 * sizes of all files contained in it, so sbuf.st_size might
323 * be too large and the malloc() might fail with error ENOMEM.
324 * Since this is just the list of files, and not the contents
325 * of the files, let us start with a small amount and resize
326 * when necessary. Reported by Sebasting Knust. Of course the
327 * correct solution is to read the directory twice, but keeping
328 * realloc()ing as needed also solves the problem in a less
329 * elegant way.
331 avail = alloced = 4096;
332 if((sbuf.st_mode&S_IFMT) != S_IFDIR){
333 if(e)
334 snprintf(e, elen, _("Not a directory: \"%s\""), dn);
336 return(NULL);
340 if((names=(char *)malloc(alloced * sizeof(char))) == NULL){
341 if(e)
342 snprintf(e, elen, _("Can't malloc space for file names"));
344 return(NULL);
347 #ifndef _WINDOWS
348 errno = 0;
349 if((dirp=opendir(fname_to_locale(dn))) == NULL){
350 if(e)
351 snprintf(e, elen, _("Can't open \"%s\": %s"), dn, errstr(errno));
353 free((char *)names);
354 return(NULL);
356 #endif
358 np = names;
360 #if defined(ct)
361 while(fread(&dp, sizeof(struct direct), 1, dirp) > 0) {
362 /* skip empty slots with inode of 0 */
363 if(dp.d_ino == 0)
364 continue;
365 (*n)++; /* count the number of active slots */
366 (void)strncpy(fn, dp.d_name, DIRSIZ);
367 fn[14] = '\0';
368 p = fname_to_utf8(fn);
369 while((*np++ = *p++) != '\0')
372 #else /* !defined(ct) */
373 #ifdef _WINDOWS
374 strncpy(buf, dn, sizeof(buf));
375 buf[sizeof(buf)-1] = '\0';
376 snprintf(buf, sizeof(buf), "%s%s%s*%s%s", dn,
377 (dn[strlen(dn)-1] == '\\') ? "" : "\\",
378 (pat && *pat) ? pat : "",
379 (pat && *pat && strchr(pat, '.')) ? "" : ".",
380 (pat && *pat && strchr(pat, '.')) ? "" : "*");
381 if((findrv = _findfirst(buf, &dbuf)) < 0){
382 if(e)
383 sprintf(e, "Can't find first file in \"%s\"", dn);
385 free((char *) names);
386 return(NULL);
389 do {
390 p = fname_to_utf8(dbuf.name);
391 #else /* UNIX */
392 while((dp = readdir(dirp)) != NULL){
393 p = fname_to_utf8(dp->d_name);
394 if(!pat || !*pat || !strncmp(p, pat, strlen(pat))){
395 #endif /* UNIX */
396 (*n)++;
397 l = strlen(p);
398 while(avail < l+1){
399 char *oldnames;
401 alloced += incr;
402 avail += incr;
403 oldnames = names;
404 if((names=(char *)realloc((void *)names, alloced * sizeof(char)))
405 == NULL){
406 if(e)
407 snprintf(e, elen, _("Can't malloc enough space for file names"));
409 return(NULL);
412 np = names + (np-oldnames);
415 avail -= (l+1);
417 while((*np++ = *p++) != '\0')
419 #ifdef _WINDOWS
421 while(_findnext(findrv, &dbuf) == 0);
422 #else /* UNIX */
425 #endif /* UNIX */
426 #endif /* !defined(ct) */
428 #ifdef _WINDOWS
429 _findclose(findrv);
430 #else
431 closedir(dirp); /* shut down */
432 #endif
433 return(names);
438 * fioperr - given the error number and file name, display error
440 void
441 fioperr(int e, char *f)
443 EML eml;
445 eml.s = f;
447 switch(e){
448 case FIOFNF: /* File not found */
449 emlwwrite(_("File \"%s\" not found"), &eml);
450 break;
451 case FIOEOF: /* end of file */
452 emlwwrite(_("End of file \"%s\" reached"), &eml);
453 break;
454 case FIOLNG: /* name too long */
455 emlwwrite(_("File name \"%s\" too long"), &eml);
456 break;
457 case FIODIR: /* file is a directory */
458 emlwwrite(_("File \"%s\" is a directory"), &eml);
459 break;
460 case FIONWT:
461 emlwwrite(_("Write permission denied: %s"), &eml);
462 break;
463 case FIONRD:
464 emlwwrite(_("Read permission denied: %s"), &eml);
465 break;
466 case FIOPER:
467 emlwwrite(_("Permission denied: %s"), &eml);
468 break;
469 case FIONEX:
470 emlwwrite(_("Execute permission denied: %s"), &eml);
471 break;
472 default:
473 emlwwrite(_("File I/O error: %s"), &eml);
480 * pfnexpand - pico's function to expand the given file name if there is
481 * a leading '~'. Converts the homedir from user's locale to UTF-8.
482 * Fn is assumed to already be UTF-8.
484 char *
485 pfnexpand(char *fn, size_t fnlen)
487 register char *x, *y, *z;
488 char *home = NULL;
489 #ifndef _WINDOWS
490 struct passwd *pw;
491 char name[50];
493 if(*fn == '~'){
494 for(x = fn+1, y = name;
495 *x != '/' && *x != '\0' && y-name < sizeof(name)-1;
496 *y++ = *x++)
499 *y = '\0';
500 if(x == fn + 1){ /* ~/ */
501 if (!(home = (char *) getenv("HOME")))
502 if ((pw = getpwuid(geteuid())) != NULL)
503 home = pw->pw_dir;
505 else if(*name){ /* ~username/ */
506 if((pw = getpwnam(name)) != NULL)
507 home = pw->pw_dir;
510 if(!home || (strlen(home) + strlen(fn) >= fnlen))
511 return(NULL);
512 #else /* _WINDOWS */
513 char name[_MAX_PATH];
515 if(*fn == '~' && *(x = fn + 1) == '\\') {
516 if(!(home = (char *) getenv("HOME"))
517 && getenv("HOMEDRIVE") && getenv("HOMEPATH"))
518 snprintf(home = name, sizeof(name), "%s%s",
519 (char *) getenv("HOMEDRIVE"), (char *) getenv("HOMEPATH"));
520 #endif /* _WINDOWS */
521 home = fname_to_utf8(home);
523 /* make room for expanded path */
524 for(z = x + strlen(x), y = fn + strlen(x) + strlen(home);
525 z >= x;
526 *y-- = *z--)
529 /* and insert the expanded address */
530 for(x = fn, y = home; *y != '\0' && x-fn < fnlen; *x++ = *y++)
534 fn[fnlen-1] = '\0';
536 return(fn);
541 * fixpath - make the given pathname into an absolute path, incoming
542 * name is UTF-8.
544 void
545 fixpath(char *name, size_t namelen)
547 #ifdef _WINDOWS
548 char file[_MAX_PATH];
549 int dr;
551 if(!namelen)
552 return;
554 /* return the full path of given file, so drive spec? */
555 if(name[1] == ':' && isalpha((unsigned char) name[0])){
556 if(name[2] != '\\'){ /* including path? */
557 dr = toupper((unsigned char)name[0]) - 'A' + 1;
558 if((void *)_getdcwd(dr, file, _MAX_PATH) != NULL){
559 if(file[strlen(file)-1] != '\\')
560 strncat(file, "\\", sizeof(file)-1-strlen(file));
562 /* add file name */
563 strncat(file, &name[2], sizeof(file)-1-strlen(file));
565 else
566 return;
568 else
569 return; /* fully qualified with drive and path! */
571 else if(name[0] == '\\' && name[1] != '\\') { /* no drive spec! */
572 sprintf(file, "%c:%.*s", _getdrive()+'A'-1, namelen-3, name);
574 else if(name[0] == '\\' && name[1] == '\\'){ /* Windows network drive */
575 return;
577 else{
578 if(Pmaster && !(gmode & MDCURDIR)){
579 strncpy(file, ((gmode & MDTREE) || opertree[0])
580 ? opertree : gethomedir(NULL), sizeof(file)-1);
581 file[sizeof(file)-1] = '\0';
583 else if(!_getcwd(file, sizeof(file))) /* no qualification */
584 return;
586 if(*name){ /* if name, append it */
587 if(*file && file[strlen(file)-1] != '\\')
588 strncat(file, "\\", sizeof(file)-1-strlen(file));
590 strncat(file, name, sizeof(file)-1-strlen(file));
594 strncpy(name, file, namelen-1); /* copy back to real buffer */
595 name[namelen-1] = '\0'; /* tie off just in case */
596 #else /* UNIX */
597 char *shft;
599 /* filenames relative to ~ */
600 if(!((name[0] == '/')
601 || (name[0] == '.'
602 && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))))){
603 if(Pmaster && !(gmode&MDCURDIR)
604 && (*name != '~' && strlen(name)+2 < namelen)){
606 if(gmode&MDTREE && strlen(name)+strlen(opertree)+1 < namelen){
607 int off = strlen(opertree);
609 for(shft = strchr(name, '\0'); shft >= name; shft--)
610 shft[off+1] = *shft;
612 strncpy(name, opertree, MIN(off,namelen-1));
613 name[MIN(off,namelen-1)] = '/';
615 else{
616 for(shft = strchr(name, '\0'); shft >= name; shft--)
617 shft[2] = *shft;
619 name[0] = '~';
620 name[1] = '/';
624 pfnexpand(name, namelen);
626 #endif /* UNIX */
631 * compresspath - given a base path and an additional directory, collapse
632 * ".." and "." elements and return absolute path (appending
633 * base if necessary).
635 * returns 1 if OK,
636 * 0 if there's a problem
637 * new path, by side effect, if things went OK
640 compresspath(char *base, char *path, size_t pathlen)
642 register int i;
643 int depth = 0;
644 char *p;
645 char *stack[32];
646 char pathbuf[NLINE];
648 #define PUSHD(X) (stack[depth++] = X)
649 #define POPD() ((depth > 0) ? stack[--depth] : "")
651 #ifndef _WINDOWS
652 if(*path == '~'){
653 fixpath(path, pathlen);
654 strncpy(pathbuf, path, sizeof(pathbuf));
655 pathbuf[sizeof(pathbuf)-1] = '\0';
657 else if(*path != C_FILESEP)
658 snprintf(pathbuf, sizeof(pathbuf), "%s%c%s", base, C_FILESEP, path);
659 else{
660 strncpy(pathbuf, path, sizeof(pathbuf));
661 pathbuf[sizeof(pathbuf)-1] = '\0';
663 #else /* _WINDOWS */
664 strncpy(pathbuf, path, sizeof(pathbuf));
665 pathbuf[sizeof(pathbuf)-1] = '\0';
666 fixpath(pathbuf, sizeof(pathbuf));
667 #endif /* _WINDOWS */
669 p = &pathbuf[0];
670 for(i=0; pathbuf[i] != '\0'; i++){ /* pass thru path name */
671 if(pathbuf[i] == C_FILESEP){
672 if(p != pathbuf)
673 PUSHD(p); /* push dir entry */
675 p = &pathbuf[i+1]; /* advance p */
676 pathbuf[i] = '\0'; /* cap old p off */
677 continue;
680 if(pathbuf[i] == '.'){ /* special cases! */
681 if(pathbuf[i+1] == '.' /* parent */
682 && (pathbuf[i+2] == C_FILESEP || pathbuf[i+2] == '\0')){
683 if(!strcmp(POPD(), "")) /* bad news! */
684 return(0);
686 i += 2;
687 p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
689 else if(pathbuf[i+1] == C_FILESEP || pathbuf[i+1] == '\0'){
690 i++;
691 p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
696 if(*p != '\0')
697 PUSHD(p); /* get last element */
699 path[0] = '\0';
700 for(i = 0; i < depth; i++){
701 strncat(path, S_FILESEP, pathlen-strlen(path)-1);
702 path[pathlen-1] = '\0';
703 strncat(path, stack[i], pathlen-strlen(path)-1);
704 path[pathlen-1] = '\0';
707 return(1); /* everything's ok */
713 * tmpname - return a temporary file name in the given buffer, the filename
714 * is in the directory dir unless dir is NULL. The file did not exist at
715 * the time of the temp_nam call, but was created by temp_nam.
717 void
718 tmpname(char *dir, char *name)
720 char *t;
721 #ifndef _WINDOWS
722 if((t = temp_nam((dir && *dir) ? dir : NULL, "pico.")) != NULL){
723 #else /* _WINDOWS */
724 char tmp[_MAX_PATH];
726 if(!((dir && *dir) ||
727 (dir = getenv("TMPDIR")) ||
728 (dir = getenv("TMP")) ||
729 (dir = getenv("TEMP"))))
730 if(!(getcwd(dir = tmp, _MAX_PATH)
731 && fexist(dir, "w", (off_t *) NULL) == FIOSUC))
732 dir = "c:\\";
734 if((t = temp_nam_ext(dir, "ae", "txt")) != NULL){
735 #endif /* _WINDOWS */
736 strncpy(name, t, NFILEN-1);
737 name[NFILEN-1] = '\0';
738 free((void *)t);
740 else {
741 emlwrite("Unable to construct temp file name", NULL);
742 name[0] = '\0';
748 * Take a file name, and from it
749 * fabricate a buffer name. This routine knows
750 * about the syntax of file names on the target system.
751 * I suppose that this information could be put in
752 * a better place than a line of code.
754 void
755 makename(char bname[], char fname[])
757 register char *cp1;
758 register char *cp2;
760 cp1 = &fname[0];
761 while (*cp1 != 0)
762 ++cp1;
764 while (cp1!=&fname[0] && cp1[-1]!=C_FILESEP)
765 --cp1;
767 cp2 = &bname[0];
768 while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
769 *cp2++ = *cp1++;
771 *cp2 = 0;
776 * copy - copy contents of file 'a' into a file named 'b'. Return error
777 * if either isn't accessible or is a directory
780 copy(char *a, char *b)
782 int in, out, n, rv = 0;
783 char *cb;
784 struct stat tsb, fsb;
785 EML eml;
786 extern int errno;
788 if(our_stat(a, &fsb) < 0){ /* get source file info */
789 eml.s = errstr(errno);
790 emlwrite("Can't Copy: %s", &eml);
791 return(-1);
794 if(!(fsb.st_mode&S_IREAD)){ /* can we read it? */
795 eml.s = a;
796 emlwwrite(_("Read permission denied: %s"), &eml);
797 return(-1);
800 if((fsb.st_mode&S_IFMT) == S_IFDIR){ /* is it a directory? */
801 eml.s = a;
802 emlwwrite(_("Can't copy: %s is a directory"), &eml);
803 return(-1);
806 if(our_stat(b, &tsb) < 0){ /* get dest file's mode */
807 switch(errno){
808 case ENOENT:
809 break; /* these are OK */
810 default:
811 eml.s = errstr(errno);
812 emlwwrite(_("Can't Copy: %s"), &eml);
813 return(-1);
816 else{
817 if(!(tsb.st_mode&S_IWRITE)){ /* can we write it? */
818 eml.s = b;
819 emlwwrite(_("Write permission denied: %s"), &eml);
820 return(-1);
823 if((tsb.st_mode&S_IFMT) == S_IFDIR){ /* is it directory? */
824 eml.s = b;
825 emlwwrite(_("Can't copy: %s is a directory"), &eml);
826 return(-1);
829 if(fsb.st_dev == tsb.st_dev && fsb.st_ino == tsb.st_ino){
830 emlwwrite(_("Identical files. File not copied"), NULL);
831 return(-1);
835 if((in = our_open(a, O_RDONLY|O_BINARY, 0600)) < 0){
836 eml.s = errstr(errno);
837 emlwrite("Copy Failed: %s", &eml);
838 return(-1);
841 if((out=our_creat(b, fsb.st_mode&0xfff)) < 0){
842 eml.s = errstr(errno);
843 emlwrite("Can't Copy: %s", &eml);
844 close(in);
845 return(-1);
848 if((cb = (char *)malloc(NLINE*sizeof(char))) == NULL){
849 emlwrite("Can't allocate space for copy buffer!", NULL);
850 close(in);
851 close(out);
852 return(-1);
855 while(1){ /* do the copy */
856 if((n = read(in, cb, NLINE)) < 0){
857 eml.s = errstr(errno);
858 emlwrite("Can't Read Copy: %s", &eml);
859 rv = -1;
860 break; /* get out now */
863 if(n == 0) /* done! */
864 break;
866 if(write(out, cb, n) != n){
867 eml.s = errstr(errno);
868 emlwrite("Can't Write Copy: %s", &eml);
869 rv = -1;
870 break;
874 free(cb);
875 close(in);
876 close(out);
877 return(rv);
882 * Open a file for writing. Return TRUE if all is well, and FALSE on error
883 * (cannot create).
886 ffwopen(char *fn, int readonly)
888 extern FIOINFO g_pico_fio;
889 #ifndef _WINDOWS
890 int fd;
891 EML eml;
892 #endif
893 #ifndef MODE_READONLY
894 #define MODE_READONLY (0600)
895 #endif
898 * Call open() by hand since we don't want O_TRUNC -- it'll
899 * screw over-quota users. The strategy is to overwrite the
900 * existing file's data and call ftruncate before close to lop
901 * off bytes
904 g_pico_fio.flags = FIOINFO_WRITE;
905 g_pico_fio.name = fn;
906 #ifndef _WINDOWS
907 if((fd = our_open(fn, O_CREAT|O_WRONLY, readonly ? MODE_READONLY : 0666)) >= 0
908 && (g_pico_fio.fp = fdopen(fd, "w")) != NULL
909 && fseek(g_pico_fio.fp, 0L, 0) == 0)
910 return (FIOSUC);
913 eml.s = errstr(errno);
914 emlwrite("Cannot open file for writing: %s", &eml);
915 return (FIOERR);
916 #else /* _WINDOWS */
917 if ((g_pico_fio.fp = our_fopen(fn, "w")) == NULL) {
918 emlwrite("Cannot open file for writing", NULL);
919 return (FIOERR);
922 #ifdef MODE_READONLY
923 if(readonly)
924 our_chmod(fn, MODE_READONLY); /* fix access rights */
925 #endif
927 return (FIOSUC);
928 #endif /* _WINDOWS */
933 * Close a file. Should look at the status in all systems.
936 ffclose(void)
938 extern FIOINFO g_pico_fio;
939 #ifndef _WINDOWS
940 EML eml;
942 errno = 0;
943 if((g_pico_fio.flags & FIOINFO_WRITE)
944 && (fflush(g_pico_fio.fp) == EOF
945 || ftruncate(fileno(g_pico_fio.fp),
946 (off_t) ftell(g_pico_fio.fp)) < 0)){
947 eml.s = errstr(errno);
948 emlwwrite(_("Error preparing to close file: %s"), &eml);
949 sleep(5);
952 if (fclose(g_pico_fio.fp) == EOF) {
953 eml.s = errstr(errno);
954 emlwwrite(_("Error closing file: %s"), &eml);
955 return(FIOERR);
957 #else /* _WINDOWS */
958 if (fclose(g_pico_fio.fp) != FALSE) {
959 emlwrite("Error closing file", NULL);
960 return(FIOERR);
962 #endif /* _WINDOWS */
964 return(FIOSUC);
969 #define EXTEND_BLOCK 1024
973 * ffelbowroom - make sure the destination's got enough room to receive
974 * what we're about to write...
977 ffelbowroom(void)
979 #ifndef _WINDOWS
980 register LINE *lp;
981 register long n;
982 int x;
983 char s[EXTEND_BLOCK], *errstring = NULL;
984 struct stat fsbuf;
985 extern FIOINFO g_pico_fio;
987 /* Figure out how much room do we need */
988 /* first, what's total */
989 for(n=0L, lp=lforw(curbp->b_linep); lp != curbp->b_linep; lp=lforw(lp))
990 n += (llength(lp) + 1);
992 errno = 0; /* make sure previous error are cleared */
994 if(fstat(fileno(g_pico_fio.fp), &fsbuf) == 0){
995 n -= fsbuf.st_size;
997 if(n > 0L){ /* must be growing file, extend it */
998 memset(s, 'U', EXTEND_BLOCK);
999 if(fseek(g_pico_fio.fp, fsbuf.st_size, 0) == 0){
1000 for( ; n > 0L; n -= EXTEND_BLOCK){
1001 x = (n < EXTEND_BLOCK) ? (int) n : EXTEND_BLOCK;
1002 if(fwrite(s, x * sizeof(char), 1, g_pico_fio.fp) != 1){
1003 errstring = errstr(errno);
1004 break;
1008 if(!errstring
1009 && (fflush(g_pico_fio.fp) == EOF
1010 || fsync(fileno(g_pico_fio.fp)) < 0))
1011 errstring = errstr(errno);
1013 if(errstring) /* clean up */
1014 (void) ftruncate(fileno(g_pico_fio.fp), (off_t) fsbuf.st_size);
1015 else if(fseek(g_pico_fio.fp, 0L, 0) != 0)
1016 errstring = errstr(errno);
1018 else
1019 errstring = errstr(errno);
1022 else
1023 errstring = errstr(errno);
1025 if(errstring){
1026 snprintf(s, sizeof(s), "Error writing to %s: %s", g_pico_fio.name, errstring);
1027 emlwrite(s, NULL);
1028 (void) fclose(g_pico_fio.fp);
1029 return(FALSE);
1031 #endif /* UNIX */
1032 return(TRUE);