1 //========================================================================
5 // Miscellaneous file and directory name manipulation.
7 // Copyright 1996-2003 Glyph & Cog, LLC
9 //========================================================================
11 //========================================================================
13 // Modified under the Poppler project - http://poppler.freedesktop.org
15 // All changes made under the Poppler project to this file are licensed
16 // under GPL version 2 or later
18 // Copyright (C) 2006 Takashi Iwai <tiwai@suse.de>
19 // Copyright (C) 2006 Kristian Høgsberg <krh@redhat.com>
20 // Copyright (C) 2008 Adam Batkin <adam@batkin.net>
21 // Copyright (C) 2008, 2010, 2012, 2013 Hib Eris <hib@hiberis.nl>
22 // Copyright (C) 2009, 2012, 2014 Albert Astals Cid <aacid@kde.org>
23 // Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net>
24 // Copyright (C) 2013 Adam Reichold <adamreichold@myopera.com>
25 // Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com>
26 // Copyright (C) 2013 Peter Breitenlohner <peb@mppmu.mpg.de>
27 // Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
29 // To see a description of the changes please see the Changelog file that
30 // came with your tarball or type make ChangeLog if you are building from git
32 //========================================================================
40 # include <sys/stat.h>
41 # elif !defined(ACORN)
42 # include <sys/types.h>
43 # include <sys/stat.h>
49 # if !defined(VMS) && !defined(ACORN) && !defined(MACOS)
52 # if defined(VMS) && (__DECCXX_VER < 50200000)
58 #include "GooString.h"
61 // Some systems don't define this, so just make it something reasonably
67 //------------------------------------------------------------------------
69 GooString
*getCurrentDir() {
73 if (_getcwd2(buf
, sizeof(buf
)))
75 if (GetCurrentDirectory(sizeof(buf
), buf
))
81 if (getcwd(buf
, sizeof(buf
)))
83 return new GooString(buf
);
84 return new GooString();
87 GooString
*appendToPath(GooString
*path
, const char *fileName
) {
89 //---------- VMS ----------
90 //~ this should handle everything necessary for file
91 //~ requesters, but it's certainly not complete
95 p0
= path
->getCString();
96 p1
= p0
+ path
->getLength() - 1;
97 if (!strcmp(fileName
, "-")) {
99 for (p2
= p1
; p2
> p0
&& *p2
!= '.' && *p2
!= '['; --p2
) ;
102 path
->del(p2
- p0
, p1
- p2
);
103 } else if (*p1
== ':') {
109 } else if ((q1
= strrchr(fileName
, '.')) && !strncmp(q1
, ".DIR;", 5)) {
111 path
->insert(p1
- p0
, '.');
112 path
->insert(p1
- p0
+ 1, fileName
, q1
- fileName
);
113 } else if (*p1
== ':') {
116 path
->append(fileName
, q1
- fileName
);
119 path
->append(fileName
, q1
- fileName
);
122 if (*p1
!= ']' && *p1
!= ':')
124 path
->append(fileName
);
128 #elif defined(_WIN32)
129 //---------- Win32 ----------
134 tmp
= new GooString(path
);
136 tmp
->append(fileName
);
137 GetFullPathName(tmp
->getCString(), sizeof(buf
), buf
, &fp
);
144 //---------- RISCOS ----------
149 i
= path
->getLength();
150 path
->append(fileName
);
151 for (p
= path
->getCString() + i
; *p
; ++p
) {
154 } else if (*p
== '.') {
161 //---------- MacOS ----------
166 i
= path
->getLength();
167 path
->append(fileName
);
168 for (p
= path
->getCString() + i
; *p
; ++p
) {
171 } else if (*p
== '.') {
177 #elif defined(__EMX__)
178 //---------- OS/2+EMX ----------
181 // appending "." does nothing
182 if (!strcmp(fileName
, "."))
185 // appending ".." goes up one directory
186 if (!strcmp(fileName
, "..")) {
187 for (i
= path
->getLength() - 2; i
>= 0; --i
) {
188 if (path
->getChar(i
) == '/' || path
->getChar(i
) == '\\' ||
189 path
->getChar(i
) == ':')
193 if (path
->getChar(0) == '/' || path
->getChar(0) == '\\') {
194 path
->del(1, path
->getLength() - 1);
195 } else if (path
->getLength() >= 2 && path
->getChar(1) == ':') {
196 path
->del(2, path
->getLength() - 2);
202 if (path
->getChar(i
-1) == ':')
204 path
->del(i
, path
->getLength() - i
);
209 // otherwise, append "/" and new path component
210 if (path
->getLength() > 0 &&
211 path
->getChar(path
->getLength() - 1) != '/' &&
212 path
->getChar(path
->getLength() - 1) != '\\')
214 path
->append(fileName
);
218 //---------- Unix ----------
221 // appending "." does nothing
222 if (!strcmp(fileName
, "."))
225 // appending ".." goes up one directory
226 if (!strcmp(fileName
, "..")) {
227 for (i
= path
->getLength() - 2; i
>= 0; --i
) {
228 if (path
->getChar(i
) == '/')
232 if (path
->getChar(0) == '/') {
233 path
->del(1, path
->getLength() - 1);
239 path
->del(i
, path
->getLength() - i
);
244 // otherwise, append "/" and new path component
245 if (path
->getLength() > 0 &&
246 path
->getChar(path
->getLength() - 1) != '/')
248 path
->append(fileName
);
253 GooString
*grabPath(char *fileName
) {
255 //---------- VMS ----------
258 if ((p
= strrchr(fileName
, ']')))
259 return new GooString(fileName
, p
+ 1 - fileName
);
260 if ((p
= strrchr(fileName
, ':')))
261 return new GooString(fileName
, p
+ 1 - fileName
);
262 return new GooString();
264 #elif defined(__EMX__) || defined(_WIN32)
265 //---------- OS/2+EMX and Win32 ----------
268 if ((p
= strrchr(fileName
, '/')))
269 return new GooString(fileName
, p
- fileName
);
270 if ((p
= strrchr(fileName
, '\\')))
271 return new GooString(fileName
, p
- fileName
);
272 if ((p
= strrchr(fileName
, ':')))
273 return new GooString(fileName
, p
+ 1 - fileName
);
274 return new GooString();
277 //---------- RISCOS ----------
280 if ((p
= strrchr(fileName
, '.')))
281 return new GooString(fileName
, p
- fileName
);
282 return new GooString();
285 //---------- MacOS ----------
288 if ((p
= strrchr(fileName
, ':')))
289 return new GooString(fileName
, p
- fileName
);
290 return new GooString();
293 //---------- Unix ----------
296 if ((p
= strrchr(fileName
, '/')))
297 return new GooString(fileName
, p
- fileName
);
298 return new GooString();
302 GBool
isAbsolutePath(char *path
) {
304 //---------- VMS ----------
305 return strchr(path
, ':') ||
306 (path
[0] == '[' && path
[1] != '.' && path
[1] != '-');
308 #elif defined(__EMX__) || defined(_WIN32)
309 //---------- OS/2+EMX and Win32 ----------
310 return path
[0] == '/' || path
[0] == '\\' || path
[1] == ':';
313 //---------- RISCOS ----------
314 return path
[0] == '$';
317 //---------- MacOS ----------
318 return path
[0] != ':';
321 //---------- Unix ----------
322 return path
[0] == '/';
326 time_t getModTime(char *fileName
) {
328 //~ should implement this, but it's (currently) only used in xpdf
333 if (stat(fileName
, &statBuf
)) {
336 return statBuf
.st_mtime
;
340 GBool
openTempFile(GooString
**name
, FILE **f
, const char *mode
) {
342 //---------- Win32 ----------
348 // this has the standard race condition problem, but I haven't found
349 // a better way to generate temp file names with extensions on
351 if ((tempDir
= getenv("TEMP"))) {
352 s
= new GooString(tempDir
);
357 s
->appendf("x_{0:d}_{1:d}_",
358 (int)GetCurrentProcessId(), (int)GetCurrentThreadId());
360 for (i
= 0; i
< 1000; ++i
) {
361 s2
= s
->copy()->appendf("{0:d}", t
+ i
);
362 if (!(f2
= fopen(s2
->getCString(), "r"))) {
363 if (!(f2
= fopen(s2
->getCString(), mode
))) {
378 #elif defined(VMS) || defined(__EMX__) || defined(ACORN) || defined(MACOS)
379 //---------- non-Unix ----------
382 // There is a security hole here: an attacker can create a symlink
383 // with this file name after the tmpnam call and before the fopen
384 // call. I will happily accept fixes to this function for non-Unix
386 if (!(s
= tmpnam(NULL
))) {
389 *name
= new GooString(s
);
390 if (!(*f
= fopen((*name
)->getCString(), mode
))) {
397 //---------- Unix ----------
402 if ((s
= getenv("TMPDIR"))) {
403 *name
= new GooString(s
);
405 *name
= new GooString("/tmp");
407 (*name
)->append("/XXXXXX");
408 fd
= mkstemp((*name
)->getCString());
409 #else // HAVE_MKSTEMP
410 if (!(s
= tmpnam(NULL
))) {
413 *name
= new GooString(s
);
414 fd
= open((*name
)->getCString(), O_WRONLY
| O_CREAT
| O_EXCL
, 0600);
415 #endif // HAVE_MKSTEMP
416 if (fd
< 0 || !(*f
= fdopen(fd
, mode
))) {
426 GooString
*fileNameToUTF8(char *path
) {
431 for (p
= path
; *p
; ++p
) {
433 s
->append((char)(0xc0 | ((*p
>> 6) & 0x03)));
434 s
->append((char)(0x80 | (*p
& 0x3f)));
442 GooString
*fileNameToUTF8(wchar_t *path
) {
447 for (p
= path
; *p
; ++p
) {
450 } else if (*p
< 0x800) {
451 s
->append((char)(0xc0 | ((*p
>> 6) & 0x1f)));
452 s
->append((char)(0x80 | (*p
& 0x3f)));
454 s
->append((char)(0xe0 | ((*p
>> 12) & 0x0f)));
455 s
->append((char)(0x80 | ((*p
>> 6) & 0x3f)));
456 s
->append((char)(0x80 | (*p
& 0x3f)));
463 FILE *openFile(const char *path
, const char *mode
) {
465 OSVERSIONINFO version
;
466 wchar_t wPath
[_MAX_PATH
+ 1];
467 char nPath
[_MAX_PATH
+ 1];
472 // NB: _wfopen is only available in NT
473 version
.dwOSVersionInfoSize
= sizeof(version
);
474 GetVersionEx(&version
);
475 if (version
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) {
476 for (p
= path
, i
= 0; *p
&& i
< _MAX_PATH
; ++i
) {
477 if ((p
[0] & 0xe0) == 0xc0 &&
478 p
[1] && (p
[1] & 0xc0) == 0x80) {
479 wPath
[i
] = (wchar_t)(((p
[0] & 0x1f) << 6) |
482 } else if ((p
[0] & 0xf0) == 0xe0 &&
483 p
[1] && (p
[1] & 0xc0) == 0x80 &&
484 p
[2] && (p
[2] & 0xc0) == 0x80) {
485 wPath
[i
] = (wchar_t)(((p
[0] & 0x0f) << 12) |
486 ((p
[1] & 0x3f) << 6) |
490 wPath
[i
] = (wchar_t)(p
[0] & 0xff);
494 wPath
[i
] = (wchar_t)0;
495 for (i
= 0; (i
< sizeof(mode
) - 1) && mode
[i
]; ++i
) {
496 wMode
[i
] = (wchar_t)(mode
[i
] & 0xff);
498 wMode
[i
] = (wchar_t)0;
499 return _wfopen(wPath
, wMode
);
501 for (p
= path
, i
= 0; *p
&& i
< _MAX_PATH
; ++i
) {
502 if ((p
[0] & 0xe0) == 0xc0 &&
503 p
[1] && (p
[1] & 0xc0) == 0x80) {
504 nPath
[i
] = (char)(((p
[0] & 0x1f) << 6) |
507 } else if ((p
[0] & 0xf0) == 0xe0 &&
508 p
[1] && (p
[1] & 0xc0) == 0x80 &&
509 p
[2] && (p
[2] & 0xc0) == 0x80) {
510 nPath
[i
] = (char)(((p
[1] & 0x3f) << 6) |
519 return fopen(nPath
, mode
);
522 return fopen(path
, mode
);
526 char *getLine(char *buf
, int size
, FILE *f
) {
530 while (i
< size
- 1) {
531 if ((c
= fgetc(f
)) == EOF
) {
540 if (c
== '\x0a' && i
< size
- 1) {
542 } else if (c
!= EOF
) {
555 int Gfseek(FILE *f
, Goffset offset
, int whence
) {
557 return fseeko(f
, offset
, whence
);
559 return fseek64(f
, offset
, whence
);
560 #elif defined(__MINGW32__)
561 return fseeko64(f
, offset
, whence
);
563 return _fseeki64(f
, offset
, whence
);
565 return fseek(f
, offset
, whence
);
569 Goffset
Gftell(FILE *f
) {
574 #elif defined(__MINGW32__)
583 Goffset
GoffsetMax() {
585 return (std::numeric_limits
<off_t
>::max
)();
586 #elif HAVE_FSEEK64 || defined(__MINGW32__)
587 return (std::numeric_limits
<off64_t
>::max
)();
589 return (std::numeric_limits
<__int64
>::max
)();
591 return (std::numeric_limits
<long>::max
)();
595 //------------------------------------------------------------------------
597 //------------------------------------------------------------------------
601 int GooFile::read(char *buf
, int n
, Goffset offset
) const {
604 LARGE_INTEGER largeInteger
= {0};
605 largeInteger
.QuadPart
= offset
;
607 OVERLAPPED overlapped
= {0};
608 overlapped
.Offset
= largeInteger
.LowPart
;
609 overlapped
.OffsetHigh
= largeInteger
.HighPart
;
611 return FALSE
== ReadFile(handle
, buf
, n
, &m
, &overlapped
) ? -1 : m
;
614 Goffset
GooFile::size() const {
615 LARGE_INTEGER size
= {(DWORD
)-1,-1};
617 GetFileSizeEx(handle
, &size
);
619 return size
.QuadPart
;
622 GooFile
* GooFile::open(const GooString
*fileName
) {
623 HANDLE handle
= CreateFile(fileName
->getCString(),
625 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
628 FILE_ATTRIBUTE_NORMAL
, NULL
);
630 return handle
== INVALID_HANDLE_VALUE
? NULL
: new GooFile(handle
);
633 GooFile
* GooFile::open(const wchar_t *fileName
) {
634 HANDLE handle
= CreateFileW(fileName
,
636 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
639 FILE_ATTRIBUTE_NORMAL
, NULL
);
641 return handle
== INVALID_HANDLE_VALUE
? NULL
: new GooFile(handle
);
646 int GooFile::read(char *buf
, int n
, Goffset offset
) const {
648 return pread64(fd
, buf
, n
, offset
);
650 return pread(fd
, buf
, n
, offset
);
654 Goffset
GooFile::size() const {
656 return lseek64(fd
, 0, SEEK_END
);
658 return lseek(fd
, 0, SEEK_END
);
662 GooFile
* GooFile::open(const GooString
*fileName
) {
664 int fd
= ::open(fileName
->getCString(), Q_RDONLY
, "ctx=stm");
666 int fd
= ::open(fileName
->getCString(), O_RDONLY
);
669 return fd
< 0 ? NULL
: new GooFile(fd
);
674 //------------------------------------------------------------------------
675 // GDir and GDirEntry
676 //------------------------------------------------------------------------
678 GDirEntry::GDirEntry(char *dirPath
, char *nameA
, GBool doStat
) {
681 #elif defined(_WIN32)
688 name
= new GooString(nameA
);
690 fullPath
= new GooString(dirPath
);
691 appendToPath(fullPath
, nameA
);
694 if (!strcmp(nameA
, "-") ||
695 ((p
= strrchr(nameA
, '.')) && !strncmp(p
, ".DIR;", 5)))
700 fa
= GetFileAttributes(fullPath
->getCString());
701 dir
= (fa
!= 0xFFFFFFFF && (fa
& FILE_ATTRIBUTE_DIRECTORY
));
703 if (stat(fullPath
->getCString(), &st
) == 0)
704 dir
= S_ISDIR(st
.st_mode
);
710 GDirEntry::~GDirEntry() {
715 GDir::GDir(char *name
, GBool doStatA
) {
716 path
= new GooString(name
);
723 hnd
= FindFirstFile(tmp
->getCString(), &ffd
);
730 needParent
= strchr(name
, '[') != NULL
;
738 if (hnd
!= INVALID_HANDLE_VALUE
) {
740 hnd
= INVALID_HANDLE_VALUE
;
750 GDirEntry
*GDir::getNextEntry() {
754 if (hnd
!= INVALID_HANDLE_VALUE
) {
755 e
= new GDirEntry(path
->getCString(), ffd
.cFileName
, doStat
);
756 if (!FindNextFile(hnd
, &ffd
)) {
758 hnd
= INVALID_HANDLE_VALUE
;
767 e
= new GDirEntry(path
->getCString(), "-", doStat
);
773 e
= new GDirEntry(path
->getCString(), ent
->d_name
, doStat
);
782 while (ent
&& (!strcmp(ent
->d_name
, ".") || !strcmp(ent
->d_name
, "..")));
784 e
= new GDirEntry(path
->getCString(), ent
->d_name
, doStat
);
792 void GDir::rewind() {
796 if (hnd
!= INVALID_HANDLE_VALUE
)
800 hnd
= FindFirstFile(tmp
->getCString(), &ffd
);
808 needParent
= strchr(path
->getCString(), '[') != NULL
;