Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * Software License Agreement (BSD License)
4 : *
5 : * Copyright (c) 2006, ScalingWeb.com
6 : * All rights reserved.
7 : *
8 : * Redistribution and use of this software in source and binary forms, with or without modification, are
9 : * permitted provided that the following conditions are met:
10 : *
11 : * * Redistributions of source code must retain the above
12 : * copyright notice, this list of conditions and the
13 : * following disclaimer.
14 : *
15 : * * Redistributions in binary form must reproduce the above
16 : * copyright notice, this list of conditions and the
17 : * following disclaimer in the documentation and/or other
18 : * materials provided with the distribution.
19 : *
20 : * * Neither the name of ScalingWeb.com nor the names of its
21 : * contributors may be used to endorse or promote products
22 : * derived from this software without specific prior
23 : * written permission of ScalingWeb.com.
24 :
25 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
26 : * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
27 : * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
28 : * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 : * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 : * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
31 : * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32 : * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 : */
34 :
35 : #include "MorkParser.hxx"
36 : #include <boost/io/ios_state.hpp>
37 : #include <stdlib.h>
38 : #include <sstream>
39 : #include <string>
40 : #include <string.h>
41 : #include <stdexcept>
42 : #include <fstream>
43 : #include <iostream>
44 : #include <algorithm>
45 :
46 2 : std::string g_Empty = "";
47 :
48 : // Mork header of supported format version
49 : const char *MorkMagicHeader = "// <!-- <mdb:mork:z v=\"1.4\"/> -->";
50 :
51 : const char *MorkDictColumnMeta = "<(a=c)>";
52 :
53 :
54 6 : MorkParser::MorkParser( int DefaultScope ) :
55 : columns_(),
56 : values_(),
57 : mork_(),
58 : currentCells_(0),
59 : error_(NoError),
60 : morkData_(),
61 : morkPos_(0),
62 : nextAddValueId_(0x7fffffff),
63 : defaultScope_(DefaultScope),
64 : defaultListScope_(0x81),
65 : defaultTableId_(1),
66 6 : nowParsing_(NPValues)
67 : {
68 6 : }
69 :
70 3 : bool MorkParser::open( const std::string &path )
71 : {
72 3 : initVars();
73 3 : std::string line;
74 6 : std::ifstream infile(path.c_str(), std::ios_base::in);
75 3 : if(!infile.is_open())
76 : {
77 0 : error_ = FailedToOpen;
78 0 : return false;
79 : }
80 :
81 483 : while (getline(infile, line, '\n'))
82 : {
83 477 : morkData_.append(line);
84 477 : morkData_.append("\n");
85 : }
86 :
87 : // Parse mork
88 6 : return parse();
89 : }
90 :
91 : inline MorkErrors MorkParser::error()
92 : {
93 : return error_;
94 : }
95 :
96 3 : void MorkParser::initVars()
97 : {
98 3 : error_ = NoError;
99 3 : morkPos_ = 0;
100 3 : nowParsing_ = NPValues;
101 3 : currentCells_ = 0;
102 3 : nextAddValueId_ = 0x7fffffff;
103 3 : }
104 :
105 3 : bool MorkParser::parse()
106 : {
107 3 : bool Result = true;
108 :
109 : // Run over mork chars and parse each term
110 3 : char cur = nextChar();
111 :
112 3 : int i = 0;
113 :
114 306 : while ( Result && cur )
115 : {
116 300 : if ( !isWhiteSpace( cur ) )
117 : {
118 132 : i++;
119 : // Figure out what a term
120 132 : switch ( cur )
121 : {
122 : case '<':
123 : // Dict
124 24 : Result = parseDict();
125 24 : break;
126 : case '/':
127 : // Comment
128 3 : Result = parseComment();
129 3 : break;
130 : case '{':
131 6 : Result = parseTable();
132 : // Table
133 6 : break;
134 : case '[':
135 33 : Result = parseRow( 0, 0 );
136 : // Row
137 33 : break;
138 : case '@':
139 66 : Result = parseGroup();
140 : // Group
141 66 : break;
142 : default:
143 0 : error_ = DefectedFormat;
144 0 : Result = false;
145 0 : break;
146 : }
147 : }
148 :
149 : // Get next char
150 300 : cur = nextChar();
151 : }
152 :
153 3 : return Result;
154 : }
155 :
156 4368 : bool MorkParser::isWhiteSpace( char c )
157 : {
158 4368 : switch ( c )
159 : {
160 : case ' ':
161 : case '\t':
162 : case '\r':
163 : case '\n':
164 : case '\f':
165 1425 : return true;
166 : default:
167 2943 : return false;
168 : }
169 : }
170 :
171 21927 : inline char MorkParser::nextChar()
172 : {
173 21927 : char cur = 0;
174 :
175 :
176 21927 : if ( morkPos_ < morkData_.length() )
177 : {
178 21924 : cur = morkData_[ morkPos_ ];
179 21924 : morkPos_++;
180 : }
181 :
182 21927 : if ( !cur )
183 : {
184 3 : cur = 0;
185 : }
186 :
187 21927 : return cur;
188 : }
189 :
190 24 : bool MorkParser::parseDict()
191 : {
192 24 : char cur = nextChar();
193 24 : bool Result = true;
194 24 : nowParsing_ = NPValues;
195 :
196 813 : while ( Result && cur != '>' && cur )
197 : {
198 765 : if ( !isWhiteSpace( cur ) )
199 : {
200 441 : switch ( cur )
201 : {
202 : case '<':
203 : {
204 :
205 18 : if ( morkData_.substr( morkPos_ - 1, strlen( MorkDictColumnMeta ) ) == MorkDictColumnMeta )
206 : {
207 18 : nowParsing_ = NPColumns;
208 18 : morkPos_ += strlen( MorkDictColumnMeta ) - 1;
209 : }
210 :
211 :
212 18 : break;
213 : }
214 : case '(':
215 405 : Result = parseCell();
216 405 : break;
217 : case '/':
218 18 : Result = parseComment();
219 18 : break;
220 :
221 : }
222 : }
223 :
224 765 : cur = nextChar();
225 : }
226 :
227 24 : return Result;
228 : }
229 :
230 21 : inline bool MorkParser::parseComment()
231 : {
232 21 : char cur = nextChar();
233 21 : if ( '/' != cur ) return false;
234 :
235 426 : while ( cur != '\r' && cur != '\n' && cur )
236 : {
237 384 : cur = nextChar();
238 : }
239 :
240 21 : return true;
241 : }
242 :
243 2427 : bool MorkParser::parseCell()
244 : {
245 2427 : bool Result = true;
246 2427 : bool bValueOid = false;
247 2427 : bool bColumn = true;
248 2427 : int Corners = 0;
249 :
250 : // Column = Value
251 2427 : std::string Column;
252 4854 : std::string Text;
253 2427 : Column.reserve( 4 );
254 2427 : Text.reserve( 32 );
255 :
256 2427 : char cur = nextChar();
257 :
258 : // Process cell start with column (bColumn == true)
259 18972 : while ( Result && cur != ')' && cur )
260 : {
261 14118 : switch ( cur )
262 : {
263 : case '^':
264 : // Oids
265 2274 : Corners++;
266 2274 : if ( 1 == Corners )
267 : {
268 : }
269 252 : else if ( 2 == Corners )
270 : {
271 252 : bColumn = false;
272 252 : bValueOid = true;
273 : }
274 : else
275 : {
276 0 : Text += cur;
277 : }
278 :
279 2274 : break;
280 : case '=':
281 : // From column to value
282 2175 : if ( bColumn )
283 : {
284 2175 : bColumn = false;
285 : }
286 : else
287 : {
288 0 : Text += cur;
289 : }
290 2175 : break;
291 : case '\\':
292 : {
293 : // Get next two chars
294 0 : char NextChar= nextChar();
295 0 : if ( '\r' != NextChar && '\n' != NextChar )
296 : {
297 0 : Text += NextChar;
298 : }
299 : else
300 : {
301 0 : (void)nextChar();
302 : }
303 : }
304 0 : break;
305 : case '$':
306 : {
307 : // Get next two chars
308 0 : std::string HexChar;
309 0 : HexChar += nextChar();
310 0 : HexChar += nextChar();
311 0 : Text += (char)strtoul(HexChar.c_str(), 0, 16);
312 : }
313 0 : break;
314 : default:
315 : // Just a char
316 9669 : if ( bColumn )
317 : {
318 4869 : Column += cur;
319 : }
320 : else
321 : {
322 4800 : Text += cur;
323 : }
324 9669 : break;
325 : }
326 :
327 14118 : cur = nextChar();
328 : }
329 :
330 : // Apply column and text
331 2427 : int ColumnId = strtoul(Column.c_str(), 0, 16);
332 :
333 2427 : if ( NPRows != nowParsing_ )
334 : {
335 : // Dicts
336 405 : if ( "" != Text )
337 : {
338 402 : if ( nowParsing_ == NPColumns )
339 : {
340 252 : columns_[ ColumnId ] = Text;
341 : }
342 : else
343 : {
344 150 : values_[ ColumnId ] = Text;
345 : }
346 : }
347 : }
348 : else
349 : {
350 2022 : if ( "" != Text )
351 : {
352 : // Rows
353 : //int ValueId = string( Text.c_str() ).toInt( 0, 16 );
354 630 : int ValueId = strtoul(Text.c_str(), 0, 16);
355 :
356 630 : if ( bValueOid )
357 : {
358 252 : ( *currentCells_ )[ ColumnId ] = ValueId;
359 : }
360 : else
361 : {
362 378 : nextAddValueId_--;
363 378 : values_[ nextAddValueId_ ] = Text;
364 378 : ( *currentCells_ )[ ColumnId ] = nextAddValueId_;
365 : }
366 : }
367 : }
368 :
369 4854 : return Result;
370 : }
371 :
372 6 : bool MorkParser::parseTable()
373 : {
374 6 : bool Result = true;
375 6 : std::string TextId;
376 6 : int Id = 0, Scope = 0;
377 :
378 6 : char cur = nextChar();
379 :
380 : // Get id
381 48 : while ( cur != '{' && cur != '[' && cur != '}' && cur )
382 : {
383 36 : if ( !isWhiteSpace( cur ) )
384 : {
385 30 : TextId += cur;
386 : }
387 :
388 36 : cur = nextChar();
389 : }
390 :
391 6 : parseScopeId( TextId, &Id, &Scope );
392 :
393 : // Parse the table
394 174 : while ( Result && cur != '}' && cur )
395 : {
396 162 : if ( !isWhiteSpace( cur ) )
397 : {
398 45 : switch ( cur )
399 : {
400 : case '{':
401 6 : Result = parseMeta( '}' );
402 6 : break;
403 : case '[':
404 39 : Result = parseRow( Id, Scope );
405 39 : break;
406 : case '-':
407 : case '+':
408 0 : break;
409 : default:
410 : {
411 0 : std::string JustId;
412 0 : while ( !isWhiteSpace( cur ) && cur )
413 : {
414 0 : JustId += cur;
415 0 : cur = nextChar();
416 :
417 0 : if ( cur == '}' )
418 : {
419 0 : return Result;
420 : }
421 : }
422 :
423 0 : int JustIdNum = 0, JustScopeNum = 0;
424 0 : parseScopeId( JustId, &JustIdNum, &JustScopeNum );
425 :
426 0 : setCurrentRow( Scope, Id, JustScopeNum, JustIdNum );
427 : }
428 0 : break;
429 : }
430 : }
431 :
432 162 : cur = nextChar();
433 : }
434 :
435 6 : return Result;
436 : }
437 :
438 78 : void MorkParser::parseScopeId( const std::string &TextId, int *Id, int *Scope )
439 : {
440 78 : int Pos = 0;
441 :
442 78 : if ( ( Pos = TextId.find( ':' ) ) >= 0 )
443 : {
444 48 : std::string tId = TextId.substr( 0, Pos );
445 96 : std::string tSc = TextId.substr( Pos + 1, TextId.length() - Pos );
446 :
447 48 : if ( tSc.length() > 1 && '^' == tSc[ 0 ] )
448 : {
449 : // Delete '^'
450 48 : tSc.erase( 0, 1 );
451 : }
452 :
453 48 : *Id = strtoul(tId.c_str(), 0, 16);
454 :
455 96 : *Scope = strtoul(tSc.c_str(), 0, 16);
456 : }
457 : else
458 : {
459 30 : *Id = strtoul(TextId.c_str(), 0, 16);
460 : }
461 78 : }
462 :
463 72 : inline void MorkParser::setCurrentRow( int TableScope, int TableId, int RowScope, int RowId )
464 : {
465 72 : if ( !RowScope )
466 : {
467 30 : RowScope = defaultScope_;
468 : }
469 :
470 72 : if ( !TableScope )
471 : {
472 33 : TableScope = defaultScope_;
473 : }
474 :
475 : // 01.08.2012 davido
476 : // TableId 0 is wrong here.
477 : // Straying rows (rows that defined outside the table) belong to the default scope and table is the last was seen: 1:^80
478 : // (at least i read so the specification)
479 72 : if (TableId)
480 : {
481 39 : defaultTableId_ = TableId;
482 : }
483 :
484 72 : if (!TableId)
485 : {
486 33 : TableId = defaultTableId_;
487 : }
488 :
489 72 : currentCells_ = &( mork_.map[ abs( TableScope ) ].map[ abs( TableId ) ].map[ abs( RowScope ) ].map[ abs( RowId ) ] );
490 72 : }
491 :
492 72 : bool MorkParser::parseRow( int TableId, int TableScope )
493 : {
494 72 : bool Result = true;
495 72 : std::string TextId;
496 72 : int Id = 0, Scope = 0;
497 72 : nowParsing_ = NPRows;
498 :
499 72 : char cur = nextChar();
500 :
501 : // Get id
502 417 : while ( cur != '(' && cur != ']' && cur != '[' && cur )
503 : {
504 273 : if ( !isWhiteSpace( cur ) )
505 : {
506 273 : TextId += cur;
507 : }
508 :
509 273 : cur = nextChar();
510 : }
511 :
512 72 : parseScopeId( TextId, &Id, &Scope );
513 72 : setCurrentRow( TableScope, TableId, Scope, Id );
514 :
515 : // Parse the row
516 2976 : while ( Result && cur != ']' && cur )
517 : {
518 2832 : if ( !isWhiteSpace( cur ) )
519 : {
520 2022 : switch ( cur )
521 : {
522 : case '(':
523 2022 : Result = parseCell();
524 2022 : break;
525 : case '[':
526 0 : Result = parseMeta( ']' );
527 0 : break;
528 : default:
529 0 : Result = false;
530 0 : break;
531 : }
532 : }
533 :
534 2832 : cur = nextChar();
535 : }
536 :
537 72 : return Result;
538 : }
539 :
540 66 : bool MorkParser::parseGroup()
541 : {
542 66 : return parseMeta( '@' );
543 : }
544 :
545 72 : bool MorkParser::parseMeta( char c )
546 : {
547 72 : char cur = nextChar();
548 :
549 576 : while ( cur != c && cur )
550 : {
551 432 : cur = nextChar();
552 : }
553 :
554 72 : return true;
555 : }
556 :
557 171 : MorkTableMap *MorkParser::getTables( int TableScope )
558 : {
559 171 : TableScopeMap::Map::iterator iter;
560 171 : iter = mork_.map.find( TableScope );
561 :
562 171 : if ( iter == mork_.map.end() )
563 : {
564 84 : return 0;
565 : }
566 :
567 87 : return &iter->second;
568 : }
569 :
570 84 : MorkRowMap *MorkParser::getRows( int RowScope, RowScopeMap *table )
571 : {
572 84 : RowScopeMap::Map::iterator iter;
573 84 : iter = table->map.find( RowScope );
574 :
575 84 : if ( iter == table->map.end() )
576 : {
577 0 : return 0;
578 : }
579 :
580 84 : return &iter->second;
581 : }
582 :
583 364 : std::string &MorkParser::getValue( int oid )
584 : {
585 364 : MorkDict::iterator foundIter = values_.find( oid );
586 :
587 364 : if ( values_.end() == foundIter )
588 : {
589 0 : return g_Empty;
590 : }
591 :
592 364 : return foundIter->second;
593 : }
594 :
595 195 : std::string &MorkParser::getColumn( int oid )
596 : {
597 195 : MorkDict::iterator foundIter = columns_.find( oid );
598 :
599 195 : if ( columns_.end() == foundIter )
600 : {
601 0 : return g_Empty;
602 : }
603 :
604 195 : return foundIter->second;
605 : }
606 :
607 162 : void MorkParser::retrieveLists(std::set<std::string>& lists)
608 : {
609 : #ifdef VERBOSE
610 : boost::io::ios_all_saver ias(std::cout);
611 : std::cout << std::hex << std::uppercase;
612 : #endif
613 :
614 162 : MorkTableMap* tables = getTables(defaultScope_);
615 162 : if (!tables) return;
616 486 : for (MorkTableMap::Map::iterator TableIter = tables->map.begin();
617 324 : TableIter != tables->map.end(); ++TableIter )
618 : {
619 : #ifdef VERBOSE
620 : std::cout << "\t Table:"
621 : << ( ( int ) TableIter->first < 0 ? "-" : " " )
622 : << TableIter->first << std::endl;
623 : #endif
624 81 : MorkRowMap* rows = getRows( defaultListScope_, &TableIter->second );
625 81 : if (!rows) return;
626 729 : for ( MorkRowMap::Map::iterator RowIter = rows->map.begin();
627 486 : RowIter != rows->map.end(); ++RowIter )
628 : {
629 : #ifdef VERBOSE
630 : std::cout << "\t\t\t Row Id:"
631 : << ( ( int ) RowIter->first < 0 ? "-" : " ")
632 : << RowIter->first << std::endl;
633 : std::cout << "\t\t\t\t Cells:\r\n";
634 : #endif
635 : // Get cells
636 1944 : for ( MorkCells::iterator cellsIter = RowIter->second.begin();
637 1296 : cellsIter != RowIter->second.end(); ++cellsIter )
638 : {
639 648 : if (cellsIter->first == 0xC1)
640 : {
641 162 : lists.insert(getValue( cellsIter->second ));
642 162 : break;
643 : }
644 : }
645 : }
646 : }
647 : }
648 :
649 1 : void MorkParser::getRecordKeysForListTable(std::string& listName, std::set<int>& records)
650 : {
651 : #ifdef VERBOSE
652 : boost::io::ios_all_saver ias(std::cout);
653 : std::cout << std::hex << std::uppercase;
654 : #endif
655 :
656 1 : MorkTableMap* tables = getTables(defaultScope_);
657 1 : if (!tables) return;
658 6 : for (MorkTableMap::Map::iterator TableIter = tables->map.begin();
659 4 : TableIter != tables->map.end(); ++TableIter )
660 : {
661 : #ifdef VERBOSE
662 : std::cout << "\t Table:"
663 : << ( ( int ) TableIter->first < 0 ? "-" : " " )
664 : << TableIter->first << std::endl;
665 : #endif
666 1 : MorkRowMap* rows = getRows( 0x81, &TableIter->second );
667 1 : if (!rows) return;
668 9 : for ( MorkRowMap::Map::iterator RowIter = rows->map.begin();
669 6 : RowIter != rows->map.end(); ++RowIter )
670 : {
671 : #ifdef VERBOSE
672 : std::cout << "\t\t\t Row Id:"
673 : << ( ( int ) RowIter->first < 0 ? "-" : " ")
674 : << RowIter->first << std::endl;
675 : std::cout << "\t\t\t\t Cells:\r\n";
676 : #endif
677 : // Get cells
678 2 : bool listFound = false;
679 60 : for ( MorkCells::iterator cellsIter = RowIter->second.begin();
680 40 : cellsIter != RowIter->second.end(); ++cellsIter )
681 : {
682 18 : if (listFound)
683 : {
684 5 : if (cellsIter->first >= 0xC7)
685 : {
686 5 : std::string value = getValue(cellsIter->second);
687 5 : int id = strtoul(value.c_str(), 0, 16);
688 5 : records.insert(id);
689 : }
690 : }
691 15 : else if ((cellsIter->first == 0xC1) &&
692 2 : listName == getValue( cellsIter->second ))
693 : {
694 1 : listFound = true;
695 : }
696 : }
697 :
698 : }
699 : }
700 : }
701 :
702 0 : void MorkParser::dump()
703 : {
704 0 : boost::io::ios_all_saver ias(std::cout);
705 0 : std::cout << std::hex << std::uppercase;
706 :
707 0 : std::cout << "Column Dict:\r\n";
708 0 : std::cout << "=============================================\r\n\r\n";
709 :
710 : //// columns dict
711 0 : for ( MorkDict::iterator iter = columns_.begin();
712 0 : iter != columns_.end(); ++iter )
713 : {
714 0 : std::cout << iter->first
715 0 : << " : "
716 0 : << iter->second
717 0 : << std::endl;
718 : }
719 :
720 : //// values dict
721 0 : std::cout << "\r\nValues Dict:\r\n";
722 0 : std::cout << "=============================================\r\n\r\n";
723 :
724 0 : for ( MorkDict::iterator iter = values_.begin();
725 0 : iter != values_.end(); ++iter )
726 : {
727 0 : if (iter->first >= nextAddValueId_) {
728 0 : continue;
729 : }
730 :
731 0 : std::cout << iter->first
732 0 : << " : "
733 0 : << iter->second
734 0 : << "\r\n";
735 : }
736 :
737 0 : std::cout << std::endl << "Data:" << std::endl;
738 0 : std::cout << "============================================="
739 0 : << std::endl << std::endl;
740 :
741 : //// Mork data
742 0 : for ( TableScopeMap::Map::iterator iter = mork_.map.begin();
743 0 : iter != mork_.map.end(); ++iter )
744 : {
745 0 : std::cout << "\r\n Scope:" << iter->first << std::endl;
746 :
747 0 : for ( MorkTableMap::Map::iterator TableIter = iter->second.map.begin();
748 0 : TableIter != iter->second.map.end(); ++TableIter )
749 : {
750 0 : std::cout << "\t Table:"
751 0 : << ( ( int ) TableIter->first < 0 ? "-" : " " )
752 0 : << TableIter->first << std::endl;
753 :
754 0 : for (RowScopeMap::Map::iterator RowScopeIter = TableIter->second.map.begin();
755 0 : RowScopeIter != TableIter->second.map.end(); ++RowScopeIter )
756 : {
757 0 : std::cout << "\t\t RowScope:"
758 0 : << RowScopeIter->first << std::endl;
759 :
760 0 : for (MorkRowMap::Map::iterator RowIter = RowScopeIter->second.map.begin();
761 0 : RowIter != RowScopeIter->second.map.end(); ++RowIter )
762 : {
763 0 : std::cout << "\t\t\t Row Id:"
764 0 : << ((int) RowIter->first < 0 ? "-" : " ")
765 0 : << RowIter->first << std::endl;
766 0 : std::cout << "\t\t\t\t Cells:" << std::endl;
767 :
768 0 : for (MorkCells::iterator CellsIter = RowIter->second.begin();
769 0 : CellsIter != RowIter->second.end(); ++CellsIter )
770 : {
771 : // Write ids
772 0 : std::cout << "\t\t\t\t\t"
773 0 : << CellsIter->first
774 0 : << " : "
775 0 : << CellsIter->second
776 0 : << " => ";
777 :
778 0 : MorkDict::iterator FoundIter = values_.find( CellsIter->second );
779 0 : if ( FoundIter != values_.end() )
780 : {
781 : // Write string values
782 0 : std::cout << columns_[ CellsIter->first ].c_str()
783 0 : << " : "
784 0 : << FoundIter->second.c_str()
785 0 : << std::endl;
786 : }
787 : }
788 : }
789 : }
790 : }
791 0 : }
792 6 : }
793 :
794 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|