//----------------------------------------------------------- // Copyright Christian Arnault LAL-Orsay CNRS // arnault@lal.in2p3.fr // See the complete license in cmt_license.txt "http://www.cecill.info". //----------------------------------------------------------- #ifdef WIN32 #include #define popen _popen #define pclose _pclose #endif #include "cmt_awk.h" #include "cmt_system.h" class Parser { public: Parser (Awk* awk, const cmt_string pattern, const cmt_regexp* expression) : m_pattern (pattern), m_expression (expression), m_awk(awk) { } /** * this first level parsing function extracts individual lines * from the text, taking care of both Unix and Windows EOL styles. * * Then the second level parsing function parse_line is called. */ Awk::condition parse (const cmt_string& text) { Awk::condition result = Awk::ok; cmt_string line; int pos; int max_pos; pos = 0; max_pos = text.size (); m_accumulator.erase (0); for (pos = 0; pos < max_pos;) { int eol = text.find (pos, '\n'); if (eol == cmt_string::npos) { // Last line, since there is no eol at all text.substr (pos, line); pos = max_pos; } else { int length = 1; int cr = text.find (pos, "\r\n"); if (cr == (eol-1)) { eol = cr; length = 2; } if (eol == pos) { // this is an empty line line = ""; pos += length; } else { // The eol was found beyond the current position // (ie. this is a non empty line) text.substr (pos, eol - pos, line); pos = eol + length; } } if (m_awk != 0) m_awk->inc_line_number (); //cout << "parse> line=[" << line << "]" << endl; result = parse_line (line); if (result != Awk::ok) break; } return (result); } /** * This second level parsing function accumulates individual lines * with real trailing back slashes. * Eventually the possible text pattern or regular expression is * checked and the Awk::filter function is called in case of * succesful match onto the accumulated line. */ Awk::condition parse_line (const cmt_string& line) { Awk::condition result = Awk::ok; int length; cmt_string temp_line = line; // // We scan the line for handling backslashes. // // Really terminating backslashes (ie those only followed by spaces/tabs // mean continued line // // bool finished = true; length = temp_line.size (); if (length == 0) { // An empty line following a backslash terminates the continuation. finished = true; } else { int back_slash = temp_line.find_last_of ('\\'); if (back_slash != cmt_string::npos) { // // This is the last backslash // check if there are only space chars after it // bool at_end = true; for (int i = (back_slash + 1); i < length; i++) { char c = temp_line[i]; if ((c != ' ') && (c != '\t')) { at_end = false; break; } } if (at_end) { temp_line.erase (back_slash); finished = false; } else { // This was not a trailing backslash. finished = true; } } m_accumulator += temp_line; } //cout << "parse_line1> accumulator=[" << m_accumulator << "]" << endl; //cout << "parse_line1> finished=[" << finished << "]" << endl; if (!finished) { // We still need to accumulate forthcoming lines // before parsing the resulting text. return (Awk::ok); } // now filter the complete accumulated line (if non empty) if (m_accumulator != "") { bool ok = false; if (m_expression != 0) { if (m_expression->match (m_accumulator)) { ok = true; } } else { if ((m_pattern == "") || (m_accumulator.find (m_pattern) != cmt_string::npos)) { ok = true; } } if (ok && (m_awk != 0)) { //cout << "parse_line> accumulator=[" << m_accumulator << "]" << endl; m_awk->filter (m_accumulator); result = m_awk->get_last_condition (); } m_accumulator.erase (0); } return (result); } private: cmt_string m_accumulator; cmt_string m_pattern; const cmt_regexp* m_expression; Awk* m_awk; }; //------------------------------------------------ Awk::Awk () { m_condition = ok; } //------------------------------------------------ Awk::~Awk () { } //------------------------------------------------ Awk::condition Awk::run (const cmt_string& text, const cmt_string& pattern) { m_line_number = 0; m_condition = ok; begin (); if (m_condition != ok) return (m_condition); if (CmtSystem::testenv ("CMTTESTAWK")) { Parser p (this, pattern, 0); m_condition = p.parse (text); if (m_condition != ok) return (m_condition); } else { cmt_string line; int pos = 0; int max_pos; max_pos = text.size (); for (pos = 0; pos < max_pos;) { int cr = text.find (pos, "\r\n"); int nl = text.find (pos, '\n'); // Get the first end-of-line (either lf or cr-lf) //-------------------- // // cr 1 0 // nl // // 1 a b // // 0 c d // //-------------------- int first = nl; if (cr != cmt_string::npos) { // cases a or c if (nl == cmt_string::npos) { // case a first = cr; } else { // case c first = (nl < cr) ? nl : cr; } } if (first == cmt_string::npos) { // This is likely the last line since there is no end-of-line text.substr (pos, line); pos = max_pos; } else if (first > pos) { // The eol was found beyond the current position // (ie. this is a non empty line) text.substr (pos, first - pos, line); pos = first + 1; } else { // an empty line line = ""; pos++; } m_line_number++; if (line != "") { if ((pattern == "") || (line.find (pattern) != cmt_string::npos)) { filter (line); if (m_condition != ok) return (m_condition); } } } } end (); return (m_condition); } //------------------------------------------------ Awk::condition Awk::run (const cmt_string& text, const cmt_regexp& expression) { m_line_number = 0; m_condition = ok; begin (); if (m_condition != ok) return (m_condition); Parser p (this, "", &expression); m_condition = p.parse (text); if (m_condition != ok) return (m_condition); /* if (CmtSystem::testenv ("CMTTESTAWK")) { } else { cmt_string line; int pos = 0; int max_pos; max_pos = text.size (); for (pos = 0; pos < max_pos;) { int cr = text.find (pos, "\r\n"); int nl = text.find (pos, '\n'); // Get the first end-of-line (either lf or cr-lf) int first = nl; if (cr != cmt_string::npos) { if (nl == cmt_string::npos) { first = cr; } else { first = (nl < cr) ? nl : cr; } } if (first == cmt_string::npos) { // This is likely the last line since there is no end-of-line text.substr (pos, line); pos = max_pos; } else if (first > pos) { // The eol was found beyond the current position // (ie. this is a non empty line) text.substr (pos, first - pos, line); pos = first + 1; } else { // an empty line line = ""; pos++; } m_line_number++; if (line != "") { if (expression.match (line)) { filter (line); if (m_condition != ok) return (m_condition); } } } } */ end (); return (m_condition); } //------------------------------------------------ void Awk::stop () { m_condition = stopped; } //------------------------------------------------ void Awk::abort () { m_condition = failed; } //------------------------------------------------ void Awk::allow_continuation () { m_continuation_allowed = true; } //------------------------------------------------ Awk::condition Awk::get_last_condition () const { return (m_condition); } //------------------------------------------------ void Awk::begin () { } //------------------------------------------------ void Awk::filter (const cmt_string& /*line*/) { //cout << "awk> " << line << endl; } //------------------------------------------------ void Awk::end () { } //------------------------------------------------ void Awk::inc_line_number () { m_line_number++; } //------------------------------------------------ Awk::condition FAwk::run (const cmt_string& file_name, const cmt_string& pattern) { if (!CmtSystem::test_file (file_name)) return (failed); CmtSystem::basename (file_name, m_file_name); CmtSystem::dirname (file_name, m_dir_name); cmt_string text; text.read (file_name); return (Awk::run (text, pattern)); } //------------------------------------------------ Awk::condition FAwk::run (const cmt_string& file_name, const cmt_regexp& expression) { if (!CmtSystem::test_file (file_name)) return (failed); CmtSystem::basename (file_name, m_file_name); CmtSystem::dirname (file_name, m_dir_name); cmt_string text; text.read (file_name); return (Awk::run (text, expression)); } //------------------------------------------------ Awk::condition PAwk::run (const cmt_string& command, const cmt_string& pattern) { cmt_string line; m_line_number = 0; m_condition = ok; begin (); if (m_condition != ok) return (m_condition); FILE* f = popen (command.c_str (), "r"); if (f == 0) return (failed); char buffer[8192]; char* ptr; while ((ptr = fgets (buffer, sizeof (buffer), f)) != NULL) { line = ptr; if (line.find ("\n") == cmt_string::npos) { cerr << "#CMT> Warning: Line too long and truncated in PAwk::run for command " << command << endl; } line.replace ("\n", ""); m_line_number++; if (line != "") { if ((pattern == "") || (line.find (pattern) != cmt_string::npos)) { filter (line); if (m_condition != ok) return (m_condition); } } } pclose (f); end (); return (m_condition); } //------------------------------------------------ Awk::condition PAwk::run (const cmt_string& command, const cmt_regexp& expression) { cmt_string line; m_line_number = 0; m_condition = ok; begin (); if (m_condition != ok) return (m_condition); FILE* f = popen (command.c_str (), "r"); if (f == 0) return (failed); char buffer[256]; char* ptr; while ((ptr = fgets (buffer, sizeof (buffer), f)) != NULL) { line = ptr; line.replace ("\n", ""); m_line_number++; if (line != "") { if (expression.match (line)) { filter (line); if (m_condition != ok) return (m_condition); } } } pclose (f); end (); return (m_condition); } //---------------------------------------------------------- PathScanner::PathScanner () { _running = false; _level = 0; } //---------------------------------------------------------- bool PathScanner::scan_path (const cmt_string& path, actor& a) { if (_running) return (false); _level = 0; _running = true; cmt_string compressed_path = path; CmtSystem::compress_path (compressed_path); scan_path (compressed_path, 0, a); _running = false; _level = 0; return (true); } //---------------------------------------------------------- void PathScanner::scan_path (const cmt_string& path, int level, actor& a) { if (level > 10) { //cout << "#PathScanner::scan_path> too deep search path=" << path << endl; return; } // // Only do something if it is a directory. // if (!CmtSystem::test_directory (path)) return; CmtSystem::cmt_string_vector list; CmtSystem::cmt_string_vector entrylist; CmtSystem::scan_dir (path, list); if (list.size () == 0) return; _level++; // Will be set if at least one directory is a version directory bool has_package = false; cmt_string name; cmt_string version; cmt_string where; int i; for (i = 0; i < list.size (); i++) { const cmt_string& here = list[i]; if (!CmtSystem::test_directory (here)) continue; name = ""; version = ""; cmt_string entry; CmtSystem::basename (here, entry); CmtSystem::dirname (path, where); // cout << "## here=" << here << " entry=" << entry << " where=" << where << endl; if ((level == 0) && (entry == "InstallArea")) continue; cmt_string req; req = here; req += CmtSystem::file_separator (); req += "mgr"; req += CmtSystem::file_separator (); req += "requirements"; if (CmtSystem::test_file (req)) { // We have found /mgr/requirements // this is an old directory convention. // The version directory is the directory above version = entry; CmtSystem::basename (path, name); // cout << "#1" << endl; a.run (name, version, where); has_package = true; continue; } req = here; req += CmtSystem::file_separator (); req += "cmt"; req += CmtSystem::file_separator (); req += "requirements"; if (CmtSystem::test_file (req)) { // We have found /cmt/requirements // Question now is to detect the directory structure: // // if cmt/version.cmt exists it's a non-version-directory structure // else // if there is a package statement in the requirements file we find it upward // else // if up is a version directory // else // cmt_string vreq; vreq = here; vreq += CmtSystem::file_separator (); vreq += "cmt"; vreq += CmtSystem::file_separator (); vreq += "version.cmt"; if (CmtSystem::test_file (vreq)) { version.read (vreq); int pos; pos = version.find ('\n'); if (pos != cmt_string::npos) version.erase (pos); pos = version.find ('\r'); if (pos != cmt_string::npos) version.erase (pos); //cout << "#2" << endl; a.run (entry, version, path); has_package = true; continue; } cmt_string p; p.read (req); int pos; pos = p.find ("package"); if (pos != cmt_string::npos) { p.erase (0, pos+8); pos = p.find ('\n'); if (pos != cmt_string::npos) p.erase (pos); pos = p.find ('\r'); if (pos != cmt_string::npos) p.erase (pos); p.replace_all (" ", ""); p.replace_all ("\t", ""); if (p != "") name = p; } if (name != "") { // The package name was specified in the requirements file if (entry == name) { // The structure is without the version directory. //cout << "#3" << endl; a.run (name, "v1", path); has_package = true; continue; } version = entry; CmtSystem::basename (path, entry); if (entry == name) { // The structure is with the version directory. //cout << "#4" << endl; a.run (name, version, where); has_package = true; continue; } // No directory structure matches the package name // Is it a typo in the requirements file ? // probably we should display it and quit... } else { version = entry; CmtSystem::basename (path, entry); } // The package name is not specified in the requirements file // or did not match the directory structure // We'll have to guess it from the structure if (CmtSystem::is_version_directory (version)) { // cout << "#5" << endl; a.run (entry, version, where); has_package = true; continue; } name = version; where += CmtSystem::file_separator (); where += entry; // cout << "#6" << endl; a.run (name, "v1", where); has_package = true; continue; } //cout << "#7" << endl; scan_path (here, level + 1, a); } if (has_package) { // // At least one version was found here. Thus we want to scan further down. // for (i = 0; i < entrylist.size (); i++) { const cmt_string& e = entrylist[i]; cmt_string p = path; p += CmtSystem::file_separator (); p += e; /* for (j = 1; j < _level; j++) cout << " "; cout << "Restarting scan_path on p=" << p << endl; */ cout << "#PathScanner::scan_path> Restarting scan_path on p=" << p << endl; scan_path (p, 1, a); } } _level--; } //---------------------------------------------------------- bool PathScanner::scan_package (const cmt_string& path, const cmt_string& package) { // // Only do something if it is a directory. // if (!CmtSystem::test_directory (path)) return (false); cmt_string pattern = path; pattern += CmtSystem::file_separator (); pattern += package; if (!CmtSystem::test_directory (pattern)) return (false); CmtSystem::cmt_string_vector list; CmtSystem::scan_dir (pattern, list); if (list.size () == 0) { return (false); } bool result = false; int i; for (i = 0; i < list.size (); i++) { const cmt_string& name = list[i]; cmt_string version; CmtSystem::basename (name, version); if (version == "cmt") { cmt_string req; req = name; req += CmtSystem::file_separator (); req += "requirements"; if (CmtSystem::test_file (req)) { //cout << " -> no version" << endl; cmt_string req; req = name; req += CmtSystem::file_separator (); req += "version.cmt"; cmt_string version; if (CmtSystem::test_file (req)) { version.read (req); int pos; pos = version.find ('\n'); if (pos != cmt_string::npos) version.erase (pos); pos = version.find ('\r'); if (pos != cmt_string::npos) version.erase (pos); } else { version = "v*"; } cout << package << " " << version << " " << path << endl; result = true; } } else if (CmtSystem::is_version_directory (version)) { cmt_string req; req = name; req += CmtSystem::file_separator (); req += "cmt"; req += CmtSystem::file_separator (); req += "requirements"; if (CmtSystem::test_file (req)) { //cout << " -> cmt" << endl; cout << package << " " << version << " " << path << endl; result = true; } else { //cout << " -> no cmt" << endl; req = name; req += CmtSystem::file_separator (); req += "mgr"; req += CmtSystem::file_separator (); req += "requirements"; if (CmtSystem::test_file (req)) { //cout << " -> mgr" << endl; cout << package << " " << version << " " << path << endl; result = true; } else { //cout << " -> no mgr" << endl; } } } else { //cout << " -> stop" << endl; } } return (result); }