source: CMT/v1r21/source/cmt_awk.cxx

Last change on this file was 542, checked in by rybkin, 14 years ago

See C.L. 429

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