Annotation of parser3/src/classes/xdoc.C, revision 1.14
1.1 parser 1: /** @file
1.2 parser 2: Parser: @b xdoc parser class.
1.1 parser 3:
4: Copyright (c) 2001 ArtLebedev Group (http://www.artlebedev.com)
5: Author: Alexander Petrosyan <paf@design.ru> (http://design.ru/paf)
6:
1.14 ! parser 7: $Id: xdoc.C,v 1.13 2001/10/09 14:25:30 parser Exp $
1.1 parser 8: */
9: #include "classes.h"
10: #ifdef XML
11:
12: #include "pa_request.h"
13: #include "pa_vxdoc.h"
14: #include "pa_xslt_stylesheet_manager.h"
15: #include "pa_stylesheet_connection.h"
16: #include "pa_vfile.h"
17: #include "xnode.h"
18:
19: #include <strstream>
20: #include <Include/PlatformDefinitions.hpp>
21: #include <util/PlatformUtils.hpp>
1.5 parser 22: #include <util/TransENameMap.hpp>
1.13 parser 23: #include "XalanTransformer2.hpp"
1.1 parser 24: #include <XalanTransformer/XalanParsedSource.hpp>
1.9 parser 25: # include <XalanTransformer/XalanDefaultParsedSource.hpp>
26: # include <XalanSourceTree/XalanSourceTreeDocument.hpp>
1.10 parser 27: # include <XalanSourceTree/XalanSourceTreeContentHandler.hpp>
28: # include <sax2/XMLReaderFactory.hpp>
1.1 parser 29: #include <XMLSupport/FormatterToXML.hpp>
30: #include <XMLSupport/FormatterToHTML.hpp>
31: #include <XMLSupport/FormatterToText.hpp>
32: #include <XMLSupport/FormatterTreeWalker.hpp>
33: #include <PlatformSupport/XalanFileOutputStream.hpp>
34: #include <PlatformSupport/XalanOutputStreamPrintWriter.hpp>
35: #include <PlatformSupport/DOMStringPrintWriter.hpp>
1.2 parser 36: #include <XalanDOM/XalanElement.hpp>
37: #include <XalanDOM/XalanNodeList.hpp>
1.1 parser 38:
39: // defines
40:
41: #define XDOC_CLASS_NAME "xdoc"
42:
43: #define XDOC_OUTPUT_METHOD_OPTION_NAME "method"
44: #define XDOC_OUTPUT_METHOD_OPTION_VALUE_XML "xml"
45: #define XDOC_OUTPUT_METHOD_OPTION_VALUE_HTML "html"
46: #define XDOC_OUTPUT_METHOD_OPTION_VALUE_TEXT "text"
47:
48: #define XDOC_OUTPUT_ENCODING_OPTION_NAME "encoding"
49:
50: #define XDOC_OUTPUT_DEFAULT_INDENT 4
51:
52: // class
53:
54: class MXdoc : public MXnode {
55: public: // VStateless_class
56: Value *create_new_value(Pool& pool) { return new(pool) VXdoc(pool); }
57:
58: public:
59: MXdoc(Pool& pool);
60:
61: public: // Methoded
62: bool used_directly() { return true; }
1.5 parser 63: void configure_admin(Request& r);
1.1 parser 64: };
65:
66: // methods
67:
68: class ParserStringXalanOutputStream: public XalanOutputStream {
69: public:
70:
71: explicit ParserStringXalanOutputStream(String& astring) : fstring(astring) {}
72:
73: protected: // XalanOutputStream
74:
75: virtual void writeData(const char *theBuffer, unsigned long theBufferLength) {
76: char *copy=(char *)fstring.malloc((size_t)theBufferLength);
77: memcpy(copy, theBuffer, (size_t)theBufferLength);
1.2 parser 78: fstring.APPEND_CLEAN(copy, (size_t)theBufferLength, "xdoc", 0);
1.1 parser 79: }
80:
81: virtual void doFlush() {}
82:
83: private:
84:
85: String& fstring;
86:
87: };
88:
1.9 parser 89: class XalanSourceTreeParserLiaison2: public XalanSourceTreeParserLiaison {
90: public:
91: XalanSourceTreeParserLiaison2(XalanSourceTreeDOMSupport& theSupport) : XalanSourceTreeParserLiaison(theSupport),
92: ferror_handler(new HandlerBase) {
93: }
94:
1.10 parser 95: XalanDocument*
96: parseXMLStream2(
97: const InputSource& inputSource) {
98: XalanSourceTreeContentHandler theContentHandler(createXalanSourceTreeDocument());
99: XalanAutoPtr<SAX2XMLReader> theReader(XMLReaderFactory::createXMLReader());
100: theReader->setContentHandler(&theContentHandler);
101: theReader->setDTDHandler(&theContentHandler);
102: theReader->setErrorHandler(ferror_handler); // disable stderr output
103: theReader->setLexicalHandler(&theContentHandler);
104: EntityResolver* const theResolver = getEntityResolver();
105: if (theResolver != 0) {
106: theReader->setEntityResolver(theResolver);
107: }
108: theReader->parse(inputSource);
109: return theContentHandler.getDocument();
110: }
111:
1.9 parser 112: ~XalanSourceTreeParserLiaison2() {
1.11 parser 113: delete ferror_handler;
1.9 parser 114: }
115: private:
116: ErrorHandler *ferror_handler;
117: };
118:
119: class XalanDefaultParsedSource2 : public XalanParsedSource
120: {
121: public:
122:
123: XalanDefaultParsedSource2(const XSLTInputSource& theInputSource);
124:
125: virtual XalanDocument*
126: getDocument() const;
127:
128: virtual XalanParsedSourceHelper*
129: createHelper() const;
130:
131: private:
132:
133: XalanSourceTreeDOMSupport m_domSupport;
134:
1.10 parser 135: XalanSourceTreeParserLiaison2 m_parserLiaison2;
1.9 parser 136:
137: XalanSourceTreeDocument* const m_parsedSource;
138: };
139:
140: XalanDefaultParsedSource2::XalanDefaultParsedSource2(const XSLTInputSource& theInputSource):
141: XalanParsedSource(),
142: m_domSupport(),
1.10 parser 143: m_parserLiaison2(m_domSupport),
144: m_parsedSource(m_parserLiaison2.mapDocument(m_parserLiaison2.parseXMLStream2(theInputSource)))
1.9 parser 145: {
146: assert(m_parsedSource != 0);
147:
1.10 parser 148: m_domSupport.setParserLiaison(&m_parserLiaison2);
1.9 parser 149: }
150:
151:
152:
153: XalanDocument*
154: XalanDefaultParsedSource2::getDocument() const
155: {
156: return m_parsedSource;
157: }
158:
159:
160:
161: XalanParsedSourceHelper*
162: XalanDefaultParsedSource2::createHelper() const
163: {
164: return new XalanDefaultParsedSourceHelper(m_domSupport);
165: }
166:
167:
168:
169:
1.1 parser 170: static void create_optioned_listener(
171: const char *& content_type, const char *& charset, FormatterListener *& listener,
172: Pool& pool,
173: const String& method_name, MethodParams *params, int index, Writer& writer) {
174: // default encoding from pool
175: const String *scharset=&pool.get_charset();
176: const String *method=0;
177: XalanDOMString xalan_encoding;
178:
179: if(params->size()>index) {
180: Value& voptions=params->as_no_junction(index, "options must not be code");
181: if(voptions.is_defined()) {
182: if(Hash *options=voptions.get_hash()) {
183: // $.method[xml|html|text]
184: if(Value *vmethod=static_cast<Value *>(options->get(*new(pool)
185: String(pool, XDOC_OUTPUT_METHOD_OPTION_NAME))))
186: method=&vmethod->as_string();
187:
188: // $.encoding[windows-1251|...]
189: if(Value *vencoding=static_cast<Value *>(options->get(*new(pool)
190: String(pool, XDOC_OUTPUT_ENCODING_OPTION_NAME)))) {
191: scharset=&vencoding->as_string();
192: }
193: } else
194: PTHROW(0, 0,
195: &method_name,
196: "options must be hash");
197: }
198: }
199:
200: xalan_encoding.append(charset=scharset->cstr());
201: if(!method/*default='xml'*/ || *method == XDOC_OUTPUT_METHOD_OPTION_VALUE_XML) {
202: content_type="text/xml";
203: listener=new FormatterToXML(writer,
204: XalanDOMString(), // version
205: true, // doIndent
206: XDOC_OUTPUT_DEFAULT_INDENT, // indent
207: xalan_encoding // encoding
208: );
209: } else if(*method == XDOC_OUTPUT_METHOD_OPTION_VALUE_HTML) {
210: content_type="text/html";
211: listener=new FormatterToHTML(writer,
212: xalan_encoding, // encoding
213: XalanDOMString(), // mediaType
214: XalanDOMString(), // doctypeSystem; String to be printed at the top of the document
215: XalanDOMString(), // doctypePublic
216: true, // doIndent
217: XDOC_OUTPUT_DEFAULT_INDENT // indent
218: );
219: } else if(*method == XDOC_OUTPUT_METHOD_OPTION_VALUE_TEXT) {
220: content_type="text/plain";
221: listener=new FormatterToText(writer,
222: xalan_encoding // encoding
223: );
224: } else
225: PTHROW(0, 0,
226: method,
227: XDOC_OUTPUT_METHOD_OPTION_NAME " option is invalid; valid methods are: "
228: "'" XDOC_OUTPUT_METHOD_OPTION_VALUE_XML "', "
229: "'" XDOC_OUTPUT_METHOD_OPTION_VALUE_HTML "', "
230: "'" XDOC_OUTPUT_METHOD_OPTION_VALUE_TEXT "'");
231:
232: // never reached
233: }
234:
235: static void _save(Request& r, const String& method_name, MethodParams *params) {
236: Pool& pool=r.pool();
237: VXnode& vnode=*static_cast<VXnode *>(r.self);
238:
239: // filespec
240: const String& file_name=params->as_string(0, "file name must not be code");
241: const char *filespec=r.absolute(file_name).cstr(String::UL_FILE_SPEC);
242:
243: // node
244: XalanNode& node=vnode.get_node(pool, &method_name);
245:
246: try {
247: XalanFileOutputStream stream(XalanDOMString(filespec, strlen(filespec)));
248: XalanOutputStreamPrintWriter writer(stream);
249: const char *content_type, *charset;
250: FormatterListener *formatterListener;
251: create_optioned_listener(content_type, charset, formatterListener,
252: pool, method_name, params, 1, writer);
253: FormatterTreeWalker treeWalker(*formatterListener);
254: treeWalker.traverse(&node); // Walk that node and produce the XML...
255: } catch(const XSLException& e) {
1.14 ! parser 256: pool.exception()._throw(pool, &method_name, e);
1.1 parser 257: }
258: }
259:
260: static void _string(Request& r, const String& method_name, MethodParams *params) {
261: Pool& pool=r.pool();
262: VXnode& vnode=*static_cast<VXnode *>(r.self);
263:
264: // node
265: XalanNode& node=vnode.get_node(pool, &method_name);
266:
267: try {
268: String parserString=*new(pool) String(pool);
269: ParserStringXalanOutputStream stream(parserString);
270: XalanOutputStreamPrintWriter writer(stream);
271: const char *content_type, *charset;
272: FormatterListener *formatterListener;
273: create_optioned_listener(content_type, charset, formatterListener,
274: pool, method_name, params, 0, writer);
275: FormatterTreeWalker treeWalker(*formatterListener);
276: treeWalker.traverse(&node); // Walk that node and produce the XML...
277:
278: // write out result
279: r.write_no_lang(parserString);
280: } catch(const XSLException& e) {
1.14 ! parser 281: pool.exception()._throw(pool, &method_name, e);
1.1 parser 282: }
283: }
284:
285:
286: static void _file(Request& r, const String& method_name, MethodParams *params) {
287: Pool& pool=r.pool();
288: VXnode& vnode=*static_cast<VXnode *>(r.self);
289:
290: // node
291: XalanNode& node=vnode.get_node(pool, &method_name);
292:
293: try {
294: String& parserString=*new(pool) String(pool);
295: ParserStringXalanOutputStream stream(parserString);
296: XalanOutputStreamPrintWriter writer(stream);
297: const char *content_type, *charset;
298: FormatterListener *formatterListener;
299: create_optioned_listener(content_type, charset, formatterListener,
300: pool, method_name, params, 0, writer);
301: FormatterTreeWalker treeWalker(*formatterListener);
302: treeWalker.traverse(&node); // Walk that node and produce the XML...
303:
304: // write out result
305: VFile& vfile=*new(pool) VFile(pool);
306: const char *cstr=parserString.cstr();
307: String *scontent_type=new(pool) String(pool, content_type);
308: Value *vcontent_type;
309: if(charset) {
310: VHash *vhcontent_type=new(pool) VHash(pool);
311: vhcontent_type->hash().put(*value_name, new(pool) VString(*scontent_type));
312: String *scharset=new(pool) String(pool, charset);
313: vhcontent_type->hash().put(*new(pool) String(pool, "charset"), new(pool) VString(*scharset));
314: vcontent_type=vhcontent_type;
315: } else
316: vcontent_type=new(pool) VString(*scontent_type);
317: vfile.set(false/*tainted*/, cstr, strlen(cstr), 0/*file_name*/, vcontent_type);
318: r.write_no_lang(vfile);
319: } catch(const XSLException& e) {
1.14 ! parser 320: pool.exception()._throw(pool, &method_name, e);
1.1 parser 321: }
322: }
323:
324: static void _set(Request& r, const String& method_name, MethodParams *params) {
325: Pool& pool=r.pool();
1.5 parser 326: VXdoc& vdoc=*static_cast<VXdoc *>(r.self);
1.1 parser 327:
328: Value& vxml=params->as_junction(0, "xml must be code");
329: Temp_lang temp_lang(r, String::UL_XML);
330: const String& xml=r.process(vxml).as_string();
331:
332: std::istrstream stream(xml.cstr());
333: const XalanParsedSource* parsedSource;
1.9 parser 334:
1.11 parser 335: try {
336: parsedSource = new XalanDefaultParsedSource2(&stream);
1.9 parser 337: }
1.10 parser 338: catch (XSLException& e) {
1.14 ! parser 339: pool.exception()._throw(pool, &method_name, e);
1.10 parser 340: }
341: catch (SAXParseException& e) {
1.14 ! parser 342: pool.exception()._throw(pool, &method_name, e);
1.9 parser 343: }
1.10 parser 344: catch (SAXException& e) {
1.14 ! parser 345: pool.exception()._throw(pool, &method_name, e);
1.9 parser 346: }
1.10 parser 347: catch (XMLException& e) {
1.14 ! parser 348: pool.exception()._throw(pool, &method_name, e);
1.9 parser 349: }
1.10 parser 350: catch(const XalanDOMException& e) {
1.14 ! parser 351: pool.exception()._throw(pool, &method_name, e);
1.9 parser 352: }
1.1 parser 353:
354: // replace any previous parsed source
1.5 parser 355: vdoc.set_parsed_source(*parsedSource);
1.1 parser 356: }
357:
358: static void _load(Request& r, const String& method_name, MethodParams *params) {
359: Pool& pool=r.pool();
1.5 parser 360: VXdoc& vdoc=*static_cast<VXdoc *>(r.self);
1.1 parser 361:
362: // filespec
363: const String& file_name=params->as_string(0, "file name must not be code");
364: const char *filespec=r.absolute(file_name).cstr(String::UL_FILE_SPEC);
365:
366: const XalanParsedSource* parsedSource;
1.12 parser 367: try {
368: parsedSource = new XalanDefaultParsedSource2(filespec);
369: }
370: catch (XSLException& e) {
1.14 ! parser 371: pool.exception()._throw(pool, &method_name, e);
1.12 parser 372: }
373: catch (SAXParseException& e) {
1.14 ! parser 374: pool.exception()._throw(pool, &method_name, e);
1.12 parser 375: }
376: catch (SAXException& e) {
1.14 ! parser 377: pool.exception()._throw(pool, &method_name, e);
1.12 parser 378: }
379: catch (XMLException& e) {
1.14 ! parser 380: pool.exception()._throw(pool, &method_name, e);
1.12 parser 381: }
382: catch(const XalanDOMException& e) {
1.14 ! parser 383: pool.exception()._throw(pool, &method_name, e);
1.12 parser 384: }
1.1 parser 385:
386: // replace any previous parsed source
1.5 parser 387: vdoc.set_parsed_source(*parsedSource);
1.1 parser 388: }
389:
390: static void add_xslt_param(const Hash::Key& aattribute, Hash::Val *ameaning,
391: void *info) {
1.13 parser 392: XalanTransformer2& transformer=*static_cast<XalanTransformer2 *>(info);
1.1 parser 393: const char *attribute_cstr=aattribute.cstr();
394: const char *meaning_cstr=static_cast<Value *>(ameaning)->as_string().cstr();
395:
396: transformer.setStylesheetParam(
397: XalanDOMString(attribute_cstr),
398: XalanDOMString(meaning_cstr));
399: }
1.8 parser 400: static void _transform(Request& r, const String& method_name, MethodParams *params) {
1.1 parser 401: Pool& pool=r.pool();
1.5 parser 402: VXdoc& vdoc=*static_cast<VXdoc *>(r.self);
1.1 parser 403:
404: // params
405: if(params->size()>1) {
406: Value& vparams=params->as_no_junction(1, "transform parameters parameter must not be code");
407: if(vparams.is_defined())
408: if(Hash *params=vparams.get_hash())
1.5 parser 409: params->for_each(add_xslt_param, &vdoc.transformer());
1.1 parser 410: else
411: PTHROW(0, 0,
412: &method_name,
413: "transform parameters parameter must be hash");
414: }
415:
416: // source
1.5 parser 417: const XalanParsedSource &parsed_source=vdoc.get_parsed_source(pool, &method_name);
1.1 parser 418:
419: // stylesheet
420: const String& stylesheet_file_name=params->as_string(0, "file name must not be code");
421: const String& stylesheet_filespec=r.absolute(stylesheet_file_name);
422: //_asm int 3;
423: Stylesheet_connection& connection=XSLT_stylesheet_manager->get_connection(stylesheet_filespec);
424:
425: // target
1.5 parser 426: XalanDocument* target=vdoc.parser_liaison().createDocument();
1.1 parser 427:
428: // transform
1.13 parser 429: try {
430: vdoc.transformer().transform2(
431: parsed_source,
432: &connection.stylesheet(true/*nocache*/),
433: target);
434: }
435: catch (XSLException& e) {
436: connection.close();
1.14 ! parser 437: pool.exception()._throw(pool, &stylesheet_file_name, e);
1.13 parser 438: }
439: catch (SAXParseException& e) {
440: connection.close();
1.14 ! parser 441: pool.exception()._throw(pool, &stylesheet_file_name, e);
1.13 parser 442: }
443: catch (SAXException& e) {
444: connection.close();
1.14 ! parser 445: pool.exception()._throw(pool, &stylesheet_file_name, e);
1.13 parser 446: }
447: catch (XMLException& e) {
448: connection.close();
1.14 ! parser 449: pool.exception()._throw(pool, &stylesheet_file_name, e);
1.13 parser 450: }
451: catch(const XalanDOMException& e) {
452: connection.close();
1.14 ! parser 453: pool.exception()._throw(pool, &stylesheet_file_name, e);
1.13 parser 454: }
1.1 parser 455:
456: // write out result
457: VXdoc& result=*new(pool) VXdoc(pool);
458: result.set_document(*target);
459: r.write_no_lang(result);
460: }
461:
1.2 parser 462: static void _getElementById(Request& r, const String& method_name, MethodParams *params) {
463: Pool& pool=r.pool();
464: VXdoc& vdoc=*static_cast<VXdoc *>(r.self);
465:
466: // elementId
467: const char *elementId=params->as_string(0, "elementID must not be code").cstr(String::UL_AS_IS);
468:
469: if(XalanElement *element=
470: vdoc.get_document(pool, &method_name).getElementById(XalanDOMString(elementId))) {
471: // write out result
472: VXnode& result=*new(pool) VXnode(pool, element);
473: r.write_no_lang(result);
474: }
475: }
1.4 parser 476: /*
1.2 parser 477: static void _getElementsByTagName(Request& r, const String& method_name, MethodParams *params) {
478: Pool& pool=r.pool();
479: VXdoc& vdoc=*static_cast<VXdoc *>(r.self);
480:
481: // tagname
482: const char *tagname=params->as_string(0, "tagname must not be code").cstr(String::UL_AS_IS);
483:
484: VHash& result=*new(pool) VHash(pool);
485: if(const XalanNodeList *nodes=
486: vdoc.get_document(pool, &method_name).getElementsByTagName(XalanDOMString(tagname))) {
487: for(int i=0; i<nodes->getLength(); i++) {
488: String& skey=*new(pool) String(pool);
489: {
490: char *buf=(char *)pool.malloc(MAX_NUMBER);
491: snprintf(buf, MAX_NUMBER, "%d", i);
492: skey << buf;
493: }
494:
495: result.hash().put(skey, new(pool) VXnode(pool, nodes->item(i)));
496: }
497: }
498:
499: // write out result
500: r.write_no_lang(result);
501: }
502:
503: static void _getElementsByTagNameNS(Request& r, const String& method_name, MethodParams *params) {
504: Pool& pool=r.pool();
505: VXdoc& vdoc=*static_cast<VXdoc *>(r.self);
506:
507: // namespaceURI;localName
508: const char *namespaceURI=params->as_string(0, "namespaceURI must not be code").cstr(String::UL_AS_IS);
509: const char *localName=params->as_string(0, "localName must not be code").cstr(String::UL_AS_IS);
510:
511: VHash& result=*new(pool) VHash(pool);
512: if(const XalanNodeList *nodes=
513: vdoc.get_document(pool, &method_name).getElementsByTagNameNS(
514: XalanDOMString(namespaceURI), XalanDOMString(localName))) {
515: for(int i=0; i<nodes->getLength(); i++) {
516: String& skey=*new(pool) String(pool);
517: {
518: char *buf=(char *)pool.malloc(MAX_NUMBER);
519: snprintf(buf, MAX_NUMBER, "%d", i);
520: skey << buf;
521: }
522:
523: result.hash().put(skey, new(pool) VXnode(pool, nodes->item(i)));
524: }
525: }
526:
527: // write out result
528: r.write_no_lang(result);
529: }
1.4 parser 530: */
1.1 parser 531: // constructor
532:
533: MXdoc::MXdoc(Pool& apool) : MXnode(apool) {
534: set_name(*NEW String(pool(), XDOC_CLASS_NAME));
535:
1.2 parser 536: // ^xdoc.save[some.xml]
537: // ^xdoc.save[some.xml;options hash]
1.1 parser 538: add_native_method("save", Method::CT_DYNAMIC, _save, 1, 2);
539:
1.2 parser 540: // ^xdoc.string[] <doc/>
541: // ^xdoc.string[options hash] <doc/>
1.1 parser 542: add_native_method("string", Method::CT_DYNAMIC, _string, 0, 1);
543:
1.2 parser 544: // ^xdoc.file[] file with "<doc/>"
545: // ^xdoc.file[options hash] file with "<doc/>"
1.1 parser 546: add_native_method("file", Method::CT_DYNAMIC, _file, 0, 1);
547:
1.2 parser 548: // ^xdoc::set[<some>xml</some>]
1.1 parser 549: add_native_method("set", Method::CT_DYNAMIC, _set, 1, 1);
550:
1.2 parser 551: // ^xdoc::load[some.xml]
1.1 parser 552: add_native_method("load", Method::CT_DYNAMIC, _load, 1, 1);
553:
1.8 parser 554: // ^xdoc.transform[stylesheet file_name]
555: // ^xdoc.transform[stylesheet file_name;params hash]
556: add_native_method("transform", Method::CT_DYNAMIC, _transform, 1, 2);
1.2 parser 557:
558: // ^xdoc.getElementById[elementId]
559: add_native_method("getElementById", Method::CT_DYNAMIC, _getElementById, 1, 1);
1.4 parser 560: /*
1.3 parser 561: // ^xdoc.getElementsByTagName[tagname]
1.2 parser 562: add_native_method("getElementsByTagName", Method::CT_DYNAMIC, _getElementsByTagName, 1, 1);
563:
564: // ^xdoc.getElementsByTagNameNS[namespaceURI;localName] = array of nodes
565: add_native_method("getElementsByTagNameNS", Method::CT_DYNAMIC, _getElementsByTagNameNS, 2, 2);
1.4 parser 566: */
1.1 parser 567: }
1.5 parser 568:
569: void MXdoc::configure_admin(Request& r) {
570: }
571:
1.1 parser 572: // global variable
573:
574: Methoded *Xdoc_class;
575:
576: // creator
577:
578: #endif
579:
580: Methoded *MXdoc_create(Pool& pool) {
581: return
582: #ifdef XML
583: Xdoc_class=new(pool) MXdoc(pool);
584: #else
585: 0
586: #endif
587: ;
588: }
E-mail: