diff --git a/common.cpp b/common.cpp index f77af4c..b02fb32 100644 --- a/common.cpp +++ b/common.cpp @@ -514,7 +514,7 @@ size_t powInt (size_t a, if (b) return 0; else - throw runtime_error ("powInt: 0^0"); + throw runtime_error (FUNC "powInt: 0^0"); } @@ -724,6 +724,35 @@ bool strBlank (const string &s) +bool getScientific (string numberS, + bool &hasPoint, + streamsize &decimals) +{ + strUpper (numberS); + const size_t ePos = numberS. find ('E'); + const size_t pointPos = numberS. find ('.'); + + hasPoint = (pointPos != string::npos); + decimals = 0; + if (ePos == string::npos) + { + if (hasPoint) + decimals = (streamoff) (numberS. size () - (pointPos + 1)); + } + else + { + ASSERT (ePos != pointPos); + if (hasPoint && ePos < pointPos) + hasPoint = false; + if (hasPoint) + decimals = (streamoff) (ePos - (pointPos + 1)); + } + + return ePos != string::npos; +} + + + void strUpper (string &s) { for (char& c : s) @@ -760,14 +789,23 @@ bool isLower (const string &s) -string::const_iterator stringInSet (const string &s, - const string &charSet) +size_t stringNotInSet (const string &s, + const string &charSet, + ebool uppercase) { - CONST_ITER (string, it, s) - if (! charInSet (*it, charSet)) - return it; - - return s. end (); + FFOR (size_t, i, s. size ()) + { + char c = s [i]; + switch (uppercase) + { + case efalse: c = toLower (c); break; + case etrue: c = toUpper (c); break; + case enull: break; + } + if (! charInSet (c, charSet)) + return i; + } + return no_index; } @@ -1075,7 +1113,7 @@ string unpercent (const string &s) { for (const char c : s) if (between (c, '\0', ' ') /*! printable (c)*/) - throw runtime_error (FUNC "Non-printable character: " + to_string (uchar (c))); + throw runtime_error (FUNC "Non-printable character: " + to_string (uint (c))); string r; constexpr size_t hex_pos_max = 2; @@ -1271,7 +1309,7 @@ void removeDirectory (const string &dirName) { case Filetype::link: if (unlink (name. c_str ())) - throw logic_error ("cannot unlink " + strQuote (name)); + throw runtime_error (FUNC "cannot unlink " + strQuote (name)); break; case Filetype::dir: removeDirectory (name); @@ -1280,11 +1318,11 @@ void removeDirectory (const string &dirName) removeFile (name); break; default: - throw logic_error ("Cannot remove directory item " + strQuote (name) + " of type " + strQuote (filetype2name (t))); + throw runtime_error (FUNC "Cannot remove directory item " + strQuote (name) + " of type " + strQuote (filetype2name (t))); } } if (rmdir (dirName. c_str ())) - throw logic_error ("Cannot remove directory " + strQuote (dirName)); + throw runtime_error (FUNC "Cannot remove directory " + strQuote (dirName)); } @@ -1490,7 +1528,7 @@ bool getChar (istream &is, return false; } if (! (i >= 0 && i <= 255)) - throw runtime_error ("Cannot read character: " + to_string (i)); + throw runtime_error (FUNC "Cannot read character: " + to_string (i)); c = static_cast (i); return true; @@ -1902,7 +1940,7 @@ void Named::qc () const Root::qc (); if (! goodName (name)) - throw runtime_error ("Bad name: " + strQuote (name)); + throw runtime_error (FUNC "Bad name: " + strQuote (name)); } @@ -2164,7 +2202,7 @@ Input::Input (istream &is_arg, { QC_ASSERT (is); if (! is->good ()) - throw runtime_error ("Bad input stream"); + throw runtime_error (FUNC "Bad input stream"); } @@ -2420,7 +2458,7 @@ void Token::qc () const QC_ASSERT (Common_sp::isDelimiter (name [0])); break; case eDateTime: break; - default: throw runtime_error ("Token: Unknown type"); + default: throw logic_error ("Token: Unknown type"); } } } @@ -2452,7 +2490,7 @@ void Token::saveText (ostream &os) const break; case eDelimiter: os << name; break; - default: throw runtime_error ("Token: Unknown type"); + default: throw logic_error ("Token: Unknown type"); } } @@ -2871,9 +2909,13 @@ void OFStream::open (const string &dirName, pathName += fileName; if (! extension. empty ()) pathName += "." + extension; - + exceptions (std::ios::failbit | std::ios::badbit); // In ifstream these flags collide with eofbit - ofstream::open (pathName); + try { ofstream::open (pathName); } + catch (const exception &e) + { + throw runtime_error ("Cannot create file " + shellQuote (pathName) + "\n" + e. what ()); + } if (! good ()) throw runtime_error ("Cannot create file " + shellQuote (pathName)); @@ -2894,9 +2936,9 @@ bool PairFile::next () iss >> name1 >> name2; if (name2. empty ()) - throw runtime_error ("No pair: " + strQuote (name1) + " - " + strQuote (name2)); + throw runtime_error (FUNC "No pair: " + strQuote (name1) + " - " + strQuote (name2)); if (! sameAllowed && name1 == name2) - throw runtime_error ("Same name: " + name1); + throw runtime_error (FUNC "Same name: " + name1); if (orderNames && name1 > name2) swap (name1, name2); @@ -3042,10 +3084,10 @@ string Json::getString () const { const auto* this_ = this; if (! this_ || asJsonNull ()) - throw runtime_error ("undefined"); + throw logic_error ("undefined"); if (const JsonString* j = asJsonString ()) return j->s; - throw runtime_error ("Not a JsonString"); + throw logic_error ("Not a JsonString"); } @@ -3054,10 +3096,10 @@ long long Json::getInt () const { const auto* this_ = this; if (! this_ || asJsonNull ()) - throw runtime_error ("undefined"); + throw logic_error ("undefined"); if (const JsonInt* j = asJsonInt ()) return j->n; - throw runtime_error ("Not a JsonInt"); + throw logic_error ("Not a JsonInt"); } @@ -3066,12 +3108,12 @@ double Json::getDouble () const { const auto* this_ = this; if (! this_) - throw runtime_error ("undefined"); + throw logic_error ("undefined"); if (asJsonNull ()) return numeric_limits::quiet_NaN (); if (const JsonDouble* j = asJsonDouble ()) return j->n; - throw runtime_error ("Not a JsonDouble"); + throw logic_error ("Not a JsonDouble"); } @@ -3080,10 +3122,10 @@ bool Json::getBoolean () const { const auto* this_ = this; if (! this_ || asJsonNull ()) - throw runtime_error ("undefined"); + throw logic_error ("undefined"); if (const JsonBoolean* j = asJsonBoolean ()) return j->b; - throw runtime_error ("Not a JsonBoolean"); + throw logic_error ("Not a JsonBoolean"); } @@ -3092,12 +3134,12 @@ const Json* Json::at (const string& name_arg) const { const auto* this_ = this; if (! this_) - throw runtime_error ("undefined"); + throw logic_error ("undefined"); if (asJsonNull ()) return nullptr; if (const JsonMap* j = asJsonMap ()) return findPtr (j->data, name_arg); - throw runtime_error ("Not a JsonMap"); + throw logic_error ("Not a JsonMap"); } @@ -3106,17 +3148,17 @@ const Json* Json::at (size_t index) const { const auto* this_ = this; if (! this_) - throw runtime_error ("undefined"); + throw logic_error ("undefined"); if (asJsonNull ()) return nullptr; if (const JsonArray* j = asJsonArray ()) { if (index >= j->data. size ()) - throw runtime_error ("Index out of range"); + throw runtime_error (FUNC "Index out of range"); else return j->data [index]; } - throw runtime_error ("Not a JsonArray"); + throw logic_error ("Not a JsonArray"); } @@ -3125,12 +3167,12 @@ size_t Json::getSize () const { const auto* this_ = this; if (! this_) - throw runtime_error ("undefined"); + throw logic_error ("undefined"); if (asJsonNull ()) return 0; if (const JsonArray* j = asJsonArray ()) return j->data. size (); - throw runtime_error ("Not a JsonArray"); + throw logic_error ("Not a JsonArray"); } @@ -3623,7 +3665,7 @@ void Application::addKey (const string &name, ASSERT (! name. empty ()); ASSERT (! contains (name2arg, name)); if (acronym && contains (char2arg, acronym)) - throw logic_error ("Duplicate option " + strQuote (string (1, acronym))); + throw logic_error ("Duplicate key " + strQuote (string (1, acronym))); keyArgs << Key (*this, name, argDescription, defaultValue, acronym, var); name2arg [name] = & keyArgs. back (); if (acronym) @@ -3639,7 +3681,7 @@ void Application::addFlag (const string &name, ASSERT (! name. empty ()); ASSERT (! contains (name2arg, name)); if (acronym && contains (char2arg, acronym)) - throw logic_error ("Duplicate option " + strQuote (string (1, acronym))); + throw logic_error ("Duplicate flag " + strQuote (string (1, acronym))); keyArgs << Key (*this, name, argDescription, acronym); name2arg [name] = & keyArgs. back (); if (acronym) @@ -3662,13 +3704,13 @@ void Application::addPositional (const string &name, Application::Key* Application::getKey (const string &keyName) const { if (! contains (name2arg, keyName)) - errorExitStr ("Unknown key: " + strQuote (keyName) + "\n\n" + getInstruction (false)); + throw runtime_error ("Unknown key: " + strQuote (keyName) + "\n\n" + getInstruction (false)); Key* key = nullptr; if (const Arg* arg = findPtr (name2arg, keyName)) key = var_cast (arg->asKey ()); if (! key) - errorExitStr (strQuote (keyName) + " is not a key\n\n" + getInstruction (false)); + throw runtime_error (strQuote (keyName) + " is not a key\n\n" + getInstruction (false)); return key; } @@ -3681,9 +3723,9 @@ void Application::setPositional (List::iterator &posIt, if (posIt == positionalArgs. end ()) { if (isLeft (value, "-")) - errorExitStr (strQuote (value) + " is not a valid option\n\n" + getInstruction (false)); + throw runtime_error (strQuote (value) + " is not a valid option\n\n" + getInstruction (false)); else - errorExitStr (strQuote (value) + " cannot be a positional parameter\n\n" + getInstruction (false)); + throw runtime_error (strQuote (value) + " cannot be a positional parameter\n\n" + getInstruction (false)); } (*posIt). value = value; posIt++; @@ -3792,6 +3834,31 @@ string Application::key2shortHelp (const string &name) const +void Application::initEnvironment () +{ + ASSERT (! programArgs. empty ()); + + // execDir, programName + execDir = getProgramDirName (); + if (execDir. empty ()) + execDir = which (programArgs. front ()); + if (! isDirName (execDir)) + throw logic_error ("Cannot identify the directory of the executable"); + { + string s (programArgs. front ()); + programName = rfindSplit (s, fileSlash); + execDir = getDirName (path2canonical (execDir + programName)); + } + + string execDir_ (execDir); + trimSuffix (execDir_, "/"); + for (Key& key : keyArgs) + if (! key. flag) + replaceStr (key. defaultValue, "$BASE", execDir_); +} + + + string Application::getInstruction (bool screen) const { string instr (description); @@ -3955,7 +4022,7 @@ int Application::run (int argc, const string s1 (s. substr (1)); if (s1. empty ()) - errorExitStr ("Dash with no key\n\n" + getInstruction (false)); + throw runtime_error ("Dash with no key\n\n" + getInstruction (false)); string name; const char c = s1 [0]; // Valid if name.empty() @@ -3964,12 +4031,12 @@ int Application::run (int argc, { name = s1. substr (1); if (name. empty ()) - errorExitStr ("Dashes with no key\n\n" + getInstruction (false)); + throw runtime_error ("Dashes with no key\n\n" + getInstruction (false)); } else { if (s1. size () != 1) - errorExitStr ("Single character expected: " + strQuote (s1) + "\n\n" + getInstruction (false)); + throw runtime_error ("Single character expected: " + strQuote (s1) + "\n\n" + getInstruction (false)); } else name = s1; @@ -3999,7 +4066,7 @@ int Application::run (int argc, if (key) { if (keysRead. contains (key)) - errorExitStr ("Parameter " + strQuote (key->name) + " is used more than once"); + throw runtime_error ("Parameter " + strQuote (key->name) + " is used more than once"); else keysRead << key; @@ -4018,7 +4085,7 @@ int Application::run (int argc, first = false; } if (key) - errorExitStr ("Key with no value: " + key->name + "\n\n" + getInstruction (false)); + throw runtime_error ("Key with no value: " + key->name + "\n\n" + getInstruction (false)); if (programArgs. size () == 1 && (! positionalArgs. empty () || needsArg)) @@ -4030,7 +4097,7 @@ int Application::run (int argc, if (posIt != positionalArgs. end ()) - errorExitStr ("Too few positional parameters\n\n" + getInstruction (false)); + throw runtime_error ("Too few positional parameters\n\n" + getInstruction (false)); } @@ -4064,7 +4131,12 @@ int Application::run (int argc, string logFName (getArg ("log")); ASSERT (! logPtr); if (! logFName. empty ()) - logPtr = new ofstream (logFName, ios_base::app); + { + logPtr = new ofstream (logFName, ios_base::app); + time_t rawtime; + time (& rawtime); + *logPtr << endl << ctime (& rawtime) << "$ " << getCommandLine () << endl; + } } string jsonFName; @@ -4085,7 +4157,7 @@ int Application::run (int argc, seed_global = str2 (getArg ("seed")); if (! seed_global) - throw runtime_error ("seed cannot be 0"); + throw runtime_error ("Seed cannot be 0"); jsonFName = getArg ("json"); ASSERT (! jRoot); @@ -4180,25 +4252,7 @@ ShellApplication::~ShellApplication () void ShellApplication::initEnvironment () { ASSERT (tmp. empty ()); - ASSERT (! programArgs. empty ()); - - // execDir, programName - execDir = getProgramDirName (); - if (execDir. empty ()) - execDir = which (programArgs. front ()); - if (! isDirName (execDir)) - throw logic_error ("Cannot identify the directory of the executable"); - { - string s (programArgs. front ()); - programName = rfindSplit (s, fileSlash); - execDir = getDirName (path2canonical (execDir + programName)); - } - - string execDir_ (execDir); - trimSuffix (execDir_, "/"); - for (Key& key : keyArgs) - if (! key. flag) - replaceStr (key. defaultValue, "$BASE", execDir_); + Application::initEnvironment (); } diff --git a/common.hpp b/common.hpp index d282cd0..d03164d 100644 --- a/common.hpp +++ b/common.hpp @@ -155,20 +155,6 @@ void errorExitStr (const string &msg); // Invokes: throw logic_error -#if 0 -struct InputError : runtime_error // ?? -{ - static bool on; - // Init: false - - InputError (const string &what_arg) - : runtime_error (what_arg) - { on = true; } -}; -#endif - - - void sleepNano (long nanoSec); @@ -431,7 +417,11 @@ template template inline void insertAll (To &to, const From &from) - { to. insert (to. begin (), from. begin (), from. end ()); } + { + #pragma GCC diagnostic ignored "-Waggressive-loop-optimizations" + to. insert (to. begin (), from. begin (), from. end ()); + #pragma GCC diagnostic warning "-Waggressive-loop-optimizations" + } template inline void insertIter (To &to, @@ -659,6 +649,10 @@ inline string unQuote (const string &s) bool strBlank (const string &s); +bool getScientific (string numberS, + bool &hasPoint, + streamsize &decimals); + template string toString (const T &t) { ostringstream oss; @@ -687,28 +681,6 @@ template catch (...) { return false; } } -inline bool isLeft (const string &s, - const string &left) - { return s. substr (0, left. size ()) == left; } - -bool isRight (const string &s, - const string &right); - -bool trimPrefix (string &s, - const string &prefix); - // Return: success - -bool trimSuffix (string &s, - const string &suffix); - // Return: success - -void trimSuffixNonAlphaNum (string &s); - -bool trimTailAt (string &s, - const string &tailStart); - // Return: trimmed - // Update: s - void commaize (string &s); // ' ' --> ',' @@ -752,9 +724,10 @@ inline bool charInSet (char c, const string &charSet) { return charSet. find (c) != string::npos; } -string::const_iterator stringInSet (const string &s, - const string &charSet); - // Return: != s.end() => *Return is not in charSet +size_t stringNotInSet (const string &s, + const string &charSet, + ebool uppercase); + // Return: index in s which is not in charSet; may be no_index size_t strCountSet (const string &s, const string &charSet); @@ -762,6 +735,28 @@ size_t strCountSet (const string &s, void strDeleteSet (string &s, const string &charSet); +inline bool isLeft (const string &s, + const string &left) + { return s. substr (0, left. size ()) == left; } + +bool isRight (const string &s, + const string &right); + +bool trimPrefix (string &s, + const string &prefix); + // Return: success + +bool trimSuffix (string &s, + const string &suffix); + // Return: success + +void trimSuffixNonAlphaNum (string &s); + +bool trimTailAt (string &s, + const string &tailStart); + // Return: trimmed + // Update: s + void trimLeading (string &s); void trimTrailing (string &s); @@ -785,6 +780,17 @@ inline void trim (string &s, trimLeading (s, c); } +inline bool strNull (const string &s) + { if (strBlank (s)) + return true; + string s1 (s); + trim (s1); + strUpper (s1); + return s1 == "NULL" + || s1 == "NA" + || s1 == "N/A"; + } + inline bool contains (const string &hay, const string &needle) { return hay. find (needle) != string::npos; } @@ -920,7 +926,7 @@ struct Rand { static const long /*actually ulong*/ max_; private: - long seed; + long seed {0}; // 0 < seed < max_ public: @@ -974,11 +980,11 @@ struct DisjointCluster // Cormen, Leiserson, Rivest, Introduction to Algorithms, p. 449 { protected: - DisjointCluster* parentDC; + DisjointCluster* parentDC {nullptr}; // !nullptr // Tree // = this <=> root - size_t rankDC; + size_t rankDC {0}; // Upper bound on the height of *this // (Height = max. # arcs between *this and a leaf) public: @@ -1008,8 +1014,8 @@ struct Nocopy { protected: Nocopy () = default; - Nocopy (const Nocopy &) = delete; - Nocopy (Nocopy &&) = delete; + explicit Nocopy (const Nocopy &) = delete; + explicit Nocopy (Nocopy &&) = delete; Nocopy& operator= (const Nocopy &) = delete; }; @@ -1125,6 +1131,18 @@ template template */> explicit List (const vector &other) { *this << other; } + bool empty () const + { + #pragma GCC diagnostic ignored "-Wnull-dereference" + return P::empty (); + #pragma GCC diagnostic warning "-Wnull-dereference" + } + size_t size () const + { + #pragma GCC diagnostic ignored "-Wnull-dereference" + return P::size (); + #pragma GCC diagnostic warning "-Wnull-dereference" + } T at (size_t index) const @@ -1451,7 +1469,7 @@ void readLine (istream &is, -struct Istringstream : istringstream +struct Istringstream final : istringstream { Istringstream () = default; void reset (const string &s) @@ -1510,7 +1528,7 @@ struct Color #ifdef _MSC_VER return noString #else - return string ("\033[") + (bright ? "1;" : "") + to_string (color) + "m"; + return string ("\033[") + (bright ? "1;" : "") + to_string ((int) color) + "m"; #endif } }; @@ -1518,20 +1536,25 @@ struct Color inline string colorize_raw (const string &s, Color::Type color, + bool bright, bool screen) { if (! screen) return s; - return Color::code (color, true) + s + Color::code (); + return Color::code (color, bright) + s + Color::code (); } inline string colorize (const string &s, bool screen) - { return colorize_raw (s, Color::white, screen); } - + { return colorize_raw (s, Color::white, true, screen); } + inline string colorizeUrl (const string &s, bool screen) - { return colorize_raw (s, Color::blue, screen); } + { return colorize_raw (s, Color::blue, true, screen); } +inline string colorizeDir (const string &s, + bool screen) + { return colorize_raw (s, Color::green, false, screen); } + class OColor // Output color @@ -1602,13 +1625,13 @@ struct COutErr : Singleton int fd2); public: #endif -template - const COutErr& operator<< (const T &val) const - { cout << val; - if (! both) - cerr << val; - return *this; - } + template + const COutErr& operator<< (const T &val) const + { cout << val; + if (! both) + cerr << val; + return *this; + } const COutErr& operator<< (ostream& (*pfun) (ostream&)) const { pfun (cout); if (! both) @@ -1961,7 +1984,7 @@ struct Xml - struct TextFile : File + struct TextFile final : File // Tag::name: idenifier with possible '-' { private: @@ -1991,7 +2014,7 @@ struct Xml - struct BinFile : File + struct BinFile final : File // Binary XML // ::= * 0 0 0 // ::= @@ -2089,7 +2112,7 @@ inline ostream& operator<< (ostream &os, template - struct AutoPtr : unique_ptr + struct AutoPtr final : unique_ptr { private: typedef unique_ptr P; @@ -2799,7 +2822,7 @@ template } void deleteData () { for (const T* t : *this) - delete t; + delete t; P::clear (); } void erasePtr (size_t index) @@ -2913,7 +2936,11 @@ template return *this; } ~VectorOwn () - { P::deleteData (); } + { + #pragma GCC diagnostic ignored "-Wnull-dereference" + P::deleteData (); + #pragma GCC diagnostic warning "-Wnull-dereference" + } VectorOwn& operator<< (const T* value) @@ -3214,9 +3241,7 @@ struct StringVector : Vector {} - string toString (const string& sep) const; - string toString () const - { return toString (noString); } + string toString (const string& sep = noString) const; bool same (const StringVector &vec, const Vector &indexes) const; void to_xml (Xml::File &f, @@ -4155,7 +4180,7 @@ struct Json : Root, Nocopy // Heaponly -struct JsonNull : Json +struct JsonNull final : Json { explicit JsonNull (JsonContainer* parent, const string& name = noString) @@ -4171,7 +4196,7 @@ struct JsonNull : Json -struct JsonString : Json +struct JsonString final : Json { const string s; @@ -4192,7 +4217,7 @@ struct JsonString : Json -struct JsonInt : Json +struct JsonInt final : Json { const long long n; @@ -4212,7 +4237,7 @@ struct JsonInt : Json -struct JsonDouble : Json +struct JsonDouble final : Json { const double n; const streamsize decimals; @@ -4243,7 +4268,7 @@ struct JsonDouble : Json -struct JsonBoolean : Json +struct JsonBoolean final : Json { const bool b; @@ -4277,7 +4302,7 @@ struct JsonContainer : Json -struct JsonArray : JsonContainer +struct JsonArray final : JsonContainer { friend struct Json; private: @@ -4306,7 +4331,7 @@ struct JsonArray : JsonContainer -struct JsonMap : JsonContainer +struct JsonMap final : JsonContainer { friend struct Json; private: @@ -4384,7 +4409,7 @@ struct ItemGenerator -struct FileItemGenerator : ItemGenerator, Nocopy +struct FileItemGenerator final : ItemGenerator, Nocopy { private: string fName; @@ -4404,7 +4429,7 @@ struct FileItemGenerator : ItemGenerator, Nocopy #ifndef _MSC_VER -struct RawDirItemGenerator : ItemGenerator, Nocopy +struct RawDirItemGenerator final : ItemGenerator, Nocopy { private: string dirName; @@ -4431,7 +4456,7 @@ struct RawDirItemGenerator : ItemGenerator, Nocopy -struct DirItemGenerator : ItemGenerator +struct DirItemGenerator final : ItemGenerator { private: StringVector vec; @@ -4460,7 +4485,7 @@ struct DirItemGenerator : ItemGenerator -struct NumberItemGenerator : ItemGenerator +struct NumberItemGenerator final : ItemGenerator { private: size_t index {0}; @@ -4586,6 +4611,9 @@ struct Application : Singleton, Root const bool needsArg; const bool gnu; const bool threadsUsed; + string execDir; + // Ends with '/' + // Physically real directory of the software static constexpr const char* helpS {"help"}; static constexpr const char* versionS {"version"}; @@ -4607,7 +4635,7 @@ struct Application : Singleton, Root virtual const Key* asKey () const { return nullptr; } }; - struct Positional : Arg + struct Positional final : Arg { Positional (const string &name_arg, const string &description_arg) @@ -4618,7 +4646,7 @@ struct Application : Singleton, Root const Positional* asPositional () const final { return this; } }; - struct Key : Arg + struct Key final : Arg { const Application& app; const bool flag; @@ -4735,8 +4763,7 @@ struct Application : Singleton, Root string getProgramDirName () const { return getDirName (programArgs. front ()); } protected: - virtual void initEnvironment () - {} + virtual void initEnvironment (); virtual void initVar () {} string getInstruction (bool screen) const; @@ -4773,9 +4800,6 @@ struct ShellApplication : Application // Temporary directory: ($TMPDIR or "/tmp") + "/" + programName + "XXXXXX" // If log is used then tmp is printed in the log file and the temporary files are not deleted // !empty() => useTmp - string execDir; - // Ends with '/' - // Physically real directory of the software mutable KeyValue prog2dir; mutable Stderr stderr; time_t startTime {0}; diff --git a/stxtyper.cpp b/stxtyper.cpp index 7e365fc..e95cf23 100644 --- a/stxtyper.cpp +++ b/stxtyper.cpp @@ -32,6 +32,8 @@ * Dependencies: NCBI BLAST, gunzip (optional) * * Release changes: +* 1.0.28 12/03/2024 tblastn -gapextend 2 +* 10/30/2024 colorizeDir() * 1.0.27 10/23/2024 PD-5155 "Hierarchy node" with mixed types is :: * 1.0.26 10/22/2024 PD-5085 Change column "Element length" to "Target length" * 1.0.25 08/16/2024 PD-5085 AMRFinderPlus column names to match MicroBIGG-E @@ -813,7 +815,7 @@ void goodBlasts2operons (const VectorPtr &goodBlastAls, // ThisApplication -struct ThisApplication : ShellApplication +struct ThisApplication final : ShellApplication { ThisApplication () : ShellApplication ("Determine stx type(s) of a genome, print .tsv-file", true, false, true, true) @@ -849,15 +851,15 @@ struct ThisApplication : ShellApplication throw runtime_error ("--print_node requires --amrfinder"); - stderr << "Software directory: " << shellQuote (execDir) << '\n'; + const bool screen = ! isRedirected (cerr); + + stderr << "Software directory: " << colorizeDir (execDir, screen) << '\n'; stderr << "Version: " << version << '\n'; const string logFName (tmp + "/log"); const string qcS (qc_on ? " -qc" : ""); - #define BLASTX 0 - // blast_bin if (blast_bin. empty ()) if (const char* s = getenv ("BLAST_BIN")) @@ -865,62 +867,34 @@ struct ThisApplication : ShellApplication if (! blast_bin. empty ()) { addDirSlash (blast_bin); - #if BLASTX - prog2dir ["blastx"] = blast_bin; - #else prog2dir ["tblastn"] = blast_bin; prog2dir ["makeblastdb"] = blast_bin; - #endif } const string dna_flat = uncompress (fName, "dna_flat"); - #if BLASTX - size_t nDna = 0; - size_t dnaLen_max = 0; - size_t dnaLen_total = 0; - #endif { prog2dir ["fasta_check"] = execDir; exec (fullProg ("fasta_check") + dna_flat + " -hyphen -ambig " + qcS + " -log " + logFName + " > " + tmp + "/nseq", logFName); const StringVector vec (tmp + "/nseq", (size_t) 10, true); if (vec. size () != 3) throw runtime_error ("fasta_check failed: " + vec. toString ("\n")); - #if BLASTX - nDna = str2 (vec [0]); - dnaLen_max = str2 (vec [1]); - dnaLen_total = str2 (vec [2]); - #endif } - #if BLASTX - QC_ASSERT (nDna); - QC_ASSERT (dnaLen_max); - QC_ASSERT (dnaLen_total); - #endif //stderr. section ("Running blast"); const string blastOut (tmp + "/blast"); { const Chronometer_OnePass_cerr cop ("blast"); // Database: created by ~brovervv/code/database/stx.prot.sh - #if BLASTX - findProg ("blastx"); - const string blast_fmt ("-outfmt '6 qseqid sseqid qstart qend qlen sstart send slen qseq sseq'"); - exec (fullProg ("blastx") + " -query " + dna_flat + " -db " + execDir + "stx.prot " - + "-comp_based_stats 0 -evalue 1e-10 -seg no -max_target_seqs 10000 -word_size 5 -query_gencode " + to_string (gencode) + " " - + getBlastThreadsParam ("blastx", min (nDna, dnaLen_total / 10002)) - + " " + blast_fmt + " -out " + blastOut + " > /dev/null 2> " + tmp + "/blast-err", tmp + "/blast-err"); - #else findProg ("makeblastdb"); exec (fullProg ("makeblastdb") + "-in " + dna_flat + " -dbtype nucl -out " + tmp + "/db -logfile " + tmp + "/db.log > /dev/null", tmp + "db.log"); findProg ("tblastn"); const string blast_fmt ("-outfmt '6 sseqid qseqid sstart send slen qstart qend qlen sseq qseq'"); exec (fullProg ("tblastn") + " -query " + execDir + "stx.prot -db " + tmp + "/db " - + "-comp_based_stats 0 -evalue 1e-10 -seg no -max_target_seqs 10000 -word_size 5 -db_gencode " + to_string (gencode) + + "-comp_based_stats 0 -evalue 1e-10 -seg no -max_target_seqs 10000 -word_size 5 -gapextend 2 -db_gencode " + to_string (gencode) //+ " -task tblastn-fast -threshold 100 -window_size 15" // from amrfinder.cpp: Reduces time by 9% //+ " -num_threads 10 -mt_mode 1" // Reduces time by 30% + " " + blast_fmt + " -out " + blastOut + " > /dev/null 2> " + tmp + "/blast-err", tmp + "/blast-err"); - #endif } diff --git a/tsv.cpp b/tsv.cpp index 6bd81b3..037ad17 100644 --- a/tsv.cpp +++ b/tsv.cpp @@ -132,11 +132,12 @@ void TextTable::Header::qc () const TextTable::TextTable (const string &tableFName, - const string &columnSynonymsFName) + const string &columnSynonymsFName, + uint displayPeriod) : Named (tableFName) { { - LineInput f (tableFName); + LineInput f (tableFName, displayPeriod); bool dataExists = true; // header while (f. nextLine ()) @@ -244,6 +245,8 @@ void TextTable::setHeader () maximize (h. len_max, field. size ()); if (h. choices. size () <= Header::choices_max) h. choices << field; + if (strNull (field)) + continue; if (! h. numeric) continue; { @@ -260,7 +263,7 @@ void TextTable::setHeader () { bool hasPoint = false; streamsize decimals = 0; - if (getDecimals (field, hasPoint, decimals)) + if (getScientific (field, hasPoint, decimals)) h. scientific = true; maximize (h. decimals, decimals); } @@ -271,17 +274,17 @@ void TextTable::setHeader () for (const StringVector& row : rows) FFOR (RowNum, i, row. size ()) { - const string& field = row [i]; - if (field. empty ()) - continue; Header& h = header [i]; - if (h. numeric) - { - bool hasPoint = false; - streamsize decimals = 0; - getDecimals (field, hasPoint, decimals); - maximize (h. len_max, field. size () + (size_t) (h. decimals - decimals) + (! hasPoint)); - } + string field (row [i]); + trim (field); + if (strNull (field)) + continue; + if (! h. numeric) + continue; + bool hasPoint = false; + streamsize decimals = 0; + getScientific (field, hasPoint, decimals); + maximize (h. len_max, field. size () + (size_t) (h. decimals - decimals) + (! hasPoint)); } } @@ -354,33 +357,6 @@ void TextTable::saveText (ostream &os) const -bool TextTable::getDecimals (string s, - bool &hasPoint, - streamsize &decimals) -{ - strUpper (s); - const size_t ePos = s. find ('E'); - const size_t pointPos = s. find ('.'); - - hasPoint = pointPos != string::npos; - - decimals = 0; - if (ePos == string::npos) - { - if (hasPoint) - decimals = (streamoff) (s. size () - (pointPos + 1)); - } - else - { - if (hasPoint && ePos > pointPos) - decimals = (streamoff) (ePos - (pointPos + 1)); - } - - return ePos != string::npos; -} - - - void TextTable::printHeader (ostream &os) const { FFOR (size_t, i, header. size ()) @@ -484,8 +460,8 @@ int TextTable::compare (const StringVector& row1, if (header [column]. numeric) { - const double a = s1. empty () ? 0.0 : stod (s1); - const double b = s2. empty () ? 0.0 : stod (s2); + const double a = strNull (s1) ? 0.0 : stod (s1); + const double b = strNull (s2) ? 0.0 : stod (s2); if (a < b) return -1; if (a > b) @@ -642,8 +618,8 @@ void TextTable::merge (RowNum toRowNum, ONumber on (oss, h. decimals, h. scientific); const string& s1 = to [i]; const string& s2 = from [i]; - const double d1 = s1. empty () ? 0.0 : stod (s1); - const double d2 = s2. empty () ? 0.0 : stod (s2); + const double d1 = strNull (s1) ? 0.0 : stod (s1); + const double d2 = strNull (s2) ? 0.0 : stod (s2); oss << (d1 + d2); to [i] = oss. str (); } diff --git a/tsv.hpp b/tsv.hpp index 261a024..2ae34a2 100644 --- a/tsv.hpp +++ b/tsv.hpp @@ -117,15 +117,18 @@ struct Date : Root struct TextTable : Named -// Tab-delimited (tsv) table with a header +// Tab-separated value (tsv) table with a header // name: file name or empty() { bool pound {false}; // '#' in the beginning of header bool saveHeader {true}; + + struct Header : Named { size_t len_max {0}; + // For trim()'ed fields // Type bool numeric {true}; // Valid if numeric @@ -136,6 +139,7 @@ struct TextTable : Named static constexpr size_t choices_max {7}; // PAR Set choices; // size() <= choices_max + 1 + Header () = default; explicit Header (const string &name_arg) : Named (name_arg) @@ -147,6 +151,7 @@ struct TextTable : Named << '\t' << (numeric ? ((scientific ? "float" : "int") + string ("(") + to_string (decimals) + ")") : "char") << '\t' << (null ? "null" : "not null"); } + void saveSql (ostream& os) const { os << name << ' '; if (numeric) @@ -165,6 +170,8 @@ struct TextTable : Named Vector
header; // Header::name's are unique // size() = number of columns + + Vector rows; // StringVector::size() = header.size() // Values are trim()'ed @@ -185,7 +192,8 @@ struct TextTable : Named explicit TextTable (const string &tableFName, - const string &columnSynonymsFName = noString); + const string &columnSynonymsFName = noString, + uint displayPeriod = 0); // Input: tableFName: format: [{'#' }* '#']
{ >}* // empty lines are skipped // columnSynonymsFName: @@ -204,13 +212,9 @@ struct TextTable : Named void saveText (ostream &os) const override; - static bool getDecimals (string s, - bool &hasPoint, - streamsize &decimals); - // Return: true => scientific number void printHeader (ostream &os) const; ColNum col2num_ (const string &columnName) const; - // Retuirn: no_index <=> no columnName + // Return: no_index <=> no columnName ColNum col2num (const string &columnName) const { const ColNum i = col2num_ (columnName); if (i == no_index) @@ -253,7 +257,7 @@ struct TextTable : Named const StringVector &maxV, const StringVector &aggr); // aggr: slow - // Invokes: filterColumns(by + sum + aggr) + // Invokes: sort(by), filterColumns(by + sum + aggr) private: void merge (RowNum toRowNum, RowNum fromRowNum, diff --git a/version.txt b/version.txt index adb7b04..8b54409 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.0.27 +1.0.28