Initial revision
[TestXSLT.git] / libsablot / src / engine / output.cpp
1 /* 
2  * The contents of this file are subject to the Mozilla Public
3  * License Version 1.1 (the "License"); you may not use this file
4  * except in compliance with the License. You may obtain a copy of
5  * the License at http://www.mozilla.org/MPL/
6  * 
7  * Software distributed under the License is distributed on an "AS
8  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9  * implied. See the License for the specific language governing
10  * rights and limitations under the License.
11  * 
12  * The Original Code is the Sablotron XSLT Processor.
13  * 
14  * The Initial Developer of the Original Code is Ginger Alliance Ltd.
15  * Portions created by Ginger Alliance are Copyright (C) 2000-2002
16  * Ginger Alliance Ltd. All Rights Reserved.
17  * 
18  * Contributor(s): Marc Lehmann <pcg@goof.com>
19  * 
20  * Alternatively, the contents of this file may be used under the
21  * terms of the GNU General Public License Version 2 or later (the
22  * "GPL"), in which case the provisions of the GPL are applicable 
23  * instead of those above.  If you wish to allow use of your 
24  * version of this file only under the terms of the GPL and not to
25  * allow others to use your version of this file under the MPL,
26  * indicate your decision by deleting the provisions above and
27  * replace them with the notice and other provisions required by
28  * the GPL.  If you do not delete the provisions above, a recipient
29  * may use your version of this file under either the MPL or the
30  * GPL.
31  */
32
33 //
34 //  output.cpp
35 //
36
37 #include "output.h"
38 #include "uri.h"
39 #include "proc.h"
40
41 #ifdef HAVE_CONFIG_H
42 #include <config.h>
43 #endif
44
45 #ifdef SABLOT_DEBUGGER
46 #include "debugger.h"
47 #endif
48
49 #include "guard.h"
50
51 // GP: clean
52
53 #define NONEMPTY_ELEMENT FALSE
54 #define EMPTY_ELEMENT TRUE
55
56
57 enum OutputterFlags
58 {
59     HTML_ELEMENT = 1,
60     HTML_SCRIPT_OR_STYLE = 2,
61     CDATA_SECTION = 4
62 };
63
64 #define IF_SAX1( FUNC )\
65     { if (mySAXHandler)\
66         { mySAXHandler -> FUNC(mySAXUserData, S.getProcessor()); }}
67 #define IF_SAX2( FUNC, ARG1 )\
68     { if (mySAXHandler)\
69         { mySAXHandler -> FUNC(mySAXUserData, S.getProcessor(), (ARG1)); }}
70 #define IF_SAX3( FUNC, ARG1, ARG2 )\
71     { if (mySAXHandler)\
72         { mySAXHandler -> FUNC(mySAXUserData, S.getProcessor(), (ARG1), (ARG2)); }}
73
74 //macros on internal handler
75 #define IF_INT_SAX4( FUNC, ARG1, ARG2, ARG3 )\
76     { if (mySAXHandler)\
77         { ((SAXHandlerInternal*)mySAXHandler) -> FUNC(mySAXUserData, S.getProcessor(), (ARG1), (ARG2), (ARG3)); }}
78
79 #define IF_PH1( FUNC )\
80     { if (physical)\
81         { physical -> FUNC(S); }}
82 #define IF_PH2( FUNC, ARG1)\
83     { if (physical)\
84         { physical -> FUNC(S, ARG1); }}
85 #define IF_PH3( FUNC, ARG1, ARG2 )\
86     { if (physical)\
87         { physical -> FUNC(S, (ARG1), (ARG2)); }}
88
89 // STRING_ITEMS_COUNT (output.h) must be in sync with the size
90 // of the following table:
91
92 XSL_ATT outputStringAtts[STRING_ITEMS_COUNT + 1] = {
93     XSLA_VERSION, XSLA_ENCODING, XSLA_OMIT_XML_DECL, XSLA_STANDALONE,
94     XSLA_DOCTYPE_PUBLIC, XSLA_DOCTYPE_SYSTEM, XSLA_INDENT, XSLA_MEDIA_TYPE,
95     XSLA_NONE
96 };
97
98 const char* theEmptyHTML40Tags[] = 
99 {
100     "area", "base", "basefont", "br", "col", "frame", "hr", "img",
101     "input", "isindex", "link", "meta", "param", NULL
102 };
103
104 const char* theURI_HTMLAtts[] = 
105 {
106     "action",       // FORM
107     "archive",      // OBJECT
108     "background",   // BODY
109     "cite",         // BLOCKQUOTE, Q, DEL, INS
110     "classid",      // OBJECT
111     "codebase",     // OBJECT, APPLET
112     "data",         // OBJECT
113     "href",         // A, AREA, LINK, BASE
114     "longdesc",     // IMG, FRAME, IFRAME
115     "profile",      // HEAD
116     "src",          // SCRIPT, INPUT, FRAME, IFRAME, IMG
117     "usemap",       // IMG, INPUT, OBJECT
118     NULL
119 };
120
121 const char* theHTMLBooleanAtts[] =
122 {
123   "checked",
124   "compact",
125   "declare",
126   "defer",
127   "disabled",
128   "ismap",
129   "multiple",
130   "nohref",
131   "noresize",
132   "noshade",
133   "nowrap",
134   "readonly",
135   "selected",
136   NULL
137 };
138
139 const char* theHTMLNoEscapeTags[] = 
140 {
141   "script",
142   "style",
143   NULL
144 };
145
146 // tags new lines should not be inserted after by indent
147 const char* theNoEolHTMLTags[] = 
148 {
149     "img", "span", NULL
150 };
151
152 Bool isEmptyHTMLTag(const Str& name)
153 {
154     int ndx = lookupNoCase(name, theEmptyHTML40Tags);
155     // return TRUE iff found
156     return !!theEmptyHTML40Tags[ndx];
157 }
158
159 Bool isHTMLNoEscapeTag(const Str& name)
160 {
161     int ndx = lookupNoCase(name, theHTMLNoEscapeTags);
162     return !!theHTMLNoEscapeTags[ndx];
163 }
164
165 Bool isBooleanHTMLAtt(const Str& name)
166 {
167   int ndx = lookupNoCase(name, theHTMLBooleanAtts);
168  return !!theHTMLBooleanAtts[ndx];
169 }
170
171 //  this should be improved by checking the name of the parent element
172 //  see the element names in theURI_HTMLAtts
173 Bool isURI_HTMLAtt(const Str& name)
174 {
175     int ndx = lookupNoCase(name, theURI_HTMLAtts);
176     // return TRUE iff found
177     return !!theURI_HTMLAtts[ndx];
178 }
179
180 Bool isNoEolHTMLTag(const Str& name)
181 {
182     int ndx = lookupNoCase(name, theNoEolHTMLTags);
183     // return TRUE iff found
184     return !!theNoEolHTMLTags[ndx];
185 }
186
187 //
188 //  StrPrec and EQNamePrec implementation
189 //
190
191
192 // returns -1 if oldPrec wins, +1 if newPrec wins, 0 if equal (usually error)
193 int cmpPrecedences(int oldPrec, int newPrec)
194 {
195     if (oldPrec == OUTPUT_PRECEDENCE_UNSPECIFIED ||
196         newPrec == OUTPUT_PRECEDENCE_STRONGEST ||
197         newPrec >= 0 && newPrec < oldPrec)
198         return 1;
199     if (newPrec >= 0 && oldPrec == newPrec)
200         return 0;
201     return -1;
202 }
203
204 Bool StrPrec::set(const Str& newString, int newPrecedence)
205 {
206     int cmp = cmpPrecedences(precedence, newPrecedence);
207     // if new wins or there's a draw, set the value
208     if (cmp >= 0)
209     {
210         string = newString;
211         precedence = newPrecedence;
212     };
213     // if cmp == 0, return TRUE
214     return cmp ? FALSE : TRUE;
215 }
216
217 Bool EQNamePrec::set(const EQName& newName, int newPrecedence)
218 {
219     int cmp = cmpPrecedences(precedence, newPrecedence);
220     // if new wins or there's a draw, set the value
221     if (cmp >= 0)
222     {
223         name = newName;
224         precedence = newPrecedence;
225     };
226     // if cmp == 0, return TRUE
227     return cmp ? FALSE : TRUE;
228 }
229
230 //
231 //  OutputDefinition
232 //
233
234 Bool checkYesNo(const Str& what)
235 {
236     return(what == (const char*) "yes" || what == (const char*) "no");
237 }
238
239 OutputDefinition::OutputDefinition()
240 : method()
241 {
242 }
243
244 OutputDefinition::~OutputDefinition()
245 {
246     cdataElems.freeall(FALSE);
247 }
248
249 int lookupAttCode(XSL_ATT* table, XSL_ATT what)
250 {
251     int i;
252     for (i = 0; table[i] != XSLA_NONE && table[i] != what; i++) {};
253     return (table[i] == XSLA_NONE ? -1 : i);
254 }
255
256 eFlag OutputDefinition::setItemStr(Sit S, XSL_ATT itemId, const Str& value, Vertex *caller, int precedence)
257 {
258     if (caller)
259         precedence = caller -> getImportPrecedence();
260     switch(itemId)
261     {
262     case XSLA_OMIT_XML_DECL:
263     case XSLA_STANDALONE:
264     case XSLA_INDENT:
265     {
266         if (!checkYesNo(value))
267         {
268             S.setCurrVDoc(caller);
269             Err1(S, E1_ATTR_YES_NO, xslAttNames[itemId]);
270         }
271     }; break;
272     };
273     int index = lookupAttCode(outputStringAtts, itemId);
274     assert(index >= 0);
275     if (stringItems[index].set(value, precedence))
276     {
277         S.setCurrVDoc(caller);
278         Warn1(S, W1_OUTPUT_ATTR, xslAttNames[itemId]);
279     }
280     return OK;
281 }
282
283 eFlag OutputDefinition::setItemEQName(Sit S, XSL_ATT itemId, const EQName& value, Vertex *caller, int precedence) 
284 {
285     if (caller)
286         precedence = caller -> getImportPrecedence();
287     switch(itemId)
288     {
289     case XSLA_CDATA_SECT_ELEMS:
290         cdataElems.append(new EQName(value)); break;
291     default: 
292     {
293         assert(itemId == XSLA_METHOD);
294         if (method.set(value, precedence))
295         {
296             S.setCurrVDoc(caller);
297             Warn1(S, W1_OUTPUT_ATTR, xslAttNames[itemId]);
298         }
299     }
300     }
301     return OK;
302 }
303
304 const Str& OutputDefinition::getValueStr(XSL_ATT itemId) const
305 {
306     int index = lookupAttCode(outputStringAtts, itemId);
307     assert(index >= 0);
308     return stringItems[index].get();
309 }
310
311 const EQName& OutputDefinition::getValueEQName(XSL_ATT itemId) const
312 {
313     assert(itemId == XSLA_METHOD);
314     return method.get();
315 };
316
317 Bool OutputDefinition::askEQNameList(XSL_ATT itemId, const EQName &what) const
318 {
319     assert(itemId == XSLA_CDATA_SECT_ELEMS);
320     return (cdataElems.find(what) != NULL);
321 }
322
323 OutputMethod OutputDefinition::getMethod() const
324 {
325     const Str& method_ = getValueEQName(XSLA_METHOD).getLocal();
326     if (method_ == (const char*) "html")
327         return OUTPUT_HTML;
328     else if (method_ == (const char*) "text")
329         return OUTPUT_TEXT;
330     else if (method_ == (const char*) "xml")
331         return OUTPUT_XML;
332     else if (method_ == (const char*) "xhtml")
333         return OUTPUT_XHTML;
334     else return OUTPUT_UNKNOWN;
335 }
336
337 const Str& OutputDefinition::getEncoding() const
338 {
339     return getValueStr(XSLA_ENCODING);
340 }
341
342 Bool OutputDefinition::getIndent() const
343 {
344     const Str& indent_ = getValueStr(XSLA_INDENT);
345     return (indent_ == (char*)"yes");        
346 }
347
348 eFlag OutputDefinition::setDefaults(Sit S)
349 {
350     OutputMethod meth = getMethod();
351     assert( meth != OUTPUT_UNKNOWN );
352     char strYes[] = "yes", strNo[] = "no";
353     E( setItemStr(S, XSLA_ENCODING, "UTF-8", NULL, OUTPUT_PRECEDENCE_WEAKEST) );
354     switch(meth)
355     {
356     case OUTPUT_XML:
357         {
358             E( setItemStr(S, XSLA_VERSION, "1.0", NULL, OUTPUT_PRECEDENCE_WEAKEST) );
359             E( setItemStr(S, XSLA_INDENT, strNo, NULL, OUTPUT_PRECEDENCE_WEAKEST) );
360             E( setItemStr(S, XSLA_MEDIA_TYPE, "text/xml", NULL, OUTPUT_PRECEDENCE_WEAKEST) );
361             E( setItemStr(S, XSLA_OMIT_XML_DECL, strNo, NULL, OUTPUT_PRECEDENCE_WEAKEST) );
362         }; break;
363     case OUTPUT_HTML:
364         {
365             E( setItemStr(S, XSLA_VERSION, "4.0", NULL, OUTPUT_PRECEDENCE_WEAKEST) );
366             E( setItemStr(S, XSLA_INDENT, strYes, NULL, OUTPUT_PRECEDENCE_WEAKEST) );
367             E( setItemStr(S, XSLA_MEDIA_TYPE, "text/html", NULL, OUTPUT_PRECEDENCE_WEAKEST) );
368             E( setItemStr(S, XSLA_OMIT_XML_DECL, strYes, NULL, OUTPUT_PRECEDENCE_WEAKEST) );
369         }; break;
370     case OUTPUT_TEXT:
371         {
372             E( setItemStr(S, XSLA_INDENT, strNo, NULL, OUTPUT_PRECEDENCE_WEAKEST) );
373             E( setItemStr(S, XSLA_MEDIA_TYPE, "text/plain", NULL, OUTPUT_PRECEDENCE_WEAKEST) );
374             E( setItemStr(S, XSLA_OMIT_XML_DECL, strYes, NULL, OUTPUT_PRECEDENCE_WEAKEST) );
375         }; break;
376     case OUTPUT_XHTML:
377         {
378             E( setItemStr(S, XSLA_VERSION, "1.0", NULL, OUTPUT_PRECEDENCE_WEAKEST) );
379             E( setItemStr(S, XSLA_INDENT, strYes, NULL, OUTPUT_PRECEDENCE_WEAKEST) );
380             E( setItemStr(S, XSLA_MEDIA_TYPE, "text/html", NULL, OUTPUT_PRECEDENCE_WEAKEST) );
381             E( setItemStr(S, XSLA_OMIT_XML_DECL, strYes, NULL, OUTPUT_PRECEDENCE_WEAKEST) );
382         }; break;
383     }
384     return OK;
385 }
386
387 void OutputDefinition::report(Sit S, MsgType type, MsgCode code, const Str& arg1, const Str& arg2)
388 {
389     S.message(type, code, arg1, arg2);
390 }
391
392
393
394 //
395 //
396 //  PhysicalOutputLayerObj
397 //
398 //
399
400 #define sendStrEsc(SIT, STRING,ESCAPING) sendOut(SIT, (STRING), (STRING.length()), (ESCAPING))
401 #define sendStr(SIT, STRING)     sendStrEsc(SIT, (STRING), ESCAPING_NONE)
402 #define sendLit(SIT, LITERAL)    sendOut(SIT, (LITERAL), sizeof(LITERAL) - 1, ESCAPING_NONE)
403 // S is the situation (existing in each context indentEOL is called in) 
404 #define indentEOL() {sendLit(S, "\n");}
405 #define indentSpace() {for (int i__ = 0; i__ < level; i__++) sendLit(S, "  ");}
406 #define indentFull() {if (indent && after_markup) {indentEOL(); indentSpace();}}
407 #define ignoreTextMethod() {if (method == OUTPUT_TEXT) return OK; }
408
409 PhysicalOutputLayerObj::PhysicalOutputLayerObj(CDesc encodingCD_)
410 {
411     curr = 0;
412     encodingCD = encodingCD_;
413     indent = FALSE;
414     after_markup = FALSE;  //pc
415     level = 0;  //pc
416     defaultNSWas = false;
417 }
418
419 PhysicalOutputLayerObj::~PhysicalOutputLayerObj()
420 {
421 }
422
423 eFlag PhysicalOutputLayerObj::setOptions(Sit S, 
424     DataLine *targetDataLine_, OutputDefinition *outDef_)
425 {
426     targetDataLine = targetDataLine_;
427     outDef = outDef_;
428     method = outDef -> getMethod();
429     indent = outDef -> getIndent();
430     //enc = outDef -> getEncoding();
431     encoding = outDef -> getEncoding();
432     return OK;
433 }
434
435 eFlag PhysicalOutputLayerObj::outputTrailingNewline (Sit S)
436 {
437   switch (method) {
438   case OUTPUT_HTML:
439   case OUTPUT_XML:
440   case OUTPUT_XHTML:
441     indentEOL();
442     break;
443   }
444   return OK;
445 }
446
447 eFlag PhysicalOutputLayerObj::outputDone(Sit S)
448 {
449   E( flushBuffer(S) );
450   return OK;
451 }
452
453 Str _localName(Str fullname)
454 {
455   char *full = (char*)fullname;
456   char *colon = strchr(fullname, ':');
457   if (colon)
458     return colon + 1;
459   else 
460     return full;
461 }
462
463 eFlag PhysicalOutputLayerObj::outputElementStart(Sit S, 
464     const Str& name,
465     const NamespaceStack& namespaces, const int namespace_index,
466     const StrStrList& atts, 
467     Bool isEmpty)
468 {
469   //exit for text output method!!!
470   ignoreTextMethod();
471
472     // begin output of start tag: output element name
473     if (!isNoEolHTMLTag(name) || (method != OUTPUT_HTML && method != OUTPUT_XHTML)) 
474       indentFull();
475
476     sendLit(S, "<");
477     E( sendStr(S, name) );
478
479     // output namespace declarations
480
481     int i;
482     const Str* prefix;
483     for (i = namespace_index; i < namespaces.number(); i++)
484     {
485       NamespaceStackObj * nm = namespaces[i];
486       prefix = &(nm -> prefix);
487       //prefix = &(namespaces[i] -> prefix); 
488       //if (!namespaces.isHidden(*prefix))
489
490       // swallow hidden namespaces as well as the empty default
491       //namespace, if no default namespace was output recently
492       if (! nm -> hidden &&
493           (!(prefix -> isEmpty()) || !(nm -> uri.isEmpty()) || defaultNSWas))
494         {
495           defaultNSWas = prefix -> isEmpty();
496
497           sendLit(S, " xmlns");
498           if (!prefix -> isEmpty())
499           {
500              sendLit(S, ":");
501              E( sendStr(S, *prefix) );
502           }
503           sendLit(S, "=\"");
504           E( sendStrEsc(S, nm -> uri, 
505                         method == OUTPUT_HTML || method == OUTPUT_XHTML ?
506                         ESCAPING_HTML_URI : ESCAPING_URI) );
507           sendLit(S, "\"");
508        };
509     };
510
511     // output attributes
512
513     for (i = 0; i < atts.number(); i++)
514     {
515         sendLit(S, " ");
516         /*
517         const EQName& attQName = atts[i] -> key;
518         if (attQName.hasPrefix())
519         {
520             E( sendStr(S, attQName.getPrefix()) );
521             sendLit(S, ":");
522         };
523         const Str& localAttName = atts[i] -> key.getLocal();
524         E( sendStr(S, localAttName) );
525         */
526         sendOut(S, (char*)atts[i] -> key, atts[i] -> key.length(), 
527                 ESCAPING_NONE);
528
529         //boolean atts for html
530         if (method == OUTPUT_HTML && isBooleanHTMLAtt(atts[i]->key))
531             continue;
532
533         sendLit(S, "=\"");
534
535         /*
536         EscMode escapingMode = 
537             ((method == OUTPUT_HTML || method == OUTPUT_XHTML) ? 
538              ESCAPING_HTML_ATTR : ESCAPING_ATTR);
539         if ((method == OUTPUT_HTML || method == OUTPUT_XHTML) && 
540             isURI_HTMLAtt(_localName(atts[i]->key)))
541           {
542             escapingMode = ESCAPING_HTML_URI;
543           }
544         */
545         EscMode escapingMode = ESCAPING_ATTR;
546         if (method == OUTPUT_HTML || method == OUTPUT_XHTML) 
547           {
548             if ( strchr(name, ':') ) 
549               {
550                 escapingMode = ESCAPING_ATTR;
551               }
552             else if ( isURI_HTMLAtt(_localName(atts[i]->key)) )
553               {
554                 escapingMode = ESCAPING_HTML_URI;
555               }
556             else 
557               {
558                 if (method == OUTPUT_HTML) 
559                   escapingMode = ESCAPING_HTML_ATTR;
560               }
561           }
562
563         E( sendStrEsc(S, atts[i] -> value, escapingMode));
564         sendLit(S, "\"");
565     };
566
567     // close the tag
568
569     after_markup = TRUE;  //pc
570
571     if (!isEmpty)
572     {
573       sendLit(S, ">");
574       level++;  //pc
575     }
576     else
577     {
578         if (method == OUTPUT_HTML || method == OUTPUT_XHTML)
579         {
580             if (!isEmptyHTMLTag(name))
581             {
582                 sendLit(S, "></");
583                 sendStr(S, name);
584                 sendLit(S, ">");
585             }
586             else if (method == OUTPUT_HTML)
587                 sendLit(S, ">");
588             else
589                 sendLit(S, " />");
590
591             if (isNoEolHTMLTag(name)) 
592                 after_markup = FALSE;
593         }
594         else
595             sendLit(S, "/>");
596     };
597     return OK;
598 }
599
600
601
602 eFlag PhysicalOutputLayerObj::outputElementEnd(Sit S, 
603     const Str& name, Bool isEmpty)
604 {
605   //exit for text output method!!!
606   ignoreTextMethod();
607
608     if (!isEmpty)
609     {
610         level--;  //pc
611         indentFull();
612         sendLit(S, "</");
613         E( sendStr(S, name) );
614         sendLit(S, ">");
615         if (!isNoEolHTMLTag(name)) after_markup = TRUE;  //pc
616     };
617     return OK;
618 }
619
620
621 eFlag PhysicalOutputLayerObj::outputComment(Sit S, const Str& contents)
622 {
623   //exit for text output method!!!
624   ignoreTextMethod();
625
626     indentFull();
627     sendLit(S, "<!--");
628     const char *p = contents, *p_was = p;
629     int len = contents.length();
630     Bool trailingHyphen = len ? (contents[len - 1] == '-') : FALSE;
631     while (*p)
632     {
633         E( sendOutUntil(S, p, len - (p - p_was), ESCAPING_NONE, "--") );
634         if (*p)
635         {
636             sendLit(S, "- ");
637             p++;
638         }
639     };
640     if (trailingHyphen)
641         sendLit(S, " ");
642     sendLit(S, "-->");
643     after_markup = TRUE;  //pc
644     return OK;
645 }
646
647
648 eFlag PhysicalOutputLayerObj::outputCDataSection(Sit S, const Str& contents)
649 {         
650   switch (method) {
651   case OUTPUT_TEXT:
652     {
653       sendLit(S, contents);
654     }; break;
655   default:
656     {
657       const char *p = contents, *p_was = p;
658       if (!*p)
659         return OK;
660       indentFull();
661       sendLit(S, "<![CDATA[");
662       while (*p)
663         {
664           E( sendOutUntil(S, p, 
665                           contents.length() -(int)(p - p_was), 
666                           ESCAPING_NONE, "]]>") );
667           if (*p)
668             {
669               sendLit(S, "]]]]><![CDATA[>");
670               p += 3;
671             }
672         };
673       sendLit(S, "]]>");
674       after_markup = TRUE;  //pc
675     }
676   }
677   return OK;
678 }
679
680 eFlag PhysicalOutputLayerObj::outputPI(Sit S, const Str& target, const Str& data)
681 {
682   //exit for text output method!!!
683   ignoreTextMethod();
684
685     indentFull();
686     sendLit(S, "<?");
687     E( sendStr(S, target) );
688     sendLit(S, " ");
689     E( sendStr(S, data) );
690     if (method == OUTPUT_HTML && !(target == (const char*)"xml"))
691         sendLit(S, ">");
692     else
693         sendLit(S, "?>");
694     after_markup = TRUE;  //pc
695     return OK;
696 }
697
698 eFlag PhysicalOutputLayerObj::outputDTD(Sit S, 
699     const Str& name, const Str& publicId, const Str& systemId)
700 {
701   //exit for text output method!!!
702   ignoreTextMethod();
703
704     indentFull();
705     sendLit(S, "<!DOCTYPE ");
706     switch(method)
707     {
708     case OUTPUT_XML:
709     case OUTPUT_XHTML:
710         {
711             E( sendStr(S, name) );
712             if (!systemId.isEmpty())
713             {
714                 if (!publicId.isEmpty())
715                 {
716                     sendLit(S, " PUBLIC \"");
717                     E( sendStrEsc(S, publicId, ESCAPING_NONE) );
718                     sendLit(S, "\"");
719                 }
720                 else
721                     sendLit(S, " SYSTEM");
722                 sendLit(S, " \"");
723                 E( sendStrEsc(S, systemId, ESCAPING_URI) );
724                 sendLit(S, "\"");
725             }
726         }; break;
727     case OUTPUT_HTML:
728         {
729             sendLit(S, "html");
730             if (!publicId.isEmpty())
731             {
732                 sendLit(S, " PUBLIC \"");
733                 E( sendStrEsc(S, publicId, ESCAPING_NONE) );
734                 sendLit(S, "\"");
735             }
736             if (!systemId.isEmpty())
737             {
738                 if (publicId.isEmpty())
739                     sendLit(S, " SYSTEM");
740                 sendLit(S, " \"");
741                 E( sendStrEsc(S, systemId, ESCAPING_URI) );
742                 sendLit(S, "\"");
743             };
744         }; break;
745     };
746     if (indent)
747         sendLit(S, ">");
748         else
749         sendLit(S, ">\xa");
750     after_markup = TRUE;  //pc
751     return OK;
752 }
753
754 eFlag PhysicalOutputLayerObj::outputText(Sit S, 
755     const Str& contents, Bool disableEsc,
756     Bool inHTMLSpecial)
757 {
758     switch(method)
759     {
760     case OUTPUT_XML:
761     case OUTPUT_XHTML:
762         {
763             E( sendStrEsc(S, contents, (disableEsc || inHTMLSpecial) ? ESCAPING_NONE : ESCAPING_LT_AMP) );
764         }; break;
765     case OUTPUT_HTML:
766         {
767             E( sendStrEsc(S, contents, (disableEsc || inHTMLSpecial) ? ESCAPING_NONE : ESCAPING_LT_AMP) );
768         }; break;
769     case OUTPUT_TEXT:
770         {
771             E( sendStrEsc(S, contents, ESCAPING_NONE) );
772         }; break;
773     };
774     after_markup = FALSE;  //pc
775     return OK;
776 }
777
778
779 eFlag PhysicalOutputLayerObj::flushBuffer(Sit S)
780 {
781     E( targetDataLine -> save(S, buffer, curr) );
782     curr = 0;
783     return OK;
784 }
785
786 int PhysicalOutputLayerObj::writeCharacterRef(char* dest, const char *src, EscMode escapeMode)
787 {
788     char *dest_was = dest;
789     if (escapeMode == ESCAPING_URI || escapeMode == ESCAPING_HTML_URI)
790     {
791         int i, iLimit = utf8SingleCharLength(src);
792         for (i = 0; i < iLimit; i++)
793           {
794             dest += sprintf(dest, "%%%02hhx", (unsigned char)src[i]);
795           }
796         return (int)(dest - dest_was);
797     }
798     else
799         return sprintf(dest, "&#%lu;", utf8CharCode(src));
800 }
801
802 // data is assumed null-terminated in the following
803
804 eFlag PhysicalOutputLayerObj::sendOutUntil(Sit S, 
805     const char *& data, int length,
806     EscMode escapeMode, const char* stoppingText)
807 {
808     const char *p = strstr(data, stoppingText);
809     int sending = (p ? p - data : length);
810     E( sendOut(S, data, sending, escapeMode) );
811     data += sending;
812     return OK;
813 }
814
815
816
817 #define minimum(a,b) (((a) < (b)) ? (a) : (b))
818
819 //
820 // sendOut
821 //
822 // This is the place where *all* actual character output and conversion work takes place. The
823 // individual characters are recoded and written into a buffer. When the buffer is full, it is
824 // sent to the appropriate dataline.
825 //
826
827 eFlag PhysicalOutputLayerObj::sendOut(Sit S, 
828     const char* data, int length,
829     EscMode escapeMode)
830 {
831     int count = 0;
832     size_t srcCharLen, destCharLen; 
833     Bool served;
834     char *outbuf;
835     size_t outleft, inleft;
836     EncResult result;
837     
838     while (count < length)
839     {
840         srcCharLen = 1;
841         served = FALSE;
842         switch(*data)
843         {
844         case '<':
845             {
846                 switch(escapeMode)
847                 {
848 /*  will escape < in URIs as &lt; not the %xx way
849                 case ESCAPING_URI:
850                 case ESCAPING_HTML_URI:
851                     {
852                         E( sendOut(smallBuf, 
853                             writeCharacterRef(smallBuf, data, escapeMode),
854                             ESCAPING_NONE) );
855                         served = TRUE;
856                     }; break;
857 */
858                 case ESCAPING_URI:
859                 case ESCAPING_HTML_URI:
860                 case ESCAPING_ATTR:
861                 case ESCAPING_LT_AMP:
862                     {
863                         E( sendLit(S, "&lt;") );
864                         served = TRUE;
865                     }; break;
866                 }
867             }; break;
868         case '>':
869             {
870                 switch(escapeMode)
871                 {
872                 case ESCAPING_URI:
873                 case ESCAPING_HTML_URI:
874                 case ESCAPING_ATTR:
875                 case ESCAPING_LT_AMP:
876                     {
877                                 if (method == OUTPUT_HTML || method == OUTPUT_XHTML)
878                                         {
879                             E( sendLit(S, "&gt;") );
880                             served = TRUE;
881                                     }
882                     }; break;
883                 }
884             }; break;
885         case '&':
886             {
887                 switch(escapeMode)
888                 {
889 /*  will escape & in URIs as &amp; not the %xx way
890                 case ESCAPING_URI:
891                 case ESCAPING_HTML_URI:
892                     {
893                         E( sendOut(smallBuf, 
894                             writeCharacterRef(smallBuf, data, escapeMode),
895                             ESCAPING_NONE) );
896                         served = TRUE;
897                     }; break;
898 */
899                 case ESCAPING_HTML_ATTR:
900                     {
901                         if (data[1] == '{')
902                             break;
903                     }; // no break
904                 case ESCAPING_URI:
905                 case ESCAPING_HTML_URI:
906                 case ESCAPING_ATTR:
907                 case ESCAPING_LT_AMP:
908                     {
909                         E( sendLit(S, "&amp;") );
910                         served = TRUE;
911                     }; break;
912                 }
913             }; break;
914         case '\"':
915             {
916                 switch(escapeMode)
917                 {
918                 case ESCAPING_URI:
919                 case ESCAPING_HTML_URI:
920                     {
921                         E( sendOut(S, smallBuf, 
922                             writeCharacterRef(smallBuf, data, escapeMode),
923                             ESCAPING_NONE) );
924                         served = TRUE;
925                     }; break;
926                 case ESCAPING_HTML_ATTR:
927                 case ESCAPING_ATTR:
928                     {
929                         E( sendLit(S, "&quot;") );
930                         served = TRUE;
931                     }; break;
932                 };
933             }; break;
934         case 9:
935         case 10:
936         case 13:
937             {
938                 switch(escapeMode)
939                 {
940                 case ESCAPING_URI:
941                 case ESCAPING_HTML_URI:
942                 case ESCAPING_ATTR:
943                 case ESCAPING_HTML_ATTR:
944                     {
945                         E( sendOut(S, smallBuf, 
946                             writeCharacterRef(smallBuf, data, escapeMode),
947                             ESCAPING_NONE) );
948                         served = TRUE;
949                     }; break;
950                 }
951             }; break;
952         case ' ':
953             {
954                 switch(escapeMode)
955                 {
956                 case ESCAPING_URI:
957                 case ESCAPING_HTML_URI:
958                     {
959                         E( sendOut(S, smallBuf, 
960                             writeCharacterRef(smallBuf, data, escapeMode),
961                             ESCAPING_NONE) );
962                         served = TRUE;
963                     }; break;
964                 }
965             }; break;
966             default:
967                 {
968                         // escape non-ASCII values in URIs
969                         if (*data < 0)
970                             {
971                     switch(escapeMode)
972                     {
973                     case ESCAPING_URI:
974                     case ESCAPING_HTML_URI:
975                         {
976                             E( sendOut(S, smallBuf, 
977                                 writeCharacterRef(smallBuf, data, escapeMode),
978                                 ESCAPING_NONE) );
979                             served = TRUE;
980                         }; break;
981                     }
982                             }                           
983                     }
984         };
985
986
987         if (!served)
988         {
989             srcCharLen = utf8SingleCharLength(data);
990             assert(srcCharLen > 0);
991             // if encoding differs from utf-8 then recode
992
993 #ifdef SABLOT_DEBUGGER
994             if ( debugger )
995               {
996                 debugger -> printOutput(data, srcCharLen);
997               }
998 #endif
999
1000             if (encodingCD == (void*)-1)
1001             {
1002                 memcpy(buffer + curr, data, srcCharLen);
1003                 data += srcCharLen;
1004                 curr += srcCharLen;
1005             }
1006             else
1007             {
1008                 // convert
1009                 outbuf = buffer + curr;
1010                 outleft = OUTPUT_BUFFER_SIZE - curr;
1011                 inleft = srcCharLen;
1012                         // getProcessor must be non-null here since encodingCD is
1013                 S.recoder().conv(S, encodingCD, data, inleft, outbuf, outleft, result);
1014                 // data is shifted automatically, set curr
1015                 curr = outbuf - buffer;
1016                 // check result, write a character code if 
1017                 //couldn't convert (unless ESCAPING_NONE)
1018                 assert(result != ENC_EINVAL && result != ENC_E2BIG);
1019                 // FIXME: in NT, must check differently
1020                 if (result == ENC_EILSEQ)
1021                 {
1022                     // unable to convert
1023                     destCharLen = writeCharacterRef(smallBuf, data, escapeMode);
1024                     //if (escapeMode == ESCAPING_NONE)
1025                     if (method == OUTPUT_TEXT)
1026                         Err1(S, E1_BAD_CHAR_IN_ENC, smallBuf)
1027                     else
1028                       {
1029                         E( sendOut(S, smallBuf, destCharLen, ESCAPING_NONE) );
1030                         data += srcCharLen;
1031                         served = TRUE;
1032                       }
1033                 }
1034             }
1035         }   // if (!served)
1036         else
1037         {
1038             data += srcCharLen;
1039         }
1040
1041         // send the buffer if over a threshold
1042         if (curr > OUTPUT_BUFFER_LIMIT)
1043             flushBuffer(S);
1044
1045         count += srcCharLen;
1046         // curr already got shifted in all cases
1047
1048     }       // while    
1049     return OK;
1050 }
1051
1052
1053 eFlag PhysicalOutputLayerObj::setMethodByDefault(Sit S, OutputMethod method_)
1054 {
1055     EQName q;
1056     assert(method == OUTPUT_UNKNOWN);
1057     switch( method = method_ )
1058     {
1059     case OUTPUT_XML:
1060         q.setLocal("xml"); break;
1061     case OUTPUT_HTML:
1062         q.setLocal("html"); break;
1063     default:
1064         assert(!"PhysicalOutputLayerObj::setMethod()");
1065     }
1066     E( NZ(outDef) -> setItemEQName(S, XSLA_METHOD, q, NULL, OUTPUT_PRECEDENCE_STRONGEST) );
1067     E( outDef -> setDefaults(S) );
1068     return OK;
1069 }
1070
1071 void PhysicalOutputLayerObj::report(Sit S, MsgType type, MsgCode code, const Str& arg1, const Str& arg2)
1072 {
1073     S.message(type, code, arg1, arg2);
1074 }
1075
1076
1077
1078 //
1079 //
1080 //      FrontMatter
1081 //
1082 //
1083
1084 eFlag FrontMatter::appendConstruct(Sit S, 
1085     FrontMatterKind kind, const Str& string1,
1086     const Str& string2, Bool disableEsc)
1087 {
1088     FrontMatterItem* item;
1089
1090     M( S, item = new FrontMatterItem ); // GP: OK
1091     item -> kind = kind;
1092     item -> string1 = string1;
1093     item -> string2 = string2;
1094     item -> disableEsc = disableEsc;
1095     append(item);
1096     return OK;
1097 };
1098
1099 void FrontMatter::report(Sit S, MsgType type, MsgCode code, const Str& arg1, const Str& arg2)
1100 {
1101     S.message(type, code, arg1, arg2);
1102 }
1103
1104 //
1105 //
1106 //  OutputDocument
1107 //
1108 //
1109
1110 OutputDocument::~OutputDocument()
1111 {
1112   if (outputter) delete outputter;
1113   if (def) delete def;
1114 }
1115
1116 eFlag OutputDocument::finish(Sit S)
1117 {
1118   if (state == OUTDOC_ACTIVE)
1119     {
1120       E( NZ(outputter) -> eventTrailingNewline(S) );
1121       E( NZ(outputter) -> eventEndOutput(S) );
1122       setState(OUTDOC_FINISHED);
1123     }
1124   return OK;
1125 }
1126
1127 OutputterObj* OutputDocument::setOutputter(OutputterObj* new_)
1128 {
1129   if (outputter) delete outputter;
1130   return outputter = new_;
1131 }
1132
1133 //
1134 //
1135 //  OutputterObj
1136 //
1137 //
1138
1139
1140 Str* OutputterObj::nameForSAX(Sit S, const EQName& q)
1141 {
1142   DStr temp;
1143   if (mySAXOutputType == SAXOUTPUT_COPY_TREE || 
1144       mySAXOutputType == SAXOUTPUT_INT_PHYSICAL)
1145     {
1146       if (q.getUri().isEmpty())
1147         return new Str(q.getLocal());
1148       temp = q.getUri();
1149       temp += THE_NAMESPACE_SEPARATOR;
1150       temp += q.getLocal();
1151       temp += THE_NAMESPACE_SEPARATOR;
1152       temp += q.getPrefix();
1153       return new Str(temp);
1154     }
1155   else
1156     {
1157       Str s;
1158       q.getname(s);
1159       return new Str(s);
1160     }
1161 };
1162
1163 OutputterObj::OutputterObj()
1164 {
1165     encodingCD = (CDesc) -1;
1166     outputEscaping = TRUE;
1167     physical = NULL;
1168     outDef = NULL;
1169     mySAXHandler = NULL;
1170     mySAXOutputType = SAXOUTPUT_NONE;
1171     method = OUTPUT_UNKNOWN;
1172     noElementYet = TRUE;
1173     noHeadYet = TRUE;
1174     delayedDTD = FALSE;
1175     // to recognize the first HEAD element
1176 }
1177
1178 OutputterObj::~OutputterObj()
1179 {
1180     history.freeall(FALSE);
1181     front.freeall(FALSE);
1182     currNamespaces.freeall(FALSE);
1183     cdelete(physical);
1184 }
1185
1186 eFlag OutputterObj::setOptions(Sit S, 
1187     DataLine *targetDataLine_, OutputDefinition *outDef_)
1188 {
1189     Str encoding;
1190     outDef = NZ(outDef_);
1191     method = outDef -> getMethod(); //_PH_
1192     if (method != OUTPUT_UNKNOWN)
1193         E( outDef -> setDefaults(S) );
1194
1195     if (S.getProcessor())
1196       {
1197         encoding = S.getProcessor() -> getHardEncoding();
1198         if ( !encoding.isEmpty() )
1199           outDef -> setItemStr(S, XSLA_ENCODING, encoding, NULL, 
1200                                OUTPUT_PRECEDENCE_STRONGEST);
1201       }
1202     else
1203         encoding.empty();
1204     if (encoding.isEmpty())
1205         encoding = outDef -> getValueStr(XSLA_ENCODING);
1206     if (!encoding.isEmpty() && !encoding.eqNoCase("utf-8"))
1207     {
1208         // set the conversion descriptor
1209         if (S.getProcessor())
1210         {
1211             E( S.recoder().openFromUTF8(S, 
1212                                         encoding, encodingCD) );
1213         }
1214         else 
1215             encodingCD = (ConvInfo*)-1;
1216         if (encodingCD == (void*)-1)
1217         {
1218             Warn1(S, W1_UNSUPP_OUT_ENCODING, encoding);
1219             encoding = "UTF-8";
1220             E( outDef -> setItemStr(S, XSLA_ENCODING, encoding, 
1221                                     NULL, OUTPUT_PRECEDENCE_STRONGEST) );
1222         }
1223     }
1224     else
1225         if (!encoding.isEmpty())
1226             E( outDef -> setItemStr(S, XSLA_ENCODING, encoding, 
1227                                     NULL, OUTPUT_PRECEDENCE_WEAKEST) );
1228
1229     if (targetDataLine_)
1230     {
1231         M( S, physical = new PhysicalOutputLayerObj(encodingCD) );
1232         // GP: OK, OutputterObj gets disposed of in cleanupAfterRun
1233         E( physical -> setOptions(S, targetDataLine_, outDef_) ); 
1234     };
1235
1236     return OK;
1237 }
1238
1239 eFlag OutputterObj::setOptionsSAX(Sit S, 
1240     SAXHandler *streaming_, void* userData_, SAXOutputType saxout_)
1241 {
1242     mySAXHandler = streaming_;
1243     mySAXUserData = userData_;
1244     mySAXOutputType = saxout_;
1245     return OK;
1246 }
1247
1248 eFlag OutputterObj::reportXMLDeclIfMust(Sit S)
1249 {
1250     if (!physical || method == OUTPUT_UNKNOWN || 
1251         outDef -> getValueStr(XSLA_OMIT_XML_DECL) == (const char*)"yes")
1252         return OK;
1253     DStr declText = "version=\"";
1254     declText += outDef -> getValueStr(XSLA_VERSION);
1255     declText += "\" encoding=\"";
1256     declText += outDef -> getValueStr(XSLA_ENCODING);
1257     declText += '\"';
1258     const Str &standaloneText = outDef -> getValueStr(XSLA_STANDALONE);
1259     if (!standaloneText.isEmpty())
1260     {
1261         declText += " standalone=\"";
1262         declText += standaloneText;
1263         declText += '\"';
1264     };
1265     E( physical -> outputPI(S, Str("xml"), declText) );
1266     return OK;
1267 }
1268
1269 eFlag OutputterObj::reportDTDIfMust(Sit S, const EQName& docElementName)
1270 // to be called only after the output method is determined
1271 {
1272     assert(method != OUTPUT_TEXT);
1273     if (!physical)
1274         return OK;
1275     const Str& 
1276         DTSystem = outDef -> getValueStr(XSLA_DOCTYPE_SYSTEM),
1277         DTPublic = outDef -> getValueStr(XSLA_DOCTYPE_PUBLIC);        
1278     Bool writeDTD;
1279     switch(method)
1280     {
1281     case OUTPUT_XML:
1282     case OUTPUT_XHTML:
1283         writeDTD = !DTSystem.isEmpty();
1284         break;
1285     case OUTPUT_HTML:
1286         writeDTD = !DTSystem.isEmpty() || !DTPublic.isEmpty();
1287         break;
1288     default:
1289         writeDTD = FALSE;
1290     }
1291
1292     delayedDTD = writeDTD;
1293
1294     /*
1295     if (writeDTD)
1296     {
1297       Str fullName;
1298           docElementName.getname(fullName);
1299
1300     };
1301     */
1302     return OK;
1303 }
1304
1305
1306 eFlag OutputterObj::reportFront(Sit S)
1307 {
1308     assert(method != OUTPUT_UNKNOWN);
1309     int i, frontCount = front.number();
1310     FrontMatterItem *item;
1311     for(i = 0; i < frontCount; i++)
1312     {
1313         item = front[i];
1314         switch(item -> kind)
1315         {
1316         case FM_TEXT:
1317             {
1318                 if (item -> disableEsc)
1319                     E( eventDisableEscapingForNext(S) );
1320                 E( eventData(S, item -> string1) );
1321             }; break;
1322         case FM_PI:
1323             {
1324                 E( eventPIStart(S, item -> string1) );
1325                 E( eventData(S, item -> string2) );
1326                 E( eventPIEnd(S) );
1327             }; break;
1328         case FM_COMMENT:
1329             {
1330                 E( eventCommentStart(S) );
1331                 E( eventData(S, item -> string1) );
1332                 E( eventCommentEnd(S) );
1333             };
1334         }
1335     }
1336     return OK;
1337 }
1338
1339
1340 eFlag OutputterObj::eventBeginOutput(Sit S)
1341 {
1342   const EQName dummy;
1343   pushLevel(dummy); //experimental
1344   method = outDef ? outDef -> getMethod() : OUTPUT_UNKNOWN;
1345   if (physical)
1346     {
1347       //??JP??both physical & method !=... 
1348       //duplicit with the same in reportXMLDeclIfMust 
1349       if (method != OUTPUT_UNKNOWN)
1350         E( reportXMLDeclIfMust(S) );
1351     }
1352   // initialize the SAX interface
1353   IF_SAX1( startDocument ); 
1354   state = STATE_OUTSIDE;
1355   return OK;
1356 }
1357
1358 eFlag OutputterObj::eventBeginSubtree(Sit S)
1359 {
1360   const EQName dummy;
1361   pushLevel(dummy); //experimntal
1362     method = outDef -> getMethod();
1363     if (physical)
1364     {
1365       //      method = outDef -> getMethod();
1366     }
1367     // initialize the SAX interface
1368     IF_SAX1( startDocument ); 
1369     state = STATE_OUTSIDE;
1370     return OK;
1371 }
1372
1373
1374 eFlag OutputterObj::eventElementStart(Sit S, const EQName& name)
1375 {
1376   if (noElementYet)
1377     {
1378       noElementYet = FALSE;
1379       if (physical)
1380         {
1381           if (method == OUTPUT_UNKNOWN)
1382             {
1383               if (name.getUri().isEmpty() && name.getLocal().eqNoCase("html"))
1384                 method = OUTPUT_HTML;
1385               else
1386                 method = OUTPUT_XML;
1387               E( physical -> setMethodByDefault(S, method) );
1388               E( reportXMLDeclIfMust(S) );
1389               E( reportFront(S) );//??JP??sax
1390             };
1391           // If this is an included stylesheet, output method cd be TEXT here
1392           if (method != OUTPUT_TEXT) {
1393             E( reportDTDIfMust( S, name ) );//??JP??sax
1394           }
1395         };
1396     }
1397   switch(state)
1398     {
1399     case STATE_OUTSIDE:
1400     case STATE_IN_MARKUP:
1401     case STATE_IN_ELEMENT:
1402       {
1403         // reportStartTag also sends the event to SAX
1404         E( reportStartTag(S, NONEMPTY_ELEMENT) );
1405         E( reportCurrData(S) );
1406         pushLevel(name);
1407         // this sets state to STATE_IN_MARKUP
1408       };
1409       break;
1410     case STATE_IN_COMMENT:
1411     case STATE_IN_PI:
1412     case STATE_IN_ATTRIBUTE:
1413       Err( S, E_ELEM_IN_COMMENT_PI );
1414     default:
1415       assert(!"eventElementStart");
1416     };
1417   
1418   // determine whether we're in a HEAD html element so a META tag
1419   // needs to be added
1420   
1421   return OK;
1422 }
1423
1424 eFlag OutputterObj::throwInMeta(Sit S)
1425 {
1426     noHeadYet = FALSE;
1427
1428     if ( S.hasFlag(SAB_DISABLE_ADDING_META) ) return OK;
1429
1430     if (physical || mySAXHandler)
1431     {
1432         Str metaName("meta");
1433         Str httpEquiv("http-equiv");
1434         Str contentType("Content-Type");
1435         Str content("content");
1436         DStr contentValue = 
1437           NZ(outDef) -> getValueStr(XSLA_MEDIA_TYPE) + "; charset=" +
1438           outDef -> getValueStr(XSLA_ENCODING);
1439
1440         if (physical) {
1441           StrStrList metaAtts;
1442           //EQName 
1443           //   httpEquivName, 
1444           //   contentName;
1445           //httpEquivName.setLocal("http-equiv");
1446           //contentName.setLocal("content");
1447           metaAtts.appendConstruct(httpEquiv, contentType);
1448           metaAtts.appendConstruct(content, contentValue);
1449           E( physical -> outputElementStart(S, metaName, currNamespaces,
1450                                             getFirstOwnNS(), metaAtts, TRUE) );
1451           E( physical -> outputElementEnd(S, metaName, TRUE) );
1452           metaAtts.freeall(FALSE);
1453         };
1454
1455         if (mySAXHandler) {
1456           char *saxAtts[5];
1457           saxAtts[0] = httpEquiv;
1458           saxAtts[1] = contentType;
1459           saxAtts[2] = content;
1460           saxAtts[3] = (char *) contentValue;
1461           saxAtts[4] = NULL;
1462           mySAXHandler->startElement(mySAXUserData, S.getProcessor(),
1463                                      metaName, 
1464                                      (const char**) saxAtts);
1465           mySAXHandler->endElement(mySAXUserData, S.getProcessor(),
1466                                    metaName);
1467         };
1468
1469         state = STATE_IN_ELEMENT;//??JP?? why to change state ?
1470
1471     };
1472     return OK;
1473 }
1474     
1475
1476
1477 eFlag OutputterObj::eventElementEnd(Sit S, const EQName& name)
1478 {
1479   assert(!(physical && mySAXOutputType == SAXOUTPUT_INT_PHYSICAL));
1480   Str physName;
1481   //  if (S.getProcessor())
1482   //  temp = S.getProcessor() -> getAliasedName(name, 
1483   //                                          currNamespaces,
1484   //                                          mySAXOutputType == SAXOUTPUT_INT_PHYSICAL);
1485   //else name.getname(temp);
1486   
1487   switch(state)
1488     {
1489     case STATE_IN_MARKUP:
1490       E( reportStartTag(S, EMPTY_ELEMENT) ); 
1491       break;
1492     case STATE_IN_ELEMENT:
1493       {
1494         E( reportCurrData(S) );
1495         if (physical)
1496           {
1497             Str physName;
1498             name.getname(physName);
1499             physical -> outputElementEnd(S, physName, NONEMPTY_ELEMENT );
1500           };
1501       }; break;
1502     default:
1503       assert(!"eventElementEnd");
1504     };
1505   
1506   // send the event to SAX
1507   switch(mySAXOutputType)
1508     {
1509     case SAXOUTPUT_COPY_TREE:
1510     case SAXOUTPUT_INT_PHYSICAL:
1511       {
1512         GP( Str ) theSAXname = nameForSAX(S, name);
1513         IF_SAX2( endElement, (const char*) *theSAXname );
1514         theSAXname.del();
1515         // report namespace scope ends
1516         //while (currNamespaces.number() > getFirstOwnNS())
1517         for (int ii = currNamespaces.number() - 1; ii >= getFirstOwnNS(); ii--)
1518           {
1519             IF_SAX2( endNamespace, currNamespaces[ii] -> prefix );
1520             //debug
1521           }
1522       };
1523       break;
1524     case SAXOUTPUT_AS_PHYSICAL:
1525       {
1526         Str physName;
1527         name.getname(physName);
1528         IF_SAX2( endElement, (const char*) physName );
1529         // report namespace scope ends
1530         Str* prefix;
1531         //while (currNamespaces.number() > getFirstOwnNS())
1532         for (int ii = currNamespaces.number() - 1; ii >= getFirstOwnNS(); ii--)
1533           {
1534             prefix = &(currNamespaces[ii] -> prefix);
1535             if (! currNamespaces.isHidden(*prefix))
1536               IF_SAX2( endNamespace, currNamespaces[ii] -> prefix );
1537           }
1538       };
1539       break;
1540     case SAXOUTPUT_NONE: break;
1541     default:
1542       assert(!"eventElementEnd");
1543     };
1544   
1545   //remove our namespaces
1546   while (currNamespaces.number() > getFirstOwnNS())
1547     currNamespaces.freelast(FALSE);
1548
1549   // pop one history level
1550   history.freelast(FALSE);
1551   if (getLevel()) //experimental
1552     state = STATE_IN_ELEMENT;
1553   else
1554     state = STATE_OUTSIDE;
1555   
1556   return OK;
1557 }
1558
1559
1560 eFlag OutputterObj::eventAttributeStart(Sit S, const EQName& name)
1561 {
1562     Str fullName;
1563     name.getname(fullName);
1564     switch(state)
1565     {
1566     case STATE_IN_MARKUP:
1567         {
1568             state = STATE_IN_ATTRIBUTE;
1569             currAttName = name;
1570         }; break;
1571     case STATE_IN_ELEMENT:
1572         {
1573             Err1(S, E1_ATTRIBUTE_TOO_LATE, fullName);
1574             };
1575         break;
1576     case STATE_OUTSIDE:
1577         {
1578             Err1(S, E1_ATTRIBUTE_OUTSIDE, fullName);
1579             };
1580         break;
1581     default:
1582         assert(!"eventAttributeStart");
1583     };
1584     return OK;
1585 }
1586
1587
1588 eFlag OutputterObj::eventAttributeEnd(Sit S)
1589 {
1590     assert(state == STATE_IN_ATTRIBUTE);
1591
1592     int currAttNameNdx = currAtts.findNdx(currAttName);
1593     if (currAttNameNdx != -1)
1594         currAtts[currAttNameNdx] -> value = currData;
1595     else
1596         currAtts.appendConstruct(currAttName, currData);
1597     currData.empty();
1598     state = STATE_IN_MARKUP;
1599     return OK;
1600 }
1601
1602
1603 eFlag OutputterObj::eventCommentStart(Sit S)
1604 {
1605     switch(state)
1606     {
1607     case STATE_IN_MARKUP:
1608         E( reportStartTag(S, NONEMPTY_ELEMENT) );
1609         // no break
1610     case STATE_OUTSIDE:
1611     case STATE_IN_ELEMENT:
1612         {
1613             E( reportCurrData(S) );
1614             state = STATE_IN_COMMENT;
1615         }; break;
1616     default:
1617         assert(!"eventCommentStart");
1618     };
1619     return OK;
1620 }
1621
1622 eFlag OutputterObj::eventCommentEnd(Sit S)
1623 {
1624     assert(state == STATE_IN_COMMENT);
1625     if (physical && method == OUTPUT_UNKNOWN)
1626         E( front.appendConstruct(S, FM_COMMENT, currData, 
1627                                  ""/**theEmptyString*/, FALSE) )
1628     else
1629     {
1630         IF_PH2( outputComment, currData );
1631         IF_SAX2( comment, currData );
1632     }
1633     currData.empty();
1634     state = (getLevel() ? STATE_IN_ELEMENT : STATE_OUTSIDE); //experimental
1635     return OK;
1636 }
1637
1638 eFlag OutputterObj::eventPIStart(Sit S, const Str& name)
1639 {
1640     switch(state)
1641     {
1642     case STATE_IN_MARKUP:
1643         E( reportStartTag(S, NONEMPTY_ELEMENT) );
1644         // no break
1645     case STATE_IN_ELEMENT:
1646     case STATE_OUTSIDE:
1647         {
1648             E( reportCurrData(S) );
1649             state = STATE_IN_PI;
1650             currPIName = name;
1651         }; break;
1652     default:
1653         assert(!"eventPIStart");
1654     };
1655     return OK;
1656 }
1657
1658 eFlag OutputterObj::eventPIEnd(Sit S)
1659 {
1660     assert(state == STATE_IN_PI);
1661
1662     //check te PI data
1663     if ( strstr((char*)currData, "?>") )
1664       Err(S, E_INVALID_DATA_IN_PI);
1665
1666     if (physical && method == OUTPUT_UNKNOWN)
1667         E( front.appendConstruct(S, FM_PI, currPIName, currData, FALSE) )
1668     else
1669     {
1670         IF_PH3( outputPI, currPIName, currData );
1671         IF_SAX3( processingInstruction, currPIName, currData );
1672     }
1673     currData.empty();
1674     currPIName.empty();
1675     state = (getLevel() ? STATE_IN_ELEMENT : STATE_OUTSIDE);
1676     return OK;
1677 }
1678
1679 eFlag OutputterObj::eventNamespace(Sit S, const Str& prefix, const Str& uri,
1680                                    Bool hidden)
1681 {
1682     assert(state == STATE_IN_MARKUP);
1683     // GP: OK, not allocated
1684     Str search = uri;
1685     int existing_ndx = currNamespaces.findNum(prefix);
1686     const Str *existing_namespace = (
1687        existing_ndx == -1 ? NULL : &(currNamespaces[existing_ndx] -> uri));
1688     //explanation: if we found previously inserted namespace, we have
1689     //to add new definition to override it with one exception. Two
1690     //namespaces with the same prefix may be added (e.g. for
1691     //xsl:element insrtuction) in this case (existing_ndx >=
1692     //getFirstOwnNS()) we need override former definition in place.
1693
1694     /*
1695     if (uri == theXSLTNamespace)
1696       {
1697         //we need to reveal the xslt namespace if it is aliased
1698         Processor *proc = S.getProcessor();
1699         if (proc)
1700           { 
1701             Bool aliased;
1702             E( proc -> prefixIsAliasTarget(S, prefix, aliased) );
1703             hidden = hidden && ! aliased;
1704           }
1705       }
1706     */
1707
1708     if (!existing_namespace)
1709       {
1710         //if the namespace doesn't exist yet, we add it
1711         currNamespaces.appendConstruct(prefix, uri, hidden);
1712       }
1713     else
1714       {
1715         Bool exIsHidden = currNamespaces[existing_ndx]->hidden;
1716         // the following condition was asserted here for some reason
1717         if (!(*existing_namespace == uri))
1718           {
1719             if (existing_ndx >= getFirstOwnNS())
1720               {
1721                 currNamespaces[existing_ndx] -> uri = uri;
1722                 currNamespaces[existing_ndx] -> hidden = hidden;
1723               }
1724             else
1725               {
1726                 currNamespaces.appendConstruct(prefix, uri, hidden);
1727               }
1728           }
1729         //may be we found excluded one, but in current subtree
1730         //it sould be kept
1731         else if (exIsHidden && !hidden)
1732           {
1733             currNamespaces.appendConstruct(prefix, uri, hidden);
1734           }
1735       }
1736     return OK;
1737 }
1738
1739 eFlag OutputterObj::eventDisableEscapingForNext(Sit S)
1740 {
1741     if (method != OUTPUT_TEXT)
1742     {
1743         switch(state)
1744         {
1745         case STATE_IN_ATTRIBUTE:
1746         case STATE_IN_PI:
1747         case STATE_IN_COMMENT:
1748           Warn(S, W_DISABLE_OUTPUT_ESC); break;
1749         default:
1750           outputEscaping = FALSE;
1751         };
1752     }
1753     return OK;
1754 }
1755
1756 eFlag OutputterObj::eventData(Sit S, const Str& data, Bool hardCData /* = FALSE */)
1757 {
1758     if (physical && method == OUTPUT_UNKNOWN && state == STATE_OUTSIDE)
1759     {
1760         E( front.appendConstruct(S, FM_TEXT, data, 
1761                                  "" /**theEmptyString*/, !outputEscaping) );
1762         if (!isAllWhite((const char*) data))
1763         {
1764             E( physical -> setMethodByDefault(S, method = OUTPUT_XML) );
1765             E( reportXMLDeclIfMust(S) );
1766             E( reportFront(S) );
1767         }
1768         return OK;
1769     }
1770
1771     switch(state)
1772     {
1773     case STATE_IN_MARKUP:
1774         E( reportStartTag(S, NONEMPTY_ELEMENT) );
1775         // no break
1776     case STATE_IN_ELEMENT:
1777     case STATE_OUTSIDE:
1778         {   // this is a text node
1779             if (!(getFlags() & CDATA_SECTION) && !hardCData)
1780             {
1781                 int htmlScriptOrStyle = ( getFlags() & HTML_SCRIPT_OR_STYLE );
1782
1783                 if (physical)
1784                     E( physical -> outputText(S, data, !outputEscaping, 
1785                                               htmlScriptOrStyle) );
1786             }
1787             // reset outputEscaping to default after use
1788             // even if we just save a part of cdata section
1789             outputEscaping = TRUE;
1790             state = getLevel() ? STATE_IN_ELEMENT : STATE_OUTSIDE;
1791         }   // no break
1792     case STATE_IN_COMMENT:
1793     case STATE_IN_PI:
1794     case STATE_IN_ATTRIBUTE:
1795         {
1796             currData += data;
1797         }; break;
1798     default:
1799         assert(!"eventData()");
1800     }
1801     return OK;
1802 }
1803
1804 eFlag OutputterObj::eventCDataSection(Sit S, const Str& data)
1805 {
1806     switch(state)
1807     {
1808         case STATE_IN_MARKUP:
1809                 E( reportStartTag(S, NONEMPTY_ELEMENT) );
1810                 // no break
1811                 case STATE_OUTSIDE:
1812                 case STATE_IN_ELEMENT:
1813                 {
1814                     E( reportCurrData(S) );
1815                     E( eventData(S, data, /* hardCData = */ TRUE) );
1816                     E( reportCurrData(S, /* hardCData = */ TRUE) );
1817                 }; break;
1818                 default:
1819                     assert(!"eventCDataSection()");
1820     };    
1821     return OK;
1822 }
1823
1824 eFlag OutputterObj::eventEndOutput(Sit S)
1825 {
1826     assert(state == STATE_OUTSIDE);
1827     E( reportCurrData(S) );
1828     if (physical && method == OUTPUT_UNKNOWN)
1829     {
1830         E( physical -> setMethodByDefault(S, method = OUTPUT_XML) );
1831         E( reportXMLDeclIfMust(S) );
1832         E( reportFront(S) );
1833     }
1834     IF_PH1( outputDone );
1835     IF_SAX1( endDocument );
1836     state = STATE_DONE;
1837
1838     //pop fake history item
1839     history.freelast(FALSE);
1840
1841     return OK;
1842 }
1843
1844 eFlag OutputterObj::eventTrailingNewline(Sit S)
1845 {
1846   assert(state == STATE_OUTSIDE);
1847   if (physical)
1848     {
1849       E(physical -> outputTrailingNewline(S))
1850     }
1851   return OK;
1852 }
1853
1854 eFlag OutputterObj::reportCurrData(Sit S, Bool hardCData /* = FALSE */)
1855 {
1856     if (currData.isEmpty())
1857         return OK;
1858     switch(state)
1859     {
1860     case STATE_OUTSIDE:
1861     case STATE_IN_MARKUP:
1862     case STATE_IN_ELEMENT:
1863         {
1864             if (getFlags() & CDATA_SECTION || hardCData)
1865             {
1866                 // we might now call the SAX startCData handler (if there was one)
1867                 IF_SAX3( characters, currData, currData.length() );
1868                 IF_PH2( outputCDataSection, currData );
1869                 // we might call the SAX endCData handler
1870             }
1871             else
1872             {
1873                 // the text had been output to physical in parts
1874                 IF_SAX3( characters, currData, currData.length());
1875             }
1876         }; break;
1877     default:
1878         assert(!"reportCurrData()");
1879     }
1880     currData.empty();
1881     return OK;
1882 }
1883
1884
1885 /*
1886     GP: if this function is modified, care must be taken about the guarded
1887     pointers
1888 */
1889
1890 eFlag OutputterObj::reportStartTag(Sit S, Bool isEmpty)
1891 {
1892   assert(!(physical && mySAXOutputType == SAXOUTPUT_INT_PHYSICAL));
1893   // FIXME: couldn't improve reportStartTag from GP point of view?
1894   if (state == STATE_OUTSIDE || currElement.isEmpty())
1895     return OK;
1896   
1897   int doThrowMeta = method == OUTPUT_HTML && noHeadYet && 
1898     currElement.getUri().isEmpty() && currElement.getLocal().eqNoCase("head");
1899   int i;
1900   //get attr aliased names
1901   StrStrList attAliased;
1902   // the temporary string is a workaround for VisualAge on AIX
1903   //Str temp;
1904
1905
1906   Str physName;
1907   Bool intPhys = mySAXOutputType == SAXOUTPUT_INT_PHYSICAL;
1908
1909   if (physical || (mySAXHandler && (mySAXOutputType == SAXOUTPUT_AS_PHYSICAL)))
1910     {
1911       //if (S.getProcessor())
1912       //temp = S.getProcessor() -> getAliasedName(currElement, 
1913       //                                          currNamespaces, intPhys);
1914       //else
1915       //currElement.getname(temp);
1916       currElement.getname(physName);
1917       
1918       for (i = 0; i < currAtts.number(); i++)
1919         {
1920           Str aliname;
1921           //if (S.getProcessor())
1922           //  aliname = S.getProcessor() -> getAliasedName(currAtts[i] -> key,
1923           //                                     currNamespaces, 
1924           //                                     intPhys);
1925           //else
1926           //  currAtts[i] -> key.getname(aliname); 
1927           currAtts[i] -> key.getname(aliname);
1928           attAliased.appendConstruct(aliname, currAtts[i] -> value);
1929         }
1930       
1931       if (physical) {
1932         if ( delayedDTD )
1933           {
1934             const Str& 
1935               DTSystem = outDef -> getValueStr(XSLA_DOCTYPE_SYSTEM),
1936               DTPublic = outDef -> getValueStr(XSLA_DOCTYPE_PUBLIC);        
1937             E( physical -> outputDTD( S, physName, DTPublic, DTSystem) );
1938             delayedDTD = FALSE;
1939           }
1940
1941         E( physical -> outputElementStart(S, physName, currNamespaces, 
1942                                           getFirstOwnNS(), attAliased, 
1943                                           isEmpty && !doThrowMeta) );
1944       }
1945     }
1946   
1947   if (mySAXHandler)
1948     {
1949       const char** attsTable;
1950       GPA( ConstCharP ) attsArr = attsTable = new const char*[(currAtts.number() * 2) + 1];
1951       // GP: the allocations are OK: no exit routes
1952       
1953       PList<Str*> stringsForDeletion;
1954       attsTable[currAtts.number() * 2] = NULL;
1955       
1956       switch(mySAXOutputType)
1957         {
1958         case SAXOUTPUT_COPY_TREE:
1959         case SAXOUTPUT_INT_PHYSICAL:
1960           {
1961             // report only the new namespaces
1962             Str* prefix;
1963             for (i = getFirstOwnNS(); i < currNamespaces.number(); i++)
1964               {
1965                 prefix = &(currNamespaces[i] -> prefix);
1966                 if (intPhys)
1967                   {
1968                     IF_INT_SAX4( startNamespace2, currNamespaces[i] -> prefix, 
1969                                  currNamespaces[i] -> uri, 
1970                                  currNamespaces.isHidden(*prefix) );
1971                   }
1972                 else
1973                   IF_SAX3( startNamespace, currNamespaces[i] -> prefix, 
1974                            currNamespaces[i] -> uri );
1975               }
1976             // create the attribute table
1977             for (i = 0; i < currAtts.number(); i++)
1978               {
1979                 Str *expatName = nameForSAX(S, currAtts[i] -> key);
1980                 stringsForDeletion.append(expatName);
1981                 attsTable[i * 2] = *expatName;
1982                 attsTable[(i * 2) + 1] = currAtts[i] -> value;
1983               };
1984             Str* theSAXname = nameForSAX(S, currElement);
1985             stringsForDeletion.append(theSAXname);
1986             IF_SAX3(startElement, *theSAXname, attsTable);
1987             stringsForDeletion.freeall(FALSE);
1988           };
1989           break;
1990         case SAXOUTPUT_AS_PHYSICAL:
1991           // report only the new not hidden namespaces
1992           Str* prefix;
1993           for (i = getFirstOwnNS(); i < currNamespaces.number(); i++)
1994             {
1995               prefix = &(currNamespaces[i] -> prefix);
1996               if (! currNamespaces.isHidden(*prefix)) 
1997                 IF_SAX3( startNamespace, currNamespaces[i] -> prefix, 
1998                          currNamespaces[i] -> uri );
1999             }
2000           // create the attribute table
2001           for (i = 0; i < currAtts.number(); i++)
2002             {
2003               attsTable[i * 2] = (char *)attAliased[i] -> key;
2004               attsTable[(i * 2) + 1] = (char *)attAliased[i] -> value;
2005             };
2006           
2007           IF_SAX3(startElement, physName, attsTable);
2008           break;
2009         case SAXOUTPUT_NONE:
2010           assert(!"saxoutput == NONE");
2011         default:
2012           assert(!"eventElementEnd");
2013         };
2014       //attsTable.delArray();   
2015     }
2016   
2017   if (physical || (mySAXHandler && mySAXOutputType == SAXOUTPUT_AS_PHYSICAL))
2018     {
2019       attAliased.freeall(FALSE);
2020     }
2021   
2022   eFlag result = OK;
2023   if (doThrowMeta) {
2024     result = throwInMeta(S);
2025     //__PH__ close head if needed
2026     if (isEmpty) {
2027       if (physical) {
2028         E( physical ->outputElementEnd(S, physName, FALSE) );
2029       };
2030     };
2031   }
2032   
2033   currElement.empty();
2034   currAtts.freeall(FALSE);
2035   currData.empty();
2036   return result;
2037 }
2038
2039 void OutputterObj::pushLevel(const EQName& name)
2040 {
2041     currElement = name;
2042     GP( OutputHistoryItem ) newItem = new OutputHistoryItem;
2043     if (history.number())
2044       {
2045         *newItem = *(history.last());
2046         //next line is speed optimalization (very few)
2047         (*newItem).useDocument = (*(history.last())).document;
2048       }
2049     else
2050       {
2051         (*newItem).flags = 0;
2052         (*newItem).useDocument = NULL;
2053       }
2054     //definition is used for multiple document output
2055     (*newItem).document = NULL; //in any case
2056     if (physical)
2057     {
2058         if (outDef -> askEQNameList(XSLA_CDATA_SECT_ELEMS, name))
2059             (*newItem).flags |= CDATA_SECTION;
2060         else
2061             (*newItem).flags &= ~CDATA_SECTION;
2062
2063         //style and script for html
2064         if ( (method == OUTPUT_HTML && name.getUri() == "") &&
2065              isHTMLNoEscapeTag(name.getLocal()) )
2066           {
2067             (*newItem).flags |= HTML_SCRIPT_OR_STYLE;
2068           }
2069         else
2070           {
2071             (*newItem).flags &= ~HTML_SCRIPT_OR_STYLE;
2072           }
2073     };
2074     (*newItem).firstOwnNS = currNamespaces.number();
2075     history.append(newItem.keep());
2076     state = STATE_IN_MARKUP;
2077 }
2078
2079 void OutputterObj::report(Sit S, MsgType type, MsgCode code, const Str& arg1, const Str& arg2)
2080 {
2081     S.message(type, code, arg1, arg2);
2082 }
2083
2084 eFlag OutputterObj::setDocumentForLevel(Sit S, OutputDocument *doc)
2085 {
2086   if (history.number())
2087     (*(history.last())).document = doc;
2088   return OK;
2089 }
2090
2091 OutputDocument* OutputterObj::getDocumentForLevel(Bool forElement)
2092 {
2093   if (history.number())
2094     return forElement ? (*(history.last())).useDocument :
2095     (*(history.last())).document;
2096   else 
2097     return NULL;
2098 }