source: CMT/v1r25p20130606/source/cmt_awk.cxx @ 658

Last change on this file since 658 was 607, checked in by rybkin, 12 years ago

See C.L. 482

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