source: CMT/v1r20p20081118/source/cmt_awk.cxx @ 597

Last change on this file since 597 was 461, checked in by rybkin, 16 years ago

See C.L. 362

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