filters

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