1 //========================================================================
5 // Miscellaneous file and directory name manipulation.
7 // Copyright 1996-2003 Glyph & Cog, LLC
9 //========================================================================
15 # include <sys/stat.h>
16 # elif !defined(ACORN)
17 # include <sys/types.h>
18 # include <sys/stat.h>
23 # if !defined(VMS) && !defined(ACORN) && !defined(MACOS)
26 # if defined(VMS) && (__DECCXX_VER < 50200000)
33 // Some systems don't define this, so just make it something reasonably
39 //------------------------------------------------------------------------
41 GString *getHomeDir() {
43 //---------- VMS ----------
44 return new GString("SYS$LOGIN:");
46 #elif defined(__EMX__) || defined(WIN32)
47 //---------- OS/2+EMX and Win32 ----------
51 if ((s = getenv("HOME")))
54 ret = new GString(".");
58 //---------- RISCOS ----------
59 return new GString("@");
62 //---------- MacOS ----------
63 return new GString(":");
66 //---------- Unix ----------
71 if ((s = getenv("HOME"))) {
74 if ((s = getenv("USER")))
77 pw = getpwuid(getuid());
79 ret = new GString(pw->pw_dir);
81 ret = new GString(".");
87 GString *getCurrentDir() {
91 if (_getcwd2(buf, sizeof(buf)))
93 if (GetCurrentDirectory(sizeof(buf), buf))
99 if (getcwd(buf, sizeof(buf)))
101 return new GString(buf);
102 return new GString();
105 GString *appendToPath(GString *path, char *fileName) {
107 //---------- VMS ----------
108 //~ this should handle everything necessary for file
109 //~ requesters, but it's certainly not complete
113 p0 = path->getCString();
114 p1 = p0 + path->getLength() - 1;
115 if (!strcmp(fileName, "-")) {
117 for (p2 = p1; p2 > p0 && *p2 != '.' && *p2 != '['; --p2) ;
120 path->del(p2 - p0, p1 - p2);
121 } else if (*p1 == ':') {
127 } else if ((q1 = strrchr(fileName, '.')) && !strncmp(q1, ".DIR;", 5)) {
129 path->insert(p1 - p0, '.');
130 path->insert(p1 - p0 + 1, fileName, q1 - fileName);
131 } else if (*p1 == ':') {
134 path->append(fileName, q1 - fileName);
137 path->append(fileName, q1 - fileName);
140 if (*p1 != ']' && *p1 != ':')
142 path->append(fileName);
147 //---------- Win32 ----------
152 tmp = new GString(path);
154 tmp->append(fileName);
155 GetFullPathName(tmp->getCString(), sizeof(buf), buf, &fp);
162 //---------- RISCOS ----------
167 i = path->getLength();
168 path->append(fileName);
169 for (p = path->getCString() + i; *p; ++p) {
172 } else if (*p == '.') {
179 //---------- MacOS ----------
184 i = path->getLength();
185 path->append(fileName);
186 for (p = path->getCString() + i; *p; ++p) {
189 } else if (*p == '.') {
195 #elif defined(__EMX__)
196 //---------- OS/2+EMX ----------
199 // appending "." does nothing
200 if (!strcmp(fileName, "."))
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) == ':')
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);
220 if (path->getChar(i-1) == ':')
222 path->del(i, path->getLength() - i);
227 // otherwise, append "/" and new path component
228 if (path->getLength() > 0 &&
229 path->getChar(path->getLength() - 1) != '/' &&
230 path->getChar(path->getLength() - 1) != '\\')
232 path->append(fileName);
236 //---------- Unix ----------
239 // appending "." does nothing
240 if (!strcmp(fileName, "."))
243 // appending ".." goes up one directory
244 if (!strcmp(fileName, "..")) {
245 for (i = path->getLength() - 2; i >= 0; --i) {
246 if (path->getChar(i) == '/')
250 if (path->getChar(0) == '/') {
251 path->del(1, path->getLength() - 1);
257 path->del(i, path->getLength() - i);
262 // otherwise, append "/" and new path component
263 if (path->getLength() > 0 &&
264 path->getChar(path->getLength() - 1) != '/')
266 path->append(fileName);
271 GString *grabPath(char *fileName) {
273 //---------- VMS ----------
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();
282 #elif defined(__EMX__) || defined(WIN32)
283 //---------- OS/2+EMX and Win32 ----------
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();
295 //---------- RISCOS ----------
298 if ((p = strrchr(fileName, '.')))
299 return new GString(fileName, p - fileName);
300 return new GString();
303 //---------- MacOS ----------
306 if ((p = strrchr(fileName, ':')))
307 return new GString(fileName, p - fileName);
308 return new GString();
311 //---------- Unix ----------
314 if ((p = strrchr(fileName, '/')))
315 return new GString(fileName, p - fileName);
316 return new GString();
320 GBool isAbsolutePath(char *path) {
322 //---------- VMS ----------
323 return strchr(path, ':') ||
324 (path[0] == '[' && path[1] != '.' && path[1] != '-');
326 #elif defined(__EMX__) || defined(WIN32)
327 //---------- OS/2+EMX and Win32 ----------
328 return path[0] == '/' || path[0] == '\\' || path[1] == ':';
331 //---------- RISCOS ----------
332 return path[0] == '$';
335 //---------- MacOS ----------
336 return path[0] != ':';
339 //---------- Unix ----------
340 return path[0] == '/';
344 GString *makePathAbsolute(GString *path) {
346 //---------- VMS ----------
347 char buf[PATH_MAX+1];
349 if (!isAbsolutePath(path->getCString())) {
350 if (getcwd(buf, sizeof(buf))) {
351 path->insert(0, buf);
357 //---------- Win32 ----------
362 if (!GetFullPathName(path->getCString(), _MAX_PATH, buf, &fp)) {
371 //---------- RISCOS ----------
372 path->insert(0, '@');
376 //---------- MacOS ----------
381 //---------- Unix and OS/2+EMX ----------
383 char buf[PATH_MAX+1];
388 if (path->getChar(0) == '~') {
389 if (path->getChar(1) == '/' ||
391 path->getChar(1) == '\\' ||
393 path->getLength() == 1) {
399 p1 = path->getCString() + 1;
401 for (p2 = p1; *p2 && *p2 != '/' && *p2 != '\\'; ++p2) ;
403 for (p2 = p1; *p2 && *p2 != '/'; ++p2) ;
405 if ((n = p2 - p1) > PATH_MAX)
409 if ((pw = getpwnam(buf))) {
410 path->del(0, p2 - p1 + 1);
411 path->insert(0, pw->pw_dir);
414 } else if (!isAbsolutePath(path->getCString())) {
415 if (getcwd(buf, sizeof(buf))) {
417 path->insert(0, '/');
419 path->insert(0, buf);
426 time_t getModTime(char *fileName) {
428 //~ should implement this, but it's (currently) only used in xpdf
433 if (stat(fileName, &statBuf)) {
436 return statBuf.st_mtime;
440 GBool openTempFile(GString **name, FILE **f, char *mode, char *ext) {
442 //---------- Win32 ----------
445 if (!(s = _tempnam(getenv("TEMP"), NULL))) {
448 *name = new GString(s);
451 (*name)->append(ext);
453 if (!(*f = fopen((*name)->getCString(), mode))) {
458 #elif defined(VMS) || defined(__EMX__) || defined(ACORN) || defined(MACOS)
459 //---------- non-Unix ----------
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
466 if (!(s = tmpnam(NULL))) {
469 *name = new GString(s);
471 (*name)->append(ext);
473 if (!(*f = fopen((*name)->getCString(), mode))) {
479 //---------- Unix ----------
485 if ((s = getenv("TMPDIR"))) {
486 *name = new GString(s);
488 *name = new GString("/tmp");
490 (*name)->append("/XXXXXX")->append(ext);
491 fd = mkstemps((*name)->getCString(), strlen(ext));
493 if (!(s = tmpnam(NULL))) {
496 *name = new GString(s);
497 (*name)->append(ext);
498 fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600);
502 if ((s = getenv("TMPDIR"))) {
503 *name = new GString(s);
505 *name = new GString("/tmp");
507 (*name)->append("/XXXXXX");
508 fd = mkstemp((*name)->getCString());
509 #else // HAVE_MKSTEMP
510 if (!(s = tmpnam(NULL))) {
513 *name = new GString(s);
514 fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600);
515 #endif // HAVE_MKSTEMP
517 if (fd < 0 || !(*f = fdopen(fd, mode))) {
525 GBool executeCommand(char *cmd) {
527 return system(cmd) ? gTrue : gFalse;
529 return system(cmd) ? gFalse : gTrue;
533 char *getLine(char *buf, int size, FILE *f) {
537 while (i < size - 1) {
538 if ((c = fgetc(f)) == EOF) {
547 if (c == '\x0a' && i < size - 1) {
549 } else if (c != EOF) {
562 //------------------------------------------------------------------------
563 // GDir and GDirEntry
564 //------------------------------------------------------------------------
566 GDirEntry::GDirEntry(char *dirPath, char *nameA, GBool doStat) {
578 name = new GString(nameA);
582 if (!strcmp(nameA, "-") ||
583 ((p = strrchr(nameA, '.')) && !strncmp(p, ".DIR;", 5)))
587 s = new GString(dirPath);
588 appendToPath(s, nameA);
590 fa = GetFileAttributes(s->getCString());
591 dir = (fa != 0xFFFFFFFF && (fa & FILE_ATTRIBUTE_DIRECTORY));
593 if (stat(s->getCString(), &st) == 0)
594 dir = S_ISDIR(st.st_mode);
601 GDirEntry::~GDirEntry() {
605 GDir::GDir(char *name, GBool doStatA) {
606 path = new GString(name);
613 hnd = FindFirstFile(tmp->getCString(), &ffd);
620 needParent = strchr(name, '[') != NULL;
640 GDirEntry *GDir::getNextEntry() {
645 e = new GDirEntry(path->getCString(), ffd.cFileName, doStat);
646 if (hnd && !FindNextFile(hnd, &ffd)) {
660 e = new GDirEntry(path->getCString(), "-", doStat);
666 e = new GDirEntry(path->getCString(), ent->d_name, doStat);
674 if (ent && !strcmp(ent->d_name, ".")) {
678 e = new GDirEntry(path->getCString(), ent->d_name, doStat);
686 void GDir::rewind() {
694 hnd = FindFirstFile(tmp->getCString(), &ffd);
702 needParent = strchr(path->getCString(), '[') != NULL;