upgrade to xpdf 1.01.
[swftools.git] / pdf2swf / xpdf / gfile.cc
1 //========================================================================
2 //
3 // gfile.cc
4 //
5 // Miscellaneous file and directory name manipulation.
6 //
7 // Copyright 1996-2002 Glyph & Cog, LLC
8 //
9 //========================================================================
10
11 #include <aconf.h>
12
13 #ifdef WIN32
14    extern "C" {
15 #  ifndef _MSC_VER
16 #    include <kpathsea/win32lib.h>
17 #  endif
18    }
19 #else // !WIN32
20 #  if defined(MACOS)
21 #    include <sys/stat.h>
22 #  elif !defined(ACORN)
23 #    include <sys/types.h>
24 #    include <sys/stat.h>
25 #    include <fcntl.h>
26 #  endif
27 #  include <limits.h>
28 #  include <string.h>
29 #  if !defined(VMS) && !defined(ACORN) && !defined(MACOS)
30 #    include <pwd.h>
31 #  endif
32 #  if defined(VMS) && (__DECCXX_VER < 50200000)
33 #    include <unixlib.h>
34 #  endif
35 #endif // WIN32
36 #include "GString.h"
37 #include "gfile.h"
38
39 // Some systems don't define this, so just make it something reasonably
40 // large.
41 #ifndef PATH_MAX
42 #define PATH_MAX 1024
43 #endif
44
45 //------------------------------------------------------------------------
46
47 GString *getHomeDir() {
48 #ifdef VMS
49   //---------- VMS ----------
50   return new GString("SYS$LOGIN:");
51
52 #elif defined(__EMX__) || defined(WIN32)
53   //---------- OS/2+EMX and Win32 ----------
54   char *s;
55   GString *ret;
56
57   if ((s = getenv("HOME")))
58     ret = new GString(s);
59   else
60     ret = new GString(".");
61   return ret;
62
63 #elif defined(ACORN)
64   //---------- RISCOS ----------
65   return new GString("@");
66
67 #elif defined(MACOS)
68   //---------- MacOS ----------
69   return new GString(":");
70
71 #else
72   //---------- Unix ----------
73   char *s;
74   struct passwd *pw;
75   GString *ret;
76
77   if ((s = getenv("HOME"))) {
78     ret = new GString(s);
79   } else {
80     if ((s = getenv("USER")))
81       pw = getpwnam(s);
82     else
83       pw = getpwuid(getuid());
84     if (pw)
85       ret = new GString(pw->pw_dir);
86     else
87       ret = new GString(".");
88   }
89   return ret;
90 #endif
91 }
92
93 GString *getCurrentDir() {
94   char buf[PATH_MAX+1];
95
96 #if defined(__EMX__)
97   if (_getcwd2(buf, sizeof(buf)))
98 #elif defined(WIN32)
99   if (GetCurrentDirectory(sizeof(buf), buf))
100 #elif defined(ACORN)
101   if (strcpy(buf, "@"))
102 #elif defined(MACOS)
103   if (strcpy(buf, ":"))
104 #else
105   if (getcwd(buf, sizeof(buf)))
106 #endif
107     return new GString(buf);
108   return new GString();
109 }
110
111 GString *appendToPath(GString *path, char *fileName) {
112 #if defined(VMS)
113   //---------- VMS ----------
114   //~ this should handle everything necessary for file
115   //~ requesters, but it's certainly not complete
116   char *p0, *p1, *p2;
117   char *q1;
118
119   p0 = path->getCString();
120   p1 = p0 + path->getLength() - 1;
121   if (!strcmp(fileName, "-")) {
122     if (*p1 == ']') {
123       for (p2 = p1; p2 > p0 && *p2 != '.' && *p2 != '['; --p2) ;
124       if (*p2 == '[')
125         ++p2;
126       path->del(p2 - p0, p1 - p2);
127     } else if (*p1 == ':') {
128       path->append("[-]");
129     } else {
130       path->clear();
131       path->append("[-]");
132     }
133   } else if ((q1 = strrchr(fileName, '.')) && !strncmp(q1, ".DIR;", 5)) {
134     if (*p1 == ']') {
135       path->insert(p1 - p0, '.');
136       path->insert(p1 - p0 + 1, fileName, q1 - fileName);
137     } else if (*p1 == ':') {
138       path->append('[');
139       path->append(']');
140       path->append(fileName, q1 - fileName);
141     } else {
142       path->clear();
143       path->append(fileName, q1 - fileName);
144     }
145   } else {
146     if (*p1 != ']' && *p1 != ':')
147       path->clear();
148     path->append(fileName);
149   }
150   return path;
151
152 #elif defined(WIN32)
153   //---------- Win32 ----------
154   GString *tmp;
155   char buf[256];
156   char *fp;
157
158   tmp = new GString(path);
159   tmp->append('/');
160   tmp->append(fileName);
161   GetFullPathName(tmp->getCString(), sizeof(buf), buf, &fp);
162   delete tmp;
163   path->clear();
164   path->append(buf);
165   return path;
166
167 #elif defined(ACORN)
168   //---------- RISCOS ----------
169   char *p;
170   int i;
171
172   path->append(".");
173   i = path->getLength();
174   path->append(fileName);
175   for (p = path->getCString() + i; *p; ++p) {
176     if (*p == '/') {
177       *p = '.';
178     } else if (*p == '.') {
179       *p = '/';
180     }
181   }
182   return path;
183
184 #elif defined(MACOS)
185   //---------- MacOS ----------
186   char *p;
187   int i;
188
189   path->append(":");
190   i = path->getLength();
191   path->append(fileName);
192   for (p = path->getCString() + i; *p; ++p) {
193     if (*p == '/') {
194       *p = ':';
195     } else if (*p == '.') {
196       *p = ':';
197     }
198   }
199   return path;
200
201 #elif defined(__EMX__)
202   //---------- OS/2+EMX ----------
203   int i;
204
205   // appending "." does nothing
206   if (!strcmp(fileName, "."))
207     return path;
208
209   // appending ".." goes up one directory
210   if (!strcmp(fileName, "..")) {
211     for (i = path->getLength() - 2; i >= 0; --i) {
212       if (path->getChar(i) == '/' || path->getChar(i) == '\\' ||
213           path->getChar(i) == ':')
214         break;
215     }
216     if (i <= 0) {
217       if (path->getChar(0) == '/' || path->getChar(0) == '\\') {
218         path->del(1, path->getLength() - 1);
219       } else if (path->getLength() >= 2 && path->getChar(1) == ':') {
220         path->del(2, path->getLength() - 2);
221       } else {
222         path->clear();
223         path->append("..");
224       }
225     } else {
226       if (path->getChar(i-1) == ':')
227         ++i;
228       path->del(i, path->getLength() - i);
229     }
230     return path;
231   }
232
233   // otherwise, append "/" and new path component
234   if (path->getLength() > 0 &&
235       path->getChar(path->getLength() - 1) != '/' &&
236       path->getChar(path->getLength() - 1) != '\\')
237     path->append('/');
238   path->append(fileName);
239   return path;
240
241 #else
242   //---------- Unix ----------
243   int i;
244
245   // appending "." does nothing
246   if (!strcmp(fileName, "."))
247     return path;
248
249   // appending ".." goes up one directory
250   if (!strcmp(fileName, "..")) {
251     for (i = path->getLength() - 2; i >= 0; --i) {
252       if (path->getChar(i) == '/')
253         break;
254     }
255     if (i <= 0) {
256       if (path->getChar(0) == '/') {
257         path->del(1, path->getLength() - 1);
258       } else {
259         path->clear();
260         path->append("..");
261       }
262     } else {
263       path->del(i, path->getLength() - i);
264     }
265     return path;
266   }
267
268   // otherwise, append "/" and new path component
269   if (path->getLength() > 0 &&
270       path->getChar(path->getLength() - 1) != '/')
271     path->append('/');
272   path->append(fileName);
273   return path;
274 #endif
275 }
276
277 GString *grabPath(char *fileName) {
278 #ifdef VMS
279   //---------- VMS ----------
280   char *p;
281
282   if ((p = strrchr(fileName, ']')))
283     return new GString(fileName, p + 1 - fileName);
284   if ((p = strrchr(fileName, ':')))
285     return new GString(fileName, p + 1 - fileName);
286   return new GString();
287
288 #elif defined(__EMX__) || defined(WIN32)
289   //---------- OS/2+EMX and Win32 ----------
290   char *p;
291
292   if ((p = strrchr(fileName, '/')))
293     return new GString(fileName, p - fileName);
294   if ((p = strrchr(fileName, '\\')))
295     return new GString(fileName, p - fileName);
296   if ((p = strrchr(fileName, ':')))
297     return new GString(fileName, p + 1 - fileName);
298   return new GString();
299
300 #elif defined(ACORN)
301   //---------- RISCOS ----------
302   char *p;
303
304   if ((p = strrchr(fileName, '.')))
305     return new GString(fileName, p - fileName);
306   return new GString();
307
308 #elif defined(MACOS)
309   //---------- MacOS ----------
310   char *p;
311
312   if ((p = strrchr(fileName, ':')))
313     return new GString(fileName, p - fileName);
314   return new GString();
315
316 #else
317   //---------- Unix ----------
318   char *p;
319
320   if ((p = strrchr(fileName, '/')))
321     return new GString(fileName, p - fileName);
322   return new GString();
323 #endif
324 }
325
326 GBool isAbsolutePath(char *path) {
327 #ifdef VMS
328   //---------- VMS ----------
329   return strchr(path, ':') ||
330          (path[0] == '[' && path[1] != '.' && path[1] != '-');
331
332 #elif defined(__EMX__) || defined(WIN32)
333   //---------- OS/2+EMX and Win32 ----------
334   return path[0] == '/' || path[0] == '\\' || path[1] == ':';
335
336 #elif defined(ACORN)
337   //---------- RISCOS ----------
338   return path[0] == '$';
339
340 #elif defined(MACOS)
341   //---------- MacOS ----------
342   return path[0] != ':';
343
344 #else
345   //---------- Unix ----------
346   return path[0] == '/';
347 #endif
348 }
349
350 GString *makePathAbsolute(GString *path) {
351 #ifdef VMS
352   //---------- VMS ----------
353   char buf[PATH_MAX+1];
354
355   if (!isAbsolutePath(path->getCString())) {
356     if (getcwd(buf, sizeof(buf))) {
357       path->insert(0, buf);
358     }
359   }
360   return path;
361
362 #elif defined(WIN32)
363   //---------- Win32 ----------
364   char buf[_MAX_PATH];
365   char *fp;
366
367   buf[0] = '\0';
368   if (!GetFullPathName(path->getCString(), _MAX_PATH, buf, &fp)) {
369     path->clear();
370     return path;
371   }
372   path->clear();
373   path->append(buf);
374   return path;
375
376 #elif defined(ACORN)
377   //---------- RISCOS ----------
378   path->insert(0, '@');
379   return path;
380
381 #elif defined(MACOS)
382   //---------- MacOS ----------
383   path->del(0, 1);
384   return path;
385
386 #else
387   //---------- Unix and OS/2+EMX ----------
388   struct passwd *pw;
389   char buf[PATH_MAX+1];
390   GString *s;
391   char *p1, *p2;
392   int n;
393
394   if (path->getChar(0) == '~') {
395     if (path->getChar(1) == '/' ||
396 #ifdef __EMX__
397         path->getChar(1) == '\\' ||
398 #endif
399         path->getLength() == 1) {
400       path->del(0, 1);
401       s = getHomeDir();
402       path->insert(0, s);
403       delete s;
404     } else {
405       p1 = path->getCString() + 1;
406 #ifdef __EMX__
407       for (p2 = p1; *p2 && *p2 != '/' && *p2 != '\\'; ++p2) ;
408 #else
409       for (p2 = p1; *p2 && *p2 != '/'; ++p2) ;
410 #endif
411       if ((n = p2 - p1) > PATH_MAX)
412         n = PATH_MAX;
413       strncpy(buf, p1, n);
414       buf[n] = '\0';
415       if ((pw = getpwnam(buf))) {
416         path->del(0, p2 - p1 + 1);
417         path->insert(0, pw->pw_dir);
418       }
419     }
420   } else if (!isAbsolutePath(path->getCString())) {
421     if (getcwd(buf, sizeof(buf))) {
422 #ifndef __EMX__
423       path->insert(0, '/');
424 #endif
425       path->insert(0, buf);
426     }
427   }
428   return path;
429 #endif
430 }
431
432 time_t getModTime(char *fileName) {
433 #ifdef WIN32
434   //~ should implement this, but it's (currently) only used in xpdf
435   return 0;
436 #else
437   struct stat statBuf;
438
439   if (stat(fileName, &statBuf)) {
440     return 0;
441   }
442   return statBuf.st_mtime;
443 #endif
444 }
445
446 GBool openTempFile(GString **name, FILE **f, char *mode, char *ext) {
447 #if defined(WIN32)
448   //---------- Win32 ----------
449   char *s;
450   char buf[_MAX_PATH];
451   char *fp;
452
453   if (!(s = _tempnam(getenv("TEMP"), NULL))) {
454     return gFalse;
455   }
456   *name = new GString(s);
457   free(s);
458   if (ext) {
459     (*name)->append(ext);
460   }
461   if (!(*f = fopen((*name)->getCString(), mode))) {
462     delete (*name);
463     return gFalse;
464   }
465   return gTrue;
466 #elif defined(VMS) || defined(__EMX__) || defined(ACORN) || defined(MACOS)
467   //---------- non-Unix ----------
468   char *s;
469
470   // There is a security hole here: an attacker can create a symlink
471   // with this file name after the tmpnam call and before the fopen
472   // call.  I will happily accept fixes to this function for non-Unix
473   // OSs.
474   if (!(s = tmpnam(NULL))) {
475     return gFalse;
476   }
477   *name = new GString(s);
478   if (ext) {
479     (*name)->append(ext);
480   }
481   if (!(*f = fopen((*name)->getCString(), mode))) {
482     delete (*name);
483     return gFalse;
484   }
485   return gTrue;
486 #else
487   //---------- Unix ----------
488   char *s;
489   int fd;
490
491   if (ext) {
492 #if HAVE_MKSTEMPS
493     if ((s = getenv("TMPDIR"))) {
494       *name = new GString(s);
495     } else {
496       *name = new GString("/tmp");
497     }
498     (*name)->append("/XXXXXX")->append(ext);
499     fd = mkstemps((*name)->getCString(), strlen(ext));
500 #else
501     if (!(s = tmpnam(NULL))) {
502       return gFalse;
503     }
504     *name = new GString(s);
505     (*name)->append(ext);
506     fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600);
507 #endif
508   } else {
509 #if HAVE_MKSTEMP
510     if ((s = getenv("TMPDIR"))) {
511       *name = new GString(s);
512     } else {
513       *name = new GString("/tmp");
514     }
515     (*name)->append("/XXXXXX");
516     fd = mkstemp((*name)->getCString());
517 #else // HAVE_MKSTEMP
518     if (!(s = tmpnam(NULL))) {
519       return gFalse;
520     }
521     *name = new GString(s);
522     fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600);
523 #endif // HAVE_MKSTEMP
524   }
525   if (fd < 0 || !(*f = fdopen(fd, mode))) {
526     delete *name;
527     return gFalse;
528   }
529   return gTrue;
530 #endif
531 }
532
533 GBool executeCommand(char *cmd) {
534 #ifdef VMS
535   return system(cmd) ? gTrue : gFalse;
536 #else
537   return system(cmd) ? gFalse : gTrue;
538 #endif
539 }
540
541 char *getLine(char *buf, int size, FILE *f) {
542   int c, i;
543
544   i = 0;
545   while (i < size - 1) {
546     if ((c = fgetc(f)) == EOF) {
547       break;
548     }
549     buf[i++] = (char)c;
550     if (c == '\x0a') {
551       break;
552     }
553     if (c == '\x0d') {
554       c = fgetc(f);
555       if (c == '\x0a' && i < size - 1) {
556         buf[i++] = (char)c;
557       } else if (c != EOF) {
558         ungetc(c, f);
559       }
560       break;
561     }
562   }
563   buf[i] = '\0';
564   if (i == 0) {
565     return NULL;
566   }
567   return buf;
568 }
569
570 //------------------------------------------------------------------------
571 // GDir and GDirEntry
572 //------------------------------------------------------------------------
573
574 GDirEntry::GDirEntry(char *dirPath, char *nameA, GBool doStat) {
575 #ifdef VMS
576   char *p;
577 #elif defined(WIN32)
578   int fa;
579   GString *s;
580 #elif defined(ACORN)
581 #else
582   struct stat st;
583   GString *s;
584 #endif
585
586   name = new GString(nameA);
587   dir = gFalse;
588   if (doStat) {
589 #ifdef VMS
590     if (!strcmp(nameA, "-") ||
591         ((p = strrchr(nameA, '.')) && !strncmp(p, ".DIR;", 5)))
592       dir = gTrue;
593 #elif defined(ACORN)
594 #else
595     s = new GString(dirPath);
596     appendToPath(s, nameA);
597 #ifdef WIN32
598     fa = GetFileAttributes(s->getCString());
599     dir = (fa != 0xFFFFFFFF && (fa & FILE_ATTRIBUTE_DIRECTORY));
600 #else
601     if (stat(s->getCString(), &st) == 0)
602       dir = S_ISDIR(st.st_mode);
603 #endif
604     delete s;
605 #endif
606   }
607 }
608
609 GDirEntry::~GDirEntry() {
610   delete name;
611 }
612
613 GDir::GDir(char *name, GBool doStatA) {
614   path = new GString(name);
615   doStat = doStatA;
616 #if defined(WIN32)
617   GString *tmp;
618
619   tmp = path->copy();
620   tmp->append("/*.*");
621   hnd = FindFirstFile(tmp->getCString(), &ffd);
622   delete tmp;
623 #elif defined(ACORN)
624 #elif defined(MACOS)
625 #else
626   dir = opendir(name);
627 #ifdef VMS
628   needParent = strchr(name, '[') != NULL;
629 #endif
630 #endif
631 }
632
633 GDir::~GDir() {
634   delete path;
635 #if defined(WIN32)
636   if (hnd) {
637     FindClose(hnd);
638     hnd = NULL;
639   }
640 #elif defined(ACORN)
641 #elif defined(MACOS)
642 #else
643   if (dir)
644     closedir(dir);
645 #endif
646 }
647
648 GDirEntry *GDir::getNextEntry() {
649   struct dirent *ent;
650   GDirEntry *e;
651
652   e = NULL;
653 #if defined(WIN32)
654   e = new GDirEntry(path->getCString(), ffd.cFileName, doStat);
655   if (hnd  && !FindNextFile(hnd, &ffd)) {
656     FindClose(hnd);
657     hnd = NULL;
658   }
659 #elif defined(ACORN)
660 #elif defined(MACOS)
661 #else
662   if (dir) {
663 #ifdef VMS
664     if (needParent) {
665       e = new GDirEntry(path->getCString(), "-", doStat);
666       needParent = gFalse;
667       return e;
668     }
669 #endif
670     ent = readdir(dir);
671 #ifndef VMS
672     if (ent && !strcmp(ent->d_name, "."))
673       ent = readdir(dir);
674 #endif
675     if (ent)
676       e = new GDirEntry(path->getCString(), ent->d_name, doStat);
677   }
678 #endif
679   return e;
680 }
681
682 void GDir::rewind() {
683 #ifdef WIN32
684   GString *tmp;
685
686   if (hnd)
687     FindClose(hnd);
688   tmp = path->copy();
689   tmp->append("/*.*");
690   hnd = FindFirstFile(tmp->getCString(), &ffd);
691 #elif defined(ACORN)
692 #elif defined(MACOS)
693 #else
694   if (dir)
695     rewinddir(dir);
696 #ifdef VMS
697   needParent = strchr(path->getCString(), '[') != NULL;
698 #endif
699 #endif
700 }