//======================================================================== // // gfile.cc // // Miscellaneous file and directory name manipulation. // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== //======================================================================== // // Modified under the Poppler project - http://poppler.freedesktop.org // // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // // Copyright (C) 2006 Takashi Iwai // Copyright (C) 2006 Kristian Høgsberg // Copyright (C) 2008 Adam Batkin // Copyright (C) 2008, 2010, 2012, 2013 Hib Eris // Copyright (C) 2009, 2012, 2014 Albert Astals Cid // Copyright (C) 2009 Kovid Goyal // Copyright (C) 2013 Adam Reichold // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2013 Peter Breitenlohner // Copyright (C) 2013 Thomas Freitag // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git // //======================================================================== #include #ifdef _WIN32 # include #else # if defined(MACOS) # include # elif !defined(ACORN) # include # include # include # endif # include # include # include # if !defined(VMS) && !defined(ACORN) && !defined(MACOS) # include # endif # if defined(VMS) && (__DECCXX_VER < 50200000) # include # endif #endif // _WIN32 #include #include #include "GooString.h" #include "gfile.h" // Some systems don't define this, so just make it something reasonably // large. #ifndef PATH_MAX #define PATH_MAX 1024 #endif //------------------------------------------------------------------------ GooString *getCurrentDir() { char buf[PATH_MAX+1]; #if defined(__EMX__) if (_getcwd2(buf, sizeof(buf))) #elif defined(_WIN32) if (GetCurrentDirectory(sizeof(buf), buf)) #elif defined(ACORN) if (strcpy(buf, "@")) #elif defined(MACOS) if (strcpy(buf, ":")) #else if (getcwd(buf, sizeof(buf))) #endif return new GooString(buf); return new GooString(); } GooString *appendToPath(GooString *path, const char *fileName) { #if defined(VMS) //---------- VMS ---------- //~ this should handle everything necessary for file //~ requesters, but it's certainly not complete char *p0, *p1, *p2; char *q1; p0 = path->getCString(); p1 = p0 + path->getLength() - 1; if (!strcmp(fileName, "-")) { if (*p1 == ']') { for (p2 = p1; p2 > p0 && *p2 != '.' && *p2 != '['; --p2) ; if (*p2 == '[') ++p2; path->del(p2 - p0, p1 - p2); } else if (*p1 == ':') { path->append("[-]"); } else { path->clear(); path->append("[-]"); } } else if ((q1 = strrchr(fileName, '.')) && !strncmp(q1, ".DIR;", 5)) { if (*p1 == ']') { path->insert(p1 - p0, '.'); path->insert(p1 - p0 + 1, fileName, q1 - fileName); } else if (*p1 == ':') { path->append('['); path->append(']'); path->append(fileName, q1 - fileName); } else { path->clear(); path->append(fileName, q1 - fileName); } } else { if (*p1 != ']' && *p1 != ':') path->clear(); path->append(fileName); } return path; #elif defined(_WIN32) //---------- Win32 ---------- GooString *tmp; char buf[256]; char *fp; tmp = new GooString(path); tmp->append('/'); tmp->append(fileName); GetFullPathName(tmp->getCString(), sizeof(buf), buf, &fp); delete tmp; path->clear(); path->append(buf); return path; #elif defined(ACORN) //---------- RISCOS ---------- char *p; int i; path->append("."); i = path->getLength(); path->append(fileName); for (p = path->getCString() + i; *p; ++p) { if (*p == '/') { *p = '.'; } else if (*p == '.') { *p = '/'; } } return path; #elif defined(MACOS) //---------- MacOS ---------- char *p; int i; path->append(":"); i = path->getLength(); path->append(fileName); for (p = path->getCString() + i; *p; ++p) { if (*p == '/') { *p = ':'; } else if (*p == '.') { *p = ':'; } } return path; #elif defined(__EMX__) //---------- OS/2+EMX ---------- int i; // appending "." does nothing if (!strcmp(fileName, ".")) return path; // appending ".." goes up one directory if (!strcmp(fileName, "..")) { for (i = path->getLength() - 2; i >= 0; --i) { if (path->getChar(i) == '/' || path->getChar(i) == '\\' || path->getChar(i) == ':') break; } if (i <= 0) { if (path->getChar(0) == '/' || path->getChar(0) == '\\') { path->del(1, path->getLength() - 1); } else if (path->getLength() >= 2 && path->getChar(1) == ':') { path->del(2, path->getLength() - 2); } else { path->clear(); path->append(".."); } } else { if (path->getChar(i-1) == ':') ++i; path->del(i, path->getLength() - i); } return path; } // otherwise, append "/" and new path component if (path->getLength() > 0 && path->getChar(path->getLength() - 1) != '/' && path->getChar(path->getLength() - 1) != '\\') path->append('/'); path->append(fileName); return path; #else //---------- Unix ---------- int i; // appending "." does nothing if (!strcmp(fileName, ".")) return path; // appending ".." goes up one directory if (!strcmp(fileName, "..")) { for (i = path->getLength() - 2; i >= 0; --i) { if (path->getChar(i) == '/') break; } if (i <= 0) { if (path->getChar(0) == '/') { path->del(1, path->getLength() - 1); } else { path->clear(); path->append(".."); } } else { path->del(i, path->getLength() - i); } return path; } // otherwise, append "/" and new path component if (path->getLength() > 0 && path->getChar(path->getLength() - 1) != '/') path->append('/'); path->append(fileName); return path; #endif } GooString *grabPath(char *fileName) { #ifdef VMS //---------- VMS ---------- char *p; if ((p = strrchr(fileName, ']'))) return new GooString(fileName, p + 1 - fileName); if ((p = strrchr(fileName, ':'))) return new GooString(fileName, p + 1 - fileName); return new GooString(); #elif defined(__EMX__) || defined(_WIN32) //---------- OS/2+EMX and Win32 ---------- char *p; if ((p = strrchr(fileName, '/'))) return new GooString(fileName, p - fileName); if ((p = strrchr(fileName, '\\'))) return new GooString(fileName, p - fileName); if ((p = strrchr(fileName, ':'))) return new GooString(fileName, p + 1 - fileName); return new GooString(); #elif defined(ACORN) //---------- RISCOS ---------- char *p; if ((p = strrchr(fileName, '.'))) return new GooString(fileName, p - fileName); return new GooString(); #elif defined(MACOS) //---------- MacOS ---------- char *p; if ((p = strrchr(fileName, ':'))) return new GooString(fileName, p - fileName); return new GooString(); #else //---------- Unix ---------- char *p; if ((p = strrchr(fileName, '/'))) return new GooString(fileName, p - fileName); return new GooString(); #endif } GBool isAbsolutePath(char *path) { #ifdef VMS //---------- VMS ---------- return strchr(path, ':') || (path[0] == '[' && path[1] != '.' && path[1] != '-'); #elif defined(__EMX__) || defined(_WIN32) //---------- OS/2+EMX and Win32 ---------- return path[0] == '/' || path[0] == '\\' || path[1] == ':'; #elif defined(ACORN) //---------- RISCOS ---------- return path[0] == '$'; #elif defined(MACOS) //---------- MacOS ---------- return path[0] != ':'; #else //---------- Unix ---------- return path[0] == '/'; #endif } time_t getModTime(char *fileName) { #ifdef _WIN32 //~ should implement this, but it's (currently) only used in xpdf return 0; #else struct stat statBuf; if (stat(fileName, &statBuf)) { return 0; } return statBuf.st_mtime; #endif } GBool openTempFile(GooString **name, FILE **f, const char *mode) { #if defined(_WIN32) //---------- Win32 ---------- char *tempDir; GooString *s, *s2; FILE *f2; int t, i; // this has the standard race condition problem, but I haven't found // a better way to generate temp file names with extensions on // Windows if ((tempDir = getenv("TEMP"))) { s = new GooString(tempDir); s->append('\\'); } else { s = new GooString(); } s->appendf("x_{0:d}_{1:d}_", (int)GetCurrentProcessId(), (int)GetCurrentThreadId()); t = (int)time(NULL); for (i = 0; i < 1000; ++i) { s2 = s->copy()->appendf("{0:d}", t + i); if (!(f2 = fopen(s2->getCString(), "r"))) { if (!(f2 = fopen(s2->getCString(), mode))) { delete s2; delete s; return gFalse; } *name = s2; *f = f2; delete s; return gTrue; } fclose(f2); delete s2; } delete s; return gFalse; #elif defined(VMS) || defined(__EMX__) || defined(ACORN) || defined(MACOS) //---------- non-Unix ---------- char *s; // There is a security hole here: an attacker can create a symlink // with this file name after the tmpnam call and before the fopen // call. I will happily accept fixes to this function for non-Unix // OSs. if (!(s = tmpnam(NULL))) { return gFalse; } *name = new GooString(s); if (!(*f = fopen((*name)->getCString(), mode))) { delete (*name); *name = NULL; return gFalse; } return gTrue; #else //---------- Unix ---------- char *s; int fd; #if HAVE_MKSTEMP if ((s = getenv("TMPDIR"))) { *name = new GooString(s); } else { *name = new GooString("/tmp"); } (*name)->append("/XXXXXX"); fd = mkstemp((*name)->getCString()); #else // HAVE_MKSTEMP if (!(s = tmpnam(NULL))) { return gFalse; } *name = new GooString(s); fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600); #endif // HAVE_MKSTEMP if (fd < 0 || !(*f = fdopen(fd, mode))) { delete *name; *name = NULL; return gFalse; } return gTrue; #endif } #ifdef WIN32 GooString *fileNameToUTF8(char *path) { GooString *s; char *p; s = new GooString(); for (p = path; *p; ++p) { if (*p & 0x80) { s->append((char)(0xc0 | ((*p >> 6) & 0x03))); s->append((char)(0x80 | (*p & 0x3f))); } else { s->append(*p); } } return s; } GooString *fileNameToUTF8(wchar_t *path) { GooString *s; wchar_t *p; s = new GooString(); for (p = path; *p; ++p) { if (*p < 0x80) { s->append((char)*p); } else if (*p < 0x800) { s->append((char)(0xc0 | ((*p >> 6) & 0x1f))); s->append((char)(0x80 | (*p & 0x3f))); } else { s->append((char)(0xe0 | ((*p >> 12) & 0x0f))); s->append((char)(0x80 | ((*p >> 6) & 0x3f))); s->append((char)(0x80 | (*p & 0x3f))); } } return s; } #endif FILE *openFile(const char *path, const char *mode) { #ifdef WIN32 OSVERSIONINFO version; wchar_t wPath[_MAX_PATH + 1]; char nPath[_MAX_PATH + 1]; wchar_t wMode[8]; const char *p; size_t i; // NB: _wfopen is only available in NT version.dwOSVersionInfoSize = sizeof(version); GetVersionEx(&version); if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { for (p = path, i = 0; *p && i < _MAX_PATH; ++i) { if ((p[0] & 0xe0) == 0xc0 && p[1] && (p[1] & 0xc0) == 0x80) { wPath[i] = (wchar_t)(((p[0] & 0x1f) << 6) | (p[1] & 0x3f)); p += 2; } else if ((p[0] & 0xf0) == 0xe0 && p[1] && (p[1] & 0xc0) == 0x80 && p[2] && (p[2] & 0xc0) == 0x80) { wPath[i] = (wchar_t)(((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f)); p += 3; } else { wPath[i] = (wchar_t)(p[0] & 0xff); p += 1; } } wPath[i] = (wchar_t)0; for (i = 0; (i < sizeof(mode) - 1) && mode[i]; ++i) { wMode[i] = (wchar_t)(mode[i] & 0xff); } wMode[i] = (wchar_t)0; return _wfopen(wPath, wMode); } else { for (p = path, i = 0; *p && i < _MAX_PATH; ++i) { if ((p[0] & 0xe0) == 0xc0 && p[1] && (p[1] & 0xc0) == 0x80) { nPath[i] = (char)(((p[0] & 0x1f) << 6) | (p[1] & 0x3f)); p += 2; } else if ((p[0] & 0xf0) == 0xe0 && p[1] && (p[1] & 0xc0) == 0x80 && p[2] && (p[2] & 0xc0) == 0x80) { nPath[i] = (char)(((p[1] & 0x3f) << 6) | (p[2] & 0x3f)); p += 3; } else { nPath[i] = p[0]; p += 1; } } nPath[i] = '\0'; return fopen(nPath, mode); } #else return fopen(path, mode); #endif } char *getLine(char *buf, int size, FILE *f) { int c, i; i = 0; while (i < size - 1) { if ((c = fgetc(f)) == EOF) { break; } buf[i++] = (char)c; if (c == '\x0a') { break; } if (c == '\x0d') { c = fgetc(f); if (c == '\x0a' && i < size - 1) { buf[i++] = (char)c; } else if (c != EOF) { ungetc(c, f); } break; } } buf[i] = '\0'; if (i == 0) { return NULL; } return buf; } int Gfseek(FILE *f, Goffset offset, int whence) { #if HAVE_FSEEKO return fseeko(f, offset, whence); #elif HAVE_FSEEK64 return fseek64(f, offset, whence); #elif defined(__MINGW32__) return fseeko64(f, offset, whence); #elif _WIN32 return _fseeki64(f, offset, whence); #else return fseek(f, offset, whence); #endif } Goffset Gftell(FILE *f) { #if HAVE_FSEEKO return ftello(f); #elif HAVE_FSEEK64 return ftell64(f); #elif defined(__MINGW32__) return ftello64(f); #elif _WIN32 return _ftelli64(f); #else return ftell(f); #endif } Goffset GoffsetMax() { #if HAVE_FSEEKO return (std::numeric_limits::max)(); #elif HAVE_FSEEK64 || defined(__MINGW32__) return (std::numeric_limits::max)(); #elif _WIN32 return (std::numeric_limits<__int64>::max)(); #else return (std::numeric_limits::max)(); #endif } //------------------------------------------------------------------------ // GooFile //------------------------------------------------------------------------ #ifdef _WIN32 int GooFile::read(char *buf, int n, Goffset offset) const { DWORD m; LARGE_INTEGER largeInteger = {0}; largeInteger.QuadPart = offset; OVERLAPPED overlapped = {0}; overlapped.Offset = largeInteger.LowPart; overlapped.OffsetHigh = largeInteger.HighPart; return FALSE == ReadFile(handle, buf, n, &m, &overlapped) ? -1 : m; } Goffset GooFile::size() const { LARGE_INTEGER size = {(DWORD)-1,-1}; GetFileSizeEx(handle, &size); return size.QuadPart; } GooFile* GooFile::open(const GooString *fileName) { HANDLE handle = CreateFile(fileName->getCString(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); return handle == INVALID_HANDLE_VALUE ? NULL : new GooFile(handle); } GooFile* GooFile::open(const wchar_t *fileName) { HANDLE handle = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); return handle == INVALID_HANDLE_VALUE ? NULL : new GooFile(handle); } #else int GooFile::read(char *buf, int n, Goffset offset) const { #ifdef HAVE_PREAD64 return pread64(fd, buf, n, offset); #else return pread(fd, buf, n, offset); #endif } Goffset GooFile::size() const { #ifdef HAVE_LSEEK64 return lseek64(fd, 0, SEEK_END); #else return lseek(fd, 0, SEEK_END); #endif } GooFile* GooFile::open(const GooString *fileName) { #ifdef VMS int fd = ::open(fileName->getCString(), Q_RDONLY, "ctx=stm"); #else int fd = ::open(fileName->getCString(), O_RDONLY); #endif return fd < 0 ? NULL : new GooFile(fd); } #endif // _WIN32 //------------------------------------------------------------------------ // GDir and GDirEntry //------------------------------------------------------------------------ GDirEntry::GDirEntry(char *dirPath, char *nameA, GBool doStat) { #ifdef VMS char *p; #elif defined(_WIN32) DWORD fa; #elif defined(ACORN) #else struct stat st; #endif name = new GooString(nameA); dir = gFalse; fullPath = new GooString(dirPath); appendToPath(fullPath, nameA); if (doStat) { #ifdef VMS if (!strcmp(nameA, "-") || ((p = strrchr(nameA, '.')) && !strncmp(p, ".DIR;", 5))) dir = gTrue; #elif defined(ACORN) #else #ifdef _WIN32 fa = GetFileAttributes(fullPath->getCString()); dir = (fa != 0xFFFFFFFF && (fa & FILE_ATTRIBUTE_DIRECTORY)); #else if (stat(fullPath->getCString(), &st) == 0) dir = S_ISDIR(st.st_mode); #endif #endif } } GDirEntry::~GDirEntry() { delete fullPath; delete name; } GDir::GDir(char *name, GBool doStatA) { path = new GooString(name); doStat = doStatA; #if defined(_WIN32) GooString *tmp; tmp = path->copy(); tmp->append("/*.*"); hnd = FindFirstFile(tmp->getCString(), &ffd); delete tmp; #elif defined(ACORN) #elif defined(MACOS) #else dir = opendir(name); #ifdef VMS needParent = strchr(name, '[') != NULL; #endif #endif } GDir::~GDir() { delete path; #if defined(_WIN32) if (hnd != INVALID_HANDLE_VALUE) { FindClose(hnd); hnd = INVALID_HANDLE_VALUE; } #elif defined(ACORN) #elif defined(MACOS) #else if (dir) closedir(dir); #endif } GDirEntry *GDir::getNextEntry() { GDirEntry *e = NULL; #if defined(_WIN32) if (hnd != INVALID_HANDLE_VALUE) { e = new GDirEntry(path->getCString(), ffd.cFileName, doStat); if (!FindNextFile(hnd, &ffd)) { FindClose(hnd); hnd = INVALID_HANDLE_VALUE; } } #elif defined(ACORN) #elif defined(MACOS) #elif defined(VMS) struct dirent *ent; if (dir) { if (needParent) { e = new GDirEntry(path->getCString(), "-", doStat); needParent = gFalse; return e; } ent = readdir(dir); if (ent) { e = new GDirEntry(path->getCString(), ent->d_name, doStat); } } #else struct dirent *ent; if (dir) { do { ent = readdir(dir); } while (ent && (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))); if (ent) { e = new GDirEntry(path->getCString(), ent->d_name, doStat); } } #endif return e; } void GDir::rewind() { #ifdef _WIN32 GooString *tmp; if (hnd != INVALID_HANDLE_VALUE) FindClose(hnd); tmp = path->copy(); tmp->append("/*.*"); hnd = FindFirstFile(tmp->getCString(), &ffd); delete tmp; #elif defined(ACORN) #elif defined(MACOS) #else if (dir) rewinddir(dir); #ifdef VMS needParent = strchr(path->getCString(), '[') != NULL; #endif #endif }