forwarding a radium compilation fix.
[AROS-Contrib.git] / arospdf / goo / gfile.cc
blobc8b7f82d1766818e13ddf03bc3e88fb2da39e856
1 //========================================================================
2 //
3 // gfile.cc
4 //
5 // Miscellaneous file and directory name manipulation.
6 //
7 // Copyright 1996-2003 Glyph & Cog, LLC
8 //
9 //========================================================================
11 #include <aconf.h>
13 #include <aros/debug.h>
14 #ifdef WIN32
15 # include <time.h>
16 #else
17 # if defined(MACOS)
18 # include <sys/stat.h>
19 # elif !defined(ACORN)
20 # include <sys/types.h>
21 # include <sys/stat.h>
22 # include <fcntl.h>
23 # endif
24 # include <limits.h>
25 # include <string.h>
26 # if !defined(VMS) && !defined(ACORN) && !defined(MACOS)
27 # include <pwd.h>
28 # endif
29 # if defined(VMS) && (__DECCXX_VER < 50200000)
30 # include <unixlib.h>
31 # endif
32 #endif // WIN32
33 #include "GString.h"
34 #include "gfile.h"
36 // Some systems don't define this, so just make it something reasonably
37 // large.
38 #ifndef PATH_MAX
39 #define PATH_MAX 1024
40 #endif
42 //------------------------------------------------------------------------
44 GString *getHomeDir() {
45 #ifdef VMS
46 //---------- VMS ----------
47 return new GString("SYS$LOGIN:");
48 #elif defined(__AROS__)
49 return new GString("progdir:");
50 #elif defined(__EMX__) || defined(WIN32)
51 //---------- OS/2+EMX and Win32 ----------
52 char *s;
53 GString *ret;
55 if ((s = getenv("HOME")))
56 ret = new GString(s);
57 else
58 ret = new GString(".");
59 return ret;
61 #elif defined(ACORN)
62 //---------- RISCOS ----------
63 return new GString("@");
65 #elif defined(MACOS)
66 //---------- MacOS ----------
67 return new GString(":");
69 #else
70 //---------- Unix ----------
71 char *s;
72 struct passwd *pw;
73 GString *ret;
75 if ((s = getenv("HOME"))) {
76 ret = new GString(s);
77 } else {
78 if ((s = getenv("USER")))
79 pw = getpwnam(s);
80 else
81 pw = getpwuid(getuid());
82 if (pw)
83 ret = new GString(pw->pw_dir);
84 else
85 ret = new GString(".");
87 return ret;
88 #endif
91 GString *getCurrentDir() {
92 char buf[PATH_MAX+1];
94 #if defined(__EMX__)
95 if (_getcwd2(buf, sizeof(buf)))
96 #elif defined(WIN32)
97 if (GetCurrentDirectory(sizeof(buf), buf))
98 #elif defined(ACORN)
99 if (strcpy(buf, "@"))
100 #elif defined(MACOS)
101 if (strcpy(buf, ":"))
102 #else
103 if (getcwd(buf, sizeof(buf)))
104 #endif
105 return new GString(buf);
106 return new GString();
109 GString *appendToPath(GString *path, char *fileName) {
110 #if defined(VMS)
111 //---------- VMS ----------
112 //~ this should handle everything necessary for file
113 //~ requesters, but it's certainly not complete
114 char *p0, *p1, *p2;
115 char *q1;
117 p0 = path->getCString();
118 p1 = p0 + path->getLength() - 1;
119 if (!strcmp(fileName, "-")) {
120 if (*p1 == ']') {
121 for (p2 = p1; p2 > p0 && *p2 != '.' && *p2 != '['; --p2) ;
122 if (*p2 == '[')
123 ++p2;
124 path->del(p2 - p0, p1 - p2);
125 } else if (*p1 == ':') {
126 path->append("[-]");
127 } else {
128 path->clear();
129 path->append("[-]");
131 } else if ((q1 = strrchr(fileName, '.')) && !strncmp(q1, ".DIR;", 5)) {
132 if (*p1 == ']') {
133 path->insert(p1 - p0, '.');
134 path->insert(p1 - p0 + 1, fileName, q1 - fileName);
135 } else if (*p1 == ':') {
136 path->append('[');
137 path->append(']');
138 path->append(fileName, q1 - fileName);
139 } else {
140 path->clear();
141 path->append(fileName, q1 - fileName);
143 } else {
144 if (*p1 != ']' && *p1 != ':')
145 path->clear();
146 path->append(fileName);
148 return path;
150 #elif defined(WIN32)
151 //---------- Win32 ----------
152 GString *tmp;
153 char buf[256];
154 char *fp;
156 tmp = new GString(path);
157 tmp->append('/');
158 tmp->append(fileName);
159 GetFullPathName(tmp->getCString(), sizeof(buf), buf, &fp);
160 delete tmp;
161 path->clear();
162 path->append(buf);
163 return path;
164 #elif defined(__AROS__)
165 //---------- AROS ------------
166 int i;
167 bug("Path: %s Filename: %s\n",path->getCString(),fileName);
168 // appending "." does nothing
169 if (!strcmp(fileName, "."))
170 return path;
172 // appending ".." goes up one directory
173 if (!strcmp(fileName, "..")) {
174 for (i = path->getLength() - 2; i >= 0; --i) {
175 if ((path->getChar(i) == '/') || (path->getChar(i) == ':'))
176 break;
178 if (i <= 0) {
179 path->clear();
180 path->append("sys:");
182 else {
183 path->del(i, path->getLength() - i);
186 return path;
189 // otherwise, append "/" and new path component
190 if (path->getLength() > 0 &&
191 (path->getChar(path->getLength() - 1) != ':')||(path->getChar(path->getLength() - 1) != '/'))
192 path->append('/');
193 path->append(fileName);
195 return path;
197 #elif defined(ACORN)
198 //---------- RISCOS ----------
199 char *p;
200 int i;
202 path->append(".");
203 i = path->getLength();
204 path->append(fileName);
205 for (p = path->getCString() + i; *p; ++p) {
206 if (*p == '/') {
207 *p = '.';
208 } else if (*p == '.') {
209 *p = '/';
212 return path;
214 #elif defined(MACOS)
215 //---------- MacOS ----------
216 char *p;
217 int i;
219 path->append(":");
220 i = path->getLength();
221 path->append(fileName);
222 for (p = path->getCString() + i; *p; ++p) {
223 if (*p == '/') {
224 *p = ':';
225 } else if (*p == '.') {
226 *p = ':';
229 return path;
231 #elif defined(__EMX__)
232 //---------- OS/2+EMX ----------
233 int i;
235 // appending "." does nothing
236 if (!strcmp(fileName, "."))
237 return path;
239 // appending ".." goes up one directory
240 if (!strcmp(fileName, "..")) {
241 for (i = path->getLength() - 2; i >= 0; --i) {
242 if (path->getChar(i) == '/' || path->getChar(i) == '\\' ||
243 path->getChar(i) == ':')
244 break;
246 if (i <= 0) {
247 if (path->getChar(0) == '/' || path->getChar(0) == '\\') {
248 path->del(1, path->getLength() - 1);
249 } else if (path->getLength() >= 2 && path->getChar(1) == ':') {
250 path->del(2, path->getLength() - 2);
251 } else {
252 path->clear();
253 path->append("..");
255 } else {
256 if (path->getChar(i-1) == ':')
257 ++i;
258 path->del(i, path->getLength() - i);
260 return path;
263 // otherwise, append "/" and new path component
264 if (path->getLength() > 0 &&
265 path->getChar(path->getLength() - 1) != '/' &&
266 path->getChar(path->getLength() - 1) != '\\')
267 path->append('/');
268 path->append(fileName);
269 return path;
271 #else
272 //---------- Unix ----------
273 int i;
274 // appending "." does nothing
275 if (!strcmp(fileName, "."))
276 return path;
278 // appending ".." goes up one directory
279 if (!strcmp(fileName, "..")) {
280 for (i = path->getLength() - 2; i >= 0; --i) {
281 if (path->getChar(i) == '/')
282 break;
284 if (i <= 0) {
285 if (path->getChar(0) == '/') {
286 path->del(1, path->getLength() - 1);
287 } else {
288 path->clear();
289 path->append("..");
291 } else {
292 path->del(i, path->getLength() - i);
294 return path;
297 // otherwise, append "/" and new path component
298 if (path->getLength() > 0 &&
299 path->getChar(path->getLength() - 1) != '/')
300 path->append('/');
301 path->append(fileName);
303 return path;
304 #endif
307 GString *grabPath(char *fileName) {
308 #ifdef VMS
309 //---------- VMS ----------
310 char *p;
312 if ((p = strrchr(fileName, ']')))
313 return new GString(fileName, p + 1 - fileName);
314 if ((p = strrchr(fileName, ':')))
315 return new GString(fileName, p + 1 - fileName);
316 return new GString();
318 #elif defined(__EMX__) || defined(WIN32)
319 //---------- OS/2+EMX and Win32 ----------
320 char *p;
322 if ((p = strrchr(fileName, '/')))
323 return new GString(fileName, p - fileName);
324 if ((p = strrchr(fileName, '\\')))
325 return new GString(fileName, p - fileName);
326 if ((p = strrchr(fileName, ':')))
327 return new GString(fileName, p + 1 - fileName);
328 return new GString();
330 #elif defined(ACORN)
331 //---------- RISCOS ----------
332 char *p;
334 if ((p = strrchr(fileName, '.')))
335 return new GString(fileName, p - fileName);
336 return new GString();
338 #elif defined(MACOS)
339 //---------- MacOS ----------
340 char *p;
342 if ((p = strrchr(fileName, ':')))
343 return new GString(fileName, p - fileName);
344 return new GString();
345 #elif defined(__AROS__)
346 //---------- AROS ---------------
347 char *p;
348 if ((p = strrchr(fileName, '/')))
349 return new GString(fileName, p + 1 - fileName);
350 if ((p = strrchr(fileName, ':')))
351 return new GString(fileName, p + 1 - fileName);
352 return new GString();
354 #else
355 //---------- Unix ----------
356 char *p;
358 if ((p = strrchr(fileName, '/'))) {
360 return new GString(fileName, p - fileName);
362 return new GString();
363 #endif
366 GBool isAbsolutePath(char *path) {
367 #ifdef VMS
368 //---------- VMS ----------
369 return strchr(path, ':') ||
370 (path[0] == '[' && path[1] != '.' && path[1] != '-');
372 #elif defined(__EMX__) || defined(WIN32)
373 //---------- OS/2+EMX and Win32 ----------
374 return path[0] == '/' || path[0] == '\\' || path[1] == ':';
376 #elif defined(ACORN)
377 //---------- RISCOS ----------
378 return path[0] == '$';
380 #elif defined(MACOS)
381 //---------- MacOS ----------
382 return path[0] != ':';
383 #elif defined(__AROS__)
384 return strchr(path, ':')!=NULL;
385 #else
386 //---------- Unix ----------
387 return path[0] == '/';
388 #endif
391 GString *makePathAbsolute(GString *path) {
392 #ifdef VMS
393 //---------- VMS ----------
394 char buf[PATH_MAX+1];
396 if (!isAbsolutePath(path->getCString())) {
397 if (getcwd(buf, sizeof(buf))) {
398 path->insert(0, buf);
401 return path;
403 #elif defined(WIN32)
404 //---------- Win32 ----------
405 char buf[_MAX_PATH];
406 char *fp;
408 buf[0] = '\0';
409 if (!GetFullPathName(path->getCString(), _MAX_PATH, buf, &fp)) {
410 path->clear();
411 return path;
413 path->clear();
414 path->append(buf);
415 return path;
417 #elif defined(ACORN)
418 //---------- RISCOS ----------
419 path->insert(0, '@');
420 return path;
422 #elif defined(MACOS)
423 //---------- MacOS ----------
424 path->del(0, 1);
425 return path;
426 #elif defined(AROS)
427 //---------- AROS ----------
428 struct passwd *pw;
429 char buf[PATH_MAX+1];
430 GString *s;
431 char *p1, *p2;
432 int n;
434 if (path->getChar(0) == '~') {
435 if (path->getChar(1) == '/' ||
436 path->getLength() == 1) {
437 path->del(0, 1);
438 s = getHomeDir();
439 path->insert(0, s);
440 delete s;
441 } else {
442 p1 = path->getCString() + 1;
443 for (p2 = p1; *p2 && *p2 != '/'; ++p2) ;
444 if ((n = p2 - p1) > PATH_MAX)
445 n = PATH_MAX;
446 strncpy(buf, p1, n);
447 buf[n] = '\0';
448 if ((pw = getpwnam(buf))) {
449 path->del(0, p2 - p1 + 1);
450 path->insert(0, pw->pw_dir);
453 } else if (!isAbsolutePath(path->getCString())) {
454 if (getcwd(buf, sizeof(buf))) {
455 path->insert(0, '/');
456 path->insert(0, buf);
460 return path;
462 #else
463 //---------- Unix and OS/2+EMX ----------
464 struct passwd *pw;
465 char buf[PATH_MAX+1];
466 GString *s;
467 char *p1, *p2;
468 int n;
470 if (path->getChar(0) == '~') {
471 if (path->getChar(1) == '/' ||
472 #ifdef __EMX__
473 path->getChar(1) == '\\' ||
474 #endif
475 path->getLength() == 1) {
476 path->del(0, 1);
477 s = getHomeDir();
478 path->insert(0, s);
479 delete s;
480 } else {
481 p1 = path->getCString() + 1;
482 #ifdef __EMX__
483 for (p2 = p1; *p2 && *p2 != '/' && *p2 != '\\'; ++p2) ;
484 #else
485 for (p2 = p1; *p2 && *p2 != '/'; ++p2) ;
486 #endif
487 if ((n = p2 - p1) > PATH_MAX)
488 n = PATH_MAX;
489 strncpy(buf, p1, n);
490 buf[n] = '\0';
491 if ((pw = getpwnam(buf))) {
492 path->del(0, p2 - p1 + 1);
493 path->insert(0, pw->pw_dir);
496 } else if (!isAbsolutePath(path->getCString())) {
497 if (getcwd(buf, sizeof(buf))) {
498 #ifndef __EMX__
499 path->insert(0, '/');
500 #endif
501 path->insert(0, buf);
505 return path;
506 #endif
509 time_t getModTime(char *fileName) {
510 #ifdef WIN32
511 //~ should implement this, but it's (currently) only used in xpdf
512 return 0;
513 #else
514 struct stat statBuf;
516 if (stat(fileName, &statBuf)) {
517 return 0;
519 return statBuf.st_mtime;
520 #endif
523 GBool openTempFile(GString **name, FILE **f, char *mode, char *ext) {
524 #if defined(WIN32)
525 //---------- Win32 ----------
526 char *tempDir;
527 GString *s, *s2;
528 char buf[32];
529 FILE *f2;
530 int t, i;
532 // this has the standard race condition problem, but I haven't found
533 // a better way to generate temp file names with extensions on
534 // Windows
535 if ((tempDir = getenv("TEMP"))) {
536 s = new GString(tempDir);
537 s->append('\\');
538 } else {
539 s = new GString();
541 s->append("x");
542 t = (int)time(NULL);
543 for (i = 0; i < 1000; ++i) {
544 sprintf(buf, "%d", t + i);
545 s2 = s->copy()->append(buf);
546 if (ext) {
547 s2->append(ext);
549 if (!(f2 = fopen(s2->getCString(), "r"))) {
550 if (!(f2 = fopen(s2->getCString(), mode))) {
551 delete s2;
552 delete s;
553 return gFalse;
555 *name = s2;
556 *f = f2;
557 delete s;
558 return gTrue;
560 fclose(f2);
561 delete s2;
563 delete s;
564 return gFalse;
565 #elif defined(VMS) || defined(__EMX__) || defined(ACORN) || defined(MACOS)
566 //---------- non-Unix ----------
567 char *s;
569 // There is a security hole here: an attacker can create a symlink
570 // with this file name after the tmpnam call and before the fopen
571 // call. I will happily accept fixes to this function for non-Unix
572 // OSs.
573 if (!(s = tmpnam(NULL))) {
574 return gFalse;
576 *name = new GString(s);
577 if (ext) {
578 (*name)->append(ext);
581 if (!(*f = fopen((*name)->getCString(), mode))) {
582 delete (*name);
583 return gFalse;
585 return gTrue;
586 #else
587 //---------- Unix ----------
588 char *s;
589 int fd;
591 if (ext) {
592 #if HAVE_MKSTEMPS
593 if ((s = getenv("TMPDIR"))) {
594 *name = new GString(s);
595 } else {
596 *name = new GString("T:");
598 (*name)->append("XXXXXX")->append(ext);
599 fd = mkstemps((*name)->getCString(), strlen(ext));
600 #else
601 if (!(s = tmpnam(NULL))) {
602 return gFalse;
604 *name = new GString(s);
605 (*name)->append(ext);
606 fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600);
607 #endif
608 } else {
609 #if HAVE_MKSTEMP
610 if ((s = getenv("TMPDIR"))) {
611 *name = new GString(s);
612 } else {
613 *name = new GString("T:");
615 (*name)->append("XXXXXX");
616 fd = mkstemp((*name)->getCString());
617 #else // HAVE_MKSTEMP
618 if (!(s = tmpnam(NULL))) {
619 return gFalse;
621 *name = new GString(s);
622 fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600);
623 #endif // HAVE_MKSTEMP
625 if (fd < 0 || !(*f = fdopen(fd, mode))) {
626 delete *name;
627 return gFalse;
630 return gTrue;
631 #endif
634 GBool executeCommand(char *cmd) {
635 #ifdef VMS
636 return system(cmd) ? gTrue : gFalse;
637 #else
638 return system(cmd) ? gFalse : gTrue;
639 #endif
642 char *getLine(char *buf, int size, FILE *f) {
643 int c, i;
645 i = 0;
646 while (i < size - 1) {
647 if ((c = fgetc(f)) == EOF) {
648 break;
650 buf[i++] = (char)c;
651 if (c == '\x0a') {
652 break;
654 if (c == '\x0d') {
655 c = fgetc(f);
656 if (c == '\x0a' && i < size - 1) {
657 buf[i++] = (char)c;
658 } else if (c != EOF) {
659 ungetc(c, f);
661 break;
664 buf[i] = '\0';
665 if (i == 0) {
666 return NULL;
668 return buf;
671 //------------------------------------------------------------------------
672 // GDir and GDirEntry
673 //------------------------------------------------------------------------
675 GDirEntry::GDirEntry(char *dirPath, char *nameA, GBool doStat) {
676 #ifdef VMS
677 char *p;
678 #elif defined(WIN32)
679 int fa;
680 GString *s;
681 #elif defined(ACORN)
682 #else
683 struct stat st;
684 GString *s;
685 #endif
687 name = new GString(nameA);
688 dir = gFalse;
689 if (doStat) {
690 #ifdef VMS
691 if (!strcmp(nameA, "-") ||
692 ((p = strrchr(nameA, '.')) && !strncmp(p, ".DIR;", 5)))
693 dir = gTrue;
694 #elif defined(ACORN)
695 #else
696 s = new GString(dirPath);
697 appendToPath(s, nameA);
698 #ifdef WIN32
699 fa = GetFileAttributes(s->getCString());
700 dir = (fa != 0xFFFFFFFF && (fa & FILE_ATTRIBUTE_DIRECTORY));
701 #else
702 if (stat(s->getCString(), &st) == 0)
703 dir = S_ISDIR(st.st_mode);
704 #endif
705 delete s;
706 #endif
710 GDirEntry::~GDirEntry() {
711 delete name;
714 GDir::GDir(char *name, GBool doStatA) {
715 path = new GString(name);
716 doStat = doStatA;
717 #if defined(WIN32)
718 GString *tmp;
720 tmp = path->copy();
721 tmp->append("/*.*");
722 hnd = FindFirstFile(tmp->getCString(), &ffd);
723 delete tmp;
724 #elif defined(ACORN)
725 #elif defined(MACOS)
726 #else
727 dir = opendir(name);
728 #ifdef VMS
729 needParent = strchr(name, '[') != NULL;
730 #endif
731 #endif
734 GDir::~GDir() {
735 delete path;
736 #if defined(WIN32)
737 if (hnd) {
738 FindClose(hnd);
739 hnd = NULL;
741 #elif defined(ACORN)
742 #elif defined(MACOS)
743 #else
744 if (dir)
745 closedir(dir);
746 #endif
749 GDirEntry *GDir::getNextEntry() {
750 GDirEntry *e;
752 #if defined(WIN32)
753 if (hnd) {
754 e = new GDirEntry(path->getCString(), ffd.cFileName, doStat);
755 if (hnd && !FindNextFile(hnd, &ffd)) {
756 FindClose(hnd);
757 hnd = NULL;
759 } else {
760 e = NULL;
762 #elif defined(ACORN)
763 #elif defined(MACOS)
764 #elif defined(VMS)
765 struct dirent *ent;
766 e = NULL;
767 if (dir) {
768 if (needParent) {
769 e = new GDirEntry(path->getCString(), "-", doStat);
770 needParent = gFalse;
771 return e;
773 ent = readdir(dir);
774 if (ent) {
775 e = new GDirEntry(path->getCString(), ent->d_name, doStat);
778 #else
779 struct dirent *ent;
780 e = NULL;
781 if (dir) {
782 ent = (struct dirent *)readdir(dir);
783 if (ent && !strcmp(ent->d_name, ".")) {
784 ent = (struct dirent *)readdir(dir);
786 if (ent) {
787 e = new GDirEntry(path->getCString(), ent->d_name, doStat);
790 #endif
792 return e;
795 void GDir::rewind() {
796 #ifdef WIN32
797 GString *tmp;
799 if (hnd)
800 FindClose(hnd);
801 tmp = path->copy();
802 tmp->append("/*.*");
803 hnd = FindFirstFile(tmp->getCString(), &ffd);
804 delete tmp;
805 #elif defined(ACORN)
806 #elif defined(MACOS)
807 #else
808 if (dir)
809 rewinddir(dir);
810 #ifdef VMS
811 needParent = strchr(path->getCString(), '[') != NULL;
812 #endif
813 #endif