/****************************************************************************** * filemgr.cpp - implementation of class FileMgr used for pooling file * handles * * $Id: filemgr.cpp,v 1.33 2003/12/23 01:36:12 chrislit Exp $ * * Copyright 1998 CrossWire Bible Society (http://www.crosswire.org) * CrossWire Bible Society * P. O. Box 2528 * Tempe, AZ 85280-2528 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation version 2. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * */ #include #include #include #include #include #include #include #include #if !defined(__GNUC__) && !defined(_WIN32_WCE) #include #include #else #include #endif #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef S_IRGRP #define S_IRGRP 0 #endif #ifndef S_IROTH #define S_IROTH 0 #endif SWORD_NAMESPACE_START // ---------------- statics ----------------- FileMgr FileMgr::systemFileMgr; // --------------- end statics -------------- FileDesc::FileDesc(FileMgr *parent, const char *path, int mode, int perms, bool tryDowngrade) { this->parent = parent; this->path = 0; stdstr(&this->path, path); this->mode = mode; this->perms = perms; this->tryDowngrade = tryDowngrade; offset = 0; fd = -77; } FileDesc::~FileDesc() { if (fd > 0) close(fd); if (path) delete [] path; } int FileDesc::getFd() { if (fd == -77) fd = parent->sysOpen(this); return fd; } FileMgr::FileMgr(int maxFiles) { this->maxFiles = maxFiles; // must be at least 2 files = 0; } FileMgr::~FileMgr() { FileDesc *tmp; while(files) { tmp = files->next; delete files; files = tmp; } } FileDesc *FileMgr::open(const char *path, int mode, bool tryDowngrade) { return open(path, mode, S_IREAD | S_IWRITE|S_IRGRP|S_IROTH, tryDowngrade); } FileDesc *FileMgr::open(const char *path, int mode, int perms, bool tryDowngrade) { FileDesc **tmp, *tmp2; for (tmp = &files; *tmp; tmp = &((*tmp)->next)) { if ((*tmp)->fd < 0) // insert as first non-system_open file break; } tmp2 = new FileDesc(this, path, mode, perms, tryDowngrade); tmp2->next = *tmp; *tmp = tmp2; return tmp2; } void FileMgr::close(FileDesc *file) { FileDesc **loop; for (loop = &files; *loop; loop = &((*loop)->next)) { if (*loop == file) { *loop = (*loop)->next; delete file; break; } } } // to truncate a file at its current position // leaving byte at current possition intact // deleting everything afterward. signed char FileMgr::trunc(FileDesc *file) { static const char *writeTest = "x"; long size = lseek(file->getFd(), 1, SEEK_CUR); if (size == 1) // was empty size = 0; char nibble [ 32767 ]; bool writable = write(file->getFd(), writeTest, 1); int bytes = 0; if (writable) { // get tmpfilename char *buf = new char [ strlen(file->path) + 10 ]; int i; for (i = 0; i < 9999; i++) { sprintf(buf, "%stmp%.4d", file->path, i); if (!existsFile(buf)) break; } if (i == 9999) return -2; int fd = ::open(buf, O_CREAT|O_RDWR, S_IREAD|S_IWRITE|S_IRGRP|S_IROTH); if (fd < 0) return -3; lseek(file->getFd(), 0, SEEK_SET); while (size > 0) { bytes = read(file->getFd(), nibble, 32767); write(fd, nibble, (bytes < size)?bytes:size); size -= bytes; } // zero out the file ::close(file->fd); file->fd = ::open(file->path, O_TRUNC, S_IREAD|S_IWRITE|S_IRGRP|S_IROTH); ::close(file->fd); file->fd = -77; // force file open by filemgr // copy tmp file back (dumb, but must preserve file permissions) lseek(fd, 0, SEEK_SET); do { bytes = read(fd, nibble, 32767); write(file->getFd(), nibble, bytes); } while (bytes == 32767); ::close(fd); ::close(file->fd); removeFile(buf); // remove our tmp file file->fd = -77; // causes file to be swapped out forcing open on next call to getFd() } else { // put offset back and return failure lseek(file->getFd(), -1, SEEK_CUR); return -1; } return 0; } int FileMgr::sysOpen(FileDesc *file) { FileDesc **loop; int openCount = 1; // because we are presently opening 1 file, and we need to be sure to close files to accomodate, if necessary for (loop = &files; *loop; loop = &((*loop)->next)) { if ((*loop)->fd > 0) { if (++openCount > maxFiles) { (*loop)->offset = lseek((*loop)->fd, 0, SEEK_CUR); ::close((*loop)->fd); (*loop)->fd = -77; } } if (*loop == file) { if (*loop != files) { *loop = (*loop)->next; file->next = files; files = file; } if ((!access(file->path, 04)) || ((file->mode & O_CREAT) == O_CREAT)) { // check for at least file exists / read access before we try to open char tries = (((file->mode & O_RDWR) == O_RDWR) && (file->tryDowngrade)) ? 2 : 1; // try read/write if possible for (int i = 0; i < tries; i++) { if (i > 0) { file->mode = (file->mode & ~O_RDWR); // remove write access file->mode = (file->mode | O_RDONLY);// add read access } file->fd = ::open(file->path, file->mode, file->perms); if (file->fd >= 0) break; } if (file->fd >= 0) lseek(file->fd, file->offset, SEEK_SET); } else file->fd = -1; if (!*loop) break; } } return file->fd; } signed char FileMgr::existsFile(const char *ipath, const char *ifileName) { int len = strlen(ipath) + ((ifileName)?strlen(ifileName):0) + 3; char *ch; char *path = new char [ len ]; strcpy(path, ipath); if ((path[strlen(path)-1] == '\\') || (path[strlen(path)-1] == '/')) path[strlen(path)-1] = 0; if (ifileName) { ch = path + strlen(path); sprintf(ch, "/%s", ifileName); } signed char retVal = !access(path, 04); delete [] path; return retVal; } signed char FileMgr::existsDir(const char *ipath, const char *idirName) { char *ch; int len = strlen(ipath) + ((idirName)?strlen(idirName):0) + 1; if (idirName) len += strlen(idirName); char *path = new char [ len ]; strcpy(path, ipath); if ((path[strlen(path)-1] == '\\') || (path[strlen(path)-1] == '/')) path[strlen(path)-1] = 0; if (idirName) { ch = path + strlen(path); sprintf(ch, "/%s", idirName); } signed char retVal = !access(path, 04); delete [] path; return retVal; } int FileMgr::createParent(const char *pName) { char *buf = new char [ strlen(pName) + 1 ]; int retCode = 0; strcpy(buf, pName); int end = strlen(buf) - 1; while (end) { if ((buf[end] == '/') || (buf[end] == '\\')) break; end--; } buf[end] = 0; if (strlen(buf)>0) { if (access(buf, 02)) { // not exists with write access? if ((retCode = mkdir(buf #ifndef WIN32 , 0755 #endif ))) { createParent(buf); retCode = mkdir(buf #ifndef WIN32 , 0755 #endif ); } } } else retCode = -1; delete [] buf; return retCode; } int FileMgr::createPathAndFile(const char *fName) { int fd; fd = ::open(fName, O_CREAT|O_WRONLY|O_BINARY, S_IREAD|S_IWRITE|S_IRGRP|S_IROTH); if (fd < 1) { createParent(fName); fd = ::open(fName, O_CREAT|O_WRONLY|O_BINARY, S_IREAD|S_IWRITE|S_IRGRP|S_IROTH); } return fd; } int FileMgr::copyFile(const char *sourceFile, const char *targetFile) { int sfd, dfd, len; char buf[4096]; if ((sfd = ::open(sourceFile, O_RDONLY|O_BINARY)) < 1) return -1; if ((dfd = createPathAndFile(targetFile)) < 1) return -1; do { len = read(sfd, buf, 4096); write(dfd, buf, len); } while(len == 4096); ::close(dfd); ::close(sfd); return 0; } int FileMgr::removeFile(const char *fName) { return ::remove(fName); } SWORD_NAMESPACE_END