1 //========================================================================
5 // Miscellaneous file and directory name manipulation.
7 // Copyright 1996-2002 Glyph & Cog, LLC
9 //========================================================================
12 #include "../../config.h"
17 //# include <kpathsea/win32lib.h>
22 # include <sys/stat.h>
23 # elif !defined(ACORN)
24 # include <sys/types.h>
25 # include <sys/stat.h>
30 # if !defined(VMS) && !defined(ACORN) && !defined(MACOS)
33 # if defined(VMS) && (__DECCXX_VER < 50200000)
40 // Some systems don't define this, so just make it something reasonably
46 //------------------------------------------------------------------------
48 GString
*getHomeDir() {
50 //---------- VMS ----------
51 return new GString("SYS$LOGIN:");
53 #elif defined(__EMX__) || defined(WIN32)
54 //---------- OS/2+EMX and Win32 ----------
58 if ((s
= getenv("HOME")))
61 ret
= new GString(".");
65 //---------- RISCOS ----------
66 return new GString("@");
69 //---------- MacOS ----------
70 return new GString(":");
73 //---------- Unix ----------
78 if ((s
= getenv("HOME"))) {
81 if ((s
= getenv("USER")))
84 pw
= getpwuid(getuid());
86 ret
= new GString(pw
->pw_dir
);
88 ret
= new GString(".");
94 GString
*getCurrentDir() {
98 if (_getcwd2(buf
, sizeof(buf
)))
100 if (GetCurrentDirectory(sizeof(buf
), buf
))
102 if (strcpy(buf
, "@"))
104 if (strcpy(buf
, ":"))
106 if (getcwd(buf
, sizeof(buf
)))
108 return new GString(buf
);
109 return new GString();
112 GString
*appendToPath(GString
*path
, char *fileName
) {
114 //---------- VMS ----------
115 //~ this should handle everything necessary for file
116 //~ requesters, but it's certainly not complete
120 p0
= path
->getCString();
121 p1
= p0
+ path
->getLength() - 1;
122 if (!strcmp(fileName
, "-")) {
124 for (p2
= p1
; p2
> p0
&& *p2
!= '.' && *p2
!= '['; --p2
) ;
127 path
->del(p2
- p0
, p1
- p2
);
128 } else if (*p1
== ':') {
134 } else if ((q1
= strrchr(fileName
, '.')) && !strncmp(q1
, ".DIR;", 5)) {
136 path
->insert(p1
- p0
, '.');
137 path
->insert(p1
- p0
+ 1, fileName
, q1
- fileName
);
138 } else if (*p1
== ':') {
141 path
->append(fileName
, q1
- fileName
);
144 path
->append(fileName
, q1
- fileName
);
147 if (*p1
!= ']' && *p1
!= ':')
149 path
->append(fileName
);
154 //---------- Win32 ----------
159 tmp
= new GString(path
);
161 tmp
->append(fileName
);
162 GetFullPathName(tmp
->getCString(), sizeof(buf
), buf
, &fp
);
169 //---------- RISCOS ----------
174 i
= path
->getLength();
175 path
->append(fileName
);
176 for (p
= path
->getCString() + i
; *p
; ++p
) {
179 } else if (*p
== '.') {
186 //---------- MacOS ----------
191 i
= path
->getLength();
192 path
->append(fileName
);
193 for (p
= path
->getCString() + i
; *p
; ++p
) {
196 } else if (*p
== '.') {
202 #elif defined(__EMX__)
203 //---------- OS/2+EMX ----------
206 // appending "." does nothing
207 if (!strcmp(fileName
, "."))
210 // appending ".." goes up one directory
211 if (!strcmp(fileName
, "..")) {
212 for (i
= path
->getLength() - 2; i
>= 0; --i
) {
213 if (path
->getChar(i
) == '/' || path
->getChar(i
) == '\\' ||
214 path
->getChar(i
) == ':')
218 if (path
->getChar(0) == '/' || path
->getChar(0) == '\\') {
219 path
->del(1, path
->getLength() - 1);
220 } else if (path
->getLength() >= 2 && path
->getChar(1) == ':') {
221 path
->del(2, path
->getLength() - 2);
227 if (path
->getChar(i
-1) == ':')
229 path
->del(i
, path
->getLength() - i
);
234 // otherwise, append "/" and new path component
235 if (path
->getLength() > 0 &&
236 path
->getChar(path
->getLength() - 1) != '/' &&
237 path
->getChar(path
->getLength() - 1) != '\\')
239 path
->append(fileName
);
243 //---------- Unix ----------
246 // appending "." does nothing
247 if (!strcmp(fileName
, "."))
250 // appending ".." goes up one directory
251 if (!strcmp(fileName
, "..")) {
252 for (i
= path
->getLength() - 2; i
>= 0; --i
) {
253 if (path
->getChar(i
) == '/')
257 if (path
->getChar(0) == '/') {
258 path
->del(1, path
->getLength() - 1);
264 path
->del(i
, path
->getLength() - i
);
269 // otherwise, append "/" and new path component
270 if (path
->getLength() > 0 &&
271 path
->getChar(path
->getLength() - 1) != '/')
273 path
->append(fileName
);
278 GString
*grabPath(char *fileName
) {
280 //---------- VMS ----------
283 if ((p
= strrchr(fileName
, ']')))
284 return new GString(fileName
, p
+ 1 - fileName
);
285 if ((p
= strrchr(fileName
, ':')))
286 return new GString(fileName
, p
+ 1 - fileName
);
287 return new GString();
289 #elif defined(__EMX__) || defined(WIN32)
290 //---------- OS/2+EMX and Win32 ----------
293 if ((p
= strrchr(fileName
, '/')))
294 return new GString(fileName
, p
- fileName
);
295 if ((p
= strrchr(fileName
, '\\')))
296 return new GString(fileName
, p
- fileName
);
297 if ((p
= strrchr(fileName
, ':')))
298 return new GString(fileName
, p
+ 1 - fileName
);
299 return new GString();
302 //---------- RISCOS ----------
305 if ((p
= strrchr(fileName
, '.')))
306 return new GString(fileName
, p
- fileName
);
307 return new GString();
310 //---------- MacOS ----------
313 if ((p
= strrchr(fileName
, ':')))
314 return new GString(fileName
, p
- fileName
);
315 return new GString();
318 //---------- Unix ----------
321 if ((p
= strrchr(fileName
, '/')))
322 return new GString(fileName
, p
- fileName
);
323 return new GString();
327 GBool
isAbsolutePath(char *path
) {
329 //---------- VMS ----------
330 return strchr(path
, ':') ||
331 (path
[0] == '[' && path
[1] != '.' && path
[1] != '-');
333 #elif defined(__EMX__) || defined(WIN32)
334 //---------- OS/2+EMX and Win32 ----------
335 return path
[0] == '/' || path
[0] == '\\' || path
[1] == ':';
338 //---------- RISCOS ----------
339 return path
[0] == '$';
342 //---------- MacOS ----------
343 return path
[0] != ':';
346 //---------- Unix ----------
347 return path
[0] == '/';
351 GString
*makePathAbsolute(GString
*path
) {
353 //---------- VMS ----------
354 char buf
[PATH_MAX
+1];
356 if (!isAbsolutePath(path
->getCString())) {
357 if (getcwd(buf
, sizeof(buf
))) {
358 path
->insert(0, buf
);
364 //---------- Win32 ----------
369 if (!GetFullPathName(path
->getCString(), _MAX_PATH
, buf
, &fp
)) {
378 //---------- RISCOS ----------
379 path
->insert(0, '@');
383 //---------- MacOS ----------
388 //---------- Unix and OS/2+EMX ----------
390 char buf
[PATH_MAX
+1];
395 if (path
->getChar(0) == '~') {
396 if (path
->getChar(1) == '/' ||
398 path
->getChar(1) == '\\' ||
400 path
->getLength() == 1) {
406 p1
= path
->getCString() + 1;
408 for (p2
= p1
; *p2
&& *p2
!= '/' && *p2
!= '\\'; ++p2
) ;
410 for (p2
= p1
; *p2
&& *p2
!= '/'; ++p2
) ;
412 if ((n
= p2
- p1
) > PATH_MAX
)
416 if ((pw
= getpwnam(buf
))) {
417 path
->del(0, p2
- p1
+ 1);
418 path
->insert(0, pw
->pw_dir
);
421 } else if (!isAbsolutePath(path
->getCString())) {
422 if (getcwd(buf
, sizeof(buf
))) {
424 path
->insert(0, '/');
426 path
->insert(0, buf
);
433 time_t getModTime(char *fileName
) {
435 //~ should implement this, but it's (currently) only used in xpdf
440 if (stat(fileName
, &statBuf
)) {
443 return statBuf
.st_mtime
;
447 static char* getTempDir()
450 char*dir
= getenv("TMP");
451 if(!dir
) dir
= getenv("TEMP");
452 if(!dir
) dir
= getenv("tmp");
453 if(!dir
) dir
= getenv("temp");
454 if(!dir
) dir
= "C:\\";
461 char* mktmpname(char*ptr
) {
462 static char tmpbuf
[128];
463 char*dir
= getTempDir();
468 if(l
&& dir
[l
-1]!='/' && dir
[l
-1]!='\\') {
476 // used to be mktemp. This does remove the warnings, but
477 // It's not exactly an improvement.
479 sprintf(ptr
, "%s%s%08x%08x",dir
,sep
,lrand48(),lrand48());
482 sprintf(ptr
, "%s%s%08x%08x",dir
,sep
,rand(),rand());
484 static int count
= 1;
485 sprintf(ptr
, "%s%s%08x%04x%04x",dir
,sep
,time(0),(unsigned int)tmpbuf
^((unsigned int)tmpbuf
)>>16,count
);
492 GBool
openTempFile(GString
**name
, FILE **f
, char *mode
, char *ext
) {
494 //---------- Win32 ----------
499 if (!(s
= _tempnam(getenv("TEMP"), NULL
))) {
502 *name
= new GString(s
);
505 (*name
)->append(ext
);
507 if (!(*f
= fopen((*name
)->getCString(), mode
))) {
512 #elif defined(VMS) || defined(__EMX__) || defined(ACORN) || defined(MACOS)
513 //---------- non-Unix ----------
516 // There is a security hole here: an attacker can create a symlink
517 // with this file name after the tmpnam call and before the fopen
518 // call. I will happily accept fixes to this function for non-Unix
520 if (!(s
= mktmpname(NULL
))) { //was: tmpnam
523 *name
= new GString(s
);
525 (*name
)->append(ext
);
527 if (!(*f
= fopen((*name
)->getCString(), mode
))) {
533 //---------- Unix ----------
539 if ((s
= getenv("TMPDIR"))) {
540 *name
= new GString(s
);
542 *name
= new GString("/tmp");
544 (*name
)->append("/XXXXXX")->append(ext
);
545 fd
= mkstemps((*name
)->getCString(), strlen(ext
));
547 if (!(s
= mktmpname(NULL
))) { //was: tmpnam
550 *name
= new GString(s
);
551 (*name
)->append(ext
);
552 fd
= open((*name
)->getCString(), O_WRONLY
| O_CREAT
| O_EXCL
, 0600);
556 if ((s
= getenv("TMPDIR"))) {
557 *name
= new GString(s
);
559 *name
= new GString("/tmp");
561 (*name
)->append("/XXXXXX");
562 fd
= mkstemp((*name
)->getCString());
563 #else // HAVE_MKSTEMP
564 if (!(s
= mktmpname(NULL
))) { //was: tmpnam
567 *name
= new GString(s
);
568 fd
= open((*name
)->getCString(), O_WRONLY
| O_CREAT
| O_EXCL
, 0600);
569 #endif // HAVE_MKSTEMP
571 if (fd
< 0 || !(*f
= fdopen(fd
, mode
))) {
579 GBool
executeCommand(char *cmd
) {
581 return system(cmd
) ? gTrue
: gFalse
;
583 return system(cmd
) ? gFalse
: gTrue
;
587 char *getLine(char *buf
, int size
, FILE *f
) {
591 while (i
< size
- 1) {
592 if ((c
= fgetc(f
)) == EOF
) {
601 if (c
== '\x0a' && i
< size
- 1) {
603 } else if (c
!= EOF
) {
616 //------------------------------------------------------------------------
617 // GDir and GDirEntry
618 //------------------------------------------------------------------------
620 GDirEntry::GDirEntry(char *dirPath
, char *nameA
, GBool doStat
) {
632 name
= new GString(nameA
);
636 if (!strcmp(nameA
, "-") ||
637 ((p
= strrchr(nameA
, '.')) && !strncmp(p
, ".DIR;", 5)))
641 s
= new GString(dirPath
);
642 appendToPath(s
, nameA
);
644 fa
= GetFileAttributes(s
->getCString());
645 dir
= (fa
!= 0xFFFFFFFF && (fa
& FILE_ATTRIBUTE_DIRECTORY
));
647 if (stat(s
->getCString(), &st
) == 0)
648 dir
= S_ISDIR(st
.st_mode
);
655 GDirEntry::~GDirEntry() {
659 GDir::GDir(char *name
, GBool doStatA
) {
660 path
= new GString(name
);
667 hnd
= FindFirstFile(tmp
->getCString(), &ffd
);
674 needParent
= strchr(name
, '[') != NULL
;
694 GDirEntry
*GDir::getNextEntry() {
700 e
= new GDirEntry(path
->getCString(), ffd
.cFileName
, doStat
);
701 if (hnd
&& !FindNextFile(hnd
, &ffd
)) {
711 e
= new GDirEntry(path
->getCString(), "-", doStat
);
718 if (ent
&& !strcmp(ent
->d_name
, "."))
722 e
= new GDirEntry(path
->getCString(), ent
->d_name
, doStat
);
728 void GDir::rewind() {
736 hnd
= FindFirstFile(tmp
->getCString(), &ffd
);
743 needParent
= strchr(path
->getCString(), '[') != NULL
;