Updated Polish translation
[anjuta-extras.git] / plugins / scintilla / FilePath.cxx
blobcb9ebaed59dbebd57d90322f1777fc5754fc5857
1 // SciTE - Scintilla based Text Editor
2 /** @file FilePath.cxx
3 ** Encapsulate a file path.
4 **/
5 // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <stdio.h>
12 #include <fcntl.h>
13 #include <sys/stat.h>
14 #include <time.h>
16 #include <unistd.h>
17 #include <gtk/gtk.h>
19 #include <dirent.h>
20 #include <errno.h>
22 #include "SString.h"
23 #include "PropSet.h"
24 #include "FilePath.h"
26 #ifdef __unix__
27 const char pathSepString[] = "/";
28 const char pathSepChar = '/';
29 const char listSepString[] = ":";
30 const char configFileVisibilityString[] = ".";
31 const char fileRead[] = "rb";
32 const char fileWrite[] = "wb";
33 #endif
34 #ifdef __vms
35 const char pathSepString[] = "/";
36 const char pathSepChar = '/';
37 const char listSepString[] = ":";
38 const char configFileVisibilityString[] = "";
39 const char fileRead[] = "r";
40 const char fileWrite[] = "w";
41 #endif
42 #ifdef WIN32
43 // Windows
44 const char pathSepString[] = "\\";
45 const char pathSepChar = '\\';
46 const char listSepString[] = ";";
47 const char configFileVisibilityString[] = "";
48 const char fileRead[] = "rb";
49 const char fileWrite[] = "wb";
50 #endif
52 FilePath::FilePath(const char *fileName_) : fileName(fileName_) {}
54 FilePath::FilePath(FilePath const &directory, FilePath const &name) {
55 Set(directory, name);
58 void FilePath::Set(const char *fileName_) {
59 fileName = fileName_;
62 const char *FilePath::AsFileSystem() const {
63 return AsInternal();
66 void FilePath::Set(FilePath const &other) {
67 fileName = other.fileName;
70 void FilePath::Set(FilePath const &directory, FilePath const &name) {
71 if (name.IsAbsolute()) {
72 fileName = name.fileName;
73 } else {
74 fileName = directory.fileName;
75 fileName.appendwithseparator(name.fileName.c_str(),
76 fileName.endswith(pathSepString) ? '\0' : pathSepChar);
80 void FilePath::SetDirectory(FilePath directory) {
81 FilePath curName(*this);
82 Set(directory, curName);
85 void FilePath::Init() {
86 fileName = "";
89 bool FilePath::SameNameAs(const char *other) const {
90 #ifdef WIN32
91 return EqualCaseInsensitive(fileName.c_str(), other);
92 #else
93 return fileName == other;
94 #endif
97 bool FilePath::SameNameAs(const FilePath &other) const {
98 return SameNameAs(other.fileName.c_str());
101 bool FilePath::IsSet() const {
102 return fileName.length() > 0;
105 bool FilePath::IsUntitled() const {
106 const char *dirEnd = strrchr(AsInternal(), pathSepChar);
107 return !dirEnd || !dirEnd[1];
110 bool FilePath::IsAbsolute() const {
111 if (fileName.length() == 0)
112 return false;
113 #ifdef __unix__
114 if (fileName[0] == '/')
115 return true;
116 #endif
117 #ifdef __vms
118 if (fileName[0] == '/')
119 return true;
120 #endif
121 #ifdef WIN32
122 if (fileName[0] == pathSepChar || fileName[1] == ':') // UNC path or drive separator
123 return true;
124 #endif
126 return false;
129 bool FilePath::IsRoot() const {
130 #ifdef WIN32
131 if ((fileName[0] == pathSepChar) && (fileName[1] == pathSepChar) && (fileName.search(pathSepString, 2) < 0))
132 return true; // UNC path like \\server
133 return (fileName.length() == 3) && (fileName[1] == ':') && (fileName[2] == pathSepChar);
134 #else
135 return fileName == "/";
136 #endif
139 int FilePath::RootLength() {
140 #ifdef WIN32
141 return 3;
142 #else
143 return 1;
144 #endif
147 const char *FilePath::AsInternal() const {
148 return fileName.c_str();
151 FilePath FilePath::Name() const {
152 const char *dirEnd = strrchr(fileName.c_str(), pathSepChar);
153 if (dirEnd)
154 return dirEnd + 1;
155 else
156 return fileName.c_str();
159 FilePath FilePath::BaseName() const {
160 const char *dirEnd = strrchr(fileName.c_str(), pathSepChar);
161 const char *extStart = strrchr(fileName.c_str(), '.');
162 if (dirEnd) {
163 if (extStart > dirEnd) {
164 return FilePath(SString(dirEnd + 1, 0, extStart - dirEnd - 1).c_str());
165 } else {
166 return FilePath(dirEnd + 1);
168 } else if (extStart) {
169 return FilePath(SString(fileName.c_str(), 0, extStart - fileName.c_str()).c_str());
170 } else {
171 return fileName.c_str();
175 FilePath FilePath::Extension() const {
176 const char *dirEnd = strrchr(fileName.c_str(), pathSepChar);
177 const char *extStart = strrchr(fileName.c_str(), '.');
178 if (extStart > dirEnd)
179 return extStart + 1;
180 else
181 return "";
184 FilePath FilePath::Directory() const {
185 if (IsRoot()) {
186 return FilePath(fileName.c_str());
187 } else {
188 const char *dirEnd = strrchr(fileName.c_str(), pathSepChar);
189 if (dirEnd) {
190 int lenDirectory = dirEnd - fileName.c_str();
191 if (lenDirectory < RootLength()) {
192 lenDirectory = RootLength();
194 return FilePath(fileName.substr(0, lenDirectory).c_str());
195 } else {
196 return FilePath();
201 static char *split(char*& s, char c) {
202 char *t = s;
203 if (s && (s = strchr(s, c)) != NULL)
204 * s++ = '\0';
205 return t;
208 FilePath FilePath::NormalizePath() const {
209 char *path = new char[fileName.length() + 1];
210 strcpy(path, AsInternal());
211 #ifdef WIN32
212 // Convert unix path separators to Windows
213 for (char *cp = path; *cp; cp++) {
214 if (*cp == '/')
215 *cp = pathSepChar;
217 #endif
218 char *absPath = new char[fileName.length() + 1];
219 char *cur = absPath;
220 *cur = '\0';
221 char *tmp = path;
222 if (*tmp == pathSepChar) {
223 *cur++ = pathSepChar;
224 *cur = '\0';
225 tmp++;
227 char *part;
228 while ((part = split(tmp, pathSepChar)) != NULL) {
229 char *last;
230 if (strcmp(part, ".") == 0)
232 else if (strcmp(part, "..") == 0 && (last = strrchr(absPath, pathSepChar)) != NULL) {
233 if (last > absPath)
234 cur = last;
235 else
236 cur = last + 1;
237 *cur = '\0';
238 } else {
239 if (cur > absPath && *(cur - 1) != pathSepChar)
240 *cur++ = pathSepChar;
241 strcpy(cur, part);
242 cur += strlen(part);
245 FilePath ret(absPath);
246 delete []path;
247 delete []absPath;
248 return ret;
251 #ifdef __vms
253 FilePath FilePath::VMSToUnixStyle() {
254 // possible formats:
255 // o disk:[dir.dir]file.type
256 // o logical:file.type
257 // o [dir.dir]file.type
258 // o file.type
259 // o /disk//dir/dir/file.type
260 // o /disk/dir/dir/file.type
262 char unixStyleFileName[MAX_PATH + 20];
263 const char *vmsName = FullPath();
265 if (strchr(vmsName, ':') == NULL && strchr(vmsName, '[') == NULL) {
266 // o file.type
267 // o /disk//dir/dir/file.type
268 // o /disk/dir/dir/file.type
269 if (strstr (vmsName, "//") == NULL) {
270 return FilePath(vmsName);
272 strcpy(unixStyleFileName, vmsName);
273 char *p;
274 while ((p = strstr (unixStyleFileName, "//")) != NULL) {
275 strcpy (p, p + 1);
277 return FilePath(unixStyleFileName);
280 // o disk:[dir.dir]file.type
281 // o logical:file.type
282 // o [dir.dir]file.type
284 if (vmsName [0] == '/') {
285 strcpy(unixStyleFileName, vmsName);
286 } else {
287 unixStyleFileName [0] = '/';
288 strcpy(unixStyleFileName + 1, vmsName);
289 char *p = strstr(unixStyleFileName, ":[");
290 if (p == NULL) {
291 // o logical:file.type
292 p = strchr(unixStyleFileName, ':');
293 *p = '/';
294 } else {
295 *p = '/';
296 strcpy(p + 1, p + 2);
297 char *end = strchr(unixStyleFileName, ']');
298 if (*end != NULL) {
299 *end = '/';
301 while (p = strchr(unixStyleFileName, '.'), p != NULL && p < end) {
302 *p = '/';
306 return FilePath(unixStyleFileName);
307 } // VMSToUnixStyle
309 #endif
312 * Take a filename or relative path and put it at the end of the current path.
313 * If the path is absolute, return the same path.
315 FilePath FilePath::AbsolutePath() const {
316 #ifdef WIN32
317 // The runtime libraries for GCC and Visual C++ give different results for _fullpath
318 // so use the OS.
319 char absPath[2000];
320 absPath[0] = '\0';
321 LPTSTR fileBit = 0;
322 ::GetFullPathNameA(AsFileSystem(), sizeof(absPath), absPath, &fileBit);
323 return FilePath(absPath);
324 #else
325 if (IsAbsolute()) {
326 return NormalizePath();
327 } else {
328 return FilePath(GetWorkingDirectory(), *this).NormalizePath();
330 #endif
333 // Only used on Windows to fix the case of file names
335 FilePath FilePath::GetWorkingDirectory() {
336 char dir[MAX_PATH + 1];
337 dir[0] = '\0';
338 if (getcwd(dir, MAX_PATH)) {
339 dir[MAX_PATH] = '\0';
340 // In Windows, getcwd returns a trailing backslash
341 // when the CWD is at the root of a disk, so remove it
342 size_t endOfPath = strlen(dir) - 1;
343 if (dir[endOfPath] == pathSepChar) {
344 dir[endOfPath] = '\0';
347 return FilePath(dir);
350 bool FilePath::SetWorkingDirectory() const {
351 return chdir(AsFileSystem()) == 0;
354 void FilePath::FixCase() {}
356 void FilePath::List(FilePathSet &directories, FilePathSet &files) {
357 #ifdef WIN32
358 FilePath wildCard(*this, "*.*");
359 bool complete = false;
360 WIN32_FIND_DATA findFileData;
361 HANDLE hFind = ::FindFirstFile(wildCard.AsFileSystem(), &findFileData);
362 if (hFind != INVALID_HANDLE_VALUE) {
363 while (!complete) {
364 if ((strcmp(findFileData.cFileName, ".") != 0) && (strcmp(findFileData.cFileName, "..") != 0)) {
365 if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
366 directories.Append(FilePath(AsInternal(), findFileData.cFileName));
367 } else {
368 files.Append(FilePath(AsInternal(), findFileData.cFileName));
371 if (!::FindNextFile(hFind, &findFileData)) {
372 complete = true;
375 ::FindClose(hFind);
377 #else
378 errno = 0;
379 DIR *dp = opendir(AsInternal());
380 if (dp == NULL) {
381 //~ fprintf(stderr, "%s: cannot open for reading: %s\n", AsInternal(), strerror(errno));
382 return;
384 struct dirent *ent;
385 while ((ent = readdir(dp)) != NULL) {
386 if ((strcmp(ent->d_name, ".") != 0) && (strcmp(ent->d_name, "..") != 0)) {
387 FilePath pathFull(AsInternal(), ent->d_name);
388 if (pathFull.IsDirectory()) {
389 directories.Append(pathFull);
390 } else {
391 files.Append(pathFull);
396 if (errno == 0) {
397 closedir(dp);
399 #endif
402 FILE *FilePath::Open(const char *mode) const {
403 if (IsSet()) {
404 return fopen(fileName.c_str(), mode);
405 } else {
406 return NULL;
410 void FilePath::Remove() const {
411 unlink(AsFileSystem());
414 #ifndef R_OK
415 // Neither Borland nor Microsoft define the constants used to call access
416 #define R_OK 4
417 #endif
419 time_t FilePath::ModifiedTime() const {
420 if (IsUntitled())
421 return 0;
422 if (access(AsFileSystem(), R_OK) == -1)
423 return 0;
424 struct stat statusFile;
425 if (stat(AsFileSystem(), &statusFile) != -1)
426 return statusFile.st_mtime;
427 else
428 return 0;
431 int FilePath::GetFileLength() const {
432 int size = -1;
433 if (IsSet()) {
434 FILE *fp = Open(fileRead);
435 if (fp) {
436 fseek(fp, 0, SEEK_END);
437 size = ftell(fp);
438 fseek(fp, 0, SEEK_SET);
439 fclose(fp);
442 return size;
445 bool FilePath::Exists() const {
446 bool ret = false;
447 if (IsSet()) {
448 FILE *fp = Open(fileRead);
449 if (fp) {
450 ret = true;
451 fclose(fp);
454 return ret;
457 bool FilePath::IsDirectory() const {
458 struct stat statusFile;
459 if (stat(AsFileSystem(), &statusFile) != -1)
460 #ifdef WIN32
461 return statusFile.st_mode & _S_IFDIR;
462 #else
463 return statusFile.st_mode & S_IFDIR;
464 #endif
465 else
466 return false;
469 bool FilePath::Matches(const char *pattern) const {
470 SString pat(pattern);
471 pat.substitute(' ', '\0');
472 SString nameCopy(Name().fileName);
473 nameCopy.lowercase();
474 size_t start = 0;
475 while (start < pat.length()) {
476 const char *patElement = pat.c_str() + start;
477 if (patElement[0] == '*') {
478 if (nameCopy.endswith(patElement + 1)) {
479 return true;
481 } else {
482 if (nameCopy == SString(patElement).lowercase()) {
483 return true;
486 start += strlen(patElement) + 1;
488 return false;
491 #ifdef WIN32
493 * Makes a long path from a given, possibly short path/file.
495 * The short path/file must exist, and if it is a file it must be fully specified
496 * otherwise the function fails.
498 * sizeof @a longPath buffer must be a least _MAX_PATH
499 * @returns true on success, and the long path in @a longPath buffer,
500 * false on failure, and copies the @a shortPath arg to the @a longPath buffer.
502 bool MakeLongPath(const char* shortPath, char* longPath) {
503 // when we have pfnGetLong, we assume it never changes as kernel32 is always loaded
504 static DWORD (STDAPICALLTYPE* pfnGetLong)(const char* lpszShortPath, char* lpszLongPath, DWORD cchBuffer) = NULL;
505 static bool kernelTried = FALSE;
506 bool ok = FALSE;
508 if (!kernelTried) {
509 HMODULE hModule;
510 kernelTried = true;
511 hModule = ::GetModuleHandleA("KERNEL32");
512 //assert(hModule != NULL); // must not call FreeLibrary on such handle
514 // attempt to get GetLongPathName (implemented in Win98/2000 only!)
515 (FARPROC&)pfnGetLong = ::GetProcAddress(hModule, "GetLongPathNameA");
518 // the kernel GetLongPathName proc is faster and (hopefully) more reliable
519 if (pfnGetLong != NULL) {
520 // call kernel proc
521 ok = (pfnGetLong)(shortPath, longPath, _MAX_PATH) != 0;
522 } else {
523 char short_path[_MAX_PATH]; // copy, so we can modify it
524 char* tok;
526 *longPath = '\0';
528 lstrcpyn(short_path, shortPath, _MAX_PATH);
530 for (;;) {
531 tok = strtok(short_path, "\\");
532 if (tok == NULL)
533 break;
535 if ((strlen(shortPath) > 3) &&
536 (shortPath[0] == '\\') && (shortPath[1] == '\\')) {
537 // UNC, skip first seps
538 strcat(longPath, "\\\\");
539 strcat(longPath, tok);
540 strcat(longPath, "\\");
542 tok = strtok(NULL, "\\");
543 if (tok == NULL)
544 break;
546 strcat(longPath, tok);
548 bool isDir = false;
550 for (;;) {
551 WIN32_FIND_DATA fd;
552 HANDLE hfind;
553 char* tokend;
555 tok = strtok(NULL, "\\");
556 if (tok == NULL)
557 break;
559 strcat(longPath, "\\");
560 tokend = longPath + strlen(longPath);
562 // temporary add short component
563 strcpy(tokend, tok);
565 hfind = ::FindFirstFile(longPath, &fd);
566 if (hfind == INVALID_HANDLE_VALUE)
567 break;
569 isDir = (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
571 // finally add long component we got
572 strcpy(tokend, fd.cFileName);
574 ::FindClose(hfind);
576 ok = tok == NULL;
578 if (ok && isDir)
579 strcat(longPath, "\\");
581 break;
585 if (!ok) {
586 lstrcpyn(longPath, shortPath, _MAX_PATH);
588 return ok;
590 #endif
592 void FilePath::FixName() {
593 #ifdef WIN32
594 // Only used on Windows to use long file names and fix the case of file names
595 char longPath[_MAX_PATH];
596 // first try MakeLongPath which corrects the path and the case of filename too
597 if (MakeLongPath(AsFileSystem(), longPath)) {
598 Set(longPath);
599 } else {
600 // On Windows file comparison is done case insensitively so the user can
601 // enter scite.cxx and still open this file, SciTE.cxx. To ensure that the file
602 // is saved with correct capitalisation FindFirstFile is used to find out the
603 // real name of the file.
604 WIN32_FIND_DATA FindFileData;
605 HANDLE hFind = ::FindFirstFile(AsFileSystem(), &FindFileData);
606 FilePath dir = Directory();
607 if (hFind != INVALID_HANDLE_VALUE) { // FindFirstFile found the file
608 Set(dir, FindFileData.cFileName);
609 ::FindClose(hFind);
612 #endif
615 FilePathSet &FilePathSet::operator=(const FilePathSet &) {
616 // Private so won't be called.
617 return *this;
620 FilePathSet::FilePathSet(int size_) {
621 size = size_;
622 body = new FilePath[size];
623 lengthBody = 0;
626 FilePathSet::FilePathSet(const FilePathSet &other) {
627 size = other.size;
628 lengthBody = other.lengthBody;
629 body = new FilePath[size];
630 for (size_t i = 0; i < lengthBody; i++) {
631 body[i] = other.body[i];
635 FilePathSet::~FilePathSet() {
636 delete []body;
637 body = NULL;
638 size = 0;
639 lengthBody = 0;
642 FilePath FilePathSet::At(size_t pos) const {
643 return body[pos];
646 void FilePathSet::Append(FilePath fp) {
647 if (lengthBody >= size) {
648 size *= 2;
649 FilePath *bodyNew = new FilePath[size];
650 for (size_t i = 0; i < lengthBody; i++) {
651 bodyNew[i] = body[i];
653 delete []body;
654 body = bodyNew;
656 body[lengthBody++] = fp;
659 size_t FilePathSet::Length() const {
660 return lengthBody;