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

Last change on this file since 327 was 197, checked in by garonne, 19 years ago

add the STRUCTURED_OUTPUT support on linux +mac

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