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