*/
bool ReByteBuffer::endsWith(const Byte* tail, size_t tailLength,
bool ignoreCase) const {
- bool rc;
- if (tailLength == (size_t)-1)
- tailLength = strlen(tail);
- rc = indexOf(tail, tailLength, m_length - tailLength, m_length, ignoreCase) >= 0;
+ bool rc = tail != NULL;
+ if (rc){
+ if (tailLength == (size_t)-1)
+ tailLength = strlen(tail);
+ rc = indexOf(tail, tailLength, m_length - tailLength, m_length, ignoreCase) >= 0;
+ }
return rc;
}
* <code>false</code>: otherwise
*/
bool ReByteBuffer::startsWith(const Byte* head, size_t headLength, const bool ignoreCase) const{
- bool rc;
- if (headLength == (size_t) -1)
- headLength = strlen(head);
- rc = indexOf(head, headLength, 0, headLength, ignoreCase) == 0;
+ bool rc = head != NULL;
+ if (rc){
+ if (headLength == (size_t) -1)
+ headLength = strlen(head);
+ rc = indexOf(head, headLength, 0, headLength, ignoreCase) == 0;
+ }
return rc;
}
#elif defined __WIN32__
#endif
}
-/** @brief Sets the directory.
+/** @brief Returns the name of the current file found by the last <code>findFirst()</code>
+ * or <code>findNext()</code>.
+ *
+ * @return NULL: No current file exists. Otherwise: The name of the current file.
*
- * @param path The name of the directory.
*/
-void ReDirectory::setDir(const char* path){
- m_path.set(path, -1);
+const char* ReDirectory::currentFile(){
#if defined __linux__
- if (m_dir != NULL){
- closedir(m_dir);
- m_dir = NULL;
- }
- m_entry = NULL;
- m_dir = opendir(path);
- m_valid = m_dir != NULL;
- if (m_valid){
- if (m_path.at(m_path.length() - 1) != OS_SEPARATOR_CHAR)
- m_path.append(OS_SEPARATOR, 1);
- }
+ const char* rc = m_entry == NULL ? NULL : m_entry->d_name;
#elif defined __WIN32__
- struct stat info;
- ReByteBuffer thePath(m_path);
- if (m_path.endsWith(OS_SEPARATOR))
- thePath.setLength(thePath.length() - 1);
- m_valid = stat(thePath.str(), &info) == 0 && S_ISDIR(info.st_mode);
+ const char* rc = m_data.cFileName;
#endif
-}
-/** @brief Returns the name of the directory.
- *
- * @return The name of the directory.
- */
-const char* ReDirectory::getDir(void) const {
- return m_path.buffer();
+ return rc;
}
-/** @brief Tests whether the directory state is valid.
+/**
+ * Deletes a directory tree.
*
- * @return true: The instance is OK. false: Some error has occurred.
+ * @param base the name of the directory
+ * @param deleteBaseToo true: the directory itself will be deleted
+ * false: only the files and subdirs in the dir will be deleted
*/
-bool ReDirectory::isValid(){
- return m_valid;
+void ReDirectory::deleteTree(const char* base, bool deleteBaseToo){
+ ReDirectory dir(base);
+ if (dir.findFirst("*", false)){
+ ReByteBuffer full;
+ do {
+ struct stat info;
+ const char* node = dir.currentFile();
+ if (! (node[0] == '.' && (node[1] == '\0' || node[1] == '.' && node[2] == '\0'))
+ && stat(dir.fullpath(full).str(), &info) == 0){
+ if (S_ISDIR(info.st_mode))
+ deleteTree(full.str(), true);
+ else
+ _unlink(full.str());
+ }
+ } while(dir.findNext());
+ }
+ if (deleteBaseToo){
+ _rmdir(base);
+ }
}
-/** @brief Sets the flags of the regular expression handling.
+
+/** @brief Returns the name of the directory.
*
- * @param flags Usefull flags are: REG_ICASE: ignore case.
+ * @return The name of the directory.
*/
-void ReDirectory::setRegExprFlags(int flags){
-#if defined __linux__
- m_regExprFlags = flags;
-#endif
+const char* ReDirectory::getDir(void) const {
+ return m_path.buffer();
}
/** @brief Find the first file with a given filename pattern.
}
/** @brief Find the next file with the pattern defined with the last <code>findFirst()</code> call.
*
- * @return true: A file has been found. false: The directory contains no more file with the given pattern.
+ * @return true: a file has been found<br>
+ * false: the directory contains no more file with the given pattern
*/
bool ReDirectory::findNext(){
bool rc = false;
#endif
return rc;
}
-/** @brief Returns the name of the current file found by the last <code>findFirst()</code>
- * or <code>findNext()</code>.
- *
- * @return NULL: No current file exists. Otherwise: The name of the current file.
- *
- */
-const char* ReDirectory::currentFile(){
-#if defined __linux__
- const char* rc = m_entry == NULL ? NULL : m_entry->d_name;
-#elif defined __WIN32__
- const char* rc = m_data.cFileName;
-#endif
- return rc;
-}
+
/** @brief Returns the full path of the current file or a given node.
*
* The current file is the file found by the last call of <code>findFirst()</code> or <code>findNext()</code>.
return path;
}
-/** @brief Find the first file matching a given filename pattern.
+/** @brief Tests whether the directory state is valid.
+ *
+ * @return true: The instance is OK. false: Some error has occurred.
+ */
+bool ReDirectory::isValid(){
+ return m_valid;
+}
+
+/** @brief Sets the directory.
+ *
+ * @param path The name of the directory.
+ */
+void ReDirectory::setDir(const char* path){
+ m_path.set(path, -1);
+#if defined __linux__
+ if (m_dir != NULL){
+ closedir(m_dir);
+ m_dir = NULL;
+ }
+ m_entry = NULL;
+ m_dir = opendir(path);
+ m_valid = m_dir != NULL;
+#elif defined __WIN32__
+ struct stat info;
+ ReByteBuffer thePath(m_path);
+ if (m_path.endsWith(OS_SEPARATOR))
+ thePath.setLength(thePath.length() - 1);
+ m_valid = stat(thePath.str(), &info) == 0 && S_ISDIR(info.st_mode);
+#endif
+ if (m_path.at(m_path.length() - 1) != OS_SEPARATOR_CHAR)
+ m_path.append(OS_SEPARATOR, 1);
+}
+
+/** @brief Sets the flags of the regular expression handling.
*
- * @param dir The directory handle.
- * @param pattern The pattern for the searched files. May contain wildcards.
- * @param useRegExpr True: <code>pattern</code> is a regular expression.
+ * @param flags Usefull flags are: REG_ICASE: ignore case.
*/
+void ReDirectory::setRegExprFlags(int flags){
+#if defined __linux__
+ m_regExprFlags = flags;
+#endif
+}
ReDirectory(const char* path);
~ReDirectory();
public:
- void setDir(const char* path);
- const char* getDir(void) const;
- bool isValid();
- void setRegExprFlags(int flags);
+ const char* currentFile();
bool findFirst(const char* pattern, bool isRegExpr);
bool findNext();
bool findYoungest(ReByteBuffer& filename);
- const char* currentFile();
ReByteBuffer& fullpath(ReByteBuffer& path, const char* name = NULL);
+ const char* getDir(void) const;
+ bool isValid();
+ void setDir(const char* path);
+ void setRegExprFlags(int flags);
+public:
+ static void deleteTree(const char* base, bool deleteBaseToo = false);
private:
//@ true: The directory is ok. false: The directory is undefined.
bool m_valid;
void ReStringList::split(const char* list, char separator, bool append){
if (! append)
clear();
- const char* end = strchr(list, separator);
+ const char* end = list == NULL ? NULL : strchr(list, separator);
const char* end2;
ReByteBuffer item;
while(end != NULL){
list++;
end = strchr(list, separator);
}
- if (list[0] != '\0')
+ if (list != NULL && list[0] != '\0')
add(-1, list, strlen(list) + 1);
}
/** @brief Joins all string members into a string.
* @param lineNo the line number of the test (for the error message)
*/
void ReTestUnit::assertEqual(const char* expected, const char* current, int lineNo){
+ if (expected == NULL)
+ expected = "";
if (current == NULL || strcmp(expected, current) != 0){
logF(true, i18n("%s-%d: expected / current: length: %d / %d\n%.512s\n%.512s"),
m_sourceFile.str(), lineNo, strlen(expected), current == NULL ? 0 : strlen(current),
public:
TestReDirTools() : ReTestUnit("ReTraverser", __FILE__){
m_base = testDir();
+ ReDirectory::deleteTree(m_base.str());
m_base.append("traverser").append(OS_SEPARATOR, -1);
_mkdir(m_base.str(), ALLPERMS);
run();
ReStringList cols;
for (size_t ix = 0; ix < current.count(); ix++){
line.setLength(0).append(current.strOf(ix), -1);
+#if defined __WIN32__
+ line.replaceAll(OS_SEPARATOR, 1, "/", 1);
+#endif
cols.split(expected.strOf(ix), '*');
- checkT(line.startsWith(cols.strOf(0)));
- checkT(line.endsWith(cols.strOf(1)));
+ if (! line.startsWith(cols.strOf(0)))
+ checkEqu(cols.strOf(0), line.str());
+ if (! line.endsWith(cols.strOf(1)))
+ checkEqu(cols.strOf(1), line.str());
}
}
void testTouch(){
"2015.03.28 10:21:31 *traverser/1.txt\n"
"2015.03.28 10:21:31 *traverser/dir1/dir1_2/dir1_2_1/x1.txt\n"
"2015.03.28 10:21:31 *traverser/dir1/dir1_2/dir1_2_1/x2.txt\n"
- "=== 3 file(s) 0.000059 MByte 0 dirs(s) * sec");
+ "=== filtered: 3 file(s) 0.000059 MByte 0 dirs(s) * sec\n"
+ "=== total: 4 file(s) 0.000067 MByte 6 dirs(s) * sec");
const char* argv2[] = { "dt", "list", "-P;*;-cache", "-tr", "-p;*.txt",
optOutput.str(), m_base.str(), NULL };
*/\r
void ReTool::processFileArguments(){\r
int max = m_programArgs.getArgCount() - m_reservedLast;\r
- struct stat info;\r
// Test whether the arguments are files or directories:\r
+ ReByteBuffer arg;\r
for (int ii = m_reservedFirst; ii < max; ii++){\r
- const char* arg = m_programArgs.getArg(ii);\r
- if (stat(arg, &info) != 0)\r
+ arg = m_programArgs.getArg(ii);\r
+ if (! exists(arg) != 0)\r
m_programArgs.help(\r
ReByteBuffer(i18n("not a file or a directory: ")).append(arg).str(),\r
false, stderr);\r
// process the files:\r
for (int ii = m_reservedFirst; ii < max; ii++){\r
const char* arg = m_programArgs.getArg(ii);\r
- if (S_ISDIR(info.st_mode))\r
+ if (S_ISDIR(m_statInfo.st_mode))\r
processTree(arg);\r
else\r
processSingleFile(arg);\r
}\r
/**\r
* Issues a summary message if verbose level allows this.\r
+ * \r
+ * @param prefix NULL or a line prefix\r
*/\r
-void ReTool::printSummary(){\r
+void ReTool::printSummary(const char* prefix){\r
if (m_verboseLevel >= V_SUMMARY){\r
int duration = int(time(NULL) - m_start);\r
ReByteBuffer line;\r
+ ReByteBuffer line2;\r
statisticAsString(line);\r
- line.append(" ", 1).appendTime(duration).append(" ", 1).append(i18n("sec"));\r
- fprintf(m_output, "=== %s\n", line.str());\r
+ double rate = duration == 0 ? 0.0 : (m_files + m_directories) / duration;\r
+ line.append(rate, " %.1f").append(i18n("/sec"), -1);\r
+ m_traverser.statisticAsString(line2);\r
+ line2.append(" ", 1).appendTime(duration).append(" ", 1).append(i18n("sec"));\r
+ fprintf(m_output, "%s=== filtered: %s\n", prefix == NULL ? "" : prefix, line.str());\r
+ fprintf(m_output, "%s=== total: %s\n", prefix == NULL ? "" : prefix, line2.str());\r
}\r
\r
}\r
}\r
\r
static bool isAbsoluteTime(ReFileTime_t& time){\r
+#if defined __linux__\r
static struct tm year1980 = { 0, 0, 0, 1, 1 - 1, 1980-1900 };\r
static time_t time1980 = mktime(&year1980);\r
-#if defined __linux__\r
return time.tv_sec >= time1980;\r
#elif defined __WIN32__\r
- static ReFileTime_t time2 = { 0, 0 };\r
- if (time2.dwHighDateTime == 0 && time2.dwLowDateTime == 0)\r
- ReDirStatus_t::timeToFiletime(time1980, time2);\r
- return time2 > time;\r
+ static ReFileTime_t filetime1980 = { 2148603904, 27846551 };\r
+ bool rc = time > filetime1980;\r
+ return rc;\r
#endif\r
}\r
static void addRelativeTime(ReFileTime_t& absTime, const ReFileTime_t& relTime){\r
*/\r
void ReDirSync::makeDirWithParents(ReByteBuffer& path, int minWidth,\r
ReTraverser& traverser){\r
- struct stat info;\r
- bool endsWithSlash = path.str()[path.length() - 1] == OS_SEPARATOR_CHAR;\r
- if (endsWithSlash)\r
- path.setLength(path.length() - 1);\r
- if (stat(path.str(), &info) != 0){\r
+ if (! exists(path)){\r
ReFileProperties_t* props = NULL;\r
#if defined __linux__\r
ReDirStatus_t* entry = traverser.topOfStack();\r
#endif\r
makeDirectory(path.str(), minWidth, props, ReLogger::globalLogger());\r
}\r
- if (endsWithSlash)\r
- path.append(OS_SEPARATOR, 1);\r
}\r
/**\r
* Constructor.\r
void ReDirSync::doIt(){\r
ReDirEntryFilter_t filter;\r
const char* sep = OS_SEPARATOR;\r
- struct stat info;\r
ReByteBuffer buffer;\r
ReByteBuffer target(m_programArgs.getArg(m_programArgs.getArgCount() - 1));\r
- if (target.endsWith(sep, 1))\r
- target.setLength(target.length() - 1);\r
- if (stat(target.str(), &info) != 0)\r
+ if (target.endsWith(OS_SEPARATOR))\r
+ target.setLength(target.length() - 1);\r
+ if (! exists(target))\r
help(i18n("target does not exist: $1"), target.str());\r
- else if (! S_ISDIR(info.st_mode))\r
+ else if (! S_ISDIR(m_statInfo.st_mode))\r
help(i18n("target is not a directory: $1"), target.str());\r
size_t lengthTargetBase = target.length();\r
bool addOnly = m_programArgs.getBool("add");\r
bool endsWithSlash = source.endsWith(sep, 1);\r
if (endsWithSlash)\r
source.setLength(source.length() - 1);\r
- if (stat(source.str(), &info) != 0)\r
+ if (! exists(source))\r
help(i18n("source does not exist: $1"), source.str());\r
- else if (! S_ISDIR(info.st_mode))\r
+ else if (! S_ISDIR(m_statInfo.st_mode))\r
help(i18n("source is not a directory: $1"), source.str());\r
if (! endsWithSlash){\r
// the basename of the source will be appended to the target:\r
// append the new relative path from source to target:\r
target.setLength(ixTargetRelative);\r
target.append(entry->m_path.str() + ixSourceRelative, -1);\r
- if (stat(target.str(), &info) != 0)\r
+ if (! exists(target))\r
makeDirWithParents(target, ixTargetRelative, traverser);\r
targetFile.set(target).append(entry->node(), -1);\r
const char* targetRelativePath = targetFile.str() + ixTargetRelative + 1;\r
- bool exists = stat(targetFile.str(), &info) == 0;\r
- if (! exists && mustExist){\r
+ bool targetExists = exists(targetFile);\r
+ if (! targetExists && mustExist){\r
if (m_verboseLevel == V_CHATTER)\r
fprintf(m_output, "-ignored: %s does not exist\n", targetRelativePath);\r
continue;\r
}\r
- if (exists){\r
+ if (targetExists){\r
if (addOnly){\r
if (m_verboseLevel >= V_CHATTER)\r
fprintf(m_output, "~ignored: %s exists\n", targetRelativePath);\r
continue;\r
}\r
- if (ignoreDate && entry->fileSize() == info.st_size){\r
+ if (ignoreDate && entry->fileSize() == m_statInfo.st_size){\r
if (m_verboseLevel >= V_CHATTER)\r
fprintf(m_output, "_ignored: %s same size\n", targetRelativePath);\r
continue;\r
}\r
// target younger than source?\r
- int diff = int(info.st_mtime - entry->filetimeToTime(entry->modified()));\r
- if (! ignoreDate && info.st_mtime - entry->filetimeToTime(entry->modified())\r
+ int diff = int(m_statInfo.st_mtime - entry->filetimeToTime(entry->modified()));\r
+ if (! ignoreDate && m_statInfo.st_mtime - entry->filetimeToTime(entry->modified())\r
<= maxFileTimeDiff) {\r
if (m_verboseLevel >= V_CHATTER)\r
fprintf(m_output, "=ignored: %s same time\n", targetRelativePath);\r
files++;\r
sumSizes += entry->fileSize();\r
if (m_verboseLevel >= V_NORMAL)\r
- fprintf(m_output, "%c%s%s\n", exists ? '!' : '+', targetRelativePath,\r
+ fprintf(m_output, "%c%s%s\n", targetExists ? '!' : '+', targetRelativePath,\r
dry ? " would be copied" : "");\r
if (! dry)\r
copyFile(entry, targetFile.str());\r
int duration = int(time(NULL) - m_start);\r
fprintf(m_output, i18n(\r
"=== copied: %02d:%02d sec %7d file(s) %12.6f MByte (%.3f MB/sec).\n"\r
- "=== tree : %5d dir(s) %7d file(s) %12.6f MByte\n"),\r
+ "=== tree: %5d dir(s) %7d file(s) %12.6f MByte\n"),\r
duration / 60, duration % 60, files, sumSizes / 1E6,\r
sumSizes / 1E6 / (duration == 0 ? 1 : duration),\r
treeDirs, treeFiles, treeSumSizes / 1E6);\r
* Normally it calls <code>processFileArguments()</code> after some initializations.
*/
virtual void doIt() = 0;
- void printSummary();
+ void printSummary(const char* prefix = NULL);
virtual void processDir(ReDirStatus_t* status);
virtual void processFile(ReDirStatus_t* status);
void processFileArguments();
virtual void processSingleFile(const char* filename);
virtual void processTree(const char* filename);
+ /** Tests whether a file or directory exists:
+ @param name can be changed and recovered!
+ @return <code>true</code>: a file with the given name exists
+ */
+ inline bool exists(ReByteBuffer& name){
+#if defined __linux__
+ // linux ignores a trailing slash:
+ return stat(name.str(), &m_statInfo) == 0;
+#elif defined __WIN32__
+ int ix = name.length() - 1;
+ if (ix >= 0 && name.str()[ix] != OS_SEPARATOR_CHAR)
+ ix = -1;
+ else
+ name.setLength(ix);
+ bool rc = stat(name.str(), &m_statInfo) == 0;
+ if (ix >= 0)
+ name.append(OS_SEPARATOR_CHAR);
+ return rc;
+#endif
+ }
protected:
int m_minArguments;
int m_reservedFirst;
ReTraverser m_traverser;
ReDirEntryFilter_t m_filter;
int64_t m_start;
+ struct stat m_statInfo;
};
class ReDirBatch : public ReTool {
#ifdef __linux__\r
return filetime->tv_sec;\r
#elif defined __WIN32__\r
- // takes the last modified date\r
+ // 64-bit arithmetic:\r
LARGE_INTEGER date, adjust;\r
date.HighPart = filetime->dwHighDateTime;\r
date.LowPart = filetime->dwLowDateTime;\r
// removes the diff between 1970 and 1601\r
date.QuadPart -= adjust.QuadPart;\r
// converts back from 100-nanoseconds to seconds\r
- return (time_t) (date.QuadPart / 10000000);\r
+ time_t rc = (time_t) (date.QuadPart / 10000000);\r
+ return rc;\r
#endif\r
}\r
/**\r