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