source: CMT/v1r20p20070517/source/cmt_awk.cxx

Last change on this file was 400, checked in by arnault, 17 years ago

Text formatting
Sending warnings & errors to stderr
Using internally PWD for every cd/pwd
CL 327

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