source: CMT/v1r25-branch/source/cmt_awk.cxx@ 636

Last change on this file since 636 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
Line 
1//-----------------------------------------------------------
2// Copyright Christian Arnault LAL-Orsay CNRS
3// arnault@lal.in2p3.fr
4// See the complete license in cmt_license.txt "http://www.cecill.info".
5//-----------------------------------------------------------
6
7#ifdef WIN32
8#include <direct.h>
9#define popen _popen
10#define pclose _pclose
11#include <stdlib.h>
12#define PATH_MAX _MAX_PATH
13
14#else
15#include <limits.h> // define PATH_MAX
16#endif
17
18#include "cmt_awk.h"
19#include "cmt_system.h"
20#include "cmt_log.h"
21#include "cmt_use.h"
22
23class Parser
24{
25public:
26 Parser (Awk* awk, const cmt_string pattern, const cmt_regexp* expression) :
27 m_pattern (pattern), m_expression (expression), m_awk(awk)
28 {
29 }
30
31 /**
32 * this first level parsing function extracts individual lines
33 * from the text, taking care of both Unix and Windows EOL styles.
34 *
35 * Then the second level parsing function parse_line is called.
36 */
37 Awk::condition parse (const cmt_string& text)
38 {
39 Awk::condition result = Awk::ok;
40
41 cmt_string line;
42 /*
43 int pos;
44 int max_pos;
45
46 pos = 0;
47 max_pos = text.size ();
48 */
49 m_accumulator.erase (0);
50
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 /*
82 for (pos = 0; pos < max_pos;)
83 {
84 int eol = text.find (pos, '\n');
85
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;
95
96 int cr = text.find (pos, "\r\n");
97
98 if (cr == (eol-1))
99 {
100 eol = cr;
101 length = 2;
102 }
103
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 }
118
119 if (m_awk != 0) m_awk->inc_line_number ();
120
121 result = parse_line (line);
122 if (result != Awk::ok) break;
123 }
124 */
125 return (result);
126 }
127
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 */
135 Awk::condition parse_line (const cmt_string& line)
136 {
137 Awk::condition result = Awk::ok;
138 int length;
139 cmt_string temp_line = line;
140
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 //
148
149 bool finished = true;
150
151 length = temp_line.size ();
152
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 ('\\');
161
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 //
168
169 bool at_end = true;
170
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 }
180
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 }
192
193 m_accumulator += temp_line;
194 }
195
196 if (!finished)
197 {
198 // We still need to accumulate forthcoming lines
199 // before parsing the resulting text.
200 return (Awk::ok);
201 }
202
203 // now filter the complete accumulated line (if non empty)
204
205 if (m_accumulator != "")
206 {
207 bool ok = false;
208
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 }
224
225 if (ok && (m_awk != 0))
226 {
227 m_awk->filter (m_accumulator);
228 result = m_awk->get_last_condition ();
229 }
230
231 m_accumulator.erase (0);
232 }
233
234 return (result);
235 }
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
266 if (Cmt::get_debug () &&
267 CmtSystem::testenv ("CMTTESTAWK"))
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;
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 /*
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
332 // Get the first end-of-line (either lf or cr-lf)
333
334 //--------------------
335 //
336 // cr 1 0
337 // nl
338 //
339 // 1 a b
340 //
341 // 0 c d
342 //
343 //--------------------
344
345 int first = nl;
346
347 if (cr != cmt_string::npos)
348 {
349 // cases a or c
350
351 if (nl == cmt_string::npos)
352 {
353 // case a
354 first = cr;
355 }
356 else
357 {
358 // case c
359 first = (nl < cr) ? nl : cr;
360 }
361 }
362
363 if (first == cmt_string::npos)
364 {
365 // This is likely the last line since there is no end-of-line
366 text.substr (pos, line);
367 pos = max_pos;
368 }
369 else if (first > pos)
370 {
371 // The eol was found beyond the current position
372 // (ie. this is a non empty line)
373 text.substr (pos, first - pos, line);
374 pos = first + 1;
375 }
376 else
377 {
378 // an empty line
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 }
395 */
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
418 /*
419 if (CmtSystem::testenv ("CMTTESTAWK"))
420 {
421 }
422 else
423 {
424 cmt_string line;
425 int pos = 0;
426 int max_pos;
427
428 max_pos = text.size ();
429
430 for (pos = 0; pos < max_pos;)
431 {
432 int cr = text.find (pos, "\r\n");
433 int nl = text.find (pos, '\n');
434
435 // Get the first end-of-line (either lf or cr-lf)
436
437 int first = nl;
438
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 }
450
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 }
470
471 m_line_number++;
472
473 if (line != "")
474 {
475 if (expression.match (line))
476 {
477 filter (line);
478 if (m_condition != ok) return (m_condition);
479 }
480 }
481 }
482 }
483 */
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
546 if (!text.read (file_name)) return (failed);
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
562 if (!text.read (file_name)) return (failed);
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 {
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;
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
634 char buffer[PATH_MAX];
635 // char buffer[256];
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);
680 CmtSystem::realpath (compressed_path, compressed_path);
681 scan_path (compressed_path, 0, a);
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{
692 if (level > 10)
693 {
694 return;
695 }
696
697 //
698 // Only do something if it is a directory.
699 //
700 if (!CmtSystem::test_directory (path)) return;
701
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
727 name = "";
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;
735 if (entry == ".svn" || entry == "CVS") continue;
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";
744
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
757 // We don't keep on looking in this directory since the versioned structure
758 // does not expect subpackages there
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";
785 // vreq += CmtSystem::file_separator ();
786 // vreq += "version.cmt";
787
788 // if (CmtSystem::test_file (vreq))
789 if (CmtSystem::test_file (vreq
790 + CmtSystem::file_separator ()
791 + Package::get_version_file_name ())
792 )
793 {
794 Package::get_version (version, vreq);
795
796 a.run (entry, version, path, true);
797 has_package = true;
798
799 // We scan further down
800 scan_path (here, 1, a);
801 continue;
802 }
803
804 cmt_string p;
805
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 }
820
821 if (name != "")
822 {
823 // The package name was specified in the requirements file
824
825 if (entry == name)
826 {
827 // The structure is without the version directory.
828
829 if (!Package::get_version (version, vreq))
830 version = "v*";
831
832 a.run (name, version, path, true);
833 // a.run (name, "v1", path);
834 has_package = true;
835
836 // We scan further down
837 scan_path (here, 1, a);
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 }
853
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 }
863
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
867
868 if (CmtSystem::is_version_directory (version))
869 {
870 a.run (entry, version, where);
871 has_package = true;
872
873 continue;
874 }
875
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
881 name = version;
882
883 where += CmtSystem::file_separator ();
884 where += entry;
885
886 a.run (name, "v1", where);
887 has_package = true;
888
889 // We scan further down
890 scan_path (here, 1, a);
891 continue;
892 }
893
894 //
895 // Here this is a non-package directory. Let's search further down
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;
968 cmt_string v;
969 if (Package::get_version (v, name))
970 {
971 version = v;
972 }
973 else
974 {
975 version = "v*";
976 }
977
978 cout << package << " " << version << " " << path << endl;
979
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.