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

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

See C.L. 482

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