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

Last change on this file since 689 was 664, checked in by rybkin, 12 years ago

merge -r 646:663 HEAD

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