/* * ======================================================================== * Copyright 2006-2007 University of Washington * Copyright 2013-2021 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 #include #if HAVE_PWD_H # include #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 #else # define dirent direct # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # 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, _("File not found: \"%s\""), dn); break; #ifdef ENAMETOOLONG case ENAMETOOLONG : /* Name is too long */ if(e) snprintf(e, elen, _("File name too long: \"%s\""), dn); break; #endif default: /* Some other error */ if(e) snprintf(e, elen, _("Error 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. * * In some systems the size of a directory is the sum of the * sizes of all files contained in it, so sbuf.st_size might * be too large and the malloc() might fail with error ENOMEM. * Since this is just the list of files, and not the contents * of the files, let us start with a small amount and resize * when necessary. Reported by Sebasting Knust. Of course the * correct solution is to read the directory twice, but keeping * realloc()ing as needed also solves the problem in a less * elegant way. */ avail = alloced = 4096; if((sbuf.st_mode&S_IFMT) != S_IFDIR){ if(e) snprintf(e, elen, _("Not a directory: \"%s\""), dn); return(NULL); } } if((names=(char *)malloc(alloced * sizeof(char))) == NULL){ if(e) snprintf(e, elen, _("Can'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, _("Can'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, _("Can'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 */ emlwwrite(_("File \"%s\" not found"), &eml); break; case FIOEOF: /* end of file */ emlwwrite(_("End of file \"%s\" reached"), &eml); break; case FIOLNG: /* name too long */ emlwwrite(_("File name \"%s\" too long"), &eml); break; case FIODIR: /* file is a directory */ emlwwrite(_("File \"%s\" is a directory"), &eml); break; case FIONWT: emlwwrite(_("Write permission denied: %s"), &eml); break; case FIONRD: emlwwrite(_("Read permission denied: %s"), &eml); break; case FIOPER: emlwwrite(_("Permission denied: %s"), &eml); break; case FIONEX: emlwwrite(_("Execute permission denied: %s"), &eml); break; default: emlwwrite(_("File 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; emlwwrite(_("Read permission denied: %s"), &eml); return(-1); } if((fsb.st_mode&S_IFMT) == S_IFDIR){ /* is it a directory? */ eml.s = a; emlwwrite(_("Can'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); emlwwrite(_("Can't Copy: %s"), &eml); return(-1); } } else{ if(!(tsb.st_mode&S_IWRITE)){ /* can we write it? */ eml.s = b; emlwwrite(_("Write permission denied: %s"), &eml); return(-1); } if((tsb.st_mode&S_IFMT) == S_IFDIR){ /* is it directory? */ eml.s = b; emlwwrite(_("Can't copy: %s is a directory"), &eml); return(-1); } if(fsb.st_dev == tsb.st_dev && fsb.st_ino == tsb.st_ino){ emlwwrite(_("Identical 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); emlwwrite(_("Error preparing to close file: %s"), &eml); sleep(5); } if (fclose(g_pico_fio.fp) == EOF) { eml.s = errstr(errno); emlwwrite(_("Error 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); }