append(" ", 1);
}
append("\n", 1);
- length = length <= (int) bytesPerLine ? 0 : length - bytesPerLine;
+ length = length <= (size_t) bytesPerLine ? 0 : length - bytesPerLine;
data += bytesPerLine;
offset += bytesPerLine;
}
/** @brief Appends a string at the end.
*
* @param source the new string
- * @param tagOf An item which will stored with the string. It can be retrieved
- * by the same index.This class knows nothing about this
+ * @param tag a number which will stored with the string. Not interpreted by the instance
* @return the instance itself (for chaining)
*/
-ReStringList& ReStringList::append(const ReByteBuffer& source, Tag tagOf){
- int ix = add(-1, source.str(), source.length() + 1, tagOf);
+ReStringList& ReStringList::append(const ReByteBuffer& source, Tag tag){
+ int ix = add(-1, source.str(), source.length() + 1, tag);
return *this;
}
* @param source the new string of the replaced element
*/
void ReStringList::replaceString(Index index, const char* source){
+ Tag tag;
if (index < count()){
Sequence* seq = getInfo(index);
- set(index, source, strlen(source) + 1, seq->m_tag);
+ getLengthAndTag(seq, tag);
+ set(index, source, -1, tag);
}
}
/** @brief Replaces a tagOf in the internal array.
* The string of the element remains unchanged.
*
* @param index the element with this index will be replaced
- * @param source the new string of the replaced element
+ * @param tag the new tag
*/
-void ReStringList::replaceTag(Index index, Tag tagOf){
- if (index < count()){
- Sequence* seq = getInfo(index);
- seq->m_tag = tagOf;
- }
+void ReStringList::replaceTag(Index index, Tag tag){
+ ReByteBuffer buffer;
+ if (get(index, buffer))
+ set(index, buffer.str(), buffer.length(), tag);
}
/** @brief Returns the C string given by an index.
*/
ReSeqArray::Tag ReStringList::tagOf(Index index) const{
Tag rc = -1;
- if (index < count()){
- Sequence* seq = getInfo(index);
- rc = seq->m_tag;
+ ReByteBuffer buffer;
+ if (! get(index, buffer, &rc)){
+ rc = -1;
}
return rc;
}
size_t rc = 0;
if (index < count()){
Sequence* seq = getInfo(index);
- //@ToDo
rc = getLength(seq);
}
return rc;
*/
size_t ReStringList::strLengthOf(Index index) const{
size_t rc = 0;
- if (index < count()){
- Sequence* seq = getInfo(index);
- rc = rc = getLength(seq) - 1;
- }
+ ReByteBuffer buffer;
+ if (get(index, buffer))
+ rc = buffer.length() - 1;
return rc;
}
/** @brief Returns the sum of all string lengths stored in the array.
*
- * @return the sum of all string lengths stored in the array
+ * @return the sum of all string lengths stored in the array (including trailing '\0')
*/
size_t ReStringList::sumOfSizes() const{
size_t rc = 0;
-
+ ReByteBuffer buffer;
for (int ii = count() - 1; ii >= 0; ii--){
- Sequence* seq = getInfo(ii);
- rc += rc = getLength(seq);
+ get(ii, buffer);
+ rc += buffer.length();
}
return rc;
}
const char ReStringUtils::AUTO_SEPARATOR = '\0';
-/** @brief Splits a filename into its parts.
- *
- * Example: file:/etc/samba/smb.conf
- * The 4 parts are:
- * <ul><li>A protocol: file:</li>
- * <li>The path. /etc/samba/ </li>
- * <li>The name: smb</li>
- * <li>The extension: .conf</li>
- * </ul>
- * All parts exclusive the name can be empty.
- *
- * <pre><code>Example:
- * ReByteBuffer protocol, path, name, ext;
- * ReByteBuffer fnBackup;
- * ReStringUtils::splitPath("/etc/sambe/smb.conf", &protocol, &path, &name, NULL);
- * ext.append(".bak");
- * ReStringUtils::joinPath(fnBackup, &protocol, &path, &name, &ext);
- * assert("/etc/sambe/smb.bak", fnBackup.getBuffer());
- * </code></pre>
+/** @brief Tests whether a phrase is in a phrase list.
*
- * @param fullname The full name to split.
- * @param protocol Out: The protocol part of the filename. May be NULL.
- * @param path Out: The path of the filename. May be NULL.
- * @param name Out: The name part of the filename. May be NULL.
- * @param ext Out: The extension. May be NULL.
+ * @param phrase The word to search.
+ * @param list The list to search. All phrases of the list are separated by <code>separator</code>.
+ * @param ignoreCase true: The search is case insensitive. false: The search is case sensitive.
+ * @param separator The separator in <code>list</code>. If <code>AUTO_SEPARATOR</code> the separator will
+ * be taken from the list itself (the first character).
*/
-void ReStringUtils::splitPath(const char* fullname,
- ReByteBuffer* protocol, ReByteBuffer* path, ReByteBuffer* name, ReByteBuffer* ext){
- const char currentSlash = strchr(fullname, '/') != NULL ? '/' : OS_SEPARATOR_CHAR;
- const char* start = strchr(fullname, ':');
- if (protocol != NULL){
- protocol->setLength(0);
- if (start != NULL)
- protocol->append(fullname, start - fullname + 1);
- }
- if (start == NULL)
- start = fullname;
- else
- start++;
-
- const char* end = strrchr(start, currentSlash);
-
- if (path != 0){
- path->setLength(0);
- if (end != NULL)
- path->append(start, end - start + 1);
+bool ReStringUtils::isInList(const char* phrase, const char* list,
+ bool ignoreCase, char separator){
+ if (separator == AUTO_SEPARATOR)
+ separator = *list++;
+ const char* end = strchr(list, separator);
+ int phraseLength = strlen(phrase);
+ bool rc = false;
+ while(! rc && end != NULL){
+ if (end - list == phraseLength){
+ if (ignoreCase)
+ rc = strnicmp(list, phrase, phraseLength) == 0;
+ else
+ rc = strncmp(list, phrase, end - list) == 0;
+ if (rc)
+ break;
+ }
+ list = end + 1;
+ end = strchr(list, separator);
}
- if (end != NULL)
- start = end + 1;
- end = strrchr(start, '.');
- if (name != NULL){
- name->setLength(0);
- if (end == NULL)
- name->append(start, strlen(start));
+ if (! rc){
+ if (ignoreCase)
+ rc = strnicmp(list, phrase, end - list) == 0;
else
- name->append(start, end - start);
- }
- if (ext != NULL){
- ext->setLength(0);
- if (end != NULL)
- ext->append(end, strlen(end));
+ rc = strncmp(list, phrase, end - list) == 0;
}
+ return rc;
}
+
/** Joins a filename from parts.
*
* <pre><code>Example:
return fullpath;
}
+
+/**
+ * Returns the length of an integer in a given text.
+ *
+ * @param text the number is expected in this text
+ * @param length the length of the text. -1: <code>strlen(text)</code>
+ * @param value OUT: the value of the integer. May be NULL
+ * @return the number of digits at the start of the text
+ */
+int ReStringUtils::lengthOfUnsigned(const char* text, int length,
+ unsigned int* value){
+ int rc = 0;
+ unsigned int val = 0;
+ char cc;
+ if (length < 0)
+ // the '\0' is automatically a stopper, no need to call strlen()
+ length = INT_MAX;
+ while(rc < length && isdigit(cc = *text++)){
+ rc++;
+ val = 10*val + cc - '0';
+ }
+ if (value != NULL && rc != 0)
+ *value = val;
+ return rc;
+}
+
+/** @brief Splits a filename into its parts.
+ *
+ * Example: file:/etc/samba/smb.conf
+ * The 4 parts are:
+ * <ul><li>A protocol: file:</li>
+ * <li>The path. /etc/samba/ </li>
+ * <li>The name: smb</li>
+ * <li>The extension: .conf</li>
+ * </ul>
+ * All parts exclusive the name can be empty.
+ *
+ * <pre><code>Example:
+ * ReByteBuffer protocol, path, name, ext;
+ * ReByteBuffer fnBackup;
+ * ReStringUtils::splitPath("/etc/sambe/smb.conf", &protocol, &path, &name, NULL);
+ * ext.append(".bak");
+ * ReStringUtils::joinPath(fnBackup, &protocol, &path, &name, &ext);
+ * assert("/etc/sambe/smb.bak", fnBackup.getBuffer());
+ * </code></pre>
+ *
+ * @param fullname The full name to split.
+ * @param protocol Out: The protocol part of the filename. May be NULL.
+ * @param path Out: The path of the filename. May be NULL.
+ * @param name Out: The name part of the filename. May be NULL.
+ * @param ext Out: The extension. May be NULL.
+ */
+void ReStringUtils::splitPath(const char* fullname,
+ ReByteBuffer* protocol, ReByteBuffer* path, ReByteBuffer* name, ReByteBuffer* ext){
+ const char currentSlash = strchr(fullname, '/') != NULL ? '/' : OS_SEPARATOR_CHAR;
+ const char* start = strchr(fullname, ':');
+ if (protocol != NULL){
+ protocol->setLength(0);
+ if (start != NULL)
+ protocol->append(fullname, start - fullname + 1);
+ }
+ if (start == NULL)
+ start = fullname;
+ else
+ start++;
+
+ const char* end = strrchr(start, currentSlash);
+
+ if (path != 0){
+ path->setLength(0);
+ if (end != NULL)
+ path->append(start, end - start + 1);
+ }
+ if (end != NULL)
+ start = end + 1;
+ end = strrchr(start, '.');
+ if (name != NULL){
+ name->setLength(0);
+ if (end == NULL)
+ name->append(start, strlen(start));
+ else
+ name->append(start, end - start);
+ }
+ if (ext != NULL){
+ ext->setLength(0);
+ if (end != NULL)
+ ext->append(end, strlen(end));
+ }
+}
/** @brief Compares two strings case insensitive.
*
* @param string1 The first C string to compare.
}
return rc;
}
-
-/** @brief Tests whether a phrase is in a phrase list.
- *
- * @param phrase The word to search.
- * @param list The list to search. All phrases of the list are separated by <code>separator</code>.
- * @param ignoreCase true: The search is case insensitive. false: The search is case sensitive.
- * @param separator The separator in <code>list</code>. If <code>AUTO_SEPARATOR</code> the separator will
- * be taken from the list itself (the first character).
- */
-bool ReStringUtils::isInList(const char* phrase, const char* list,
- bool ignoreCase, char separator){
- if (separator == AUTO_SEPARATOR)
- separator = *list++;
- const char* end = strchr(list, separator);
- int phraseLength = strlen(phrase);
- bool rc = false;
- while(! rc && end != NULL){
- if (end - list == phraseLength){
- if (ignoreCase)
- rc = strnicmp(list, phrase, phraseLength) == 0;
- else
- rc = strncmp(list, phrase, end - list) == 0;
- if (rc)
- break;
- }
- list = end + 1;
- end = strchr(list, separator);
- }
- if (! rc){
- if (ignoreCase)
- rc = strnicmp(list, phrase, end - list) == 0;
- else
- rc = strncmp(list, phrase, end - list) == 0;
- }
- return rc;
-}
//@ If used in <code>isInList()</code> the first character of the list will be the separator.
static const char AUTO_SEPARATOR;
public:
- static void splitPath(const char* fullname,
- ReByteBuffer* protocol, ReByteBuffer* path, ReByteBuffer* name, ReByteBuffer* ext);
+ static bool isInList(const char* phrase, const char* list,
+ bool ignoreCase = true, char separator = AUTO_SEPARATOR);
static ReByteBuffer& joinPath(ReByteBuffer& result,
ReByteBuffer* protocol, ReByteBuffer* path, ReByteBuffer* name, ReByteBuffer* ext);
static ReByteBuffer& joinPath(ReByteBuffer& result,
const char* protocol, const char* path, const char* name, const char* ext);
+ static int lengthOfUnsigned(const char* text, int length = -1, unsigned int* value = NULL);
+ static void splitPath(const char* fullname,
+ ReByteBuffer* protocol, ReByteBuffer* path, ReByteBuffer* name, ReByteBuffer* ext);
static int strnicmp(const char* string1, const char* string2, size_t length);
- static bool isInList(const char* phrase, const char* list,
- bool ignoreCase = true, char separator = AUTO_SEPARATOR);
};
#endif /* RESTRINGUTILS_H_ */
--- /dev/null
+/*
+ * cuReTraverser.cpp
+ *
+ * License: Public domain
+ * Do what you want.
+ * No warranties and disclaimer of any damages.
+ * The latest sources: https://github.com/republib
+ */
+
+#include "base/rebase.hpp"
+#include "os/reos.hpp"
+
+static const char* s_empty[] = { NULL };
+
+class TestReDirTools : public ReTestUnit {
+public:
+ TestReDirTools() : ReTestUnit("ReTraverser", __FILE__){
+ m_base = testDir();
+ m_base.append("traverser").append(OS_SEPARATOR, -1);
+ _mkdir(m_base.str(), ALLPERMS);
+ run();
+ }
+private:
+ ReByteBuffer m_base;
+ ReByteBuffer m_buffer;
+private:
+ const char* makeDir(const char* relPath){
+ m_buffer = m_base;
+ m_buffer.append(relPath);
+ m_buffer.replaceAll("/", 1, OS_SEPARATOR, -1);
+ _mkdir(m_buffer.str(), ALLPERMS);
+ struct stat info;
+ if (stat(m_buffer.str(), &info) != 0){
+ logF(true, "cannot create dir %1$s", m_buffer.str());
+ }
+ return m_buffer.str();
+ }
+ void makeFile(const char* relPath){
+ ReByteBuffer path(m_base);
+ path.append("/").append(relPath);
+ path.replaceAll("/", 1, OS_SEPARATOR, -1);
+ createFile(path.str(), relPath);
+ struct stat info;
+ if (stat(path.str(), &info) != 0){
+ logF(true, "cannot create file %1$s", path.str());
+ }
+ }
+ void initTree(){
+ makeFile("1.txt");
+ makeDir("dir1");
+ makeDir("dir2");
+ makeDir("dir1/dir1_1");
+ makeDir("dir1/dir1_2");
+ makeDir("dir1/dir1_2/dir1_2_1");
+ makeDir("dir1/cache");
+ makeFile("dir1/dir1_2/dir1_2_1/x1.txt");
+ makeFile("dir1/dir1_2/dir1_2_1/x2.txt");
+ makeFile("dir2/2.x");
+ makeFile("dir1/cache/cache.txt");
+ }
+ void run(){
+ initTree();
+
+ testToolSync();
+ testToolStatistic();
+ testBasic();
+ testDirOptions();
+ checkSetFilterFromProgramArgs();
+ testDirStatistic();
+ testCopyFile();
+ testList();
+ }
+ void testList(){
+ ReDirTools tools;
+ const char* argv[] = { "list", m_base.str(), NULL };
+ tools.list(2, argv);
+ }
+ void testCopyFile(){
+#if defined __linux__
+ ReByteBuffer src(m_base);
+ src.append("dir1/dir1_2/dir1_2_1/x1.txt");
+ ReByteBuffer trg(testDir());
+ trg.append("copy_x1.txt");
+ ReByteBuffer buffer;
+ buffer.ensureSize(5);
+ ReDirSync::copyFile(src.str(), NULL, trg.str(), buffer,
+ ReLogger::globalLogger());
+ checkFileEqu(src.str(), trg.str());
+#else
+ log(false, "testCopyFile not implemented");
+#endif
+ }
+
+ void checkRelDate(time_t absTime, int relTime){
+ int diff = int(time(NULL) - relTime - absTime);
+ if (diff < 0)
+ diff = - diff;
+ checkT(diff < 2);
+ }
+
+ void testDirOptions(){
+ class MyOptions : public ReDirOptions{
+ public:
+ MyOptions() : ReDirOptions(s_empty, s_empty) {}
+ public:
+ int count() { return m_countCompoundUsage; }
+ const char** usage() { return m_compoundUsage; }
+ };
+ static const char* usage1[] = { "line1", "line2", NULL };
+ static const char* usage2[] = { "x1", "x2", "x3", NULL };
+ MyOptions opts;
+ opts.initCompoundUsage(sizeof usage1 + sizeof usage2);
+ opts.addCompoundUsage(usage1);
+ opts.addCompoundUsage(usage2);
+ checkEqu(7, opts.count());
+ checkEqu("line1", opts.usage()[0]);
+ checkEqu("line2", opts.usage()[1]);
+ checkEqu("x1", opts.usage()[2]);
+ checkEqu("x2", opts.usage()[3]);
+ checkEqu("x3", opts.usage()[4]);
+
+ // local time: +3600
+ const int DIFF = 3600;
+ checkEqu(24*60*60 - DIFF, (int) opts.checkDate("1970.01.02"));
+ checkEqu(24*60*60+3600-DIFF, (int) opts.checkDate("1970.01.02/1"));
+ checkEqu(24*60*60 + 2*3600 + 33*60 - DIFF, (int) opts.checkDate("1970.01.02/02:33"));
+ checkRelDate(opts.checkDate("3m"), 3*60);
+ checkRelDate(opts.checkDate("7h"), 7*60*60);
+ checkRelDate(opts.checkDate("5d"), 5*24*60*60);
+
+ checkEqu(125ll, opts.checkSize("125"));
+ checkEqu(125ll, opts.checkSize("125b"));
+ checkEqu(3000ll, opts.checkSize("3k"));
+ checkEqu(3*1024ll, opts.checkSize("3K"));
+ checkEqu(4*1000*1000ll, opts.checkSize("4m"));
+ checkEqu(4*1024*1024ll, opts.checkSize("4M"));
+ checkEqu(5*1000*1000*1000ll, opts.checkSize("5g"));
+ checkEqu(5*1024*1024*1024ll, opts.checkSize("5G"));
+
+ }
+ void checkSetFilterFromProgramArgs(){
+ ReDirOptions opts(s_empty, s_empty);
+ opts.addStandardFilterOptions();
+ const char* argv[] = { "x", "-y1970.01.02", "-o1970.01.03",
+ "-D5", "-d1", "-z1k", "-Z2M", "-p;*;-*~"
+ };
+ ReDirEntryFilter_t filter;
+ opts.programArgsChangeable().init(sizeof argv / sizeof argv[0], argv);
+ opts.setFilterFromProgramArgs(filter);
+ // local time: +3600
+ const int DIFF = 3600;
+ checkEqu(1*24*3600 - DIFF, (int) filter.m_maxAge);
+ checkEqu(2*24*3600 - DIFF, (int) filter.m_minAge);
+ checkEqu(5, (int) filter.m_maxDepth);
+ checkEqu(1, (int) filter.m_minDepth);
+ checkEqu(1000ll, filter.m_minSize);
+ checkEqu(2*1024*1024ll, filter.m_maxSize);
+ checkNN(filter.m_nodePatterns);
+ checkEqu(";*;-*~", filter.m_nodePatterns->patternString());
+ }
+ void checkOneFile(const char* node, const char* parent, const ReHashList& hash){
+ ReByteBuffer path, expected;
+ checkT(hash.get(ReByteBuffer(node), path));
+ expected.set(parent, -1);
+ if (! expected.endsWith(OS_SEPARATOR))
+ expected.append(OS_SEPARATOR);
+ if (! path.endsWith(expected.str(), -1))
+ checkT(false);
+ }
+ void testBasic(){
+ ReTraverser traverser(m_base.str());
+ RePatternList patterns;
+ // exclude */cache/*
+ ReByteBuffer buffer(";*;-*/cache");
+ patterns.set(buffer.str());
+ traverser.setDirPattern(&patterns);
+ int level = 0;
+ ReDirStatus_t* entry;
+ ReHashList hash;
+ while( (entry = traverser.rawNextFile(level)) != NULL){
+ hash.put(ReByteBuffer(entry->node(), -1), entry->m_path);
+ logF(false, "%d: %-12s %2d %s",
+ level, entry->node(),
+ int(entry->fileSize()),
+ entry->m_path.str());
+ }
+ checkOneFile("x1.txt", "dir1_2_1", hash);
+ checkOneFile("x2.txt", "dir1_2_1", hash);
+ checkOneFile("dir1_2_1", "dir1_2", hash);
+ checkOneFile("dir1_1", "dir1", hash);
+ checkOneFile("dir1_2", "dir1", hash);
+ checkF(hash.get("cache.txt", buffer));
+ }
+ void testDirStatistic(){
+ ReDirStatistic stat;
+ const ReStringList& list = stat.calculate(m_base.str(), 1);
+ ReByteBuffer buffer;
+ ReByteBuffer expected;
+ log(false, list.join("\n", buffer).str());
+ checkEqu(4u, list.count());
+ // "1 t:\temp\winfried\2\retestunit\dir1\n"
+ buffer.set(list.strOf(0), list.strLengthOf(0));
+ checkT(buffer.startsWith("1\t"));
+ expected.set(m_base.str(), m_base.length()).append("dir1", -1)
+ .append(OS_SEPARATOR);
+ // .append(OS_SEPARATOR, -1)
+ checkT(buffer.endsWith(expected.str()));
+
+ buffer.setLength(0);
+ const ReStringList& list2 = stat.calculate(m_base.str(), 1, formatWithSizeFilesAndDirs);
+ log(false, list2.join("\n", buffer).str());
+
+ buffer.set(list.strOf(0), list.strLengthOf(0));
+ checkT(buffer.startsWith(" 0.000074 MB 3 4\t"));
+ expected.set(m_base.str(), m_base.length()).append("dir1", -1)
+ .append(OS_SEPARATOR);
+ checkT(buffer.endsWith(expected.str()));
+
+ buffer.set(list.strOf(1), list.strLengthOf(1));
+ checkT(buffer.startsWith(" 0.000054 MB 2 3\t"));
+ expected.set(m_base.str(), m_base.length()).append("dir2", -1)
+ .append(OS_SEPARATOR);
+ checkT(buffer.endsWith(expected.str()));
+
+ buffer.set(list.strOf(2), list.strLengthOf(2));
+ checkT(buffer.startsWith(" 0.000087 MB 5 6\t"));
+ expected.set(m_base.str(), m_base.length());
+ }
+ void testToolStatistic(){
+ ReDirTools tools;
+ const char* argv[] = { "dt", "stat", "-P;*;-*/cache/*", m_base.str(), "2" };
+ tools.main(5, (char**) argv);
+ }
+ void testToolSync(){
+ ReDirTools tools;
+ ReByteBuffer source(m_base);
+ source.append("dir1");
+ ReByteBuffer target(makeDir("synctest"));
+ const char* argv[] = { "dt", "sync", "-P;*;-*/cache/*", "-p;*.txt",
+ source.str(), target.str() };
+ tools.main(6, (char**) argv);
+ }
+};
+extern void testReDirTools(void);
+
+void testReDirTools(void){
+ TestReDirTools unit;
+}
ReStringList list;
ReByteBuffer line;
line.set("Hi", -1);
- list.append(line, 99ll);
+ list.append(line, 99);
line.append("!", -1);
- list.append(line, -5ll);
+ list.append(line, -5);
checkEqu(2u, list.count());
checkEqu(99ll, list.tagOf(0));
checkEqu("Hi", list.strOf(0));
}
void testString(){
- void testReCString();
- testReCString();
- extern void testReStringList(void);
- testReStringList();
+ void testReStringUtils(void);
+ testReStringUtils();
extern void testReI18N(void);
testReI18N();
testReMatcher();
}
void testOs(){
+ void testReDirTools();
+ testReDirTools();
+
void testReTraverser();
testReTraverser();
+ void testReDirTools();
+ testReDirTools();
}
void testMath(){
extern void testReMD5();
void testAll(){
try
{
- testBase();
+ testString();
testOs();
testBase();
* @param example a string vector with some examples how to use the command
*/
ReDirOptions::ReDirOptions(const char* usage[], const char* examples[]) :
+ ReTraceUnit(INT_MAX, INT_MAX),
m_programArgs(usage, examples),
m_nodePatterns(),
m_pathPatterns(),
m_compoundUsage(NULL),
m_countCompoundUsage(0),
- m_output(stdout)
+ m_output(stdout),
+ m_verboseLevel(V_NORMAL)
{
}
/**
* Adds the standard filter options.
*/
void ReDirOptions::addStandardFilterOptions(){
- // standard short options: D d O o P p q T t y Z z
+ // standard short options: D d O o P p T t v y Z z
m_programArgs.addInt("maxdepth",
i18n("the depth of the subdirectory is lower or equal <number>\n"
"0: search is done only in the base directory"),
"of the 'not patterns' matches\n"
"examples: '*.cpp;*.hpp;Make*' '*;-*.bak;-*~"),
'p', "basename-pattern", false, NULL);
- m_programArgs.addBool("quiet",
- i18n("no additional information like runtime"),
- 'q', "quiet", false);
+ m_programArgs.addString("verbose",
+ i18n("verbose level: 0: no info, 1: summary only, 2: normal, 3: chatter mode, 4: debug"),
+ 'v', "verbose", false, "1");
m_programArgs.addInt("trace",
i18n("all <number> seconds the current path will be traced\n"
"0: no trace"),
checkSize(m_programArgs.getString("maxsize", buffer));
checkSize(m_programArgs.getString("minsize", buffer));
checkPatternList(m_programArgs.getString("nodepattern", buffer));
- checkPatternList(m_programArgs.getString("pathpattern", buffer));
+ checkPatternList(m_programArgs.getString("pathpattern", buffer));
+ if (m_programArgs.getString("verbose", buffer)[0] != '\0'){
+ unsigned level = V_NORMAL;
+ if (ReStringUtils::lengthOfUnsigned(buffer.str(), -1, &level)
+ != buffer.length())
+ help(i18n("verbose level is not a number (or '')"), buffer.str());
+ else
+ m_verboseLevel = VerboseLevel(level);
+ }
}
/**
m_pathPatterns.set(buffer.str());
filter.m_pathPatterns = &m_pathPatterns;
}
- filter.m_traceInterval = m_programArgs.getInt("trace");
+ if ( (m_interval = m_programArgs.getInt("trace")) != 0)
+ m_triggerCount = 10;
if (m_programArgs.getString("output", buffer)[0] != '\0'){
if ( (m_output = fopen(buffer.str(), "w")) == NULL){
help("cannot open output file", buffer.str());
time_t now = time(NULL);
int diff = int(now - m_lastTrace);
if (diff >= m_traceInterval){
- fprintf(stderr, "%s\n", current->m_path.str());
+ fprintf(m_output, "%s\n", current->m_path.str());
m_lastTrace = now;
}
}
ReDirBatch::ReDirBatch() :
ReDirOptions(s_batchUsage, s_batchExamples)
{
- // standard short options: D d O o P p q T t y Z z
+ // standard short options: D d O o P p T t v y Z z
m_programArgs.addString("first",
i18n("defines the first line of the output"),
'1', "first-line", true,
ReDirOptions(s_syncUsage, s_syncExamples),
m_buffer()
{
- // standard short options: D d O o P p q T t y Z z
+ // standard short options: D d O o P p T t v y Z z
m_buffer.ensureSize(4u*1024u*1024u);
m_programArgs.addBool("add",
i18n("copies only files which does not exist on the target"),
m_programArgs.addBool("ignoredate",
i18n("the modification is recognized only by the different size (not time)"),
'i', "ignore-time", false);
- m_programArgs.addBool("chatter",
- i18n("comments the action of each file"),
- 'h', "chatter", false);
m_programArgs.addBool("mustexist",
i18n("files which don't exist on the target will not be copied"),
'm', "must-exist", false);
help(i18n("target is not a directory: $1"), target.str());
size_t lengthTargetBase = target.length();
bool addOnly = m_programArgs.getBool("add");
- bool chatterMode = m_programArgs.getBool("chatter");
int maxFileTimeDiff = m_programArgs.getInt("timediff");
bool dry = m_programArgs.getBool("dry");
bool ignoreDate = m_programArgs.getBool("ignoredate");
bool mustExist = m_programArgs.getBool("mustexist");
- bool verbose = ! m_programArgs.getBool("quiet");
setFilterFromProgramArgs(filter);
int64_t sumSizes = 0;
int files = 0;
const char* targetRelativePath = targetFile.str() + ixTargetRelative + 1;
bool exists = stat(targetFile.str(), &info) == 0;
if (! exists && mustExist){
- if (chatterMode)
+ if (m_verboseLevel == V_CHATTER)
fprintf(m_output, "-ignored: %s does not exist\n", targetRelativePath);
continue;
}
if (exists){
if (addOnly){
- if (chatterMode)
+ if (m_verboseLevel >= V_CHATTER)
fprintf(m_output, "~ignored: %s exists\n", targetRelativePath);
continue;
}
if (ignoreDate && entry->fileSize() == info.st_size){
- if (chatterMode)
+ if (m_verboseLevel >= V_CHATTER)
fprintf(m_output, "_ignored: %s same size\n", targetRelativePath);
continue;
}
// target younger than source?
- int diff = info.st_mtime - entry->filetimeToTime(entry->modified());
+ int diff = int(info.st_mtime - entry->filetimeToTime(entry->modified()));
if (! ignoreDate && info.st_mtime - entry->filetimeToTime(entry->modified())
<= maxFileTimeDiff) {
- if (chatterMode)
+ if (m_verboseLevel >= V_CHATTER)
fprintf(m_output, "=ignored: %s same time\n", targetRelativePath);
continue;
}
}
files++;
sumSizes += entry->fileSize();
- if (verbose || chatterMode)
+ if (m_verboseLevel >= V_NORMAL)
fprintf(m_output, "%c%s%s\n", exists ? '!' : '+', targetRelativePath,
dry ? " would be copied" : "");
if (! dry)
treeDirs += traverser.directories();
treeSumSizes+= traverser.sizes();
}
- if (verbose){
+ if (m_verboseLevel >= V_SUMMARY){
int duration = int(time(NULL) - start);
fprintf(m_output, i18n(
"=== copied: %02d:%02d sec %7d file(s) %12.6f MByte (%.3f MB/sec).\n"
#ifndef OS_DIRTOOLS_HPP_
#define OS_DIRTOOLS_HPP_
-class ReDirOptions {
+class ReDirOptions : ReTraceUnit{
+public:
+ enum VerboseLevel {
+ V_UNDEF,
+ V_QUIET,
+ V_SUMMARY,
+ V_NORMAL,
+ V_CHATTER,
+ V_DEBUG
+ };
public:
ReDirOptions(const char* usage[], const char* example[]);
~ReDirOptions();
const char** m_compoundUsage;
int m_countCompoundUsage;
FILE* m_output;
+ VerboseLevel m_verboseLevel;
};
#if defined __linux__
m_minAge(0),\r
m_maxAge(0),\r
m_minDepth(0),\r
- m_maxDepth(512),\r
- m_traceInterval(0),\r
- m_lastTrace(0),\r
- m_traceCounter(0)\r
+ m_maxDepth(512)\r
{\r
}\r
\r
bool ReDirEntryFilter_t::match(ReDirStatus_t& entry){\r
bool rc = false;\r
do {\r
- if (m_traceCounter++ % 100 == 0 && m_traceInterval > 0){\r
- time_t now = time(NULL);\r
- if (int(now - m_lastTrace) > m_traceInterval){\r
- m_lastTrace = now;\r
- fprintf(stderr, "%s%s\n", entry.m_path.str(), entry.node());\r
- }\r
- }\r
if (0 == (entry.type() & m_types))\r
break;\r
int64_t size = entry.fileSize();\r
return &m_status;\r
}\r
#endif\r
+
+/**
+* Constructor.
+*
+* @param triggerCount efficiency: only every N calls a time check takes place
+* @param interval the minimum number of seconds between two traces
+*/
+ReTraceUnit::ReTraceUnit(int triggerCount, int interval) :
+ m_count(0),
+ m_triggerCount(triggerCount),
+ m_lastTrace(time(NULL)),
+ m_interval(interval)
+{
+}
+/**
+ * Destructor.
+ */
+ReTraceUnit::~ReTraceUnit(){
+}
+\r
+/**\r
+ * Prints a message.\r
+ *\r
+ * Often overwritten by a subclass.\r
+ *\r
+ * @param message message for the trace\r
+ * @return <code>true</code> (for chaining)\r
+ */\r
+bool ReTraceUnit::trace(const char* message){
+ printf("%d\n", message);
+ return true;
+}
\r
/**\r
* Constructor.\r
*\r
* @param base the base directory. The traversal starts at this point\r
*/\r
-ReTraverser::ReTraverser(const char* base) :\r
+ReTraverser::ReTraverser(const char* base, ReTraceUnit* tracer) :\r
m_minLevel(0),\r
m_maxLevel(512),\r
m_level(-1),\r
m_dirPatterns(NULL),\r
m_directories(0),\r
m_files(0),\r
- m_sizes(0ll)\r
-\r
+ m_sizes(0ll),\r
+ m_tracer(tracer)\r
{\r
memset(m_dirs, 0, sizeof m_dirs);\r
m_dirs[0] = new ReDirStatus_t();\r
if (alreadyRead || current->findNext()){\r
alreadyRead = false;\r
// a file or directory found:\r
+ if (m_tracer != NULL && m_tracer->isCountTriggered() \r
+ && m_tracer->isTimeTriggered())\r
+ m_tracer->trace(current->fullName());\r
if (current->m_passNo != m_passNoForDirSearch){\r
// we search for any file:\r
rc = m_dirs[m_level];\r
static time_t filetimeToTime(const FileTime_t* time);
static void timeToFiletime(time_t time, FileTime_t& filetime);
};
-class ReDirEntryFilter_t {
+class ReDirEntryFilter_t{
public:
ReDirEntryFilter_t();
~ReDirEntryFilter_t();
time_t m_maxAge;
int m_minDepth;
int m_maxDepth;
- int m_traceInterval;
- time_t m_lastTrace;
- int m_traceCounter;
+};
+class ReTraceUnit{
+public:
+ ReTraceUnit(int m_triggerCount = 10, int interval = 60);
+ virtual ~ReTraceUnit();
+public:
+ /** Returns whether the the instance is triggered.
+ * To avoid too the not cheap call of time() the trace unit uses a counter.
+ * If the counter reaches a given level the time check should be done.
+ * @return <code>true</code>: the counter has reached <code>m_triggerCount</code>
+ */
+ inline bool isCountTriggered(){
+ bool rc = ++m_count % m_triggerCount == 0;
+ return rc;
+ }
+ /** Tests whether a given waiting time has been gone since the last trace.
+ * @return <code>true</code>: the last trace has been done after
+ * at least <code>m_interval</code> seconds
+ */
+ inline bool isTimeTriggered(){
+ time_t now = time(NULL);
+ bool rc = now - m_lastTrace > m_interval;
+ if (rc){
+ m_lastTrace = now;
+ }
+ return rc;
+ }
+ virtual bool trace(const char* message);
+protected:
+ int m_count;
+ int m_triggerCount;
+ time_t m_lastTrace;
+ int m_interval;
};
#define MAX_ENTRY_STACK_DEPTH 256
class ReTraverser {
public:
- ReTraverser(const char* base);
+ ReTraverser(const char* base, ReTraceUnit* tracer = NULL);
virtual ~ReTraverser();
public:
/**
int m_directories;
int m_files;
int64_t m_sizes;
+ ReTraceUnit* m_tracer;
};
#endif /* OS_RETRAVERSER_HPP_ */