source: CMT/HEAD/source/cmt_awk.cxx

Last change on this file was 663, checked in by rybkin, 10 years ago

See C.L. 522

  • Property svn:eol-style set to native
File size: 21.5 KB
Line 
1//-----------------------------------------------------------
2// Copyright Christian Arnault LAL-Orsay CNRS
3// arnault@lal.in2p3.fr
4// Modified by Grigory Rybkin
5// See the complete license in cmt_license.txt "http://www.cecill.info".
6//-----------------------------------------------------------
7
8#ifdef WIN32
9#include <direct.h>
10#define popen _popen
11#define pclose _pclose
12#include <stdlib.h>
13#define PATH_MAX _MAX_PATH
14
15#else
16#include <limits.h> // define PATH_MAX
17#endif
18
19#include "cmt_awk.h"
20#include "cmt_system.h"
21#include "cmt_log.h"
22#include "cmt_use.h"
23
24class Parser
25{
26public:
27  Parser (Awk* awk, const cmt_string pattern, const cmt_regexp* expression) :
28    m_pattern (pattern), m_expression (expression), m_awk(awk)
29  {
30  }
31
32  /**
33   *  this first level parsing function extracts individual lines
34   *  from the text, taking care of both Unix and Windows EOL styles.
35   *
36   *   Then the second level parsing function parse_line is called.
37   */
38  Awk::condition parse (const cmt_string& text)
39  {
40    Awk::condition result = Awk::ok;
41
42    cmt_string line;
43    /*
44    int pos;
45    int max_pos;
46
47    pos = 0;
48    max_pos = text.size ();
49    */
50    m_accumulator.erase (0);
51
52    int nl;
53    int begin = 0;
54    int end = text.size ();
55   
56    while ((nl = text.find (begin, '\n')) != cmt_string::npos)
57      {
58        if (begin < nl && text[nl - 1] == '\r')
59          {
60            text.substr (begin, nl - 1 - begin, line);
61          }
62        else
63          {
64            text.substr (begin, nl - begin, line);
65          }
66        //      cerr << "|Awk::condition parse> [" << line << "]" << endl;
67        begin = nl + 1;
68        if (m_awk != 0) m_awk->inc_line_number ();
69
70        result = parse_line (line);
71        if (result != Awk::ok) return result;
72      } // while ((nl = text.find (begin, '\n')) != cmt_string::npos)
73   
74    if (begin < end)
75      {
76        text.substr (begin, end - begin, line);
77        if (m_awk != 0) m_awk->inc_line_number ();
78       
79        result = parse_line (line);
80      }
81   
82    /*
83    for (pos = 0; pos < max_pos;)
84      {
85        int eol = text.find (pos, '\n');
86           
87        if (eol == cmt_string::npos)
88          {
89            // Last line, since there is no eol at all
90            text.substr (pos, line);
91            pos = max_pos;
92          }
93        else
94          {
95            int length = 1;
96
97            int cr = text.find (pos, "\r\n");
98
99            if (cr == (eol-1))
100              {
101                eol = cr;
102                length = 2;
103              }
104
105            if (eol == pos)
106              {
107                // this is an empty line
108                line = "";
109                pos += length;
110              }
111            else
112              {
113                // The eol was found beyond the current position
114                // (ie. this is a non empty line)
115                text.substr (pos, eol - pos, line);
116                pos = eol + length;
117              }
118          }
119
120        if (m_awk != 0) m_awk->inc_line_number ();
121
122        result = parse_line (line);
123        if (result != Awk::ok) break;
124      }
125    */
126    return (result);
127  }
128
129  /**
130   *   This second level parsing function accumulates individual lines
131   *   with real trailing back slashes.
132   *    Eventually the possible text pattern or regular expression is
133   *   checked and the Awk::filter function is called in case of
134   *   succesful match onto the accumulated line.
135   */
136  Awk::condition parse_line (const cmt_string& line)
137  {
138    Awk::condition result = Awk::ok;
139    int length;
140    cmt_string temp_line = line;
141
142    //
143    // We scan the line for handling backslashes.
144    //
145    // Really terminating backslashes (ie those only followed by spaces/tabs
146    // mean continued line
147    //
148    //
149
150    bool finished = true;
151
152    length = temp_line.size ();
153
154    if (length == 0)
155      {
156        // An empty line following a backslash terminates the continuation.
157        finished = true;
158      }
159    else
160      {
161        int back_slash = temp_line.find_last_of ('\\');
162       
163        if (back_slash != cmt_string::npos)
164          {
165            //
166            // This is the last backslash
167            // check if there are only space chars after it
168            //
169           
170            bool at_end = true;
171           
172            for (int i = (back_slash + 1); i < length; i++)
173              {
174                char c = temp_line[i];
175                if ((c != ' ') && (c != '\t'))
176                  {
177                    at_end = false;
178                    break;
179                  }
180              }
181               
182            if (at_end)
183              {
184                temp_line.erase (back_slash);
185                finished = false;
186              }
187            else
188              {
189                // This was not a trailing backslash.
190                finished = true;
191              }
192          }
193       
194        m_accumulator += temp_line;
195      }
196
197    if (!finished)
198      {
199        // We still need to accumulate forthcoming lines
200        // before parsing the resulting text.
201        return (Awk::ok);
202      }
203
204    // now filter the complete accumulated line (if non empty)
205
206    if (m_accumulator != "")
207      {
208        bool ok = false;
209           
210        if (m_expression != 0)
211          {
212            if (m_expression->match (m_accumulator))
213              {
214                ok = true;
215              }
216          }
217        else
218          {
219            if ((m_pattern == "") ||
220                (m_accumulator.find (m_pattern) != cmt_string::npos))
221              {
222                ok = true;
223              }
224          }
225           
226        if (ok && (m_awk != 0))
227          {
228            m_awk->filter (m_accumulator);
229            result = m_awk->get_last_condition ();
230          }
231
232        m_accumulator.erase (0);
233      }
234       
235    return (result);
236  }
237
238private:
239
240  cmt_string m_accumulator;
241  cmt_string m_pattern;
242  const cmt_regexp* m_expression;
243  Awk* m_awk;
244};
245
246//------------------------------------------------
247Awk::Awk ()
248{
249  m_condition = ok;
250}
251
252//------------------------------------------------
253Awk::~Awk ()
254{
255}
256
257//------------------------------------------------
258Awk::condition Awk::run (const cmt_string& text,
259                         const cmt_string& pattern)
260{
261  m_line_number = 0;
262  m_condition = ok;
263
264  begin ();
265  if (m_condition != ok) return (m_condition);
266
267  if (Cmt::get_debug () &&
268      CmtSystem::testenv ("CMTTESTAWK"))
269    {
270      Parser p (this, pattern, 0);
271
272      m_condition = p.parse (text);
273      if (m_condition != ok) return (m_condition);
274    }
275  else
276    {
277      cmt_string line;
278      int nl;
279      int begin = 0;
280      int end = text.size ();
281     
282      while ((nl = text.find (begin, '\n')) != cmt_string::npos)
283        {
284          if (begin < nl && text[nl - 1] == '\r')
285            {
286              text.substr (begin, nl - 1 - begin, line);
287            }
288          else
289            {
290              text.substr (begin, nl - begin, line);
291            }
292          //      cerr << "|Awk::condition run> [" << line << "]" << endl;
293          begin = nl + 1;
294          m_line_number++;
295         
296          if (line != "")
297            {
298              if ((pattern == "") ||
299                  (line.find (pattern) != cmt_string::npos))
300                {
301                  filter (line);
302                  if (m_condition != ok) return (m_condition);
303                }
304            }
305        } // while ((nl = text.find (begin, '\n')) != cmt_string::npos)
306     
307      if (begin < end)
308        {
309          text.substr (begin, end - begin, line);
310          m_line_number++;
311         
312          if (line != "")
313            {
314              if ((pattern == "") ||
315                  (line.find (pattern) != cmt_string::npos))
316                {
317                  filter (line);
318                  if (m_condition != ok) return (m_condition);
319                }
320            }
321        }
322      /*
323      int pos = 0;
324      int max_pos;
325
326      max_pos = text.size ();
327
328      for (pos = 0; pos < max_pos;)
329        {
330          int cr = text.find (pos, "\r\n");
331          int nl = text.find (pos, '\n');
332         
333          // Get the first end-of-line (either lf or cr-lf)
334
335          //--------------------
336          //
337          //     cr    1    0
338          //   nl
339          //
340          //    1      a    b
341          //
342          //    0      c    d
343          //
344          //--------------------
345         
346          int first = nl;
347         
348          if (cr != cmt_string::npos)
349            {
350              // cases a or c
351
352              if (nl == cmt_string::npos)
353                {
354                  // case a
355                  first = cr;
356                }
357              else
358                {
359                  // case c
360                  first = (nl < cr) ? nl : cr;
361                }
362            }
363         
364          if (first == cmt_string::npos)
365            {
366              // This is likely the last line since there is no end-of-line
367              text.substr (pos, line);
368              pos = max_pos;
369            }
370          else if (first > pos)
371            {
372              // The eol was found beyond the current position
373              // (ie. this is a non empty line)
374              text.substr (pos, first - pos, line);
375              pos = first + 1;
376            }
377          else
378            {
379              // an empty line
380              line = "";
381              pos++;
382            }
383         
384          m_line_number++;
385         
386          if (line != "")
387            {
388              if ((pattern == "") ||
389                  (line.find (pattern) != cmt_string::npos))
390                {
391                  filter (line);
392                  if (m_condition != ok) return (m_condition);
393                }
394            }
395        }
396      */
397    }
398
399  end ();
400
401  return (m_condition);
402}
403
404//------------------------------------------------
405Awk::condition Awk::run (const cmt_string& text,
406                         const cmt_regexp& expression)
407{
408  m_line_number = 0;
409  m_condition = ok;
410
411  begin ();
412  if (m_condition != ok) return (m_condition);
413
414  Parser p (this, "", &expression);
415
416  m_condition = p.parse (text);
417  if (m_condition != ok) return (m_condition);
418
419  /*
420    if (CmtSystem::testenv ("CMTTESTAWK"))
421    {
422    }
423    else
424    {
425    cmt_string line;
426    int pos = 0;
427    int max_pos;
428
429    max_pos = text.size ();
430
431    for (pos = 0; pos < max_pos;)
432    {
433    int cr = text.find (pos, "\r\n");
434    int nl = text.find (pos, '\n');
435         
436    // Get the first end-of-line (either lf or cr-lf)
437         
438    int first = nl;
439         
440    if (cr != cmt_string::npos)
441    {
442    if (nl == cmt_string::npos)
443    {
444    first = cr;
445    }
446    else
447    {
448    first = (nl < cr) ? nl : cr;
449    }
450    }
451         
452    if (first == cmt_string::npos)
453    {
454    // This is likely the last line since there is no end-of-line
455    text.substr (pos, line);
456    pos = max_pos;
457    }
458    else if (first > pos)
459    {
460    // The eol was found beyond the current position
461    // (ie. this is a non empty line)
462    text.substr (pos, first - pos, line);
463    pos = first + 1;
464    }
465    else
466    {
467    // an empty line
468    line = "";
469    pos++;
470    }
471         
472    m_line_number++;
473         
474    if (line != "")
475    {
476    if (expression.match (line))
477    {
478    filter (line);
479    if (m_condition != ok) return (m_condition);
480    }
481    }
482    }
483    }
484  */
485
486  end ();
487
488  return (m_condition);
489}
490
491//------------------------------------------------
492void Awk::stop ()
493{
494  m_condition = stopped;
495}
496
497//------------------------------------------------
498void Awk::abort ()
499{
500  m_condition = failed;
501}
502
503//------------------------------------------------
504void Awk::allow_continuation ()
505{
506  m_continuation_allowed = true;
507}
508
509//------------------------------------------------
510Awk::condition Awk::get_last_condition () const
511{
512  return (m_condition);
513}
514
515//------------------------------------------------
516void Awk::begin ()
517{
518}
519
520//------------------------------------------------
521void Awk::filter (const cmt_string& /*line*/)
522{
523}
524
525//------------------------------------------------
526void Awk::end ()
527{
528}
529
530//------------------------------------------------
531void Awk::inc_line_number ()
532{
533  m_line_number++;
534}
535
536//------------------------------------------------
537Awk::condition FAwk::run (const cmt_string& file_name,
538                          const cmt_string& pattern)
539{
540  if (!CmtSystem::test_file (file_name)) return (failed);
541
542  CmtSystem::basename (file_name, m_file_name);
543  CmtSystem::dirname (file_name, m_dir_name);
544
545  cmt_string text;
546
547  if (!text.read (file_name)) return (failed);
548
549  return (Awk::run (text, pattern));
550}
551
552//------------------------------------------------
553Awk::condition FAwk::run (const cmt_string& file_name,
554                          const cmt_regexp& expression)
555{
556  if (!CmtSystem::test_file (file_name)) return (failed);
557
558  CmtSystem::basename (file_name, m_file_name);
559  CmtSystem::dirname (file_name, m_dir_name);
560
561  cmt_string text;
562
563  if (!text.read (file_name)) return (failed);
564
565  return (Awk::run (text, expression));
566}
567
568//------------------------------------------------
569Awk::condition PAwk::run (const cmt_string& command, 
570                          const cmt_string& pattern)
571{
572  cmt_string line;
573
574  m_line_number = 0;
575  m_condition = ok;
576
577  begin ();
578  if (m_condition != ok) return (m_condition);
579
580  FILE* f = popen (command.c_str (), "r"); 
581 
582  if (f == 0) return (failed);
583
584  char buffer[8192]; 
585  char* ptr;
586
587  while ((ptr = fgets (buffer, sizeof (buffer), f)) != NULL) 
588    {
589      line = ptr;
590
591      if (line.find ("\n") == cmt_string::npos)
592        {
593          CmtMessage::warning ("Line too long and truncated in PAwk::run for command " + command);
594          //          cerr << "#CMT> Warning: Line too long and truncated in PAwk::run for command " << command << endl;
595        }
596
597      line.replace ("\n", "");
598
599      m_line_number++;
600
601      if (line != "")
602        {
603          if ((pattern == "") ||
604              (line.find (pattern) != cmt_string::npos))
605            {
606              filter (line);
607              if (m_condition != ok) return (m_condition);
608            }
609        }
610    }
611
612  pclose (f);
613
614  end ();
615
616  return (m_condition);
617}
618
619//------------------------------------------------
620Awk::condition PAwk::run (const cmt_string& command, 
621                          const cmt_regexp& expression)
622{
623  cmt_string line;
624
625  m_line_number = 0;
626  m_condition = ok;
627
628  begin ();
629  if (m_condition != ok) return (m_condition);
630
631  FILE* f = popen (command.c_str (), "r"); 
632 
633  if (f == 0) return (failed);
634
635  char buffer[PATH_MAX]; 
636  //  char buffer[256];
637  char* ptr;
638
639  while ((ptr = fgets (buffer, sizeof (buffer), f)) != NULL) 
640    {
641      line = ptr;
642
643      line.replace ("\n", "");
644
645      m_line_number++;
646
647      if (line != "")
648        {
649          if (expression.match (line))
650            {
651              filter (line);
652              if (m_condition != ok) return (m_condition);
653            }
654        }
655    }
656
657  pclose (f);
658
659  end ();
660
661  return (m_condition);
662}
663
664//----------------------------------------------------------
665PathScanner::PathScanner ()
666{
667  _running = false;
668  _level = 0;
669}
670
671//----------------------------------------------------------
672bool PathScanner::scan_path (const cmt_string& path, actor& a)
673{
674  if (_running) return (false);
675
676  _level = 0;
677  _running = true;
678
679  cmt_string compressed_path = path;
680  CmtSystem::compress_path (compressed_path);
681  CmtSystem::realpath      (compressed_path, compressed_path); 
682  scan_path                (compressed_path, 0, a);
683
684  _running = false;
685  _level = 0;
686
687  return (true);
688}
689
690//----------------------------------------------------------
691void PathScanner::scan_path (const cmt_string& path, int level, actor& a)
692{
693  if (level > 10)
694    {
695      return;
696    }
697
698  //
699  // Only do something if it is a directory.
700  //
701  if (!CmtSystem::test_directory (path)) return;
702 
703
704  CmtSystem::cmt_string_vector list;
705  CmtSystem::cmt_string_vector entrylist;
706
707  CmtSystem::scan_dir (path, list);
708
709  if (list.size () == 0) return;
710
711  _level++;
712
713  // Will be set if at least one directory is a version directory
714  bool has_package = false;
715
716  cmt_string name;
717  cmt_string version;
718  cmt_string where;
719
720  int i;
721
722  for (i = 0; i < list.size (); i++)
723    {
724      const cmt_string& here = list[i];
725
726      if (!CmtSystem::test_directory (here)) continue;
727
728      name    = "";
729      version = "";
730
731      cmt_string entry;
732      CmtSystem::basename (here, entry);
733      CmtSystem::dirname (path, where);
734
735      if ((level == 0) && (entry == "InstallArea")) continue;
736      if (entry == ".svn" || entry == "CVS") continue;
737
738      cmt_string req;
739
740      req = here;
741      req += CmtSystem::file_separator ();
742      req += "mgr";
743      req += CmtSystem::file_separator ();
744      req += "requirements";
745     
746      if (CmtSystem::test_file (req))
747        {
748          // We have found <path>/mgr/requirements
749          // this is an old directory convention.
750          // The version directory is the directory above
751
752          version = entry;
753          CmtSystem::basename (path, name);
754
755          a.run (name, version, where);
756          has_package = true;
757
758          // We don't keep on looking in this directory since the versioned structure
759          // does not expect subpackages there
760          continue;
761        }
762
763      req = here;
764      req += CmtSystem::file_separator ();
765      req += "cmt";
766      req += CmtSystem::file_separator ();
767      req += "requirements";
768
769      if (CmtSystem::test_file (req))
770        {
771          // We have found <path>/cmt/requirements
772          // Question now is to detect the directory structure:
773          //
774          // if cmt/version.cmt exists it's a non-version-directory structure
775          // else
776          //   if there is a package statement in the requirements file we find it upward
777          //   else
778          //     if up is a version directory
779          //     else
780          //
781
782          cmt_string vreq;
783          vreq = here;
784          vreq += CmtSystem::file_separator ();
785          vreq += "cmt";
786          //      vreq += CmtSystem::file_separator ();
787          //      vreq += "version.cmt";
788
789          //      if (CmtSystem::test_file (vreq))
790          if (CmtSystem::test_file (vreq
791                                    + CmtSystem::file_separator ()
792                                    + Package::get_version_file_name ())
793              )
794            {
795              Package::get_version (version, vreq);
796             
797              a.run (entry, version, path, true);
798              has_package = true;
799
800              // We scan further down
801              scan_path (here, 1, a);
802              continue;
803            }
804
805          cmt_string p;
806         
807          p.read (req);
808          int pos;
809          pos = p.find ("package");
810          if (pos != cmt_string::npos)
811            {
812              p.erase (0, pos+8);
813              pos = p.find ('\n');
814              if (pos != cmt_string::npos) p.erase (pos);
815              pos = p.find ('\r');
816              if (pos != cmt_string::npos) p.erase (pos);
817              p.replace_all (" ", "");
818              p.replace_all ("\t", "");
819              if (p != "") name = p;
820            }
821         
822          if (name != "")
823            {
824              // The package name was specified in the requirements file
825             
826              if (entry == name)
827                {
828                  // The structure is without the version directory.
829                 
830                  if (!Package::get_version (version, vreq))
831                    version = "v*";
832
833                  a.run (name, version, path, true);
834                  //              a.run (name, "v1", path);
835                  has_package = true;
836                 
837                  // We scan further down
838                  scan_path (here, 1, a);
839                  continue;
840                }
841             
842              version = entry;
843              CmtSystem::basename (path, entry);
844             
845              if (entry == name)
846                {
847                  // The structure is with the version directory.
848                 
849                  a.run (name, version, where);
850                  has_package = true;
851                 
852                  continue;
853                }
854             
855              // No directory structure matches the package name
856              // Is it a typo in the requirements file ?
857              // probably we should display it and quit...
858            }
859          else
860            {
861              version = entry;
862              CmtSystem::basename (path, entry);
863            }
864         
865          // The package name is not specified in the requirements file
866          // or did not match the directory structure
867          // We'll have to guess it from the structure
868         
869          if (CmtSystem::is_version_directory (version))
870            {
871              a.run (entry, version, where);
872              has_package = true;
873             
874              continue;
875            }
876
877          // Here we suppose that the directory up is the package name
878          // although no specification of the version has been found
879          // So we decide that
880          // - it's a non-versioned structure
881          // - the version is set by default to v1
882          name = version;
883         
884          where += CmtSystem::file_separator ();
885          where += entry;
886         
887          a.run (name, "v1", where);
888          has_package = true;
889         
890          // We scan further down
891          scan_path (here, 1, a);
892          continue;
893        }
894
895      //
896      // Here this is a non-package directory. Let's search further down
897
898      scan_path (here, level + 1, a);
899    }
900
901  if (has_package)
902    {
903      //
904      // At least one version was found here. Thus we want to scan further down.
905      //
906
907      for (i = 0; i < entrylist.size (); i++)
908        {
909          const cmt_string& e = entrylist[i];
910
911          cmt_string p = path;
912          p += CmtSystem::file_separator ();
913          p += e;
914
915          scan_path (p, 1, a);
916        }
917    }
918
919  _level--;
920}
921
922
923//----------------------------------------------------------
924bool PathScanner::scan_package (const cmt_string& path,
925                                const cmt_string& package)
926{
927  //
928  // Only do something if it is a directory.
929  //
930
931  if (!CmtSystem::test_directory (path)) return (false);
932
933  cmt_string pattern = path;
934  pattern += CmtSystem::file_separator ();
935  pattern += package;
936
937  if (!CmtSystem::test_directory (pattern)) return (false);
938
939  CmtSystem::cmt_string_vector list;
940
941  CmtSystem::scan_dir (pattern, list);
942
943  if (list.size () == 0) 
944    {
945      return (false);
946    }
947
948  bool result = false;
949
950  int i;
951  for (i = 0; i < list.size (); i++)
952    {
953      const cmt_string& name = list[i];
954
955      cmt_string version;
956      CmtSystem::basename (name, version);
957
958      if (version == "cmt")
959        {
960          cmt_string req;
961
962          req = name;
963          req += CmtSystem::file_separator ();
964          req += "requirements";
965
966          if (CmtSystem::test_file (req))
967            {
968              cmt_string version;
969              cmt_string v;
970              if (Package::get_version (v, name))
971                {
972                  version = v;
973                }
974              else
975                {
976                  version = "v*";
977                }
978             
979              cout << package << " " << version << " " << path << endl;
980
981              result = true;
982            }
983        }
984      else if (CmtSystem::is_version_directory (version))
985        {
986          cmt_string req;
987
988          req = name;
989          req += CmtSystem::file_separator ();
990          req += "cmt";
991          req += CmtSystem::file_separator ();
992          req += "requirements";
993
994          if (CmtSystem::test_file (req))
995            {
996              cout << package << " " << version << " " << path << endl;
997
998              result = true;
999            }
1000          else
1001            {
1002              req = name;
1003              req += CmtSystem::file_separator ();
1004              req += "mgr";
1005              req += CmtSystem::file_separator ();
1006              req += "requirements";
1007
1008              if (CmtSystem::test_file (req))
1009                {
1010                  cout << package << " " << version << " " << path << endl;
1011
1012                  result = true;
1013                }
1014              else
1015                {
1016                }
1017            }
1018        }
1019      else
1020        {
1021        }
1022    }
1023
1024  return (result);
1025}
1026
Note: See TracBrowser for help on using the repository browser.