Fix warnings on FreeBSD
[oweals/cde.git] / cde / programs / dtinfo / DtMmdb / StyleSheet / DocParser.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*      Copyright (c) 1995 FUJITSU LIMITED      */
24 /*      All Rights Reserved                     */
25
26 /* $TOG: DocParser.C /main/16 1998/04/17 11:48:07 mgreess $ */
27 #ifdef DEBUG
28 #include "assert.h"
29 #endif
30 #include "Debug.h"
31 #include "StyleSheetExceptions.h"
32 #include "DocParser.h"
33 #include "Resolver.h"
34 #include "Element.h"
35 #include "AttributeList.h"
36
37 #define DATA_BUF_SIZ 4096
38
39 #if defined(SC3)
40 static ostrstream& terminate(ostrstream& ost)
41 {
42     char* pstring = ost.str();
43     *(pstring + ost.pcount()) = '\0';
44
45     return ost;
46 }
47 #endif
48
49 DocParser::DocParser(Resolver &r)
50 : f_ignoring_element(0), f_resolver(r),
51 #if defined(SC3)
52   f_buffer(new char[DATA_BUF_SIZ]),
53   f_output(f_buffer, DATA_BUF_SIZ)
54 #else
55   f_streambuf(new stringbuf()),
56   f_output()
57 #endif
58 {
59 }
60
61 DocParser::~DocParser()
62 {
63 #if defined(SC3)
64   if (f_buffer) delete[] f_buffer;
65 #else
66   // this causes a free memory read when f_output is deleted as part of this
67   // object...nothing we can do about it
68   delete f_streambuf ;
69 #endif
70 }
71
72 unsigned int
73 DocParser::parse(istream &input)
74 {
75    f_resolver.Begin();
76    unsigned int ok = rawParse(input);
77    f_resolver.End();
78    return ok;
79 }
80
81 unsigned int
82 DocParser::rawParse(istream &input)
83 {
84   string data;
85
86   input.unsetf(ios::skipws);
87
88   f_ignoring_element = 0 ;
89
90   switch(read_tag(input, f_output))
91     {
92     case StartTag:
93       {
94 #if defined(SC3)
95         Symbol name(gElemSymTab->intern(terminate(f_output).str()));
96         f_output.rdbuf()->freeze(0);
97 #else
98         data = f_output.str().c_str();
99
100 /*
101 MESSAGE(cerr, "StartTag case:");
102 debug(cerr, f_output.str().size());
103 debug(cerr, data.c_str());
104 */
105
106         Symbol name(gElemSymTab->intern(data.c_str()));
107 #endif
108         process(input, f_output, name, 1, 1);
109       }
110       break;
111     case EndTag:
112     case AttributeSection:
113     case OliasAttribute:
114       throw(CASTDPUTEXCEPT docParserUnexpectedTag());
115       break;
116     case NoTag:
117       throw(CASTDPUDEXCEPT docParserUnexpectedData());
118       break;
119     }      
120   return 1;
121 }
122
123                 
124 void
125 update_last_seen_child_name(Symbol*& last_seen_child_name, unsigned int& child_relative_sibling_number, const Symbol& new_child_name)
126 {
127    if ( last_seen_child_name == 0 || 
128         !(*last_seen_child_name == Symbol(new_child_name)) 
129       ) 
130    {
131      delete last_seen_child_name ;
132      last_seen_child_name = new Symbol(new_child_name);
133      child_relative_sibling_number= 1;
134    } else
135      child_relative_sibling_number++;
136
137    return;
138 }
139
140 void
141 DocParser::process(istream &input, ostringstream &output,
142                    const Symbol &name,
143                    unsigned int sibling_number, unsigned int this_sibling_number)
144 {
145   ON_DEBUG(cerr << "process(" << name << ") -> " << sibling_number << endl);
146
147   Symbol* last_seen_child_name = 0; 
148             
149   unsigned int child_relative_sibling_number = 0;
150
151   unsigned int child = 1 ;      // sibling numbers for child elements 
152
153 #if !defined(SC3)
154   string pstring;
155 #endif
156   string data;
157
158   char c ;
159   while ((input >> c) && (c == '\n'));
160   input.putback(c);
161
162   if (input.eof())
163     throw(CASTDPUEEXCEPT docParserUnexpectedEof());
164
165   int ignore = 0 ;
166
167   mtry
168     {
169       // process whatever comes right after start tag 
170       TagType tt = read_tag(input, output);
171       switch (tt)
172         {
173         case StartTag:
174           {
175             ON_DEBUG(cerr << "beginElement" << endl);
176             // have to begin this element before processing child elements 
177             if (!f_ignoring_element)
178               {
179                 ignore = f_resolver.beginElement(new Element(name,
180                                                              sibling_number, 0,
181                                                              0,
182                                                              this_sibling_number)); 
183                 f_ignoring_element = ignore ;
184               }
185
186                
187 /////////////////////////////
188 // first child of this node
189 /////////////////////////////
190 #if defined(SC3)
191             Symbol name(gElemSymTab->intern(terminate(f_output).str()));
192
193             update_last_seen_child_name(last_seen_child_name, 
194                                  child_relative_sibling_number, name);
195
196             f_output.rdbuf()->freeze(0);
197
198             process(input, output, name, child++, child_relative_sibling_number);
199 #else
200             data = f_output.str().c_str();
201 //#if !defined(SC3)
202 //          data[f_output.str().size()] = '\0';
203 //#endif
204             Symbol name(gElemSymTab->intern(data.c_str()));
205             update_last_seen_child_name(last_seen_child_name, 
206                                  child_relative_sibling_number, name);
207
208             process(input, output, name,
209                     child++, child_relative_sibling_number);
210 #endif
211           }
212           break;
213         case EndTag:
214           // hit an end tag right after start tag 
215 #ifdef DEBUG
216           {
217 #if defined(SC3)
218             data = terminate(f_output).str();
219             f_output.rdbuf()->freeze(0);
220 #else
221             data = f_output.str().c_str();
222 //#ifdef _IBMR2
223 //#if !defined(SC3)
224 //          data[f_output.str().size()] = '\0';
225 //#endif
226 #endif
227             cerr << "EndTag: " << data.c_str() << endl;
228             assert(gElemSymTab->intern(data.c_str()) == name);
229           }
230 #endif
231
232 // this node
233           if (!f_ignoring_element)
234             {
235               int ignore = f_resolver.beginElement(new Element(name,
236                                                                sibling_number,
237                                                                0, 0, this_sibling_number)); 
238               if (!ignore)
239                 f_resolver.endElement(name);
240             }
241           return ;              // EXIT FUNCTION 
242           break;
243         case AttributeSection:
244           {
245 #if !defined(SC3) && !defined(_IBMR2) && !defined(__linux__) && \
246     !defined(CSRG_BASED) && !defined(sun)
247             volatile
248 #endif
249             AttributeList *attrs = 0;
250 #if !defined(SC3) && !defined(_IBMR2) && !defined(__linux__) && \
251     !defined(CSRG_BASED) && !defined(sun)
252             volatile
253 #endif
254             AttributeList *olias_attrs = 0;
255
256             mtry
257               {
258                 process_attributes(input, output, attrs, olias_attrs);
259
260                 if (!f_ignoring_element)
261                   {
262 //////////////////////////////
263 // this node with attributes
264 //////////////////////////////
265                     ignore = f_resolver.beginElement(new Element(name,
266                                                                  sibling_number,
267                                                                  attrs,
268                                                                  olias_attrs, 
269                                                                  this_sibling_number
270                                                         ));
271                     f_ignoring_element = ignore ;
272                   }
273               }
274             mcatch_any()
275               {
276 /*
277                 delete attrs ;
278                 delete olias_attrs ;
279 */
280                 attrs = 0 ;
281                 olias_attrs = 0 ;
282               }
283             end_try;
284           }
285           break;
286         case OliasAttribute:
287           throw(CASTDPUTEXCEPT docParserUnexpectedTag());
288           break;
289
290         case NoTag:
291           {
292             if (!f_ignoring_element)
293               {
294 // this node
295                 ignore = f_resolver.beginElement(new Element(name,
296                                                              sibling_number,
297                                                              0, 0, this_sibling_number)); 
298                 f_ignoring_element = ignore ;
299               }
300             // process data 
301             read_data(input, output);
302
303             if (!f_ignoring_element)
304               {
305                 //  the str() call seems to add the null byte to the stream
306                 //  and increment the pcount, so we must make sure it gets
307                 //  called first
308 #if defined(SC3)
309                 char *pstring = terminate(f_output).str();
310                 int   size = f_output.pcount();
311                 f_resolver.data(pstring, size);
312                 f_output.rdbuf()->freeze(0);
313 #else
314                 pstring = f_output.str().c_str();
315                 int   size = pstring.size() + 1;
316                 f_resolver.data(pstring.c_str(), size);
317 #endif
318               }
319           }
320           break;
321         }
322       
323       while ((tt = read_tag(input, output)) != EndTag)
324         switch (tt)
325           {
326           case StartTag:
327             {
328 /////////////////////////////
329 // second child and beyond.
330 /////////////////////////////
331               data = f_output.str().c_str();
332 #if defined(SC3)
333               f_output.rdbuf()->freeze(0);
334 #endif
335
336 /*
337 MESSAGE(cerr, "StartTag case2");
338 debug(cerr, data);
339 debug(cerr, f_output.str().size());
340 */
341
342               Symbol name(gElemSymTab->intern(data.c_str()));
343               update_last_seen_child_name(last_seen_child_name, 
344                                  child_relative_sibling_number, name);
345
346               process(input, output, name, child++, child_relative_sibling_number);
347             }
348             break;
349           case EndTag:          // should never get this 
350             break;
351             // we have already processed these for this tag
352           case AttributeSection:
353           case OliasAttribute: 
354             throw(CASTDPUTEXCEPT docParserUnexpectedTag());
355             break;
356           case NoTag:
357             {
358               read_data(input, output);
359
360               if (!f_ignoring_element)
361                 {
362                   //  the str() call seems to add the null byte to the stream
363                   //  and increment the pcount, so we must make sure it gets
364                   //  called first
365 #if defined(SC3)
366                   char *pstring = f_output.str();
367                   int   size = f_output.pcount();
368                   *(pstring + size) = 0;
369                   f_resolver.data(pstring, size);
370                   f_output.rdbuf()->freeze(0);
371 #else
372                   pstring = f_output.str().c_str();
373                   int   size = pstring.size() + 1;
374                   f_resolver.data(pstring.c_str(), size);
375 #endif
376                 }
377             }
378           }
379 #ifdef DEBUG
380       {
381 #if defined(SC3)
382         data = terminate(f_output).str();
383         f_output.rdbuf()->freeze(0);
384 #else
385         data = f_output.str().c_str();
386 #endif
387         cerr << "EndTag: " << data.c_str() << endl;
388         assert(gElemSymTab->intern(data.c_str()) == name);
389       }
390 #endif
391       // hit end tag, end processing
392       if (!f_ignoring_element)
393         f_resolver.endElement(name);
394
395       // if we set ignore flag, unset it 
396       if (ignore)
397         f_ignoring_element = 0;
398     }
399   mcatch_any()
400     {
401       rethrow;
402     }
403   end_try;
404   ON_DEBUG(cerr << "exit process: " << name << endl);
405   delete last_seen_child_name; 
406 }
407
408
409 void
410 DocParser::process_attributes(istream &input, ostringstream &output,
411                               AttributeList *&attrs,
412                               AttributeList *&olias_attrs)
413 {
414 #if !defined(SC3)
415   string theData;
416 #endif
417   TagType tt ;
418
419   Attribute* newAttribute = 0;
420
421   AttributeList* orig_attrs = attrs;
422   AttributeList* orig_olias_attrs = olias_attrs;
423
424   mtry {
425      while ((tt = read_tag(input,output)) != NoTag)
426        {
427          switch (tt)
428         {
429         case StartTag:
430           {
431 #if !defined(SC3)
432           theData = f_output.str().c_str();
433 #endif
434           if (!attrs)
435             attrs = new AttributeList ;
436
437           newAttribute = 
438                 process_attribute(input, output,
439 #if defined(SC3)
440                                   gSymTab->intern(terminate(f_output).str()),
441                                   gSymTab->intern(f_streambuf->str()),
442 #else
443                                   gSymTab->intern(theData.c_str()),
444 #endif
445                                   StartTag
446                                  );
447           attrs->add(newAttribute);
448           break;
449           }
450         case EndTag:
451           return ;              // EXIT FUNCTION
452    
453         case AttributeSection:
454           throw(CASTDPUTEXCEPT docParserUnexpectedTag());
455           break;
456         case OliasAttribute:
457 #if !defined(SC3)
458           theData = f_output.str().c_str();
459 #endif
460           // mirrors attribute 
461           if (!olias_attrs)
462             olias_attrs = new AttributeList ;
463
464           newAttribute = 
465                 process_attribute(input, output,
466 #if defined(SC3)
467                                   gSymTab->intern(terminate(f_output).str()),
468                                   gSymTab->intern(f_streambuf->str()),
469 #else
470                                   gSymTab->intern(theData.c_str()),
471 #endif
472                                   OliasAttribute
473                                  );
474
475           olias_attrs->add(newAttribute);
476           break;
477         case NoTag:
478           throw(CASTDPUDEXCEPT docParserUnexpectedData());
479           break;
480         }
481        }
482    }
483    mcatch_any()
484    {
485      delete newAttribute;
486
487      if ( orig_attrs == 0 ) {
488         delete attrs;
489         attrs = 0;
490      }
491
492      if ( orig_olias_attrs == 0 ) {
493         delete olias_attrs;
494         olias_attrs = 0;
495      }
496
497      rethrow;
498    }
499    end_try;
500 }
501
502 Attribute *
503 DocParser::process_attribute(istream &input, ostringstream &output,
504                              const Symbol &name, TagType tt)
505 {
506   string data;
507
508   //ON_DEBUG(cerr << "process_attribute: " << name << endl);
509
510 // If the attribute is OLIAS internal, we use DocParser's 
511 // read_data(). This is to prevent the attribte value 
512 // from change in a call to specific renderer engine's 
513 // read_data().
514 //
515 // Example: LoutDocparser::read_data() quotes any '.' char
516 // which changes the graphic locator value if the element
517 // is OLIAS internal attribute #GRAPHIC.
518
519   if ( tt == OliasAttribute ) {
520     DocParser::read_data(input, output);
521   } else 
522     (void)read_data(input, output);
523 #if defined(SC3)
524   char *data = f_output.str();
525   *(data + f_output.pcount()) = 0;
526   f_output.rdbuf()->freeze(0);
527   Attribute *attr = new Attribute(name, strdup(data));
528 #else
529   data = f_output.str().c_str();
530   Attribute *attr = new Attribute(name, strdup(data.c_str()));
531 #endif
532
533   switch (read_tag(input, output))
534     {
535     case StartTag:
536     case AttributeSection:
537     case OliasAttribute:
538       delete attr ;
539       throw(CASTDPUTEXCEPT docParserUnexpectedTag());
540       break;
541     case NoTag:
542       delete attr;
543       throw(CASTDPUDEXCEPT docParserUnexpectedData());
544       break;
545     case EndTag:
546       break;
547     }
548
549   return attr ;
550   
551 }
552
553
554 DocParser::TagType
555 DocParser::read_tag(istream &input, ostringstream &output)
556 {
557   output.seekp(streampos(0));
558
559   TagType tt = StartTag;
560
561   char c ;
562
563   // strip newlines before/after tags
564   while ((input >> c) && (c == '\n'));
565   if (input.eof())
566     throw(CASTDPUEEXCEPT docParserUnexpectedEof());
567
568   if (c != '<')
569     {
570       input.putback(c);
571       return NoTag;
572     }
573
574
575   input >> c ;
576
577   switch (c)
578     {
579     case '/':
580       tt = EndTag ;
581       break;
582     case '#':
583       input >> c;
584       if (c == '>')
585         return AttributeSection ; // EXIT 
586       else
587         {
588           tt = OliasAttribute ;
589           output << c;          // keep char we just read 
590         }
591       break;
592     case '>':
593       throw(CASTUTEXCEPT unknownTagException());
594       // NOT REACHED 
595       break;
596     default:
597       output << c ;             // keep char we just read 
598       break;
599     }
600
601
602   // get (remainder of) tag name 
603   while ((input >> c) && (c != '>'))
604     output << c ;
605   output << ends;
606
607   return tt ;
608 }
609
610
611 void
612 DocParser::read_data(istream &input, ostringstream &output)
613 {
614   char c ;
615
616   output.seekp(streampos(0));
617
618   while ((input >> c) && (c != '<'))
619     {
620       // handle entities 
621       if (c == '&')
622         {
623           char tmpbuf[64];
624           unsigned int tmplen = 0;
625           while ((input >> c ) && (c != ';'))
626             {
627               tmpbuf[tmplen++] = c ;
628               if (tmplen > 63)
629                 {
630                   cerr << "Temp Buf overflow (ampersand problem)" << endl;
631                   throw(CASTEXCEPT Exception());
632                 }
633             }
634           if (input.eof())
635             throw(CASTDPUEEXCEPT docParserUnexpectedEof());
636             
637           tmpbuf[tmplen] = 0 ;
638
639 #ifdef ENTITY_DEBUG
640           cerr << "Entity: " << tmpbuf << endl;
641 #endif
642
643           if ((!strcmp(tmpbuf, "hardreturn")) ||
644               (!strcmp(tmpbuf, "lnfeed")))
645             c = '\n';
646           else
647             if ((!strcmp(tmpbuf, "lang")) ||
648                 (!strcmp(tmpbuf, "lt")))
649               c = '<' ;
650             else
651               if (!strcmp(tmpbuf, "amp"))
652                 c = '&' ;
653               else
654                 if (!strcmp(tmpbuf, "nbsp")) // non-break space 
655                   c = (char)0xA0 ;
656                 else
657                   c = ' ';
658
659         }
660
661       output << c;
662     }
663
664   output << ends;
665
666   // can never run out of input while reading data, tags must be balanced
667   if (input.eof())
668     throw(CASTDPUEEXCEPT docParserUnexpectedEof());
669             
670   input.putback(c);
671
672 }