1 //========================================================================
2 //
3 // gfile.cc
4 //
5 // Miscellaneous file and directory name manipulation.
6 //
7 // Copyright 1996-2003 Glyph & Cog, LLC
8 //
9 //========================================================================
10 //  Modified for TeX Live by Peter Breitenlohner <tex-live@tug.org>
11 //  See top-level ChangeLog for a list of all modifications
12 //========================================================================
13 
14 #include <aconf.h>
15 
16 #ifdef _WIN32
17 #  include <time.h>
18 #  include <direct.h>
19 #else
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 <time.h>
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 
getHomeDir()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 #ifdef _WIN32
59   if ((s = getenv("USERPROFILE")))
60 #else
61   if ((s = getenv("HOME")))
62 #endif
63     ret = new GString(s);
64   else
65     ret = new GString(".");
66   return ret;
67 
68 #elif defined(ACORN)
69   //---------- RISCOS ----------
70   return new GString("@");
71 
72 #elif defined(MACOS)
73   //---------- MacOS ----------
74   return new GString(":");
75 
76 #else
77   //---------- Unix ----------
78   char *s;
79   struct passwd *pw;
80   GString *ret;
81 
82   if ((s = getenv("HOME"))) {
83     ret = new GString(s);
84   } else {
85     if ((s = getenv("USER")))
86       pw = getpwnam(s);
87     else
88       pw = getpwuid(getuid());
89     if (pw)
90       ret = new GString(pw->pw_dir);
91     else
92       ret = new GString(".");
93   }
94   return ret;
95 #endif
96 }
97 
getCurrentDir()98 GString *getCurrentDir() {
99   char buf[PATH_MAX+1];
100 
101 #if defined(__EMX__)
102   if (_getcwd2(buf, sizeof(buf)))
103 #elif defined(_WIN32)
104   if (GetCurrentDirectoryA(sizeof(buf), buf))
105 #elif defined(ACORN)
106   if (strcpy(buf, "@"))
107 #elif defined(MACOS)
108   if (strcpy(buf, ":"))
109 #else
110   if (getcwd(buf, sizeof(buf)))
111 #endif
112     return new GString(buf);
113   return new GString();
114 }
115 
appendToPath(GString * path,const char * fileName)116 GString *appendToPath(GString *path, const char *fileName) {
117 #if defined(VMS)
118   //---------- VMS ----------
119   //~ this should handle everything necessary for file
120   //~ requesters, but it's certainly not complete
121   char *p0, *p1, *p2;
122   char *q1;
123 
124   p0 = path->getCString();
125   p1 = p0 + path->getLength() - 1;
126   if (!strcmp(fileName, "-")) {
127     if (*p1 == ']') {
128       for (p2 = p1; p2 > p0 && *p2 != '.' && *p2 != '['; --p2) ;
129       if (*p2 == '[')
130 	++p2;
131       path->del(p2 - p0, p1 - p2);
132     } else if (*p1 == ':') {
133       path->append("[-]");
134     } else {
135       path->clear();
136       path->append("[-]");
137     }
138   } else if ((q1 = strrchr(fileName, '.')) && !strncmp(q1, ".DIR;", 5)) {
139     if (*p1 == ']') {
140       path->insert(p1 - p0, '.');
141       path->insert(p1 - p0 + 1, fileName, q1 - fileName);
142     } else if (*p1 == ':') {
143       path->append('[');
144       path->append(']');
145       path->append(fileName, q1 - fileName);
146     } else {
147       path->clear();
148       path->append(fileName, q1 - fileName);
149     }
150   } else {
151     if (*p1 != ']' && *p1 != ':')
152       path->clear();
153     path->append(fileName);
154   }
155   return path;
156 
157 #elif defined(_WIN32)
158   //---------- Win32 ----------
159   GString *tmp;
160   char buf[256];
161   char *fp;
162 
163   tmp = new GString(path);
164   tmp->append('/');
165   tmp->append(fileName);
166   GetFullPathNameA(tmp->getCString(), sizeof(buf), buf, &fp);
167   delete tmp;
168   path->clear();
169   path->append(buf);
170   return path;
171 
172 #elif defined(ACORN)
173   //---------- RISCOS ----------
174   char *p;
175   int i;
176 
177   path->append(".");
178   i = path->getLength();
179   path->append(fileName);
180   for (p = path->getCString() + i; *p; ++p) {
181     if (*p == '/') {
182       *p = '.';
183     } else if (*p == '.') {
184       *p = '/';
185     }
186   }
187   return path;
188 
189 #elif defined(MACOS)
190   //---------- MacOS ----------
191   char *p;
192   int i;
193 
194   path->append(":");
195   i = path->getLength();
196   path->append(fileName);
197   for (p = path->getCString() + i; *p; ++p) {
198     if (*p == '/') {
199       *p = ':';
200     } else if (*p == '.') {
201       *p = ':';
202     }
203   }
204   return path;
205 
206 #elif defined(__EMX__)
207   //---------- OS/2+EMX ----------
208   int i;
209 
210   // appending "." does nothing
211   if (!strcmp(fileName, "."))
212     return path;
213 
214   // appending ".." goes up one directory
215   if (!strcmp(fileName, "..")) {
216     for (i = path->getLength() - 2; i >= 0; --i) {
217       if (path->getChar(i) == '/' || path->getChar(i) == '\\' ||
218 	  path->getChar(i) == ':')
219 	break;
220     }
221     if (i <= 0) {
222       if (path->getChar(0) == '/' || path->getChar(0) == '\\') {
223 	path->del(1, path->getLength() - 1);
224       } else if (path->getLength() >= 2 && path->getChar(1) == ':') {
225 	path->del(2, path->getLength() - 2);
226       } else {
227 	path->clear();
228 	path->append("..");
229       }
230     } else {
231       if (path->getChar(i-1) == ':')
232 	++i;
233       path->del(i, path->getLength() - i);
234     }
235     return path;
236   }
237 
238   // otherwise, append "/" and new path component
239   if (path->getLength() > 0 &&
240       path->getChar(path->getLength() - 1) != '/' &&
241       path->getChar(path->getLength() - 1) != '\\')
242     path->append('/');
243   path->append(fileName);
244   return path;
245 
246 #else
247   //---------- Unix ----------
248   int i;
249 
250   // appending "." does nothing
251   if (!strcmp(fileName, "."))
252     return path;
253 
254   // appending ".." goes up one directory
255   if (!strcmp(fileName, "..")) {
256     for (i = path->getLength() - 2; i >= 0; --i) {
257       if (path->getChar(i) == '/')
258 	break;
259     }
260     if (i <= 0) {
261       if (path->getChar(0) == '/') {
262 	path->del(1, path->getLength() - 1);
263       } else {
264 	path->clear();
265 	path->append("..");
266       }
267     } else {
268       path->del(i, path->getLength() - i);
269     }
270     return path;
271   }
272 
273   // otherwise, append "/" and new path component
274   if (path->getLength() > 0 &&
275       path->getChar(path->getLength() - 1) != '/')
276     path->append('/');
277   path->append(fileName);
278   return path;
279 #endif
280 }
281 
grabPath(char * fileName)282 GString *grabPath(char *fileName) {
283 #ifdef VMS
284   //---------- VMS ----------
285   char *p;
286 
287   if ((p = strrchr(fileName, ']')))
288     return new GString(fileName, p + 1 - fileName);
289   if ((p = strrchr(fileName, ':')))
290     return new GString(fileName, p + 1 - fileName);
291   return new GString();
292 
293 #elif defined(__EMX__) || defined(_WIN32)
294   //---------- OS/2+EMX and Win32 ----------
295   char *p;
296 
297   if ((p = strrchr(fileName, '/')))
298     return new GString(fileName, (int)(p - fileName));
299   if ((p = strrchr(fileName, '\\')))
300     return new GString(fileName, (int)(p - fileName));
301   if ((p = strrchr(fileName, ':')))
302     return new GString(fileName, (int)(p + 1 - fileName));
303   return new GString();
304 
305 #elif defined(ACORN)
306   //---------- RISCOS ----------
307   char *p;
308 
309   if ((p = strrchr(fileName, '.')))
310     return new GString(fileName, p - fileName);
311   return new GString();
312 
313 #elif defined(MACOS)
314   //---------- MacOS ----------
315   char *p;
316 
317   if ((p = strrchr(fileName, ':')))
318     return new GString(fileName, p - fileName);
319   return new GString();
320 
321 #else
322   //---------- Unix ----------
323   char *p;
324 
325   if ((p = strrchr(fileName, '/')))
326     return new GString(fileName, p - fileName);
327   return new GString();
328 #endif
329 }
330 
isAbsolutePath(char * path)331 GBool isAbsolutePath(char *path) {
332 #ifdef VMS
333   //---------- VMS ----------
334   return strchr(path, ':') ||
335 	 (path[0] == '[' && path[1] != '.' && path[1] != '-');
336 
337 #elif defined(__EMX__) || defined(_WIN32)
338   //---------- OS/2+EMX and Win32 ----------
339   return path[0] == '/' || path[0] == '\\' || path[1] == ':';
340 
341 #elif defined(ACORN)
342   //---------- RISCOS ----------
343   return path[0] == '$';
344 
345 #elif defined(MACOS)
346   //---------- MacOS ----------
347   return path[0] != ':';
348 
349 #else
350   //---------- Unix ----------
351   return path[0] == '/';
352 #endif
353 }
354 
makePathAbsolute(GString * path)355 GString *makePathAbsolute(GString *path) {
356 #ifdef VMS
357   //---------- VMS ----------
358   char buf[PATH_MAX+1];
359 
360   if (!isAbsolutePath(path->getCString())) {
361     if (getcwd(buf, sizeof(buf))) {
362       path->insert(0, buf);
363     }
364   }
365   return path;
366 
367 #elif defined(_WIN32)
368   //---------- Win32 ----------
369   char buf[_MAX_PATH];
370   char *fp;
371 
372   buf[0] = '\0';
373   if (!GetFullPathNameA(path->getCString(), _MAX_PATH, buf, &fp)) {
374     path->clear();
375     return path;
376   }
377   path->clear();
378   path->append(buf);
379   return path;
380 
381 #elif defined(ACORN)
382   //---------- RISCOS ----------
383   path->insert(0, '@');
384   return path;
385 
386 #elif defined(MACOS)
387   //---------- MacOS ----------
388   path->del(0, 1);
389   return path;
390 
391 #else
392   //---------- Unix and OS/2+EMX ----------
393   struct passwd *pw;
394   char buf[PATH_MAX+1];
395   GString *s;
396   char *p1, *p2;
397   int n;
398 
399   if (path->getChar(0) == '~') {
400     if (path->getChar(1) == '/' ||
401 #ifdef __EMX__
402 	path->getChar(1) == '\\' ||
403 #endif
404 	path->getLength() == 1) {
405       path->del(0, 1);
406       s = getHomeDir();
407       path->insert(0, s);
408       delete s;
409     } else {
410       p1 = path->getCString() + 1;
411 #ifdef __EMX__
412       for (p2 = p1; *p2 && *p2 != '/' && *p2 != '\\'; ++p2) ;
413 #else
414       for (p2 = p1; *p2 && *p2 != '/'; ++p2) ;
415 #endif
416       if ((n = p2 - p1) > PATH_MAX)
417 	n = PATH_MAX;
418       strncpy(buf, p1, n);
419       buf[n] = '\0';
420       if ((pw = getpwnam(buf))) {
421 	path->del(0, p2 - p1 + 1);
422 	path->insert(0, pw->pw_dir);
423       }
424     }
425   } else if (!isAbsolutePath(path->getCString())) {
426     if (getcwd(buf, sizeof(buf))) {
427 #ifndef __EMX__
428       path->insert(0, '/');
429 #endif
430       path->insert(0, buf);
431     }
432   }
433   return path;
434 #endif
435 }
436 
getModTime(char * fileName)437 time_t getModTime(char *fileName) {
438 #ifdef _WIN32
439   //~ should implement this, but it's (currently) only used in xpdf
440   return 0;
441 #else
442   struct stat statBuf;
443 
444   if (stat(fileName, &statBuf)) {
445     return 0;
446   }
447   return statBuf.st_mtime;
448 #endif
449 }
450 
451 #ifndef PDF_PARSER_ONLY
openTempFile(GString ** name,FILE ** f,const char * mode,const char * ext)452 GBool openTempFile(GString **name, FILE **f,
453 		   const char *mode, const char *ext) {
454 #if defined(_WIN32)
455   //---------- Win32 ----------
456   char *tempDir;
457   GString *s, *s2;
458   FILE *f2;
459   int t, i;
460 
461   // this has the standard race condition problem, but I haven't found
462   // a better way to generate temp file names with extensions on
463   // Windows
464   if ((tempDir = getenv("TEMP"))) {
465     s = new GString(tempDir);
466     s->append('\\');
467   } else {
468     s = new GString();
469   }
470   s->appendf("x_{0:d}_{1:d}_",
471 	     (int)GetCurrentProcessId(), (int)GetCurrentThreadId());
472   t = (int)time(NULL);
473   for (i = 0; i < 1000; ++i) {
474     s2 = s->copy()->appendf("{0:d}", t + i);
475     if (ext) {
476       s2->append(ext);
477     }
478     if (!(f2 = fopen(s2->getCString(), "r"))) {
479       if (!(f2 = fopen(s2->getCString(), mode))) {
480 	delete s2;
481 	delete s;
482 	return gFalse;
483       }
484       *name = s2;
485       *f = f2;
486       delete s;
487       return gTrue;
488     }
489     fclose(f2);
490     delete s2;
491   }
492   delete s;
493   return gFalse;
494 #elif defined(VMS) || defined(__EMX__) || defined(ACORN) || defined(MACOS)
495   //---------- non-Unix ----------
496   char *s;
497 
498   // There is a security hole here: an attacker can create a symlink
499   // with this file name after the tmpnam call and before the fopen
500   // call.  I will happily accept fixes to this function for non-Unix
501   // OSs.
502   if (!(s = tmpnam(NULL))) {
503     return gFalse;
504   }
505   *name = new GString(s);
506   if (ext) {
507     (*name)->append(ext);
508   }
509   if (!(*f = fopen((*name)->getCString(), mode))) {
510     delete (*name);
511     *name = NULL;
512     return gFalse;
513   }
514   return gTrue;
515 #else
516   //---------- Unix ----------
517   char *s;
518   int fd;
519 
520   if (ext) {
521 #if HAVE_MKSTEMPS
522     if ((s = getenv("TMPDIR"))) {
523       *name = new GString(s);
524     } else {
525       *name = new GString("/tmp");
526     }
527     (*name)->append("/XXXXXX")->append(ext);
528     fd = mkstemps((*name)->getCString(), strlen(ext));
529 #else
530     if (!(s = tmpnam(NULL))) {
531       return gFalse;
532     }
533     *name = new GString(s);
534     (*name)->append(ext);
535     fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600);
536 #endif
537   } else {
538 #if HAVE_MKSTEMP
539     if ((s = getenv("TMPDIR"))) {
540       *name = new GString(s);
541     } else {
542       *name = new GString("/tmp");
543     }
544     (*name)->append("/XXXXXX");
545     fd = mkstemp((*name)->getCString());
546 #else // HAVE_MKSTEMP
547     if (!(s = tmpnam(NULL))) {
548       return gFalse;
549     }
550     *name = new GString(s);
551     fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600);
552 #endif // HAVE_MKSTEMP
553   }
554   if (fd < 0 || !(*f = fdopen(fd, mode))) {
555     delete *name;
556     *name = NULL;
557     return gFalse;
558   }
559   return gTrue;
560 #endif
561 }
562 #endif
563 
createDir(char * path,int mode)564 GBool createDir(char *path, int mode) {
565 #ifdef _WIN32
566   return !_mkdir(path);
567 #else
568   return !mkdir(path, mode);
569 #endif
570 }
571 
executeCommand(char * cmd)572 GBool executeCommand(char *cmd) {
573 #ifdef VMS
574   return system(cmd) ? gTrue : gFalse;
575 #else
576   return system(cmd) ? gFalse : gTrue;
577 #endif
578 }
579 
580 #ifdef _WIN32
fileNameToUTF8(char * path)581 GString *fileNameToUTF8(char *path) {
582   GString *s;
583   char *p;
584 
585   s = new GString();
586   for (p = path; *p; ++p) {
587     if (*p & 0x80) {
588       s->append((char)(0xc0 | ((*p >> 6) & 0x03)));
589       s->append((char)(0x80 | (*p & 0x3f)));
590     } else {
591       s->append(*p);
592     }
593   }
594   return s;
595 }
596 
fileNameToUTF8(wchar_t * path)597 GString *fileNameToUTF8(wchar_t *path) {
598   GString *s;
599   wchar_t *p;
600 
601   s = new GString();
602   for (p = path; *p; ++p) {
603     if (*p < 0x80) {
604       s->append((char)*p);
605     } else if (*p < 0x800) {
606       s->append((char)(0xc0 | ((*p >> 6) & 0x1f)));
607       s->append((char)(0x80 | (*p & 0x3f)));
608     } else {
609       s->append((char)(0xe0 | ((*p >> 12) & 0x0f)));
610       s->append((char)(0x80 | ((*p >> 6) & 0x3f)));
611       s->append((char)(0x80 | (*p & 0x3f)));
612     }
613   }
614   return s;
615 }
616 #endif
617 
openFile(const char * path,const char * mode)618 FILE *openFile(const char *path, const char *mode) {
619 #ifdef _WIN32
620   OSVERSIONINFO version;
621   wchar_t wPath[_MAX_PATH + 1];
622   char nPath[_MAX_PATH + 1];
623   wchar_t wMode[8];
624   const char *p;
625   int i;
626 
627   // NB: _wfopen is only available in NT
628   version.dwOSVersionInfoSize = sizeof(version);
629   GetVersionEx(&version);
630   if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
631     for (p = path, i = 0; *p && i < _MAX_PATH; ++i) {
632       if ((p[0] & 0xe0) == 0xc0 &&
633 	  p[1] && (p[1] & 0xc0) == 0x80) {
634 	wPath[i] = (wchar_t)(((p[0] & 0x1f) << 6) |
635 			     (p[1] & 0x3f));
636 	p += 2;
637       } else if ((p[0] & 0xf0) == 0xe0 &&
638 		 p[1] && (p[1] & 0xc0) == 0x80 &&
639 		 p[2] && (p[2] & 0xc0) == 0x80) {
640 	wPath[i] = (wchar_t)(((p[0] & 0x0f) << 12) |
641 			     ((p[1] & 0x3f) << 6) |
642 			     (p[2] & 0x3f));
643 	p += 3;
644       } else {
645 	wPath[i] = (wchar_t)(p[0] & 0xff);
646 	p += 1;
647       }
648     }
649     wPath[i] = (wchar_t)0;
650     for (i = 0; mode[i] && i < sizeof(mode) - 1; ++i) {
651       wMode[i] = (wchar_t)(mode[i] & 0xff);
652     }
653     wMode[i] = (wchar_t)0;
654     return _wfopen(wPath, wMode);
655   } else {
656     for (p = path, i = 0; *p && i < _MAX_PATH; ++i) {
657       if ((p[0] & 0xe0) == 0xc0 &&
658 	  p[1] && (p[1] & 0xc0) == 0x80) {
659 	nPath[i] = (char)(((p[0] & 0x1f) << 6) |
660 			  (p[1] & 0x3f));
661 	p += 2;
662       } else if ((p[0] & 0xf0) == 0xe0 &&
663 		 p[1] && (p[1] & 0xc0) == 0x80 &&
664 		 p[2] && (p[2] & 0xc0) == 0x80) {
665 	nPath[i] = (char)(((p[1] & 0x3f) << 6) |
666 			  (p[2] & 0x3f));
667 	p += 3;
668       } else {
669 	nPath[i] = p[0];
670 	p += 1;
671       }
672     }
673     nPath[i] = '\0';
674     return fopen(nPath, mode);
675   }
676 #else
677   return fopen(path, mode);
678 #endif
679 }
680 
getLine(char * buf,int size,FILE * f)681 char *getLine(char *buf, int size, FILE *f) {
682   int c, i;
683 
684   i = 0;
685   while (i < size - 1) {
686     if ((c = fgetc(f)) == EOF) {
687       break;
688     }
689     buf[i++] = (char)c;
690     if (c == '\x0a') {
691       break;
692     }
693     if (c == '\x0d') {
694       c = fgetc(f);
695       if (c == '\x0a' && i < size - 1) {
696 	buf[i++] = (char)c;
697       } else if (c != EOF) {
698 	ungetc(c, f);
699       }
700       break;
701     }
702   }
703   buf[i] = '\0';
704   if (i == 0) {
705     return NULL;
706   }
707   return buf;
708 }
709 
gfseek(FILE * f,GFileOffset offset,int whence)710 int gfseek(FILE *f, GFileOffset offset, int whence) {
711 #if HAVE_FSEEKO
712   return fseeko(f, offset, whence);
713 #elif HAVE_FSEEK64
714   return fseek64(f, offset, whence);
715 #elif HAVE_FSEEKI64
716   return _fseeki64(f, offset, whence);
717 #else
718   return fseek(f, offset, whence);
719 #endif
720 }
721 
gftell(FILE * f)722 GFileOffset gftell(FILE *f) {
723 #if HAVE_FSEEKO
724   return ftello(f);
725 #elif HAVE_FSEEK64
726   return ftell64(f);
727 #elif HAVE_FSEEKI64
728   return _ftelli64(f);
729 #else
730   return ftell(f);
731 #endif
732 }
733 
734 //------------------------------------------------------------------------
735 // GDir and GDirEntry
736 //------------------------------------------------------------------------
737 
GDirEntry(char * dirPath,char * nameA,GBool doStat)738 GDirEntry::GDirEntry(char *dirPath, char *nameA, GBool doStat) {
739 #ifdef VMS
740   char *p;
741 #elif defined(_WIN32)
742   int fa;
743   GString *s;
744 #elif defined(ACORN)
745 #else
746   struct stat st;
747   GString *s;
748 #endif
749 
750   name = new GString(nameA);
751   dir = gFalse;
752   if (doStat) {
753 #ifdef VMS
754     if (!strcmp(nameA, "-") ||
755 	((p = strrchr(nameA, '.')) && !strncmp(p, ".DIR;", 5)))
756       dir = gTrue;
757 #elif defined(ACORN)
758 #else
759     s = new GString(dirPath);
760     appendToPath(s, nameA);
761 #ifdef _WIN32
762     fa = GetFileAttributesA(s->getCString());
763     dir = (fa != 0xFFFFFFFF && (fa & FILE_ATTRIBUTE_DIRECTORY));
764 #else
765     if (stat(s->getCString(), &st) == 0)
766       dir = S_ISDIR(st.st_mode);
767 #endif
768     delete s;
769 #endif
770   }
771 }
772 
~GDirEntry()773 GDirEntry::~GDirEntry() {
774   delete name;
775 }
776 
GDir(char * name,GBool doStatA)777 GDir::GDir(char *name, GBool doStatA) {
778   path = new GString(name);
779   doStat = doStatA;
780 #if defined(_WIN32)
781   GString *tmp;
782 
783   tmp = path->copy();
784   tmp->append("/*.*");
785   hnd = FindFirstFileA(tmp->getCString(), &ffd);
786   delete tmp;
787 #elif defined(ACORN)
788 #elif defined(MACOS)
789 #elif defined(ANDROID)
790 #else
791   dir = opendir(name);
792 #ifdef VMS
793   needParent = strchr(name, '[') != NULL;
794 #endif
795 #endif
796 }
797 
~GDir()798 GDir::~GDir() {
799   delete path;
800 #if defined(_WIN32)
801   if (hnd) {
802     FindClose(hnd);
803     hnd = NULL;
804   }
805 #elif defined(ACORN)
806 #elif defined(MACOS)
807 #elif defined(ANDROID)
808 #else
809   if (dir)
810     closedir(dir);
811 #endif
812 }
813 
getNextEntry()814 GDirEntry *GDir::getNextEntry() {
815   GDirEntry *e;
816 
817 #if defined(_WIN32)
818   if (hnd) {
819     e = new GDirEntry(path->getCString(), ffd.cFileName, doStat);
820     if (hnd  && !FindNextFileA(hnd, &ffd)) {
821       FindClose(hnd);
822       hnd = NULL;
823     }
824   } else {
825     e = NULL;
826   }
827 #elif defined(ACORN)
828 #elif defined(MACOS)
829 #elif defined(ANDROID)
830 #elif defined(VMS)
831   struct dirent *ent;
832   e = NULL;
833   if (dir) {
834     if (needParent) {
835       e = new GDirEntry(path->getCString(), "-", doStat);
836       needParent = gFalse;
837       return e;
838     }
839     ent = readdir(dir);
840     if (ent) {
841       e = new GDirEntry(path->getCString(), ent->d_name, doStat);
842     }
843   }
844 #else
845   struct dirent *ent;
846   e = NULL;
847   if (dir) {
848     ent = (struct dirent *)readdir(dir);
849     if (ent && !strcmp(ent->d_name, ".")) {
850       ent = (struct dirent *)readdir(dir);
851     }
852     if (ent) {
853       e = new GDirEntry(path->getCString(), ent->d_name, doStat);
854     }
855   }
856 #endif
857 
858   return e;
859 }
860 
rewind()861 void GDir::rewind() {
862 #ifdef _WIN32
863   GString *tmp;
864 
865   if (hnd)
866     FindClose(hnd);
867   tmp = path->copy();
868   tmp->append("/*.*");
869   hnd = FindFirstFileA(tmp->getCString(), &ffd);
870   delete tmp;
871 #elif defined(ACORN)
872 #elif defined(MACOS)
873 #elif defined(ANDROID)
874 #else
875   if (dir)
876     rewinddir(dir);
877 #ifdef VMS
878   needParent = strchr(path->getCString(), '[') != NULL;
879 #endif
880 #endif
881 }
882