29 #include <sys/types.h>
35 static const char *DirectorySeparators = R
"(/\)";
37 static const char *DirectorySeparators =
"/";
44 if (!szPath)
return nullptr;
45 char *pPos,*pFilename=szPath;
46 for (pPos=szPath; *pPos; pPos++)
if (*pPos==
DirectorySeparator || *pPos==
'/') pFilename = pPos+1;
51 if (!szPath)
return nullptr;
52 const char *pPos,*pFilename=szPath;
53 for (pPos=szPath; *pPos; pPos++)
if (*pPos==
DirectorySeparator || *pPos==
'/') pFilename = pPos+1;
71 if (!szPath)
return nullptr;
72 const char *pPos,*pFilename=szPath;
73 for (pPos=szPath; *pPos; pPos++)
77 if (pPos >= szPath+4 &&
SEqual2NoCase(pPos-4,
".oc"))
return pFilename;
89 const char *cpPos = strString +
SLen(strString);
91 while ((cpPos > strString) &&
Inside(*(cpPos - 1),
'0',
'9')) cpPos--;
93 sscanf(cpPos,
"%d", &iNumber);
103 if (!szPath)
return nullptr;
104 char *pPos, *pFilename=szPath;
105 for (pPos=szPath; *pPos; pPos++)
if (*pPos ==
'/') pFilename = pPos+1;
110 if (!szPath)
return nullptr;
111 const char *pPos, *pFilename=szPath;
112 for (pPos=szPath; *pPos; pPos++)
if (*pPos ==
'/') pFilename = pPos+1;
121 for (end=0; szFilename[end]; end++) {}
123 while ((pos > 0) && (szFilename[pos-1] !=
'.') && (szFilename[pos-1] !=
DirectorySeparator)) --pos;
124 if ((pos > 0) && szFilename[pos-1] ==
'.')
return szFilename + pos;
125 return szFilename + end;
130 for (end=0; szFilename[end]; end++) {}
132 while ((pos>0) && (szFilename[pos-1] !=
'.') && (szFilename[pos-1] !=
DirectorySeparator)) pos--;
133 if ((pos > 0) && szFilename[pos-1] ==
'.')
return szFilename+pos;
134 return szFilename+end;
138 void RealPath(
const char *szFilename,
char *pFullFilename)
141 wchar_t *wpath = _wfullpath(
nullptr,
GetWideChar(szFilename), 0);
148 char *pSuffix =
nullptr;
153 if (realpath(szFilename, pFullFilename))
160 pSuffix = szCopy +
SLen(szCopy);
164 while (pSuffix > szCopy)
165 if (*--pSuffix ==
'/')
167 if (pSuffix <= szCopy)
214 if (
SEqual2(strPath, strRelativeTo))
228 if (*szPath && szPath[1] ==
':')
return true;
239 if (!szPath)
return false;
245 if (iBSPos2 > iBSPos) fprintf(stderr,
"Warning: TruncatePath with a \\ (%s)\n", szPath);
247 if (iBSPos<0)
return false;
256 int i=
SLen(szFilename);
265 int i=
SLen(szFilename);
289 if (ext[0]) {
SCopy(szExtension,ext); }
290 else {
SAppend(
".",szFilename);
SAppend(szExtension,szFilename); }
306 if (ext[0]) ext[-1]=0;
311 if (psFileName && *psFileName)
351 if (!szString || !szWildcardList)
return false;
353 StdStrBuf sWildcard, sWildcardList(szWildcardList);
355 while (sWildcardList.
GetSection(i++, &sWildcard,
'|'))
366 if (!szString)
return false;
372 static bool WildcardMatchCharacterClass(
const char **charclass,
char matchchar)
374 const char *cc = *charclass;
375 if (*cc++ !=
'[')
return false;
381 if (cc[1] ==
'-' && cc[2] !=
']')
383 if (matchchar >= cc[0] && matchchar <= cc[2])
388 else if (*cc == matchchar)
391 while (*++cc && *cc !=
']');
399 if (!szString || !szWildcard)
return false;
401 const char *pWild = szWildcard, *pPos = szString;
402 const char *pLWild =
nullptr, *pLPos =
nullptr;
403 while (*pWild || pLWild)
406 { pLWild = ++pWild; pLPos = pPos; }
411 else if ((*pWild ==
'[' && WildcardMatchCharacterClass(&pWild, *pPos))
413 || tolower(*pWild) == tolower(*pPos))
417 { pWild = pLWild; pPos = ++pLPos; }
422 return !*pWild && !*pPos;
429 char *szFilename=szTitle, *szTitle2=szTitle;
434 fStrip = (szFilename==szTitle);
435 else if (
static_cast<unsigned int>(*szTitle2) > 127)
438 fStrip = (
SCharPos(*szTitle2, R
"(!"'%&/=?+*#:;<>\.)") >= 0);
439 if (!fStrip) *szFilename++ = *szTitle2;
443 while (
IsWhiteSpace(*--szFilename))
if (szFilename==szTitle) { --szFilename;
break; }
447 if (!*szTitle)
SCopy(
"unnamed", szTitle, 50);
456 return GetFileAttributes(
GetWideChar(szFilename)) != INVALID_FILE_ATTRIBUTES;
458 return (!access(szFilename,F_OK));
462 size_t FileSize(
const char *szFilename)
464 #if defined(_WIN32) || defined(_WIN64)
465 auto attributes = WIN32_FILE_ATTRIBUTE_DATA();
466 if (GetFileAttributesEx(
GetWideChar(szFilename), GetFileExInfoStandard, &attributes) == 0)
469 return (
static_cast<size_t>(attributes.nFileSizeHigh) << (
sizeof(attributes.nFileSizeLow) * 8)) | attributes.nFileSizeLow;
471 return attributes.nFileSizeLow;
475 if (stat(szFilename,&stStats))
return 0;
476 return stStats.st_size;
484 return _filelength(fdes);
487 if (fstat(fdes,&stStats))
return 0;
488 return stStats.st_size;
492 int FileTime(
const char *szFilename)
495 auto attributes = WIN32_FILE_ATTRIBUTE_DATA();
496 if (GetFileAttributesEx(
GetWideChar(szFilename), GetFileExInfoStandard, &attributes) == 0)
498 int64_t ft = (
static_cast<int64_t
>(attributes.ftLastWriteTime.dwHighDateTime) << (
sizeof(attributes.ftLastWriteTime.dwLowDateTime) * 8)) | attributes.ftLastWriteTime.dwLowDateTime;
499 ft -= 116444736000000000;
504 if (stat(szFilename,&stStats)!=0)
return 0;
505 return stStats.st_mtime;
512 SetFileAttributesW(
GetWideChar(szFilename), FILE_ATTRIBUTE_NORMAL);
515 switch (GetLastError())
517 case ERROR_PATH_NOT_FOUND:
518 case ERROR_FILE_NOT_FOUND:
530 if (remove(szFilename))
545 bool CopyFile(
const char *szSource,
const char *szTarget,
bool FailIfExists)
547 int fds = open (szSource, O_RDONLY |
O_CLOEXEC);
548 if (!fds)
return false;
549 struct stat info; fstat(fds, &info);
550 int fdt = open (szTarget,
O_CLOEXEC | O_WRONLY | O_CREAT | (FailIfExists? O_EXCL : O_TRUNC), info.st_mode);
557 while ((l = read(fds, buffer,
sizeof(buffer))) > 0)
558 if (write(fdt, buffer, l) < l)
569 bool RenameFile(
const char *szFilename,
const char *szNewFilename)
571 if (rename(szFilename,szNewFilename) < 0)
573 if (errno != EXDEV)
return false;
574 if (
CopyFile(szFilename, szNewFilename,
false))
585 bool CopyFile(
const char *szSource,
const char *szTarget,
bool FailIfExists)
590 bool RenameFile(
const char *szFilename,
const char *szNewFilename)
600 if (!szFilename)
return false;
603 if (
Inside(
SLen(szFilename), 2u, 3u))
if (szFilename[1]==
':')
605 szFilename[2]=
'\\'; szFilename[3]=0;
606 if (GetDriveTypeW(
GetWideChar(szFilename)) == DRIVE_NO_ROOT_DIR)
return false;
609 struct _wfinddata_t fdt; intptr_t shnd;
610 if ((shnd=_wfindfirst(
GetWideChar(szFilename),&fdt))<0)
return false;
615 if (
SCharPos(
'*', szFilename) != -1)
617 fputs (
"Warning: MakeOriginalFilename with \"", stderr);
618 fputs (szFilename, stderr);
619 fputs (
"\"!\n", stderr);
631 wchar_t *widebuf =
nullptr;
632 DWORD widebufsz = GetCurrentDirectoryW(0,
nullptr);
633 widebuf =
new wchar_t[widebufsz];
634 if (GetCurrentDirectoryW(widebufsz, widebuf) == 0) {
650 return SetCurrentDirectoryW(
GetWideChar(path)) != 0;
652 return (chdir(path)==0);
658 assert(!path.empty());
660 if (CreateDirectoryW(
GetWideChar(path.c_str()),
nullptr))
666 DWORD err = GetLastError();
669 case ERROR_PATH_NOT_FOUND:
671 case ERROR_ALREADY_EXISTS:
677 if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
678 nullptr, err, 0, (LPWSTR)&str, 0,
nullptr))
680 LogF(
"CreateDirectory failed: %s",
StdStrBuf(str).getData());
688 if (!mkdir(path.c_str(), S_IREAD | S_IWRITE | S_IEXEC))
702 std::string::size_type slash = path.find_last_of(DirectorySeparators);
703 if (slash == 0 || slash == std::string::npos)
713 if (szFilename && szFilename[0])
715 unsigned int len =
SLen(szFilename);
716 if (len > 1 && ((szFilename[len - 1] ==
'\\') || (szFilename[len - 1] ==
'/')))
719 bufFilename[
SLen(bufFilename) - 1] = 0;
720 szFilename = bufFilename;
725 struct _wfinddata_t fdt; intptr_t shnd;
726 if ((shnd=_wfindfirst(
GetWideChar(szFilename),&fdt))<0)
return false;
728 if (fdt.attrib & _A_SUBDIR)
return true;
731 if (stat(szFilename,&stStats)!=0)
return 0;
732 return (S_ISDIR(stStats.st_mode));
737 bool CopyDirectory(
const char *szSource,
const char *szTarget,
bool fResetAttributes)
740 if (!szSource || !szTarget)
return false;
751 if (_wmkdir(
GetWideChar(szTarget))!=0)
return false;
756 _wfinddata_t fdt; intptr_t hfdt;
757 if ( (hfdt=_wfindfirst(
GetWideChar(contents),&fdt)) > -1 )
764 if (!
CopyItem(itemsource,itemtarget, fResetAttributes)) status=
false;
766 while (_wfindnext(hfdt,&fdt)==0);
770 if (mkdir(szTarget,0777)!=0)
return false;
771 DIR * d = opendir(szSource);
774 while ((ent = readdir(d)))
778 if (!
CopyItem(itemsource,itemtarget, fResetAttributes)) status=
false;
798 DIR * d = opendir(szDirName);
800 while ((ent = readdir(d)))
822 return (rmdir(szDirName)==0 || errno == ENOENT);
827 bool RenameItem(
const char *szItemName,
const char *szNewItemName)
846 if (!(fhnd=_wfopen(
GetWideChar(szItemname), L
"wb")))
return false;
848 if (!(fhnd=fopen(szItemname,
"wb")))
return false;
855 bool CopyItem(
const char *szSource,
const char *szTarget,
bool fResetAttributes)
863 if (!
CopyFile(szSource,szTarget,
false))
return false;
866 if (fResetAttributes)
if (!SetFileAttributesW(
GetWideChar(szTarget), FILE_ATTRIBUTE_NORMAL))
return false;
868 if (fResetAttributes)
if (chmod(szTarget, S_IRWXU))
return false;
873 bool MoveItem(
const char *szSource,
const char *szTarget)
886 if (
SEqual(szFullFile1,szFullFile2))
return true;
905 : p(other.p), iter(other.iter)
912 p = other.p; iter = other.iter;
934 iter = p->
files.end();
939 iter = p->
files.begin();
944 if (p->
directory == dirname && !force_reread)
947 iter = p->
files.begin();
957 iter = p->
files.end();
967 void DirectoryIterator::Read(
const char *dirname)
969 assert(dirname && *dirname);
970 assert(p->
files.empty());
971 std::string search_path(dirname);
975 auto file = WIN32_FIND_DATAW();
976 HANDLE fh = FindFirstFileW(
GetWideChar((search_path +
'*').c_str()), &file);
977 if (fh == INVALID_HANDLE_VALUE)
979 switch (GetLastError())
981 case ERROR_PATH_NOT_FOUND:
982 case ERROR_FILE_NOT_FOUND:
987 Log(
"DirectoryIterator::Read(const char*): Unable to read file system");
995 if (file.cFileName[0] ==
'.' && (file.cFileName[1] ==
'\0' || (file.cFileName[1] ==
'.' && file.cFileName[2] ==
'\0')))
998 size_t size = (file.nFileSizeHigh * (size_t(MAXDWORD) + 1)) + file.nFileSizeLow;
1002 while (FindNextFileW(fh, &file));
1005 DIR *fh = opendir(dirname);
1016 LogF(
"DirectoryIterator::Read(\"%s\"): %s", dirname, strerror(errno));
1022 while ((file = readdir(fh)) !=
nullptr)
1025 if (file->d_name[0] ==
'.' && (file->d_name[1] ==
'\0' || (file->d_name[1] ==
'.' && file->d_name[2] ==
'\0')))
1027 p->
files.emplace_back(file->d_name, 0);
1033 for (
auto & file : p->
files)
1034 file.first.insert(0, search_path);
1035 iter = p->
files.begin();
1041 if (iter != p->
files.end())
1048 if (iter == p->
files.end())
1050 return iter->first.c_str();
1062 return iter->second;
1064 return FileSize(iter->first.c_str());
1068 int ForEachFile(
const char *szDirName,
bool (*fnCallback)(
const char *))
1070 if (!szDirName || !fnCallback)
1073 SCopy(szDirName,szFilename);
1074 bool fHasWildcard = (
SCharPos(
'*', szFilename)>=0);
1079 struct _wfinddata_t fdt; intptr_t fdthnd;
1082 if ((fdthnd = _wfindfirst (
GetWideChar(szFilename), &fdt)) < 0)
1086 if (!wcscmp(fdt.name, L
".") || !wcscmp(fdt.name, L
".."))
continue;
1089 if ((*fnCallback)(szFilename))
1092 while (_wfindnext(fdthnd,&fdt)==0);
1095 if (fHasWildcard) fprintf(stderr,
"Warning: ForEachFile with * (%s)\n", szDirName);
1096 DIR * d = opendir(szDirName);
1099 while ((ent = readdir(d)))
1102 if ((*fnCallback)(szFilename))
bool Log(const char *szMessage)
bool LogF(const char *strMessage,...)
StdStrBuf::wchar_t_holder GetWideChar(const char *utf8, bool double_null_terminate=false)
unsigned int SCharCount(char cTarget, const char *szInStr, const char *cpUntil)
int SCharPos(char cTarget, const char *szInStr, int iIndex)
bool SEqual2(const char *szStr1, const char *szStr2)
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
void SAppendChar(char cChar, char *szStr)
int SCharLastPos(char cTarget, const char *szInStr)
void SAppend(const char *szSource, char *szTarget, int iMaxL)
bool SEqual2NoCase(const char *szStr1, const char *szStr2, int iLen)
int osprintf(char *str, const char *fmt,...) GNUC_FORMAT_ATTRIBUTE_O
bool IsWhiteSpace(char cChar)
bool SEqual(const char *szStr1, const char *szStr2)
bool Inside(T ival, U lbound, V rbound)
size_t SLen(const char *sptr)
bool EraseItem(const char *szItemName)
bool CreateItem(const char *szItemname)
const char * GetC4Filename(const char *szPath)
const char * GetRelativePathS(const char *strPath, const char *strRelativeTo)
bool TruncatePath(char *szPath)
bool CopyDirectory(const char *szSource, const char *szTarget, bool fResetAttributes)
bool DirectoryExists(const char *szFilename)
char * GetExtension(char *szFilename)
bool GetParentPath(const char *szFilename, char *szBuffer)
bool WildcardMatch(const char *szWildcard, const char *szString)
const char * GetFilenameOnly(const char *strFilename)
void MakeTempFilename(char *szFilename)
void DefaultExtension(char *szFilename, const char *szExtension)
bool EraseDirectory(const char *szDirName)
bool SetWorkingDirectory(const char *path)
bool ItemIdentical(const char *szFilename1, const char *szFilename2)
void RealPath(const char *szFilename, char *pFullFilename)
void MakeFilenameFromTitle(char *szTitle)
char * GetFilename(char *szPath)
void AppendBackslash(char *szFilename)
bool IsGlobalPath(const char *szPath)
bool WildcardListMatch(const char *szWildcardList, const char *szString)
void EnforceExtension(char *szFilename, const char *szExtension)
bool RenameItem(const char *szItemName, const char *szNewItemName)
bool CreatePath(const std::string &path)
bool CopyItem(const char *szSource, const char *szTarget, bool fResetAttributes)
bool MoveItem(const char *szSource, const char *szTarget)
char * GetFilenameWeb(char *szPath)
bool IsWildcardString(const char *szString)
void RemoveExtension(char *szFilename)
void TruncateBackslash(char *szFilename)
int ForEachFile(const char *szDirName, bool(*fnCallback)(const char *))
const char * GetWorkingDirectory()
int GetTrailingNumber(const char *strString)
int FileTime(const char *fname)
bool FileExists(const char *szFileName)
bool EraseFile(const char *szFileName)
size_t FileSize(const char *fname)
bool RenameFile(const char *szFileName, const char *szNewFileName)
bool MakeOriginalFilename(char *szFilename)
size_t GetFileSize() const
DirectoryIterator & operator++()
DirectoryIterator & operator=(const DirectoryIterator &)
const char * operator*() const
friend struct DirectoryIteratorP
void SetLength(size_t iLength)
void ReplaceEnd(size_t iPos, const char *szNewEnd)
const char * getData() const
void AppendChar(char cChar)
void Append(const char *pnData, size_t iChars)
bool GetSection(size_t idx, StdStrBuf *psOutSection, char cSeparator=';') const
DirectoryIteratorP()=default
DirectoryIterator::FileList files