beta-0.89.2
[luatex.git] / source / libs / poppler / poppler-src / goo / gfile.cc
blob3528bb357d88f8ca3531974baab520e484a9a139
1 //========================================================================
2 //
3 // gfile.cc
4 //
5 // Miscellaneous file and directory name manipulation.
6 //
7 // Copyright 1996-2003 Glyph & Cog, LLC
8 //
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 //========================================================================
34 #include <config.h>
36 #ifdef _WIN32
37 # include <time.h>
38 #else
39 # if defined(MACOS)
40 # include <sys/stat.h>
41 # elif !defined(ACORN)
42 # include <sys/types.h>
43 # include <sys/stat.h>
44 # include <fcntl.h>
45 # endif
46 # include <time.h>
47 # include <limits.h>
48 # include <string.h>
49 # if !defined(VMS) && !defined(ACORN) && !defined(MACOS)
50 # include <pwd.h>
51 # endif
52 # if defined(VMS) && (__DECCXX_VER < 50200000)
53 # include <unixlib.h>
54 # endif
55 #endif // _WIN32
56 #include <stdio.h>
57 #include <limits>
58 #include "GooString.h"
59 #include "gfile.h"
61 // Some systems don't define this, so just make it something reasonably
62 // large.
63 #ifndef PATH_MAX
64 #define PATH_MAX 1024
65 #endif
67 //------------------------------------------------------------------------
69 GooString *getCurrentDir() {
70 char buf[PATH_MAX+1];
72 #if defined(__EMX__)
73 if (_getcwd2(buf, sizeof(buf)))
74 #elif defined(_WIN32)
75 if (GetCurrentDirectory(sizeof(buf), buf))
76 #elif defined(ACORN)
77 if (strcpy(buf, "@"))
78 #elif defined(MACOS)
79 if (strcpy(buf, ":"))
80 #else
81 if (getcwd(buf, sizeof(buf)))
82 #endif
83 return new GooString(buf);
84 return new GooString();
87 GooString *appendToPath(GooString *path, const char *fileName) {
88 #if defined(VMS)
89 //---------- VMS ----------
90 //~ this should handle everything necessary for file
91 //~ requesters, but it's certainly not complete
92 char *p0, *p1, *p2;
93 char *q1;
95 p0 = path->getCString();
96 p1 = p0 + path->getLength() - 1;
97 if (!strcmp(fileName, "-")) {
98 if (*p1 == ']') {
99 for (p2 = p1; p2 > p0 && *p2 != '.' && *p2 != '['; --p2) ;
100 if (*p2 == '[')
101 ++p2;
102 path->del(p2 - p0, p1 - p2);
103 } else if (*p1 == ':') {
104 path->append("[-]");
105 } else {
106 path->clear();
107 path->append("[-]");
109 } else if ((q1 = strrchr(fileName, '.')) && !strncmp(q1, ".DIR;", 5)) {
110 if (*p1 == ']') {
111 path->insert(p1 - p0, '.');
112 path->insert(p1 - p0 + 1, fileName, q1 - fileName);
113 } else if (*p1 == ':') {
114 path->append('[');
115 path->append(']');
116 path->append(fileName, q1 - fileName);
117 } else {
118 path->clear();
119 path->append(fileName, q1 - fileName);
121 } else {
122 if (*p1 != ']' && *p1 != ':')
123 path->clear();
124 path->append(fileName);
126 return path;
128 #elif defined(_WIN32)
129 //---------- Win32 ----------
130 GooString *tmp;
131 char buf[256];
132 char *fp;
134 tmp = new GooString(path);
135 tmp->append('/');
136 tmp->append(fileName);
137 GetFullPathName(tmp->getCString(), sizeof(buf), buf, &fp);
138 delete tmp;
139 path->clear();
140 path->append(buf);
141 return path;
143 #elif defined(ACORN)
144 //---------- RISCOS ----------
145 char *p;
146 int i;
148 path->append(".");
149 i = path->getLength();
150 path->append(fileName);
151 for (p = path->getCString() + i; *p; ++p) {
152 if (*p == '/') {
153 *p = '.';
154 } else if (*p == '.') {
155 *p = '/';
158 return path;
160 #elif defined(MACOS)
161 //---------- MacOS ----------
162 char *p;
163 int i;
165 path->append(":");
166 i = path->getLength();
167 path->append(fileName);
168 for (p = path->getCString() + i; *p; ++p) {
169 if (*p == '/') {
170 *p = ':';
171 } else if (*p == '.') {
172 *p = ':';
175 return path;
177 #elif defined(__EMX__)
178 //---------- OS/2+EMX ----------
179 int i;
181 // appending "." does nothing
182 if (!strcmp(fileName, "."))
183 return path;
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) == ':')
190 break;
192 if (i <= 0) {
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);
197 } else {
198 path->clear();
199 path->append("..");
201 } else {
202 if (path->getChar(i-1) == ':')
203 ++i;
204 path->del(i, path->getLength() - i);
206 return path;
209 // otherwise, append "/" and new path component
210 if (path->getLength() > 0 &&
211 path->getChar(path->getLength() - 1) != '/' &&
212 path->getChar(path->getLength() - 1) != '\\')
213 path->append('/');
214 path->append(fileName);
215 return path;
217 #else
218 //---------- Unix ----------
219 int i;
221 // appending "." does nothing
222 if (!strcmp(fileName, "."))
223 return path;
225 // appending ".." goes up one directory
226 if (!strcmp(fileName, "..")) {
227 for (i = path->getLength() - 2; i >= 0; --i) {
228 if (path->getChar(i) == '/')
229 break;
231 if (i <= 0) {
232 if (path->getChar(0) == '/') {
233 path->del(1, path->getLength() - 1);
234 } else {
235 path->clear();
236 path->append("..");
238 } else {
239 path->del(i, path->getLength() - i);
241 return path;
244 // otherwise, append "/" and new path component
245 if (path->getLength() > 0 &&
246 path->getChar(path->getLength() - 1) != '/')
247 path->append('/');
248 path->append(fileName);
249 return path;
250 #endif
253 GooString *grabPath(char *fileName) {
254 #ifdef VMS
255 //---------- VMS ----------
256 char *p;
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 ----------
266 char *p;
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();
276 #elif defined(ACORN)
277 //---------- RISCOS ----------
278 char *p;
280 if ((p = strrchr(fileName, '.')))
281 return new GooString(fileName, p - fileName);
282 return new GooString();
284 #elif defined(MACOS)
285 //---------- MacOS ----------
286 char *p;
288 if ((p = strrchr(fileName, ':')))
289 return new GooString(fileName, p - fileName);
290 return new GooString();
292 #else
293 //---------- Unix ----------
294 char *p;
296 if ((p = strrchr(fileName, '/')))
297 return new GooString(fileName, p - fileName);
298 return new GooString();
299 #endif
302 GBool isAbsolutePath(char *path) {
303 #ifdef VMS
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] == ':';
312 #elif defined(ACORN)
313 //---------- RISCOS ----------
314 return path[0] == '$';
316 #elif defined(MACOS)
317 //---------- MacOS ----------
318 return path[0] != ':';
320 #else
321 //---------- Unix ----------
322 return path[0] == '/';
323 #endif
326 time_t getModTime(char *fileName) {
327 #ifdef _WIN32
328 //~ should implement this, but it's (currently) only used in xpdf
329 return 0;
330 #else
331 struct stat statBuf;
333 if (stat(fileName, &statBuf)) {
334 return 0;
336 return statBuf.st_mtime;
337 #endif
340 GBool openTempFile(GooString **name, FILE **f, const char *mode) {
341 #if defined(_WIN32)
342 //---------- Win32 ----------
343 char *tempDir;
344 GooString *s, *s2;
345 FILE *f2;
346 int t, i;
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
350 // Windows
351 if ((tempDir = getenv("TEMP"))) {
352 s = new GooString(tempDir);
353 s->append('\\');
354 } else {
355 s = new GooString();
357 s->appendf("x_{0:d}_{1:d}_",
358 (int)GetCurrentProcessId(), (int)GetCurrentThreadId());
359 t = (int)time(NULL);
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))) {
364 delete s2;
365 delete s;
366 return gFalse;
368 *name = s2;
369 *f = f2;
370 delete s;
371 return gTrue;
373 fclose(f2);
374 delete s2;
376 delete s;
377 return gFalse;
378 #elif defined(VMS) || defined(__EMX__) || defined(ACORN) || defined(MACOS)
379 //---------- non-Unix ----------
380 char *s;
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
385 // OSs.
386 if (!(s = tmpnam(NULL))) {
387 return gFalse;
389 *name = new GooString(s);
390 if (!(*f = fopen((*name)->getCString(), mode))) {
391 delete (*name);
392 *name = NULL;
393 return gFalse;
395 return gTrue;
396 #else
397 //---------- Unix ----------
398 char *s;
399 int fd;
401 #if HAVE_MKSTEMP
402 if ((s = getenv("TMPDIR"))) {
403 *name = new GooString(s);
404 } else {
405 *name = new GooString("/tmp");
407 (*name)->append("/XXXXXX");
408 fd = mkstemp((*name)->getCString());
409 #else // HAVE_MKSTEMP
410 if (!(s = tmpnam(NULL))) {
411 return gFalse;
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))) {
417 delete *name;
418 *name = NULL;
419 return gFalse;
421 return gTrue;
422 #endif
425 #ifdef WIN32
426 GooString *fileNameToUTF8(char *path) {
427 GooString *s;
428 char *p;
430 s = new GooString();
431 for (p = path; *p; ++p) {
432 if (*p & 0x80) {
433 s->append((char)(0xc0 | ((*p >> 6) & 0x03)));
434 s->append((char)(0x80 | (*p & 0x3f)));
435 } else {
436 s->append(*p);
439 return s;
442 GooString *fileNameToUTF8(wchar_t *path) {
443 GooString *s;
444 wchar_t *p;
446 s = new GooString();
447 for (p = path; *p; ++p) {
448 if (*p < 0x80) {
449 s->append((char)*p);
450 } else if (*p < 0x800) {
451 s->append((char)(0xc0 | ((*p >> 6) & 0x1f)));
452 s->append((char)(0x80 | (*p & 0x3f)));
453 } else {
454 s->append((char)(0xe0 | ((*p >> 12) & 0x0f)));
455 s->append((char)(0x80 | ((*p >> 6) & 0x3f)));
456 s->append((char)(0x80 | (*p & 0x3f)));
459 return s;
461 #endif
463 FILE *openFile(const char *path, const char *mode) {
464 #ifdef WIN32
465 OSVERSIONINFO version;
466 wchar_t wPath[_MAX_PATH + 1];
467 char nPath[_MAX_PATH + 1];
468 wchar_t wMode[8];
469 const char *p;
470 size_t i;
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) |
480 (p[1] & 0x3f));
481 p += 2;
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) |
487 (p[2] & 0x3f));
488 p += 3;
489 } else {
490 wPath[i] = (wchar_t)(p[0] & 0xff);
491 p += 1;
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);
500 } else {
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) |
505 (p[1] & 0x3f));
506 p += 2;
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) |
511 (p[2] & 0x3f));
512 p += 3;
513 } else {
514 nPath[i] = p[0];
515 p += 1;
518 nPath[i] = '\0';
519 return fopen(nPath, mode);
521 #else
522 return fopen(path, mode);
523 #endif
526 char *getLine(char *buf, int size, FILE *f) {
527 int c, i;
529 i = 0;
530 while (i < size - 1) {
531 if ((c = fgetc(f)) == EOF) {
532 break;
534 buf[i++] = (char)c;
535 if (c == '\x0a') {
536 break;
538 if (c == '\x0d') {
539 c = fgetc(f);
540 if (c == '\x0a' && i < size - 1) {
541 buf[i++] = (char)c;
542 } else if (c != EOF) {
543 ungetc(c, f);
545 break;
548 buf[i] = '\0';
549 if (i == 0) {
550 return NULL;
552 return buf;
555 int Gfseek(FILE *f, Goffset offset, int whence) {
556 #if HAVE_FSEEKO
557 return fseeko(f, offset, whence);
558 #elif HAVE_FSEEK64
559 return fseek64(f, offset, whence);
560 #elif defined(__MINGW32__)
561 return fseeko64(f, offset, whence);
562 #elif _WIN32
563 return _fseeki64(f, offset, whence);
564 #else
565 return fseek(f, offset, whence);
566 #endif
569 Goffset Gftell(FILE *f) {
570 #if HAVE_FSEEKO
571 return ftello(f);
572 #elif HAVE_FSEEK64
573 return ftell64(f);
574 #elif defined(__MINGW32__)
575 return ftello64(f);
576 #elif _WIN32
577 return _ftelli64(f);
578 #else
579 return ftell(f);
580 #endif
583 Goffset GoffsetMax() {
584 #if HAVE_FSEEKO
585 return (std::numeric_limits<off_t>::max)();
586 #elif HAVE_FSEEK64 || defined(__MINGW32__)
587 return (std::numeric_limits<off64_t>::max)();
588 #elif _WIN32
589 return (std::numeric_limits<__int64>::max)();
590 #else
591 return (std::numeric_limits<long>::max)();
592 #endif
595 //------------------------------------------------------------------------
596 // GooFile
597 //------------------------------------------------------------------------
599 #ifdef _WIN32
601 int GooFile::read(char *buf, int n, Goffset offset) const {
602 DWORD m;
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(),
624 GENERIC_READ,
625 FILE_SHARE_READ | FILE_SHARE_WRITE,
626 NULL,
627 OPEN_EXISTING,
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,
635 GENERIC_READ,
636 FILE_SHARE_READ | FILE_SHARE_WRITE,
637 NULL,
638 OPEN_EXISTING,
639 FILE_ATTRIBUTE_NORMAL, NULL);
641 return handle == INVALID_HANDLE_VALUE ? NULL : new GooFile(handle);
644 #else
646 int GooFile::read(char *buf, int n, Goffset offset) const {
647 #ifdef HAVE_PREAD64
648 return pread64(fd, buf, n, offset);
649 #else
650 return pread(fd, buf, n, offset);
651 #endif
654 Goffset GooFile::size() const {
655 #ifdef HAVE_LSEEK64
656 return lseek64(fd, 0, SEEK_END);
657 #else
658 return lseek(fd, 0, SEEK_END);
659 #endif
662 GooFile* GooFile::open(const GooString *fileName) {
663 #ifdef VMS
664 int fd = ::open(fileName->getCString(), Q_RDONLY, "ctx=stm");
665 #else
666 int fd = ::open(fileName->getCString(), O_RDONLY);
667 #endif
669 return fd < 0 ? NULL : new GooFile(fd);
672 #endif // _WIN32
674 //------------------------------------------------------------------------
675 // GDir and GDirEntry
676 //------------------------------------------------------------------------
678 GDirEntry::GDirEntry(char *dirPath, char *nameA, GBool doStat) {
679 #ifdef VMS
680 char *p;
681 #elif defined(_WIN32)
682 DWORD fa;
683 #elif defined(ACORN)
684 #else
685 struct stat st;
686 #endif
688 name = new GooString(nameA);
689 dir = gFalse;
690 fullPath = new GooString(dirPath);
691 appendToPath(fullPath, nameA);
692 if (doStat) {
693 #ifdef VMS
694 if (!strcmp(nameA, "-") ||
695 ((p = strrchr(nameA, '.')) && !strncmp(p, ".DIR;", 5)))
696 dir = gTrue;
697 #elif defined(ACORN)
698 #else
699 #ifdef _WIN32
700 fa = GetFileAttributes(fullPath->getCString());
701 dir = (fa != 0xFFFFFFFF && (fa & FILE_ATTRIBUTE_DIRECTORY));
702 #else
703 if (stat(fullPath->getCString(), &st) == 0)
704 dir = S_ISDIR(st.st_mode);
705 #endif
706 #endif
710 GDirEntry::~GDirEntry() {
711 delete fullPath;
712 delete name;
715 GDir::GDir(char *name, GBool doStatA) {
716 path = new GooString(name);
717 doStat = doStatA;
718 #if defined(_WIN32)
719 GooString *tmp;
721 tmp = path->copy();
722 tmp->append("/*.*");
723 hnd = FindFirstFile(tmp->getCString(), &ffd);
724 delete tmp;
725 #elif defined(ACORN)
726 #elif defined(MACOS)
727 #else
728 dir = opendir(name);
729 #ifdef VMS
730 needParent = strchr(name, '[') != NULL;
731 #endif
732 #endif
735 GDir::~GDir() {
736 delete path;
737 #if defined(_WIN32)
738 if (hnd != INVALID_HANDLE_VALUE) {
739 FindClose(hnd);
740 hnd = INVALID_HANDLE_VALUE;
742 #elif defined(ACORN)
743 #elif defined(MACOS)
744 #else
745 if (dir)
746 closedir(dir);
747 #endif
750 GDirEntry *GDir::getNextEntry() {
751 GDirEntry *e = NULL;
753 #if defined(_WIN32)
754 if (hnd != INVALID_HANDLE_VALUE) {
755 e = new GDirEntry(path->getCString(), ffd.cFileName, doStat);
756 if (!FindNextFile(hnd, &ffd)) {
757 FindClose(hnd);
758 hnd = INVALID_HANDLE_VALUE;
761 #elif defined(ACORN)
762 #elif defined(MACOS)
763 #elif defined(VMS)
764 struct dirent *ent;
765 if (dir) {
766 if (needParent) {
767 e = new GDirEntry(path->getCString(), "-", doStat);
768 needParent = gFalse;
769 return e;
771 ent = readdir(dir);
772 if (ent) {
773 e = new GDirEntry(path->getCString(), ent->d_name, doStat);
776 #else
777 struct dirent *ent;
778 if (dir) {
779 do {
780 ent = readdir(dir);
782 while (ent && (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")));
783 if (ent) {
784 e = new GDirEntry(path->getCString(), ent->d_name, doStat);
787 #endif
789 return e;
792 void GDir::rewind() {
793 #ifdef _WIN32
794 GooString *tmp;
796 if (hnd != INVALID_HANDLE_VALUE)
797 FindClose(hnd);
798 tmp = path->copy();
799 tmp->append("/*.*");
800 hnd = FindFirstFile(tmp->getCString(), &ffd);
801 delete tmp;
802 #elif defined(ACORN)
803 #elif defined(MACOS)
804 #else
805 if (dir)
806 rewinddir(dir);
807 #ifdef VMS
808 needParent = strchr(path->getCString(), '[') != NULL;
809 #endif
810 #endif