From bea6b074be69cf587f46e9c25c6900652cab2a7d Mon Sep 17 00:00:00 2001 From: kawi Date: Sun, 4 Jan 2015 00:41:18 +0100 Subject: [PATCH] "dirtool list" command implemented --- base/ReProgramArgs.cpp | 11 ++ base/ReProgramArgs.hpp | 3 +- cunit/cuReProgramArgs.cpp | 14 +++ cunit/cuReTraverser.cpp | 6 +- os/ReDirTools.cpp | 221 +++++++++++++++++++++++++++----------- os/ReDirTools.hpp | 12 ++- os/ReTraverser.cpp | 40 ++++++- os/ReTraverser.hpp | 23 +++- 8 files changed, 253 insertions(+), 77 deletions(-) diff --git a/base/ReProgramArgs.cpp b/base/ReProgramArgs.cpp index 29bd3e4..ce7d449 100644 --- a/base/ReProgramArgs.cpp +++ b/base/ReProgramArgs.cpp @@ -61,6 +61,7 @@ ReProgramArgs::ReProgramArgs(const char* usageList[], const char* examples[]) } } } + /** @brief Constructor. * * @param usage A string with the description of the usage. @@ -97,6 +98,16 @@ ReProgramArgs::ReProgramArgs(const char* usageString, const char* examples) ReProgramArgs::~ReProgramArgs() { } +/** + * Sets the usage message. + * + * @param usage a vector of lines without '\n' + */ +void ReProgramArgs::setUsage(const char* usage[]){ + m_usage.clear(); + for (int ix = 0; usage[ix] != NULL; ix++) + m_usage.append(usage[ix]); +} /** @brief Puts the property infos into the property string. * * The property string is a string stored in the hashlist. diff --git a/base/ReProgramArgs.hpp b/base/ReProgramArgs.hpp index a07b08e..b5243d4 100644 --- a/base/ReProgramArgs.hpp +++ b/base/ReProgramArgs.hpp @@ -72,6 +72,7 @@ public: void setLastError(const char* message); void help(const char* message, bool issueLastError, ReStringList& lines); void help(const char* message, bool issueLastError, FILE* stream); + void setUsage(const char* usage[]); private: void addProperties(const char*name, const char* description, char shortOpt, @@ -80,7 +81,7 @@ private: void setValue(const char* name, const char* value, const char* dataType); bool analyseShort(const char* opt, const char* nextArg); void analyseLong(const char* opt); -private: +protected: ReStringList m_usage; ReStringList m_examples; ReHashList m_properties; diff --git a/cunit/cuReProgramArgs.cpp b/cunit/cuReProgramArgs.cpp index b41e5bd..eb05e1f 100644 --- a/cunit/cuReProgramArgs.cpp +++ b/cunit/cuReProgramArgs.cpp @@ -13,6 +13,7 @@ private: testLong(); testShort(); testWrong(); + testSetUsage(); } void testWrong(){ ReProgramArgs args("test","example"); @@ -69,6 +70,19 @@ private: args.help("Not really an error!", false, stdout); } + void testSetUsage(){ + class MyArgs : public ReProgramArgs { + public: + MyArgs() : ReProgramArgs("", "example") {} + public: + ReStringList& usage() { return m_usage; } + }; + MyArgs args; + const char* usage[] = { "x1", "x2", NULL }; + args.setUsage(usage); + checkEqu("x1", args.usage().strOf(0)); + checkEqu("x2", args.usage().strOf(1)); + } void testLong(){ const char* call[] = { "test ", diff --git a/cunit/cuReTraverser.cpp b/cunit/cuReTraverser.cpp index 59cf55e..81151da 100644 --- a/cunit/cuReTraverser.cpp +++ b/cunit/cuReTraverser.cpp @@ -79,8 +79,8 @@ private: static const char* usage2[] = { "x1", "x2", "x3", NULL }; MyOptions opts; opts.initCompoundUsage(sizeof usage1 + sizeof usage2); - opts.addCompundUsage(usage1); - opts.addCompundUsage(usage2); + opts.addCompoundUsage(usage1); + opts.addCompoundUsage(usage2); checkEqu(7, opts.count()); checkEqu("line1", opts.usage()[0]); checkEqu("line2", opts.usage()[1]); @@ -113,7 +113,7 @@ private: char* argv[] = { "x", "-y1970.01.02", "-o1970.01.03", "-D5", "-d1", "-z1k", "-Z2M", "*" }; - DirEntryFilter_t filter; + ReDirEntryFilter_t filter; opts.programArgs().init(sizeof argv / sizeof argv[0], argv); opts.setFilterFromProgramArgs(filter); // local time: +3600 diff --git a/os/ReDirTools.cpp b/os/ReDirTools.cpp index 2180298..28a707a 100644 --- a/os/ReDirTools.cpp +++ b/os/ReDirTools.cpp @@ -10,6 +10,8 @@ const char* ReDirTools::m_version = "2015.01.03"; +static const char* s_empty[] = { NULL }; + static const char* s_standardFilterUsage[] = { "-D or --max-depth= Default: 512", " the depth of the subdirectory is lower or equal ", @@ -22,6 +24,20 @@ static const char* s_standardFilterUsage[] = { " is a date (e.g. 2015.02.17) or number followed by an unit", " units: m(inutes) h(hours), d(days). Default: m(inutes)", " examples: -o25 --older-than=30d -o24h -o2009.3.2/12:00 -o1999.01.01", + "-P

or --pathname-pattern=

", + " a list of patterns for the path (without basename) separated by ';'", + " Each pattern can contain '*' as wildcard", + " If the first character is '^' the pattern is a 'not pattern':" + " A directory will be entered if at least one of the positive patterns and none", + " of the 'not patterns' matches", + " examples: '*;^*/.git/' '*/cache/;*/temp/", + "-p

or --basename-pattern=

", + " a list of patterns for the basename (filename without path) separated by ';'", + " Each pattern can contain '*' as wildcard", + " If the first character is '^' the pattern is a 'not pattern':" + " A file will be found if at least one of the positive patterns and none", + " of the 'not patterns' matches", + " examples: '*.cpp;*.hpp;Make*' '*;^*.bak;^*~", "-t or --type=", " the file type", " is a list of values:", @@ -50,6 +66,49 @@ const char* s_listUsage[] = { " lists the metadata (size, modification date ...) of the selected files", NULL }; +const char* s_listExamples[] = { + "dirtool list --min-size=10M e:\\data", + "dirtool list --type=f -y7d --size=10M -p*.cpp;*.hpp;Makefile*;^*~ /home/data" , + NULL +}; + +static const char* s_helpSummary[] = { + "dirtool or dt ", + " Useful commands around a directry tree", + " Type 'dirtool help ' for more help", + ":", + "help shows info about the arguments/options", + "list shows the meta data of the selected files", + "statistic shows statistics about a direcctory tree", + NULL +}; + +static const char* s_statisticUsage[] = { + ":" + "st(atistic) [] []", + " shows a statistic about a directory tree", + " a directory path: relative or absolute", + " 0: only the summary of will be shown", + " 1: shows the summery of each subdir of and the total", + " n: shows the summery of each subdir until level and the total", + " default: 1", + ":", + "-q or --quiet", + " does not show additional information, e.g. runtime", + "-t or --trace-interval=", + " trace the current path every seconds.", + " If n<=0: no trace. Default: 60", + "-k or --kbyte", + " output is ' path' (like du)", + NULL +}; +const char* s_statisticExamples[] = { + "Examples:", + "dirtool st -q -t0 e:\\windows", + "dirtool statistic --quiet --kbyte --trace-interval=60 ../mail 2", + "dirtool stat -q --kbyte d:data 2", + NULL +}; /** @@ -60,7 +119,8 @@ const char* s_listUsage[] = { */ ReDirOptions::ReDirOptions(const char* usage[], const char* examples[]) : m_programArgs(usage, examples), - m_patternList(), + m_nodePatterns(), + m_pathPatterns(), m_compoundUsage(NULL), m_countCompoundUsage(0) { @@ -87,7 +147,7 @@ void ReDirOptions::initCompoundUsage(size_t size){ * Adds a usage component to the compound usage message list. * @param usage a string vector containing a part of the usage message */ -void ReDirOptions::addCompundUsage(const char** usage){ +void ReDirOptions::addCompoundUsage(const char** usage){ int start = 0; while(m_compoundUsage[start] != NULL) assert(++start < m_countCompoundUsage); @@ -108,6 +168,8 @@ void ReDirOptions::addStandardFilterOptions(){ m_programArgs.addString("younger", "younger than", 'y', "younger-than", false, NULL); m_programArgs.addString("maxsize", "maximal filesize", 'Z', "max-size", false, NULL); m_programArgs.addString("minsize", "minimal filesize", 'z', "min-size", false, NULL); + m_programArgs.addString("nodepattern", "pattern list for the basename", 'p', "basename-pattern", false, NULL); + m_programArgs.addString("pathpattern", "pattern list for the path", 'P', "path-pattern", false, NULL); } /** @@ -237,24 +299,41 @@ time_t ReDirOptions::checkSize(const char* value){ * * @param filter OUT: the filter to set */ -void ReDirOptions::setFilterFromProgramArgs(DirEntryFilter_t& filter){ +void ReDirOptions::setFilterFromProgramArgs(ReDirEntryFilter_t& filter){ ReByteBuffer buffer; - if (m_programArgs.getString("younger", buffer) != NULL) - filter.m_maxAge = checkDate(m_programArgs.getString("younger", buffer)); - if (m_programArgs.getString("older", buffer) != NULL) - filter.m_minAge = checkDate(m_programArgs.getString("older", buffer)); - if (m_programArgs.getString("maxsize", buffer) != NULL) - filter.m_maxSize = checkSize(m_programArgs.getString("maxsize", buffer)); - if (m_programArgs.getString("minsize", buffer) != NULL) - filter.m_minSize = checkSize(m_programArgs.getString("minsize", buffer)); + if (m_programArgs.getString("younger", buffer)[0] != '\0') + filter.m_maxAge = checkDate(buffer.str()); + if (m_programArgs.getString("older", buffer)[0] != '\0') + filter.m_minAge = checkDate(buffer.str()); + if (m_programArgs.getString("maxsize", buffer)[0] != '\0') + filter.m_maxSize = checkSize(buffer.str()); + if (m_programArgs.getString("minsize", buffer)[0] != '\0') + filter.m_minSize = checkSize(buffer.str()); filter.m_minDepth = m_programArgs.getInt("mindepth"); filter.m_maxDepth = m_programArgs.getInt("maxdepth"); - if (m_programArgs.getArgCount() > 0) - { - m_patternList.set(m_programArgs.getArg(0)); - filter.m_nodePatterns = &m_patternList; + if (m_programArgs.getString("nodepattern", buffer) != NULL){ + m_nodePatterns.set(buffer.str()); + filter.m_nodePatterns = &m_nodePatterns; + } + if (m_programArgs.getString("pathpattern", buffer) != NULL){ + m_pathPatterns.set(buffer.str()); + filter.m_pathPatterns = &m_nodePatterns; } } +/** + * Prints a help message, the error message and exits. + * + * @param errorMessage the error message. + * @param message2 an additional message + */ + +void ReDirOptions::help(const char* errorMessage, const char* message2 ){ + ReByteBuffer msg(errorMessage, -1); + if (message2 != NULL) + msg.append(message2, -1); + m_programArgs.help(msg.str(), false, stdout); + exit(1); +} /** * Checks the correctness of the standard filter options. * @@ -490,33 +569,11 @@ void ReDirTools::usage(const char* msg, const char* msg2){ exit(1); } -static const char* statisticCall[] = { - ":" - "st(atistic) [] []", - " shows a statistic about a directory tree", - " a directory path: relative or absolute", - " 0: only the summary of will be shown", - " 1: shows the summery of each subdir of and the total", - " n: shows the summery of each subdir until level and the total", - " default: 1", - ":", - "-q or --quiet", - " does not show additional information, e.g. runtime", - "-t or --trace-interval=", - " trace the current path every seconds.", - " If n<=0: no trace. Default: 60", - "-k or --kbyte", - " output is ' path' (like du)", - NULL -}; -const char* statisticExamples[] = { - "Examples:", - "dirtool st -q -t0 e:\\windows", - "dirtool statistic --quiet --kbyte --trace-interval=60 ../mail 2", - "dirtool stat -q --kbyte d:data 2", - NULL -}; - +/** + * Prints a vector of lines. + * + * @param lines a vector of lines without newline ('\n') + */ static void printField(const char** lines){ for (int ix = 0; lines[ix] != NULL; ix++){ printf("%s\n", lines[ix]); @@ -527,8 +584,55 @@ static void printField(const char** lines){ * Prints an message how to use the statistic module and exits. */ void ReDirTools::statisticUsage(){ - printField(statisticCall); - printField(statisticExamples); + printField(s_statisticUsage); + printField(s_statisticExamples); +} + +/** + * Constructor. + */ +ReDirList::ReDirList() : + ReDirOptions(s_empty, s_listExamples) +{ + addStandardFilterOptions(); + initCompoundUsage(sizeof s_listUsage + sizeof s_standardFilterUsage); + addCompoundUsage(s_listUsage); + addCompoundUsage(s_standardFilterUsage); + m_programArgs.setUsage(m_compoundUsage); +} + +/** + * Gets the arguments for the "list" command and execute this. + * + * @param argc the number of arguments + * @param argav the argument vector + */ +void ReDirList::list(int argc, char* argv[]){ + ReDirEntryFilter_t filter; + try { + m_programArgs.init(argc, argv); + setFilterFromProgramArgs(filter); + if (m_programArgs.getArgCount() == 0) + help(i18n("no arguments given (missing path)")); + ReByteBuffer bufferRights; + ReByteBuffer bufferTime; + for (int ix = 0; ix < m_programArgs.getArgCount(); ix++){ + ReTraverser traverser(m_programArgs.getArg(ix)); + traverser.setMinLevel(filter.m_maxDepth); + traverser.setMaxLevel(filter.m_maxDepth); + int level; + ReDirStatus_t* entry; + while( (entry = traverser.nextFile(level, &filter)) != NULL){ + if (! printOneFile(entry)) + printf("%s %12lld %s %s%s\n", + entry->rightsAsString(bufferRights), entry->fileSize(), + entry->filetimeAsString(bufferTime), + entry->m_path.str(), entry->node()); + } + } + } catch(ReOptionException& exc){ + help(exc.getMessage()); + } } /** @@ -538,6 +642,8 @@ void ReDirTools::statisticUsage(){ * @param argav the argument vector */ void ReDirTools::list(int argc, char* argv[]){ + ReDirList lister; + lister.list(argc, argv); } @@ -549,7 +655,7 @@ void ReDirTools::list(int argc, char* argv[]){ */ void ReDirTools::statistic(int argc, char* argv[]){ time_t start = time(NULL); - ReProgramArgs args(statisticCall, statisticExamples); + ReProgramArgs args(s_statisticUsage, s_statisticExamples); args.addBool("quiet", "no additional information", 'q', "quiet", false); args.addBool("kbyte", "output is ' '", 'k', "kbyte", false); args.addInt("trace", "trace each seconds the current path", 't', "trace-interval", 60); @@ -595,14 +701,6 @@ static bool isArg(const char* full, const char* part){ bool rc = fullArg.startsWith(part, -1); return rc; } -static const char* helpSummary[] = { - "dirtool or dt ", - ":", - "help shows info about the arguments/options", - "list shows info about selected files", - "statistic shows statistics about a direcctory tree", - NULL -}; /** * Gets the arguments for any command and execute this. * @@ -613,13 +711,16 @@ int ReDirTools::main(int argc, char* argv[]){ ReDirTools tools; if (argc < 2) tools.usage("missing arguments"); - if (isArg("list", argv[1])) - tools.list(argc - 1, argv + 1); - else if (isArg("help", argv[1])) - printField(helpSummary); - else if (isArg("statistic", argv[1])) - tools.statistic(argc - 1, argv + 1); - else if (isArg("test", argv[1])){ + argc--; + argv++; + const char* arg0 = argv[0]; + if (isArg("list", arg0)) + tools.list(argc, argv); + else if (isArg("help", arg0)) + printField(s_helpSummary); + else if (isArg("statistic", arg0)) + tools.statistic(argc, argv); + else if (isArg("test", arg0)){ void testAll(); testAll(); }else diff --git a/os/ReDirTools.hpp b/os/ReDirTools.hpp index 1992563..53d4e7d 100644 --- a/os/ReDirTools.hpp +++ b/os/ReDirTools.hpp @@ -16,24 +16,30 @@ public: void addStandardFilterOptions(); void checkStandardFilterOptions(); void initCompoundUsage(size_t size); - void addCompundUsage(const char** usage); + void addCompoundUsage(const char** usage); const char** compoundUsage() const { return m_compoundUsage; } ReProgramArgs& programArgs() { return m_programArgs; } time_t checkDate(const char* value); int64_t checkSize(const char* value); - void setFilterFromProgramArgs(DirEntryFilter_t& filter); + void setFilterFromProgramArgs(ReDirEntryFilter_t& filter); + void help(const char* errorMessage, const char* message2 = NULL); protected: ReProgramArgs m_programArgs; - RePatternList m_patternList; + RePatternList m_nodePatterns; + RePatternList m_pathPatterns; const char** m_compoundUsage; int m_countCompoundUsage; }; class ReDirList : public ReDirOptions { +public: + ReDirList(); public: void list(int argc, char* argv[]); + virtual bool printOneFile(ReDirStatus_t* entry) + { return false; } }; class ReDirStatisticData{ public: diff --git a/os/ReTraverser.cpp b/os/ReTraverser.cpp index 9facff4..f2f9d14 100644 --- a/os/ReTraverser.cpp +++ b/os/ReTraverser.cpp @@ -66,6 +66,33 @@ const char* ReDirStatus_t::node() const{ return m_data.cFileName; #endif } +/** + * Returns the file rights as a string. + * + * @param buffer OUT: the file rights + * @return buffer.str() (for chaining) + */ +const char* ReDirStatus_t::rightsAsString(ReByteBuffer& buffer) { + buffer.setLength(0); +#if defined __linux__ +#elif defined __WIN32__ +#endif + return buffer.str(); +} + +/** + * Returns the file time as a string. + * + * @param buffer OUT: the file time + * @return buffer.str() (for chaining) + */ +const char* ReDirStatus_t::filetimeAsString(ReByteBuffer& buffer) { + time_t time1 = filetimeToTime(modified()); + struct tm* time2 = localtime(&time1); + buffer.setLength(4+2*1+2*2+1+3*2+2*1); + strftime(buffer.buffer(), buffer.length(), "%y.%m.%d %H:%M:%S", time2); + return buffer.str(); +} /** * Tests whether the instance contains data about "." or "..". * @@ -218,7 +245,7 @@ void ReDirStatus_t::timeToFiletime(time_t time, FileTime_t& filetime){ /** * Constructor. */ -DirEntryFilter_t::DirEntryFilter_t() : +ReDirEntryFilter_t::ReDirEntryFilter_t() : m_regulars(true), m_specials(true), m_directories(true), @@ -236,12 +263,12 @@ DirEntryFilter_t::DirEntryFilter_t() : /** * Destructor. */ -DirEntryFilter_t::~DirEntryFilter_t(){ +ReDirEntryFilter_t::~ReDirEntryFilter_t(){ } /** * */ -bool DirEntryFilter_t::match(ReDirStatus_t& entry){ +bool ReDirEntryFilter_t::match(ReDirStatus_t& entry){ bool rc = false; do { if (! m_directories && entry.isDirectory()) @@ -288,6 +315,8 @@ struct stat* ReDirStatus_t::getStatus() { */ ReTraverser::ReTraverser(const char* base) : m_level(-1), + m_minLevel(0), + m_maxLevel(512), m_base(base), // m_dirs m_passNoForDirSearch(2) @@ -350,7 +379,8 @@ ReDirStatus_t* ReTraverser::rawNextFile(int& level) } else { // we are interested only in true subdirectories: again = true; - if (current->isDirectory() && ! current->isDotDir() && ! current->isLink()){ + if (current->isDirectory() && ! current->isDotDir() && ! current->isLink() + && m_level < m_maxLevel){ // open a new level alreadyRead = initEntry(current->m_path, current->node() , m_level + 1); } @@ -391,7 +421,7 @@ ReDirStatus_t* ReTraverser::rawNextFile(int& level) * otherwise: the info about the next file in the * directory tree */ -ReDirStatus_t* ReTraverser::nextFile(int& level, DirEntryFilter_t* filter){ +ReDirStatus_t* ReTraverser::nextFile(int& level, ReDirEntryFilter_t* filter){ ReDirStatus_t* rc = rawNextFile(level); while (rc != NULL){ if (filter == NULL || filter->match(*rc)){ diff --git a/os/ReTraverser.hpp b/os/ReTraverser.hpp index e18dad4..d817327 100644 --- a/os/ReTraverser.hpp +++ b/os/ReTraverser.hpp @@ -35,6 +35,8 @@ public: FileSize_t fileSize(); const FileTime_t* modified(); bool isDotDir() const; + const char* rightsAsString(ReByteBuffer& buffer); + const char* filetimeAsString(ReByteBuffer& buffer); public: ReByteBuffer m_path; int m_passNo; @@ -52,10 +54,10 @@ public: static time_t filetimeToTime(const FileTime_t* time); static void timeToFiletime(time_t time, FileTime_t& filetime); }; -class DirEntryFilter_t { +class ReDirEntryFilter_t { public: - DirEntryFilter_t(); - ~DirEntryFilter_t(); + ReDirEntryFilter_t(); + ~ReDirEntryFilter_t(); public: bool match(ReDirStatus_t& entry); public: @@ -78,12 +80,23 @@ public: virtual ~ReTraverser(); public: ReDirStatus_t* rawNextFile(int& level); - ReDirStatus_t* nextFile(int& level, DirEntryFilter_t* filter = NULL); + ReDirStatus_t* nextFile(int& level, ReDirEntryFilter_t* filter = NULL); + /** Sets the maximal depth. + * @param value the value to set + */ + void setMinLevel(int value) + { m_minLevel = value; } + /** Sets the minimal depth. + * @param value the value to set + */ + void setMaxLevel(int value) + { m_minLevel = value; } protected: bool initEntry(const ReByteBuffer& parent, const char* node, int level); void freeEntry(int level); - protected: + int m_minLevel; + int m_maxLevel; int m_level; ReByteBuffer m_base; ReDirStatus_t* m_dirs[MAX_ENTRY_STACK_DEPTH]; -- 2.39.5