summaryrefslogtreecommitdiff
path: root/pico/osdep/filesys.c
diff options
context:
space:
mode:
Diffstat (limited to 'pico/osdep/filesys.c')
-rw-r--r--pico/osdep/filesys.c1027
1 files changed, 1027 insertions, 0 deletions
diff --git a/pico/osdep/filesys.c b/pico/osdep/filesys.c
new file mode 100644
index 00000000..4471882e
--- /dev/null
+++ b/pico/osdep/filesys.c
@@ -0,0 +1,1027 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: filesys.c 770 2007-10-24 00:23:09Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#if HAVE_PWD_H
+# include <pwd.h>
+#endif
+
+#include "../../pith/osdep/temp_nam.h"
+#include "../../pith/charconv/filesys.h"
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+
+#include "fsync.h"
+#include "filesys.h"
+
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+#else
+# define dirent direct
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+#endif
+
+
+/*
+ * fexist - returns TRUE if the file exists with mode passed in m,
+ * FALSE otherwise. By side effect returns length of file in l
+ * File is assumed to be a UTF-8 string.
+ */
+int
+fexist(char *file,
+ char *m, /* files mode: r,w,rw,t or x */
+ off_t *l) /* t means use lstat */
+{
+#ifndef _WINDOWS
+ struct stat sbuf;
+ int rv;
+ int (*stat_f)() = (m && *m == 't') ? our_lstat : our_stat;
+
+ if(l)
+ *l = (off_t)0;
+
+ rv = (*stat_f)(file, &sbuf);
+ if(rv < 0){
+ switch(errno){
+ case ENOENT : /* File not found */
+ rv = FIOFNF;
+ break;
+#ifdef ENAMETOOLONG
+ case ENAMETOOLONG : /* Name is too long */
+ rv = FIOLNG;
+ break;
+#endif
+ case EACCES : /* File not found */
+ rv = FIOPER;
+ break;
+ default: /* Some other error */
+ rv = FIOERR;
+ break;
+ }
+
+ return(rv);
+ }
+
+ if(l)
+ *l = (off_t)sbuf.st_size;
+
+ if((sbuf.st_mode&S_IFMT) == S_IFDIR)
+ return(FIODIR);
+ else if(*m == 't'){
+ struct stat sbuf2;
+
+ /*
+ * If it is a symbolic link pointing to a directory, treat
+ * it like it is a directory, not a link.
+ */
+ if((sbuf.st_mode&S_IFMT) == S_IFLNK){
+ rv = our_stat(file, &sbuf2);
+ if(rv < 0){
+ switch(errno){
+ case ENOENT : /* File not found */
+ rv = FIOSYM;
+ break;
+#ifdef ENAMETOOLONG
+ case ENAMETOOLONG : /* Name is too long */
+ rv = FIOLNG;
+ break;
+#endif
+ case EACCES : /* File not found */
+ rv = FIOPER;
+ break;
+ default: /* Some other error */
+ rv = FIOERR;
+ break;
+ }
+
+ return(rv);
+ }
+
+ if((sbuf2.st_mode&S_IFMT) == S_IFDIR)
+ return(FIODIR);
+ }
+
+ return(((sbuf.st_mode&S_IFMT) == S_IFLNK) ? FIOSYM : FIOSUC);
+ }
+
+ if(*m == 'r'){ /* read access? */
+ if(*(m+1) == 'w'){ /* and write access? */
+ rv = (can_access(file,READ_ACCESS)==0)
+ ? (can_access(file,WRITE_ACCESS)==0)
+ ? FIOSUC
+ : FIONWT
+ : FIONRD;
+ }
+ else if(!*(m+1)){ /* just read access? */
+ rv = (can_access(file,READ_ACCESS)==0) ? FIOSUC : FIONRD;
+ }
+ }
+ else if(*m == 'w' && !*(m+1)) /* write access? */
+ rv = (can_access(file,WRITE_ACCESS)==0) ? FIOSUC : FIONWT;
+ else if(*m == 'x' && !*(m+1)) /* execute access? */
+ rv = (can_access(file,EXECUTE_ACCESS)==0) ? FIOSUC : FIONEX;
+ else
+ rv = FIOERR; /* bad m arg */
+
+ return(rv);
+#else /* _WINDOWS */
+ struct stat sbuf;
+
+ if(l != NULL)
+ *l = (off_t)0;
+
+ if(our_stat(file, &sbuf) < 0){
+ if(errno == ENOENT) /* File not found */
+ return(FIOFNF);
+ else
+ return(FIOERR);
+ }
+
+ if(l != NULL)
+ *l = (off_t)sbuf.st_size;
+
+ if(sbuf.st_mode & S_IFDIR)
+ return(FIODIR);
+ else if(*m == 't') /* no links, just say yes */
+ return(FIOSUC);
+
+ if(m[0] == 'r') /* read access? */
+ return((S_IREAD & sbuf.st_mode) ? FIOSUC : FIONRD);
+ else if(m[0] == 'w') /* write access? */
+ return((S_IWRITE & sbuf.st_mode) ? FIOSUC : FIONWT);
+ else if(m[0] == 'x') /* execute access? */
+ return((S_IEXEC & sbuf.st_mode) ? FIOSUC : FIONEX);
+ return(FIOERR); /* what? */
+#endif /* _WINDOWS */
+}
+
+
+/*
+ * isdir - returns true if fn is a readable directory, false otherwise
+ * silent on errors (we'll let someone else notice the problem;)).
+ */
+int
+isdir(char *fn, long *l, time_t *d)
+{
+ struct stat sbuf;
+
+ if(l)
+ *l = 0;
+
+ if(our_stat(fn, &sbuf) < 0)
+ return(0);
+
+ if(l)
+ *l = sbuf.st_size;
+
+ if(d)
+ *d = sbuf.st_mtime;
+
+ return((sbuf.st_mode&S_IFMT) == S_IFDIR);
+}
+
+
+/*
+ * gethomedir - returns the users home directory in UTF-8 string
+ * Note: home is malloc'd for life of pico
+ */
+char *
+gethomedir(int *l)
+{
+ static char *home = NULL;
+ static short hlen = 0;
+
+ if(home == NULL){
+ char buf[NLINE];
+
+#ifndef _WINDOWS
+ strncpy(buf, "~", sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ fixpath(buf, sizeof(buf)); /* let fixpath do the work! */
+#else /* _WINDOWS */
+ if(Pmaster && Pmaster->home_dir)
+ snprintf(buf, sizeof(buf), "%s", Pmaster->home_dir);
+ else
+ snprintf(buf, sizeof(buf), "%c:\\", _getdrive() + 'A' - 1);
+#endif /* _WINDOWS */
+ hlen = strlen(buf);
+ if((home = (char *)malloc((hlen + 1) * sizeof(char))) == NULL){
+ emlwrite("Problem allocating space for home dir", NULL);
+ return(0);
+ }
+
+ strncpy(home, buf, hlen);
+ home[hlen] = '\0';
+ }
+
+ if(l)
+ *l = hlen;
+
+ return(home);
+}
+
+
+/*
+ * homeless - returns true if given file does not reside in the current
+ * user's home directory tree.
+ */
+int
+homeless(char *f)
+{
+ char *home;
+ int len;
+
+ home = gethomedir(&len);
+ return(strncmp(home, f, len));
+}
+
+
+/*
+ * getfnames - return all file names in the given directory in a single
+ * malloc'd string. n contains the number of names
+ */
+char *
+getfnames(char *dn, char *pat, int *n, char *e, size_t elen)
+{
+ size_t l, avail, alloced, incr = 1024;
+ char *names, *np, *p;
+ struct stat sbuf;
+#if defined(ct)
+ FILE *dirp;
+ char fn[DIRSIZ+1];
+#else
+#ifdef _WINDOWS
+ char buf[NLINE+1];
+ struct _finddata_t dbuf;
+ long findrv;
+#else
+ DIR *dirp; /* opened directory */
+#endif
+#endif
+#if defined(ct) || !defined(_WINDOWS)
+ struct dirent *dp;
+#endif
+
+ *n = 0;
+
+ if(our_stat(dn, &sbuf) < 0){
+ switch(errno){
+ case ENOENT : /* File not found */
+ if(e)
+ snprintf(e, elen, "\007File not found: \"%s\"", dn);
+
+ break;
+#ifdef ENAMETOOLONG
+ case ENAMETOOLONG : /* Name is too long */
+ if(e)
+ snprintf(e, elen, "\007File name too long: \"%s\"", dn);
+
+ break;
+#endif
+ default: /* Some other error */
+ if(e)
+ snprintf(e, elen, "\007Error getting file info: \"%s\"", dn);
+
+ break;
+ }
+ return(NULL);
+ }
+ else{
+ /*
+ * We'd like to use 512 * st_blocks as an initial estimate but
+ * some systems have a stat struct with no st_blocks in it.
+ */
+ avail = alloced = MAX(sbuf.st_size, incr);
+ if((sbuf.st_mode&S_IFMT) != S_IFDIR){
+ if(e)
+ snprintf(e, elen, "\007Not a directory: \"%s\"", dn);
+
+ return(NULL);
+ }
+ }
+
+ if((names=(char *)malloc(alloced * sizeof(char))) == NULL){
+ if(e)
+ snprintf(e, elen, "\007Can't malloc space for file names");
+
+ return(NULL);
+ }
+
+#ifndef _WINDOWS
+ errno = 0;
+ if((dirp=opendir(fname_to_locale(dn))) == NULL){
+ if(e)
+ snprintf(e, elen, "\007Can't open \"%s\": %s", dn, errstr(errno));
+
+ free((char *)names);
+ return(NULL);
+ }
+#endif
+
+ np = names;
+
+#if defined(ct)
+ while(fread(&dp, sizeof(struct direct), 1, dirp) > 0) {
+ /* skip empty slots with inode of 0 */
+ if(dp.d_ino == 0)
+ continue;
+ (*n)++; /* count the number of active slots */
+ (void)strncpy(fn, dp.d_name, DIRSIZ);
+ fn[14] = '\0';
+ p = fname_to_utf8(fn);
+ while((*np++ = *p++) != '\0')
+ ;
+ }
+#else /* !defined(ct) */
+#ifdef _WINDOWS
+ strncpy(buf, dn, sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ snprintf(buf, sizeof(buf), "%s%s%s*%s%s", dn,
+ (dn[strlen(dn)-1] == '\\') ? "" : "\\",
+ (pat && *pat) ? pat : "",
+ (pat && *pat && strchr(pat, '.')) ? "" : ".",
+ (pat && *pat && strchr(pat, '.')) ? "" : "*");
+ if((findrv = _findfirst(buf, &dbuf)) < 0){
+ if(e)
+ sprintf(e, "Can't find first file in \"%s\"", dn);
+
+ free((char *) names);
+ return(NULL);
+ }
+
+ do {
+ p = fname_to_utf8(dbuf.name);
+#else /* UNIX */
+ while((dp = readdir(dirp)) != NULL){
+ p = fname_to_utf8(dp->d_name);
+ if(!pat || !*pat || !strncmp(p, pat, strlen(pat))){
+#endif /* UNIX */
+ (*n)++;
+ l = strlen(p);
+ while(avail < l+1){
+ char *oldnames;
+
+ alloced += incr;
+ avail += incr;
+ oldnames = names;
+ if((names=(char *)realloc((void *)names, alloced * sizeof(char)))
+ == NULL){
+ if(e)
+ snprintf(e, elen, "\007Can't malloc enough space for file names");
+
+ return(NULL);
+ }
+
+ np = names + (np-oldnames);
+ }
+
+ avail -= (l+1);
+
+ while((*np++ = *p++) != '\0')
+ ;
+#ifdef _WINDOWS
+ }
+ while(_findnext(findrv, &dbuf) == 0);
+#else /* UNIX */
+ }
+ }
+#endif /* UNIX */
+#endif /* !defined(ct) */
+
+#ifdef _WINDOWS
+ _findclose(findrv);
+#else
+ closedir(dirp); /* shut down */
+#endif
+ return(names);
+}
+
+
+/*
+ * fioperr - given the error number and file name, display error
+ */
+void
+fioperr(int e, char *f)
+{
+ EML eml;
+
+ eml.s = f;
+
+ switch(e){
+ case FIOFNF: /* File not found */
+ emlwrite("\007File \"%s\" not found", &eml);
+ break;
+ case FIOEOF: /* end of file */
+ emlwrite("\007End of file \"%s\" reached", &eml);
+ break;
+ case FIOLNG: /* name too long */
+ emlwrite("\007File name \"%s\" too long", &eml);
+ break;
+ case FIODIR: /* file is a directory */
+ emlwrite("\007File \"%s\" is a directory", &eml);
+ break;
+ case FIONWT:
+ emlwrite("\007Write permission denied: %s", &eml);
+ break;
+ case FIONRD:
+ emlwrite("\007Read permission denied: %s", &eml);
+ break;
+ case FIOPER:
+ emlwrite("\007Permission denied: %s", &eml);
+ break;
+ case FIONEX:
+ emlwrite("\007Execute permission denied: %s", &eml);
+ break;
+ default:
+ emlwrite("\007File I/O error: %s", &eml);
+ }
+}
+
+
+
+/*
+ * pfnexpand - pico's function to expand the given file name if there is
+ * a leading '~'. Converts the homedir from user's locale to UTF-8.
+ * Fn is assumed to already be UTF-8.
+ */
+char *
+pfnexpand(char *fn, size_t fnlen)
+{
+ register char *x, *y, *z;
+ char *home = NULL;
+#ifndef _WINDOWS
+ struct passwd *pw;
+ char name[50];
+
+ if(*fn == '~'){
+ for(x = fn+1, y = name;
+ *x != '/' && *x != '\0' && y-name < sizeof(name)-1;
+ *y++ = *x++)
+ ;
+
+ *y = '\0';
+ if(x == fn + 1){ /* ~/ */
+ if (!(home = (char *) getenv("HOME")))
+ if ((pw = getpwuid(geteuid())) != NULL)
+ home = pw->pw_dir;
+ }
+ else if(*name){ /* ~username/ */
+ if((pw = getpwnam(name)) != NULL)
+ home = pw->pw_dir;
+ }
+
+ if(!home || (strlen(home) + strlen(fn) >= fnlen))
+ return(NULL);
+#else /* _WINDOWS */
+ char name[_MAX_PATH];
+
+ if(*fn == '~' && *(x = fn + 1) == '\\') {
+ if(!(home = (char *) getenv("HOME"))
+ && getenv("HOMEDRIVE") && getenv("HOMEPATH"))
+ snprintf(home = name, sizeof(name), "%s%s",
+ (char *) getenv("HOMEDRIVE"), (char *) getenv("HOMEPATH"));
+#endif /* _WINDOWS */
+ home = fname_to_utf8(home);
+
+ /* make room for expanded path */
+ for(z = x + strlen(x), y = fn + strlen(x) + strlen(home);
+ z >= x;
+ *y-- = *z--)
+ ;
+
+ /* and insert the expanded address */
+ for(x = fn, y = home; *y != '\0' && x-fn < fnlen; *x++ = *y++)
+ ;
+ }
+
+ fn[fnlen-1] = '\0';
+
+ return(fn);
+}
+
+
+/*
+ * fixpath - make the given pathname into an absolute path, incoming
+ * name is UTF-8.
+ */
+void
+fixpath(char *name, size_t namelen)
+{
+#ifdef _WINDOWS
+ char file[_MAX_PATH];
+ int dr;
+
+ if(!namelen)
+ return;
+
+ /* return the full path of given file, so drive spec? */
+ if(name[1] == ':' && isalpha((unsigned char) name[0])){
+ if(name[2] != '\\'){ /* including path? */
+ dr = toupper((unsigned char)name[0]) - 'A' + 1;
+ if((void *)_getdcwd(dr, file, _MAX_PATH) != NULL){
+ if(file[strlen(file)-1] != '\\')
+ strncat(file, "\\", sizeof(file)-1-strlen(file));
+
+ /* add file name */
+ strncat(file, &name[2], sizeof(file)-1-strlen(file));
+ }
+ else
+ return;
+ }
+ else
+ return; /* fully qualified with drive and path! */
+ }
+ else if(name[0] == '\\' && name[1] != '\\') { /* no drive spec! */
+ sprintf(file, "%c:%.*s", _getdrive()+'A'-1, namelen-3, name);
+ }
+ else if(name[0] == '\\' && name[1] == '\\'){ /* Windows network drive */
+ return;
+ }
+ else{
+ if(Pmaster && !(gmode & MDCURDIR)){
+ strncpy(file, ((gmode & MDTREE) || opertree[0])
+ ? opertree : gethomedir(NULL), sizeof(file)-1);
+ file[sizeof(file)-1] = '\0';
+ }
+ else if(!_getcwd(file, sizeof(file))) /* no qualification */
+ return;
+
+ if(*name){ /* if name, append it */
+ if(*file && file[strlen(file)-1] != '\\')
+ strncat(file, "\\", sizeof(file)-1-strlen(file));
+
+ strncat(file, name, sizeof(file)-1-strlen(file));
+ }
+ }
+
+ strncpy(name, file, namelen-1); /* copy back to real buffer */
+ name[namelen-1] = '\0'; /* tie off just in case */
+#else /* UNIX */
+ char *shft;
+
+ /* filenames relative to ~ */
+ if(!((name[0] == '/')
+ || (name[0] == '.'
+ && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))))){
+ if(Pmaster && !(gmode&MDCURDIR)
+ && (*name != '~' && strlen(name)+2 < namelen)){
+
+ if(gmode&MDTREE && strlen(name)+strlen(opertree)+1 < namelen){
+ int off = strlen(opertree);
+
+ for(shft = strchr(name, '\0'); shft >= name; shft--)
+ shft[off+1] = *shft;
+
+ strncpy(name, opertree, MIN(off,namelen-1));
+ name[MIN(off,namelen-1)] = '/';
+ }
+ else{
+ for(shft = strchr(name, '\0'); shft >= name; shft--)
+ shft[2] = *shft;
+
+ name[0] = '~';
+ name[1] = '/';
+ }
+ }
+
+ pfnexpand(name, namelen);
+ }
+#endif /* UNIX */
+}
+
+
+/*
+ * compresspath - given a base path and an additional directory, collapse
+ * ".." and "." elements and return absolute path (appending
+ * base if necessary).
+ *
+ * returns 1 if OK,
+ * 0 if there's a problem
+ * new path, by side effect, if things went OK
+ */
+int
+compresspath(char *base, char *path, size_t pathlen)
+{
+ register int i;
+ int depth = 0;
+ char *p;
+ char *stack[32];
+ char pathbuf[NLINE];
+
+#define PUSHD(X) (stack[depth++] = X)
+#define POPD() ((depth > 0) ? stack[--depth] : "")
+
+#ifndef _WINDOWS
+ if(*path == '~'){
+ fixpath(path, pathlen);
+ strncpy(pathbuf, path, sizeof(pathbuf));
+ pathbuf[sizeof(pathbuf)-1] = '\0';
+ }
+ else if(*path != C_FILESEP)
+ snprintf(pathbuf, sizeof(pathbuf), "%s%c%s", base, C_FILESEP, path);
+ else{
+ strncpy(pathbuf, path, sizeof(pathbuf));
+ pathbuf[sizeof(pathbuf)-1] = '\0';
+ }
+#else /* _WINDOWS */
+ strncpy(pathbuf, path, sizeof(pathbuf));
+ pathbuf[sizeof(pathbuf)-1] = '\0';
+ fixpath(pathbuf, sizeof(pathbuf));
+#endif /* _WINDOWS */
+
+ p = &pathbuf[0];
+ for(i=0; pathbuf[i] != '\0'; i++){ /* pass thru path name */
+ if(pathbuf[i] == C_FILESEP){
+ if(p != pathbuf)
+ PUSHD(p); /* push dir entry */
+
+ p = &pathbuf[i+1]; /* advance p */
+ pathbuf[i] = '\0'; /* cap old p off */
+ continue;
+ }
+
+ if(pathbuf[i] == '.'){ /* special cases! */
+ if(pathbuf[i+1] == '.' /* parent */
+ && (pathbuf[i+2] == C_FILESEP || pathbuf[i+2] == '\0')){
+ if(!strcmp(POPD(), "")) /* bad news! */
+ return(0);
+
+ i += 2;
+ p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
+ }
+ else if(pathbuf[i+1] == C_FILESEP || pathbuf[i+1] == '\0'){
+ i++;
+ p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
+ }
+ }
+ }
+
+ if(*p != '\0')
+ PUSHD(p); /* get last element */
+
+ path[0] = '\0';
+ for(i = 0; i < depth; i++){
+ strncat(path, S_FILESEP, pathlen-strlen(path)-1);
+ path[pathlen-1] = '\0';
+ strncat(path, stack[i], pathlen-strlen(path)-1);
+ path[pathlen-1] = '\0';
+ }
+
+ return(1); /* everything's ok */
+}
+
+
+
+/*
+ * tmpname - return a temporary file name in the given buffer, the filename
+ * is in the directory dir unless dir is NULL. The file did not exist at
+ * the time of the temp_nam call, but was created by temp_nam.
+ */
+void
+tmpname(char *dir, char *name)
+{
+ char *t;
+#ifndef _WINDOWS
+ if((t = temp_nam((dir && *dir) ? dir : NULL, "pico.")) != NULL){
+#else /* _WINDOWS */
+ char tmp[_MAX_PATH];
+
+ if(!((dir && *dir) ||
+ (dir = getenv("TMPDIR")) ||
+ (dir = getenv("TMP")) ||
+ (dir = getenv("TEMP"))))
+ if(!(getcwd(dir = tmp, _MAX_PATH)
+ && fexist(dir, "w", (off_t *) NULL) == FIOSUC))
+ dir = "c:\\";
+
+ if((t = temp_nam_ext(dir, "ae", "txt")) != NULL){
+#endif /* _WINDOWS */
+ strncpy(name, t, NFILEN-1);
+ name[NFILEN-1] = '\0';
+ free((void *)t);
+ }
+ else {
+ emlwrite("Unable to construct temp file name", NULL);
+ name[0] = '\0';
+ }
+}
+
+
+/*
+ * Take a file name, and from it
+ * fabricate a buffer name. This routine knows
+ * about the syntax of file names on the target system.
+ * I suppose that this information could be put in
+ * a better place than a line of code.
+ */
+void
+makename(char bname[], char fname[])
+{
+ register char *cp1;
+ register char *cp2;
+
+ cp1 = &fname[0];
+ while (*cp1 != 0)
+ ++cp1;
+
+ while (cp1!=&fname[0] && cp1[-1]!=C_FILESEP)
+ --cp1;
+
+ cp2 = &bname[0];
+ while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
+ *cp2++ = *cp1++;
+
+ *cp2 = 0;
+}
+
+
+/*
+ * copy - copy contents of file 'a' into a file named 'b'. Return error
+ * if either isn't accessible or is a directory
+ */
+int
+copy(char *a, char *b)
+{
+ int in, out, n, rv = 0;
+ char *cb;
+ struct stat tsb, fsb;
+ EML eml;
+ extern int errno;
+
+ if(our_stat(a, &fsb) < 0){ /* get source file info */
+ eml.s = errstr(errno);
+ emlwrite("Can't Copy: %s", &eml);
+ return(-1);
+ }
+
+ if(!(fsb.st_mode&S_IREAD)){ /* can we read it? */
+ eml.s = a;
+ emlwrite("\007Read permission denied: %s", &eml);
+ return(-1);
+ }
+
+ if((fsb.st_mode&S_IFMT) == S_IFDIR){ /* is it a directory? */
+ eml.s = a;
+ emlwrite("\007Can't copy: %s is a directory", &eml);
+ return(-1);
+ }
+
+ if(our_stat(b, &tsb) < 0){ /* get dest file's mode */
+ switch(errno){
+ case ENOENT:
+ break; /* these are OK */
+ default:
+ eml.s = errstr(errno);
+ emlwrite("\007Can't Copy: %s", &eml);
+ return(-1);
+ }
+ }
+ else{
+ if(!(tsb.st_mode&S_IWRITE)){ /* can we write it? */
+ eml.s = b;
+ emlwrite("\007Write permission denied: %s", &eml);
+ return(-1);
+ }
+
+ if((tsb.st_mode&S_IFMT) == S_IFDIR){ /* is it directory? */
+ eml.s = b;
+ emlwrite("\007Can't copy: %s is a directory", &eml);
+ return(-1);
+ }
+
+ if(fsb.st_dev == tsb.st_dev && fsb.st_ino == tsb.st_ino){
+ emlwrite("\007Identical files. File not copied", NULL);
+ return(-1);
+ }
+ }
+
+ if((in = our_open(a, O_RDONLY|O_BINARY, 0600)) < 0){
+ eml.s = errstr(errno);
+ emlwrite("Copy Failed: %s", &eml);
+ return(-1);
+ }
+
+ if((out=our_creat(b, fsb.st_mode&0xfff)) < 0){
+ eml.s = errstr(errno);
+ emlwrite("Can't Copy: %s", &eml);
+ close(in);
+ return(-1);
+ }
+
+ if((cb = (char *)malloc(NLINE*sizeof(char))) == NULL){
+ emlwrite("Can't allocate space for copy buffer!", NULL);
+ close(in);
+ close(out);
+ return(-1);
+ }
+
+ while(1){ /* do the copy */
+ if((n = read(in, cb, NLINE)) < 0){
+ eml.s = errstr(errno);
+ emlwrite("Can't Read Copy: %s", &eml);
+ rv = -1;
+ break; /* get out now */
+ }
+
+ if(n == 0) /* done! */
+ break;
+
+ if(write(out, cb, n) != n){
+ eml.s = errstr(errno);
+ emlwrite("Can't Write Copy: %s", &eml);
+ rv = -1;
+ break;
+ }
+ }
+
+ free(cb);
+ close(in);
+ close(out);
+ return(rv);
+}
+
+
+/*
+ * Open a file for writing. Return TRUE if all is well, and FALSE on error
+ * (cannot create).
+ */
+int
+ffwopen(char *fn, int readonly)
+{
+ extern FIOINFO g_pico_fio;
+#ifndef _WINDOWS
+ int fd;
+ EML eml;
+#endif
+#ifndef MODE_READONLY
+#define MODE_READONLY (0600)
+#endif
+
+ /*
+ * Call open() by hand since we don't want O_TRUNC -- it'll
+ * screw over-quota users. The strategy is to overwrite the
+ * existing file's data and call ftruncate before close to lop
+ * off bytes
+ */
+
+ g_pico_fio.flags = FIOINFO_WRITE;
+ g_pico_fio.name = fn;
+#ifndef _WINDOWS
+ if((fd = our_open(fn, O_CREAT|O_WRONLY, readonly ? MODE_READONLY : 0666)) >= 0
+ && (g_pico_fio.fp = fdopen(fd, "w")) != NULL
+ && fseek(g_pico_fio.fp, 0L, 0) == 0)
+ return (FIOSUC);
+
+
+ eml.s = errstr(errno);
+ emlwrite("Cannot open file for writing: %s", &eml);
+ return (FIOERR);
+#else /* _WINDOWS */
+ if ((g_pico_fio.fp = our_fopen(fn, "w")) == NULL) {
+ emlwrite("Cannot open file for writing", NULL);
+ return (FIOERR);
+ }
+
+#ifdef MODE_READONLY
+ if(readonly)
+ our_chmod(fn, MODE_READONLY); /* fix access rights */
+#endif
+
+ return (FIOSUC);
+#endif /* _WINDOWS */
+}
+
+
+/*
+ * Close a file. Should look at the status in all systems.
+ */
+int
+ffclose(void)
+{
+ extern FIOINFO g_pico_fio;
+#ifndef _WINDOWS
+ EML eml;
+
+ errno = 0;
+ if((g_pico_fio.flags & FIOINFO_WRITE)
+ && (fflush(g_pico_fio.fp) == EOF
+ || ftruncate(fileno(g_pico_fio.fp),
+ (off_t) ftell(g_pico_fio.fp)) < 0)){
+ eml.s = errstr(errno);
+ emlwrite("\007Error preparing to close file: %s", &eml);
+ sleep(5);
+ }
+
+ if (fclose(g_pico_fio.fp) == EOF) {
+ eml.s = errstr(errno);
+ emlwrite("\007Error closing file: %s", &eml);
+ return(FIOERR);
+ }
+#else /* _WINDOWS */
+ if (fclose(g_pico_fio.fp) != FALSE) {
+ emlwrite("Error closing file", NULL);
+ return(FIOERR);
+ }
+#endif /* _WINDOWS */
+
+ return(FIOSUC);
+}
+
+
+
+#define EXTEND_BLOCK 1024
+
+
+/*
+ * ffelbowroom - make sure the destination's got enough room to receive
+ * what we're about to write...
+ */
+int
+ffelbowroom(void)
+{
+#ifndef _WINDOWS
+ register LINE *lp;
+ register long n;
+ int x;
+ char s[EXTEND_BLOCK], *errstring = NULL;
+ struct stat fsbuf;
+ extern FIOINFO g_pico_fio;
+
+ /* Figure out how much room do we need */
+ /* first, what's total */
+ for(n=0L, lp=lforw(curbp->b_linep); lp != curbp->b_linep; lp=lforw(lp))
+ n += (llength(lp) + 1);
+
+ errno = 0; /* make sure previous error are cleared */
+
+ if(fstat(fileno(g_pico_fio.fp), &fsbuf) == 0){
+ n -= fsbuf.st_size;
+
+ if(n > 0L){ /* must be growing file, extend it */
+ memset(s, 'U', EXTEND_BLOCK);
+ if(fseek(g_pico_fio.fp, fsbuf.st_size, 0) == 0){
+ for( ; n > 0L; n -= EXTEND_BLOCK){
+ x = (n < EXTEND_BLOCK) ? (int) n : EXTEND_BLOCK;
+ if(fwrite(s, x * sizeof(char), 1, g_pico_fio.fp) != 1){
+ errstring = errstr(errno);
+ break;
+ }
+ }
+
+ if(!errstring
+ && (fflush(g_pico_fio.fp) == EOF
+ || fsync(fileno(g_pico_fio.fp)) < 0))
+ errstring = errstr(errno);
+
+ if(errstring) /* clean up */
+ (void) ftruncate(fileno(g_pico_fio.fp), (off_t) fsbuf.st_size);
+ else if(fseek(g_pico_fio.fp, 0L, 0) != 0)
+ errstring = errstr(errno);
+ }
+ else
+ errstring = errstr(errno);
+ }
+ }
+ else
+ errstring = errstr(errno);
+
+ if(errstring){
+ snprintf(s, sizeof(s), "Error writing to %s: %s", g_pico_fio.name, errstring);
+ emlwrite(s, NULL);
+ (void) fclose(g_pico_fio.fp);
+ return(FALSE);
+ }
+#endif /* UNIX */
+ return(TRUE);
+}