source: CMT/HEAD/source/cmt_awk.cxx @ 569

Last change on this file since 569 was 569, checked in by rybkin, 13 years ago

See C.L. 452

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