implemented stroke clipping
[swftools.git] / pdf2swf / 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 GBool openTempFile(GString **name, FILE **f, char *mode, char *ext) {
441 #if defined(WIN32)
442   //---------- Win32 ----------
443   char *s;
444
445   if (!(s = _tempnam(getenv("TEMP"), NULL))) {
446     return gFalse;
447   }
448   *name = new GString(s);
449   free(s);
450   if (ext) {
451     (*name)->append(ext);
452   }
453   if (!(*f = fopen((*name)->getCString(), mode))) {
454     delete (*name);
455     return gFalse;
456   }
457   return gTrue;
458 #elif defined(VMS) || defined(__EMX__) || defined(ACORN) || defined(MACOS)
459   //---------- non-Unix ----------
460   char *s;
461
462   // There is a security hole here: an attacker can create a symlink
463   // with this file name after the tmpnam call and before the fopen
464   // call.  I will happily accept fixes to this function for non-Unix
465   // OSs.
466   if (!(s = tmpnam(NULL))) {
467     return gFalse;
468   }
469   *name = new GString(s);
470   if (ext) {
471     (*name)->append(ext);
472   }
473   if (!(*f = fopen((*name)->getCString(), mode))) {
474     delete (*name);
475     return gFalse;
476   }
477   return gTrue;
478 #else
479   //---------- Unix ----------
480   char *s;
481   int fd;
482
483   if (ext) {
484 #if HAVE_MKSTEMPS
485     if ((s = getenv("TMPDIR"))) {
486       *name = new GString(s);
487     } else {
488       *name = new GString("/tmp");
489     }
490     (*name)->append("/XXXXXX")->append(ext);
491     fd = mkstemps((*name)->getCString(), strlen(ext));
492 #else
493     if (!(s = tmpnam(NULL))) {
494       return gFalse;
495     }
496     *name = new GString(s);
497     (*name)->append(ext);
498     fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600);
499 #endif
500   } else {
501 #if HAVE_MKSTEMP
502     if ((s = getenv("TMPDIR"))) {
503       *name = new GString(s);
504     } else {
505       *name = new GString("/tmp");
506     }
507     (*name)->append("/XXXXXX");
508     fd = mkstemp((*name)->getCString());
509 #else // HAVE_MKSTEMP
510     if (!(s = tmpnam(NULL))) {
511       return gFalse;
512     }
513     *name = new GString(s);
514     fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600);
515 #endif // HAVE_MKSTEMP
516   }
517   if (fd < 0 || !(*f = fdopen(fd, mode))) {
518     delete *name;
519     return gFalse;
520   }
521   return gTrue;
522 #endif
523 }
524
525 GBool executeCommand(char *cmd) {
526 #ifdef VMS
527   return system(cmd) ? gTrue : gFalse;
528 #else
529   return system(cmd) ? gFalse : gTrue;
530 #endif
531 }
532
533 char *getLine(char *buf, int size, FILE *f) {
534   int c, i;
535
536   i = 0;
537   while (i < size - 1) {
538     if ((c = fgetc(f)) == EOF) {
539       break;
540     }
541     buf[i++] = (char)c;
542     if (c == '\x0a') {
543       break;
544     }
545     if (c == '\x0d') {
546       c = fgetc(f);
547       if (c == '\x0a' && i < size - 1) {
548         buf[i++] = (char)c;
549       } else if (c != EOF) {
550         ungetc(c, f);
551       }
552       break;
553     }
554   }
555   buf[i] = '\0';
556   if (i == 0) {
557     return NULL;
558   }
559   return buf;
560 }
561
562 //------------------------------------------------------------------------
563 // GDir and GDirEntry
564 //------------------------------------------------------------------------
565
566 GDirEntry::GDirEntry(char *dirPath, char *nameA, GBool doStat) {
567 #ifdef VMS
568   char *p;
569 #elif defined(WIN32)
570   int fa;
571   GString *s;
572 #elif defined(ACORN)
573 #else
574   struct stat st;
575   GString *s;
576 #endif
577
578   name = new GString(nameA);
579   dir = gFalse;
580   if (doStat) {
581 #ifdef VMS
582     if (!strcmp(nameA, "-") ||
583         ((p = strrchr(nameA, '.')) && !strncmp(p, ".DIR;", 5)))
584       dir = gTrue;
585 #elif defined(ACORN)
586 #else
587     s = new GString(dirPath);
588     appendToPath(s, nameA);
589 #ifdef WIN32
590     fa = GetFileAttributes(s->getCString());
591     dir = (fa != 0xFFFFFFFF && (fa & FILE_ATTRIBUTE_DIRECTORY));
592 #else
593     if (stat(s->getCString(), &st) == 0)
594       dir = S_ISDIR(st.st_mode);
595 #endif
596     delete s;
597 #endif
598   }
599 }
600
601 GDirEntry::~GDirEntry() {
602   delete name;
603 }
604
605 GDir::GDir(char *name, GBool doStatA) {
606   path = new GString(name);
607   doStat = doStatA;
608 #if defined(WIN32)
609   GString *tmp;
610
611   tmp = path->copy();
612   tmp->append("/*.*");
613   hnd = FindFirstFile(tmp->getCString(), &ffd);
614   delete tmp;
615 #elif defined(ACORN)
616 #elif defined(MACOS)
617 #else
618   dir = opendir(name);
619 #ifdef VMS
620   needParent = strchr(name, '[') != NULL;
621 #endif
622 #endif
623 }
624
625 GDir::~GDir() {
626   delete path;
627 #if defined(WIN32)
628   if (hnd) {
629     FindClose(hnd);
630     hnd = NULL;
631   }
632 #elif defined(ACORN)
633 #elif defined(MACOS)
634 #else
635   if (dir)
636     closedir(dir);
637 #endif
638 }
639
640 GDirEntry *GDir::getNextEntry() {
641   GDirEntry *e;
642
643 #if defined(WIN32)
644   if (hnd) {
645     e = new GDirEntry(path->getCString(), ffd.cFileName, doStat);
646     if (hnd  && !FindNextFile(hnd, &ffd)) {
647       FindClose(hnd);
648       hnd = NULL;
649     }
650   } else {
651     e = NULL;
652   }
653 #elif defined(ACORN)
654 #elif defined(MACOS)
655 #elif defined(VMS)
656   struct dirent *ent;
657   e = NULL;
658   if (dir) {
659     if (needParent) {
660       e = new GDirEntry(path->getCString(), "-", doStat);
661       needParent = gFalse;
662       return e;
663     }
664     ent = readdir(dir);
665     if (ent) {
666       e = new GDirEntry(path->getCString(), ent->d_name, doStat);
667     }
668   }
669 #else
670   struct dirent *ent;
671   e = NULL;
672   if (dir) {
673     ent = readdir(dir);
674     if (ent && !strcmp(ent->d_name, ".")) {
675       ent = readdir(dir);
676     }
677     if (ent) {
678       e = new GDirEntry(path->getCString(), ent->d_name, doStat);
679     }
680   }
681 #endif
682
683   return e;
684 }
685
686 void GDir::rewind() {
687 #ifdef WIN32
688   GString *tmp;
689
690   if (hnd)
691     FindClose(hnd);
692   tmp = path->copy();
693   tmp->append("/*.*");
694   hnd = FindFirstFile(tmp->getCString(), &ffd);
695   delete tmp;
696 #elif defined(ACORN)
697 #elif defined(MACOS)
698 #else
699   if (dir)
700     rewinddir(dir);
701 #ifdef VMS
702   needParent = strchr(path->getCString(), '[') != NULL;
703 #endif
704 #endif
705 }