1 // Copyright (c) 1994 James Clark
2 // See the file COPYING for copying permission.
3
4 #ifdef __GNUG__
5 #pragma implementation
6 #endif
7 #include "splib.h"
8 #include "Attribute.h"
9 #include "MessageArg.h"
10 #include "macros.h"
11 #include "ParserMessages.h"
12 #include "StringVectorMessageArg.h"
13 #include "Syntax.h"
14 #include "Entity.h"
15 #include "Notation.h"
16
17 #ifdef SP_NAMESPACE
18 namespace SP_NAMESPACE {
19 #endif
20
DeclaredValue()21 DeclaredValue::DeclaredValue()
22 {
23 }
24
~DeclaredValue()25 DeclaredValue::~DeclaredValue()
26 {
27 }
28
makeValueFromToken(Text & text,AttributeContext & context,const StringC & name,unsigned & specLength) const29 AttributeValue *DeclaredValue::makeValueFromToken(Text &text,
30 AttributeContext &context,
31 const StringC &name,
32 unsigned &specLength) const
33 {
34 return makeValue(text, context, name, specLength);
35 }
36
makeSemantics(const TokenizedAttributeValue &,AttributeContext &,const StringC &,unsigned &,unsigned &) const37 AttributeSemantics *DeclaredValue::makeSemantics(const TokenizedAttributeValue &,
38 AttributeContext &,
39 const StringC &,
40 unsigned &,
41 unsigned &) const
42 {
43 return 0;
44 }
45
containsToken(const StringC &) const46 Boolean DeclaredValue::containsToken(const StringC &) const
47 {
48 return 0;
49 }
50
isNotation() const51 Boolean DeclaredValue::isNotation() const
52 {
53 return 0;
54 }
55
isEntity() const56 Boolean DeclaredValue::isEntity() const
57 {
58 return 0;
59 }
60
isId() const61 Boolean DeclaredValue::isId() const
62 {
63 return 0;
64 }
65
isIdref() const66 Boolean DeclaredValue::isIdref() const
67 {
68 return 0;
69 }
70
getTokens() const71 const Vector<StringC> *DeclaredValue::getTokens() const
72 {
73 return 0;
74 }
75
76
CdataDeclaredValue()77 CdataDeclaredValue::CdataDeclaredValue()
78 {
79 }
80
tokenized() const81 Boolean CdataDeclaredValue::tokenized() const
82 {
83 return 0;
84 }
85
makeValue(Text & text,AttributeContext & context,const StringC &,unsigned & specLength) const86 AttributeValue *CdataDeclaredValue::makeValue(Text &text, AttributeContext &context,
87 const StringC &,
88 unsigned &specLength) const
89 {
90 const Syntax &syntax = context.attributeSyntax();
91 size_t normsep = syntax.normsep();
92 size_t normalizedLength = text.normalizedLength(normsep);
93 specLength += normalizedLength;
94 size_t litlen = syntax.litlen();
95 // A length error will already have been given if
96 // length > litlen - normsep.
97 if (litlen >= normsep && text.size() <= litlen - normsep
98 && normalizedLength > litlen)
99 context.message(ParserMessages::normalizedAttributeValueLength,
100 NumberMessageArg(litlen),
101 NumberMessageArg(normalizedLength));
102 return new CdataAttributeValue(text);
103 }
104
buildDesc(AttributeDefinitionDesc & desc) const105 void CdataDeclaredValue::buildDesc(AttributeDefinitionDesc &desc) const
106 {
107 desc.declaredValue = AttributeDefinitionDesc::cdata;
108 }
109
copy() const110 DeclaredValue *CdataDeclaredValue::copy() const
111 {
112 return new CdataDeclaredValue(*this);
113 }
114
TokenizedDeclaredValue(TokenType type,Boolean isList)115 TokenizedDeclaredValue::TokenizedDeclaredValue(TokenType type,
116 Boolean isList)
117 : type_(type), isList_(isList)
118 {
119 switch (type) {
120 case name:
121 case entityName:
122 initialCategories_ = Syntax::nameStartCategory;
123 subsequentCategories_ = (Syntax::nameStartCategory|Syntax::digitCategory
124 | Syntax::otherNameCategory);
125 break;
126 case number:
127 initialCategories_ = Syntax::digitCategory;
128 subsequentCategories_ = Syntax::digitCategory;
129 break;
130 case nameToken:
131 initialCategories_ = (Syntax::nameStartCategory|Syntax::digitCategory
132 | Syntax::otherNameCategory);
133 subsequentCategories_ = initialCategories_;
134 break;
135 case numberToken:
136 initialCategories_ = Syntax::digitCategory;
137 subsequentCategories_ = (Syntax::nameStartCategory|Syntax::digitCategory
138 | Syntax::otherNameCategory);
139 break;
140 }
141 }
142
tokenized() const143 Boolean TokenizedDeclaredValue::tokenized() const
144 {
145 return 1;
146 }
147
makeValue(Text & text,AttributeContext & context,const StringC & str,unsigned & specLength) const148 AttributeValue *TokenizedDeclaredValue::makeValue(Text &text,
149 AttributeContext &context,
150 const StringC &str,
151 unsigned &specLength) const
152 {
153 return makeTokenizedValue(text, context, str, specLength);
154 }
155
156 TokenizedAttributeValue *
makeTokenizedValue(Text & text,AttributeContext & context,const StringC & name,unsigned & specLength) const157 TokenizedDeclaredValue::makeTokenizedValue(Text &text,
158 AttributeContext &context,
159 const StringC &name,
160 unsigned &specLength) const
161 {
162 Vector<size_t> spaceIndex;
163 const Syntax &syntax = context.attributeSyntax();
164 Char space = syntax.space();
165 text.subst(*(type_ == entityName
166 ? syntax.entitySubstTable()
167 : syntax.generalSubstTable()),
168 space);
169 const StringC &value = text.string();
170 size_t i = 0;
171 size_t length = value.size();
172
173 for (;;) {
174 if (i >= length) {
175 // ends with a space (which would have to have been entered
176 // via a numeric character reference)
177 if (context.validate())
178 context.message(ParserMessages::attributeValueSyntax);
179 break;
180 }
181 size_t startIndex = i;
182 if (context.validate()) {
183 if (!(syntax.charCategory(value[i]) & initialCategories_)) {
184 context.Messenger::setNextLocation(text.charLocation(i));
185 Char c = value[i];
186 if (!(syntax.charCategory(value[i]) & subsequentCategories_))
187 context.message(ParserMessages::attributeValueChar,
188 StringMessageArg(StringC(&c, 1)),
189 StringMessageArg(name));
190 else if (initialCategories_ == Syntax::digitCategory)
191 context.message(ParserMessages::attributeValueNumberToken,
192 StringMessageArg(StringC(&c, 1)),
193 StringMessageArg(name));
194 else
195 context.message(ParserMessages::attributeValueName,
196 StringMessageArg(StringC(&c, 1)),
197 StringMessageArg(name));
198 }
199 else {
200 for (++i;
201 i < length
202 && (syntax.charCategory(value[i]) & subsequentCategories_);
203 i++)
204 ;
205 if (i < length && value[i] != space) {
206 Char c = value[i];
207 // character value[i] is not allowed anywhere in the value
208 context.Messenger::setNextLocation(text.charLocation(i));
209 context.message(ParserMessages::attributeValueChar,
210 StringMessageArg(StringC(&c, 1)),
211 StringMessageArg(name));
212 }
213 }
214 }
215 while (i < length && value[i] != space)
216 i++;
217 if (i - startIndex > syntax.namelen()) {
218 context.Messenger::setNextLocation(text.charLocation(i));
219 context.message(ParserMessages::nameTokenLength,
220 NumberMessageArg(syntax.namelen()));
221 }
222 if (i == length)
223 break;
224 if (!isList_ && context.validate() && spaceIndex.size() == 0) {
225 context.Messenger::setNextLocation(text.charLocation(i));
226 context.message(ParserMessages::attributeValueMultiple,
227 StringMessageArg(name));
228 }
229 spaceIndex.push_back(i);
230 i++;
231 }
232 size_t normsep = syntax.normsep();
233 size_t litlen = syntax.litlen();
234 size_t normalizedLength = normsep + length;
235 // should we count CDATA and SDATA entities here?
236 if (isList_) {
237 normalizedLength += 1;
238 // length is now the number of characters in each token in the list
239 // + 1 for each token in the list; so add normsep - 1 for each
240 // token in the list.
241 if (normsep > 0)
242 normalizedLength += (normsep - 1)*(spaceIndex.size() + 1);
243 else
244 normalizedLength -= spaceIndex.size() + 1;
245 }
246 specLength += normalizedLength;
247 // A length error will already have been given if
248 // length > litlen - normsep.
249 if (litlen >= normsep && length <= litlen - normsep
250 && normalizedLength > litlen)
251 context.message(ParserMessages::normalizedAttributeValueLength,
252 NumberMessageArg(litlen),
253 NumberMessageArg(normalizedLength));
254 return new TokenizedAttributeValue(text, spaceIndex);
255 }
256
recoverUnquoted(const StringC & str,const Location & strLoc,AttributeContext & context,const StringC & name)257 Boolean TokenizedAttributeValue::recoverUnquoted(const StringC &str,
258 const Location &strLoc,
259 AttributeContext &context,
260 const StringC &name)
261 {
262 TextIter iter(text_);
263 TextItem::Type type;
264 const Char *s;
265 size_t len;
266 const Location *loc;
267 if (iter.next(type, s, len, loc)
268 && type == TextItem::data
269 && len == text_.size()
270 && loc->origin().pointer() == strLoc.origin().pointer()
271 && loc->index() + len == strLoc.index()
272 && !iter.next(type, s, len, loc)) {
273 context.Messenger::setNextLocation(strLoc);
274 context.message(ParserMessages::attributeValueChar,
275 StringMessageArg(StringC(str.data(), 1)),
276 StringMessageArg(name));
277 return 1;
278 }
279 return 0;
280 }
281
buildDesc(AttributeDefinitionDesc & desc) const282 void TokenizedDeclaredValue::buildDesc(AttributeDefinitionDesc &desc) const
283 {
284 desc.declaredValue = AttributeDefinitionDesc::DeclaredValue(
285 type_ - name + (isList_
286 ? AttributeDefinitionDesc::names
287 : AttributeDefinitionDesc::name));
288 }
289
copy() const290 DeclaredValue *TokenizedDeclaredValue::copy() const
291 {
292 return new TokenizedDeclaredValue(*this);
293 }
294
GroupDeclaredValue(TokenType type,Vector<StringC> & vec)295 GroupDeclaredValue::GroupDeclaredValue(TokenType type,
296 Vector<StringC> &vec)
297 : TokenizedDeclaredValue(type, 0)
298 {
299 vec.swap(allowedValues_);
300 }
301
buildDesc(AttributeDefinitionDesc & desc) const302 void GroupDeclaredValue::buildDesc(AttributeDefinitionDesc &desc) const
303 {
304 desc.allowedValues = allowedValues_;
305 }
306
copy() const307 DeclaredValue *GroupDeclaredValue::copy() const
308 {
309 return new GroupDeclaredValue(*this);
310 }
311
makeValue(Text & text,AttributeContext & context,const StringC & name,unsigned & specLength) const312 AttributeValue *GroupDeclaredValue::makeValue(Text &text,
313 AttributeContext &context,
314 const StringC &name,
315 unsigned &specLength) const
316 {
317 TokenizedAttributeValue *val = makeTokenizedValue(text, context, name,
318 specLength);
319 if (!val || !context.validate())
320 return val;
321 for (size_t i = 0; i < allowedValues_.size(); i++)
322 if (val->string() == allowedValues_[i])
323 return val;
324 context.message(ParserMessages::attributeValueNotInGroup,
325 StringMessageArg(val->string()),
326 StringMessageArg(name),
327 StringVectorMessageArg(allowedValues_));
328 return val;
329 }
330
makeValueFromToken(Text & text,AttributeContext & context,const StringC &,unsigned & specLength) const331 AttributeValue *GroupDeclaredValue::makeValueFromToken(Text &text,
332 AttributeContext &context,
333 const StringC &,
334 unsigned &specLength)
335 const
336 {
337 const Syntax &syntax = context.attributeSyntax();
338 size_t litlen = syntax.litlen();
339 size_t normsep = syntax.normsep();
340 if (normsep > litlen || text.size() > litlen - normsep)
341 context.message(ParserMessages::normalizedAttributeValueLength,
342 NumberMessageArg(litlen),
343 NumberMessageArg(text.size() + normsep));
344 specLength += text.size() + normsep;
345 return new TokenizedAttributeValue(text, Vector<size_t>());
346 }
347
containsToken(const StringC & token) const348 Boolean GroupDeclaredValue::containsToken(const StringC &token) const
349 {
350 for (size_t i = 0; i < allowedValues_.size(); i++)
351 if (allowedValues_[i] == token)
352 return 1;
353 return 0;
354 }
355
getTokens() const356 const Vector<StringC> *GroupDeclaredValue::getTokens() const
357 {
358 return &allowedValues_;
359 }
360
NameTokenGroupDeclaredValue(Vector<StringC> & vec)361 NameTokenGroupDeclaredValue::NameTokenGroupDeclaredValue(Vector<StringC> &vec)
362 : GroupDeclaredValue(nameToken, vec)
363 {
364 }
365
buildDesc(AttributeDefinitionDesc & desc) const366 void NameTokenGroupDeclaredValue::buildDesc(AttributeDefinitionDesc &desc) const
367 {
368 GroupDeclaredValue::buildDesc(desc);
369 desc.declaredValue = AttributeDefinitionDesc::nameTokenGroup;
370 }
371
copy() const372 DeclaredValue *NameTokenGroupDeclaredValue::copy() const
373 {
374 return new NameTokenGroupDeclaredValue(*this);
375 }
376
NotationDeclaredValue(Vector<StringC> & vec)377 NotationDeclaredValue::NotationDeclaredValue(Vector<StringC> &vec)
378 : GroupDeclaredValue(name, vec)
379 {
380 }
381
isNotation() const382 Boolean NotationDeclaredValue::isNotation() const
383 {
384 return 1;
385 }
386
387 AttributeSemantics *
makeSemantics(const TokenizedAttributeValue & value,AttributeContext & context,const StringC &,unsigned &,unsigned &) const388 NotationDeclaredValue::makeSemantics(const TokenizedAttributeValue &value,
389 AttributeContext &context,
390 const StringC &,
391 unsigned &,
392 unsigned &) const
393 {
394 ConstPtr<Notation> notation
395 = context.getAttributeNotation(value.string(),
396 value.tokenLocation(0));
397 if (notation.isNull()) {
398 if (context.validate()) {
399 context.setNextLocation(value.tokenLocation(0));
400 context.message(ParserMessages::invalidNotationAttribute,
401 StringMessageArg(value.string()));
402 }
403 return 0;
404 }
405 return new NotationAttributeSemantics(notation);
406 }
407
buildDesc(AttributeDefinitionDesc & desc) const408 void NotationDeclaredValue::buildDesc(AttributeDefinitionDesc &desc) const
409 {
410 GroupDeclaredValue::buildDesc(desc);
411 desc.declaredValue = AttributeDefinitionDesc::notation;
412 }
413
copy() const414 DeclaredValue *NotationDeclaredValue::copy() const
415 {
416 return new NotationDeclaredValue(*this);
417 }
418
EntityDeclaredValue(Boolean isList)419 EntityDeclaredValue::EntityDeclaredValue(Boolean isList)
420 : TokenizedDeclaredValue(entityName, isList)
421 {
422 }
423
isEntity() const424 Boolean EntityDeclaredValue::isEntity() const
425 {
426 return 1;
427 }
428
429 AttributeSemantics *
makeSemantics(const TokenizedAttributeValue & value,AttributeContext & context,const StringC &,unsigned &,unsigned & nEntityNames) const430 EntityDeclaredValue::makeSemantics(const TokenizedAttributeValue &value,
431 AttributeContext &context,
432 const StringC &,
433 unsigned &,
434 unsigned &nEntityNames) const
435 {
436 Boolean valid = 1;
437 size_t nTokens = value.nTokens();
438 nEntityNames += nTokens;
439 Vector<ConstPtr<Entity> > entities(nTokens);
440 for (size_t i = 0; i < nTokens; i++) {
441 entities[i] = context.getAttributeEntity(value.token(i),
442 value.tokenLocation(i));
443 if (entities[i].isNull()) {
444 if (context.validate()) {
445 context.setNextLocation(value.tokenLocation(i));
446 context.message(ParserMessages::invalidEntityAttribute,
447 StringMessageArg(value.token(i)));
448 }
449 valid = 0;
450 }
451 else if (!entities[i]->isDataOrSubdoc()) {
452 if (context.validate()) {
453 context.Messenger::setNextLocation(value.tokenLocation(i));
454 context.message(ParserMessages::notDataOrSubdocEntity,
455 StringMessageArg(value.token(i)));
456 }
457 valid = 0;
458 }
459 }
460 if (valid)
461 return new EntityAttributeSemantics(entities);
462 else
463 return 0;
464 }
465
copy() const466 DeclaredValue *EntityDeclaredValue::copy() const
467 {
468 return new EntityDeclaredValue(*this);
469 }
470
IdDeclaredValue()471 IdDeclaredValue::IdDeclaredValue()
472 : TokenizedDeclaredValue(name, 0)
473 {
474 }
475
isId() const476 Boolean IdDeclaredValue::isId() const
477 {
478 return 1;
479 }
480
481 AttributeSemantics *
makeSemantics(const TokenizedAttributeValue & value,AttributeContext & context,const StringC &,unsigned &,unsigned &) const482 IdDeclaredValue::makeSemantics(const TokenizedAttributeValue &value,
483 AttributeContext &context,
484 const StringC &,
485 unsigned &,
486 unsigned &) const
487 {
488 Location prevLoc;
489 if (!context.defineId(value.string(), value.tokenLocation(0), prevLoc)) {
490 context.setNextLocation(value.tokenLocation(0));
491 context.message(ParserMessages::duplicateId,
492 StringMessageArg(value.string()),
493 prevLoc);
494 }
495 return 0;
496 }
497
buildDesc(AttributeDefinitionDesc & desc) const498 void IdDeclaredValue::buildDesc(AttributeDefinitionDesc &desc) const
499 {
500 desc.declaredValue = AttributeDefinitionDesc::id;
501 }
502
copy() const503 DeclaredValue *IdDeclaredValue::copy() const
504 {
505 return new IdDeclaredValue(*this);
506 }
507
IdrefDeclaredValue(Boolean isList)508 IdrefDeclaredValue::IdrefDeclaredValue(Boolean isList)
509 : TokenizedDeclaredValue(name, isList)
510 {
511 }
512
513 AttributeSemantics *
makeSemantics(const TokenizedAttributeValue & value,AttributeContext & context,const StringC &,unsigned & nIdrefs,unsigned &) const514 IdrefDeclaredValue::makeSemantics(const TokenizedAttributeValue &value,
515 AttributeContext &context,
516 const StringC &,
517 unsigned &nIdrefs,
518 unsigned &) const
519 {
520 size_t nTokens = value.nTokens();
521 nIdrefs += nTokens;
522 for (size_t i = 0; i < nTokens; i++)
523 context.noteIdref(value.token(i), value.tokenLocation(i));
524 return 0;
525 }
526
isIdref() const527 Boolean IdrefDeclaredValue::isIdref() const
528 {
529 return 1;
530 }
531
buildDesc(AttributeDefinitionDesc & desc) const532 void IdrefDeclaredValue::buildDesc(AttributeDefinitionDesc &desc) const
533 {
534 TokenizedDeclaredValue::buildDesc(desc);
535 if (desc.declaredValue == AttributeDefinitionDesc::name)
536 desc.declaredValue = AttributeDefinitionDesc::idref;
537 else
538 desc.declaredValue = AttributeDefinitionDesc::idrefs;
539 }
540
copy() const541 DeclaredValue *IdrefDeclaredValue::copy() const
542 {
543 return new IdrefDeclaredValue(*this);
544 }
545
546
AttributeDefinition(const StringC & name,DeclaredValue * value)547 AttributeDefinition::AttributeDefinition(const StringC &name,
548 DeclaredValue *value)
549 : name_(name), declaredValue_(value)
550 {
551 }
552
~AttributeDefinition()553 AttributeDefinition::~AttributeDefinition()
554 {
555 }
556
checkValue(AttributeValue * p,AttributeContext &) const557 AttributeValue *AttributeDefinition::checkValue(AttributeValue *p,
558 AttributeContext &) const
559 {
560 return p;
561 }
562
missingValueWouldMatch(const Text &,const AttributeContext &) const563 Boolean AttributeDefinition::missingValueWouldMatch(const Text &,
564 const AttributeContext &) const
565 {
566 return 0;
567 }
568
569 const AttributeValue *
defaultValue(const AttributeValue *) const570 AttributeDefinition::defaultValue(const AttributeValue *) const
571 {
572 return 0;
573 }
574
getDesc(AttributeDefinitionDesc & desc) const575 void AttributeDefinition::getDesc(AttributeDefinitionDesc &desc) const
576 {
577 desc.allowedValues.clear();
578 desc.defaultValue.clear();
579 desc.currentIndex = 0;
580 buildDesc(desc);
581 declaredValue_->buildDesc(desc);
582 }
583
isConref() const584 Boolean AttributeDefinition::isConref() const
585 {
586 return 0;
587 }
588
isCurrent() const589 Boolean AttributeDefinition::isCurrent() const
590 {
591 return 0;
592 }
593
isFixed() const594 Boolean AttributeDefinition::isFixed() const
595 {
596 return 0;
597 }
598
RequiredAttributeDefinition(const StringC & name,DeclaredValue * value)599 RequiredAttributeDefinition::RequiredAttributeDefinition(const StringC &name,
600 DeclaredValue *value)
601 : AttributeDefinition(name, value)
602 {
603 }
604
605 ConstPtr<AttributeValue>
makeMissingValue(AttributeContext & context) const606 RequiredAttributeDefinition::makeMissingValue(AttributeContext &context) const
607 {
608 if (context.validate())
609 context.message(ParserMessages::requiredAttributeMissing,
610 StringMessageArg(name()));
611 return 0;
612 }
613
buildDesc(AttributeDefinitionDesc & desc) const614 void RequiredAttributeDefinition::buildDesc(AttributeDefinitionDesc &desc) const
615 {
616 desc.defaultValueType = AttributeDefinitionDesc::required;
617 }
618
copy() const619 AttributeDefinition *RequiredAttributeDefinition::copy() const
620 {
621 return new RequiredAttributeDefinition(*this);
622 }
623
CurrentAttributeDefinition(const StringC & name,DeclaredValue * value,size_t index)624 CurrentAttributeDefinition::CurrentAttributeDefinition(const StringC &name, DeclaredValue *value, size_t index)
625 : AttributeDefinition(name, value), currentIndex_(index)
626 {
627 }
628
629 ConstPtr<AttributeValue>
makeMissingValue(AttributeContext & context) const630 CurrentAttributeDefinition::makeMissingValue(AttributeContext &context) const
631 {
632 if (context.mayDefaultAttribute()) {
633 ConstPtr<AttributeValue> currentValue
634 = context.getCurrentAttribute(currentIndex_);
635 if (currentValue.isNull() && context.validate())
636 context.message(ParserMessages::currentAttributeMissing,
637 StringMessageArg(name()));
638 return currentValue;
639 }
640 if (context.validate())
641 context.message(ParserMessages::attributeMissing,
642 StringMessageArg(name()));
643 return 0;
644 }
645
missingValueWouldMatch(const Text & text,const AttributeContext & context) const646 Boolean CurrentAttributeDefinition::missingValueWouldMatch(const Text &text,
647 const AttributeContext &context) const
648 {
649 if (!context.mayDefaultAttribute())
650 return 0;
651 ConstPtr<AttributeValue> currentValue
652 = context.getCurrentAttribute(currentIndex_);
653 if (currentValue.isNull())
654 return 0;
655 return text.fixedEqual(*currentValue->text());
656 }
657
658 AttributeValue *
checkValue(AttributeValue * value,AttributeContext & context) const659 CurrentAttributeDefinition::checkValue(AttributeValue *value,
660 AttributeContext &context) const
661 {
662 context.noteCurrentAttribute(currentIndex_, value);
663 return value;
664 }
665
buildDesc(AttributeDefinitionDesc & desc) const666 void CurrentAttributeDefinition::buildDesc(AttributeDefinitionDesc &desc) const
667 {
668 desc.defaultValueType = AttributeDefinitionDesc::current;
669 desc.currentIndex = currentIndex_;
670 }
671
copy() const672 AttributeDefinition *CurrentAttributeDefinition::copy() const
673 {
674 return new CurrentAttributeDefinition(*this);
675 }
676
isCurrent() const677 Boolean CurrentAttributeDefinition::isCurrent() const
678 {
679 return 1;
680 }
681
ImpliedAttributeDefinition(const StringC & name,DeclaredValue * value)682 ImpliedAttributeDefinition::ImpliedAttributeDefinition(const StringC &name,
683 DeclaredValue *value)
684 : AttributeDefinition(name, value)
685 {
686 }
687
688 ConstPtr<AttributeValue>
makeMissingValue(AttributeContext & context) const689 ImpliedAttributeDefinition::makeMissingValue(AttributeContext &context) const
690 {
691 return context.makeImpliedAttributeValue();
692 }
693
buildDesc(AttributeDefinitionDesc & desc) const694 void ImpliedAttributeDefinition::buildDesc(AttributeDefinitionDesc &desc) const
695 {
696 desc.defaultValueType = AttributeDefinitionDesc::implied;
697 }
698
copy() const699 AttributeDefinition *ImpliedAttributeDefinition::copy() const
700 {
701 return new ImpliedAttributeDefinition(*this);
702 }
703
704 const AttributeValue *
defaultValue(const AttributeValue * impliedValue) const705 ImpliedAttributeDefinition::defaultValue(const AttributeValue *impliedValue)
706 const
707 {
708 return impliedValue;
709 }
710
ConrefAttributeDefinition(const StringC & name,DeclaredValue * value)711 ConrefAttributeDefinition::ConrefAttributeDefinition(const StringC &name,
712 DeclaredValue *value)
713 : ImpliedAttributeDefinition(name, value)
714 {
715 }
716
isConref() const717 Boolean ConrefAttributeDefinition::isConref() const
718 {
719 return 1;
720 }
721
buildDesc(AttributeDefinitionDesc & desc) const722 void ConrefAttributeDefinition::buildDesc(AttributeDefinitionDesc &desc) const
723 {
724 desc.defaultValueType = AttributeDefinitionDesc::conref;
725 }
726
copy() const727 AttributeDefinition *ConrefAttributeDefinition::copy() const
728 {
729 return new ConrefAttributeDefinition(*this);
730 }
731
DefaultAttributeDefinition(const StringC & name,DeclaredValue * declaredValue,AttributeValue * defaultValue)732 DefaultAttributeDefinition::DefaultAttributeDefinition(const StringC &name,
733 DeclaredValue *declaredValue,
734 AttributeValue *defaultValue)
735 : AttributeDefinition(name, declaredValue),
736 value_(defaultValue)
737 {
738 }
739
740 ConstPtr<AttributeValue>
makeMissingValue(AttributeContext & context) const741 DefaultAttributeDefinition::makeMissingValue(AttributeContext &context) const
742 {
743 if (context.mayDefaultAttribute())
744 return value_;
745 if (context.validate())
746 context.message(ParserMessages::attributeMissing,
747 StringMessageArg(name()));
748 return 0;
749 }
750
missingValueWouldMatch(const Text & text,const AttributeContext & context) const751 Boolean DefaultAttributeDefinition::missingValueWouldMatch(const Text &text,
752 const AttributeContext &context) const
753 {
754 return context.mayDefaultAttribute() && text.fixedEqual(*value_->text());
755 }
756
buildDesc(AttributeDefinitionDesc & desc) const757 void DefaultAttributeDefinition::buildDesc(AttributeDefinitionDesc &desc) const
758 {
759 desc.defaultValueType = AttributeDefinitionDesc::defaulted;
760 desc.defaultValue = value_;
761 }
762
copy() const763 AttributeDefinition *DefaultAttributeDefinition::copy() const
764 {
765 return new DefaultAttributeDefinition(*this);
766 }
767
FixedAttributeDefinition(const StringC & name,DeclaredValue * declaredValue,AttributeValue * defaultValue)768 FixedAttributeDefinition:: FixedAttributeDefinition(const StringC &name,
769 DeclaredValue *declaredValue,
770 AttributeValue *defaultValue)
771 : DefaultAttributeDefinition(name, declaredValue, defaultValue)
772 {
773 }
774
isFixed() const775 Boolean FixedAttributeDefinition::isFixed() const
776 {
777 return 1;
778 }
779
checkValue(AttributeValue * value,AttributeContext & context) const780 AttributeValue *FixedAttributeDefinition::checkValue(AttributeValue *value,
781 AttributeContext &context)
782 const
783 {
784 const AttributeValue *fixedValue
785 = DefaultAttributeDefinition::defaultValue(0);
786 if (value && fixedValue && context.validate()) {
787 const Text *text;
788 const StringC *str;
789 const Text *fixedText;
790 const StringC *fixedStr;
791 switch (value->info(text, str)) {
792 case AttributeValue::implied:
793 CANNOT_HAPPEN();
794 case AttributeValue::cdata:
795 if (fixedValue->info(fixedText, fixedStr) == AttributeValue::cdata) {
796 if (!text->fixedEqual(*fixedText))
797 context.message(ParserMessages::notFixedValue, StringMessageArg(name()));
798 }
799 break;
800 case AttributeValue::tokenized:
801 if (fixedValue->info(fixedText, fixedStr) == AttributeValue::tokenized) {
802 if (*str != *fixedStr)
803 context.message(ParserMessages::notFixedValue, StringMessageArg(name()));
804 }
805 break;
806 }
807 }
808 return value;
809 }
810
buildDesc(AttributeDefinitionDesc & desc) const811 void FixedAttributeDefinition::buildDesc(AttributeDefinitionDesc &desc) const
812 {
813 // get the fixed value
814 DefaultAttributeDefinition::buildDesc(desc);
815 desc.defaultValueType = AttributeDefinitionDesc::fixed;
816 }
817
copy() const818 AttributeDefinition *FixedAttributeDefinition::copy() const
819 {
820 return new FixedAttributeDefinition(*this);
821 }
822
823 AttributeDefinitionList
AttributeDefinitionList(Vector<CopyOwner<AttributeDefinition>> & vec,size_t index,Boolean anyCurrent,size_t idIndex,size_t notationIndex)824 ::AttributeDefinitionList(Vector<CopyOwner<AttributeDefinition> > &vec,
825 size_t index,
826 Boolean anyCurrent,
827 size_t idIndex,
828 size_t notationIndex)
829 : index_(index), anyCurrent_(anyCurrent), idIndex_(idIndex),
830 notationIndex_(notationIndex)
831 {
832 defs_.swap(vec);
833 }
834
AttributeDefinitionList(const ConstPtr<AttributeDefinitionList> & def)835 AttributeDefinitionList:: AttributeDefinitionList(const ConstPtr<AttributeDefinitionList> &def)
836 : prev_(def), index_(size_t(-1))
837 {
838 if (def.isNull()) {
839 anyCurrent_ = 0;
840 notationIndex_ = size_t(-1);
841 idIndex_ = size_t(-1);
842 }
843 else {
844 anyCurrent_ = def->anyCurrent_;
845 notationIndex_ = def->notationIndex_;
846 idIndex_ = def->idIndex_;
847 defs_ = def->defs_;
848 }
849 }
850
tokenIndex(const StringC & token,unsigned & index) const851 Boolean AttributeDefinitionList::tokenIndex(const StringC &token, unsigned &index) const
852 {
853 for (size_t i = 0; i < defs_.size(); i++)
854 if (defs_[i]->containsToken(token)) {
855 index = i;
856 return 1;
857 }
858 return 0;
859 }
860
tokenIndexUnique(const StringC & token,unsigned i) const861 Boolean AttributeDefinitionList::tokenIndexUnique(const StringC &token, unsigned i) const
862 {
863 for (++i; i < defs_.size(); i++)
864 if (defs_[i]->containsToken(token))
865 return 0;
866 return 1;
867 }
868
869
attributeIndex(const StringC & name,unsigned & index) const870 Boolean AttributeDefinitionList::attributeIndex(const StringC &name,
871 unsigned &index) const
872 {
873 for (size_t i = 0; i < defs_.size(); i++)
874 if (defs_[i]->name() == name) {
875 index = i;
876 return 1;
877 }
878 return 0;
879 }
880
append(AttributeDefinition * def)881 void AttributeDefinitionList::append(AttributeDefinition *def)
882 {
883 if (def->isId() && idIndex_ == size_t(-1))
884 idIndex_ = defs_.size();
885 if (def->isNotation() && notationIndex_ == size_t(-1))
886 notationIndex_ = defs_.size();
887 if (def->isCurrent())
888 anyCurrent_ = 1;
889 defs_.resize(defs_.size() + 1);
890 defs_.back() = def;
891 }
892
AttributeSemantics()893 AttributeSemantics::AttributeSemantics()
894 {
895 }
896
~AttributeSemantics()897 AttributeSemantics::~AttributeSemantics()
898 {
899 }
900
nEntities() const901 size_t AttributeSemantics::nEntities() const
902 {
903 return 0;
904 }
905
entity(size_t) const906 ConstPtr<Entity> AttributeSemantics::entity(size_t) const
907 {
908 return 0;
909 }
910
notation() const911 ConstPtr<Notation> AttributeSemantics::notation() const
912 {
913 return 0;
914 }
915
916
NotationAttributeSemantics(const ConstPtr<Notation> & notation)917 NotationAttributeSemantics::NotationAttributeSemantics(const ConstPtr<Notation> ¬ation)
918 : notation_(notation)
919 {
920 }
921
notation() const922 ConstPtr<Notation> NotationAttributeSemantics::notation() const
923 {
924 return notation_;
925 }
926
copy() const927 AttributeSemantics *NotationAttributeSemantics::copy() const
928 {
929 return new NotationAttributeSemantics(*this);
930 }
931
EntityAttributeSemantics(Vector<ConstPtr<Entity>> & entity)932 EntityAttributeSemantics::EntityAttributeSemantics(Vector<ConstPtr<Entity> > &entity)
933 {
934 entity.swap(entity_);
935 }
936
nEntities() const937 size_t EntityAttributeSemantics::nEntities() const
938 {
939 return entity_.size();
940 }
941
entity(size_t i) const942 ConstPtr<Entity> EntityAttributeSemantics::entity(size_t i) const
943 {
944 return entity_[i];
945 }
946
copy() const947 AttributeSemantics *EntityAttributeSemantics::copy() const
948 {
949 return new EntityAttributeSemantics(*this);
950 }
951
AttributeValue()952 AttributeValue::AttributeValue()
953 {
954 }
955
~AttributeValue()956 AttributeValue::~AttributeValue()
957 {
958 }
959
makeSemantics(const DeclaredValue *,AttributeContext &,const StringC &,unsigned &,unsigned &) const960 AttributeSemantics *AttributeValue::makeSemantics(const DeclaredValue *,
961 AttributeContext &,
962 const StringC &,
963 unsigned &,
964 unsigned &) const
965 {
966 return 0;
967 }
968
text() const969 const Text *AttributeValue::text() const
970 {
971 return 0;
972 }
973
recoverUnquoted(const StringC &,const Location &,AttributeContext &,const StringC &)974 Boolean AttributeValue::recoverUnquoted(const StringC &, const Location &,
975 AttributeContext &, const StringC &)
976 {
977 return 0;
978 }
979
ImpliedAttributeValue()980 ImpliedAttributeValue::ImpliedAttributeValue()
981 {
982 }
983
info(const Text * &,const StringC * &) const984 AttributeValue::Type ImpliedAttributeValue::info(const Text *&,
985 const StringC *&) const
986 {
987 return implied;
988 }
989
TokenizedAttributeValue(Text & text,const Vector<size_t> & spaceIndex)990 TokenizedAttributeValue::TokenizedAttributeValue(Text &text,
991 const Vector<size_t> &spaceIndex)
992 : spaceIndex_(spaceIndex)
993 {
994 text.swap(text_);
995 }
996
info(const Text * &,const StringC * & string) const997 AttributeValue::Type TokenizedAttributeValue::info(const Text *&,
998 const StringC *&string) const
999 {
1000 string = &text_.string();
1001 return tokenized;
1002 }
1003
text() const1004 const Text *TokenizedAttributeValue::text() const
1005 {
1006 return &text_;
1007 }
1008
1009 AttributeSemantics *
makeSemantics(const DeclaredValue * value,AttributeContext & context,const StringC & name,unsigned & nIdrefs,unsigned & nEntityNames) const1010 TokenizedAttributeValue::makeSemantics(const DeclaredValue *value,
1011 AttributeContext &context,
1012 const StringC &name,
1013 unsigned &nIdrefs,
1014 unsigned &nEntityNames) const
1015 {
1016 if (text_.size() == 0)
1017 return 0;
1018 return value->makeSemantics(*this, context, name, nIdrefs, nEntityNames);
1019 }
1020
CdataAttributeValue(Text & text)1021 CdataAttributeValue::CdataAttributeValue(Text &text)
1022 {
1023 text.swap(text_);
1024 }
1025
info(const Text * & text,const StringC * &) const1026 AttributeValue::Type CdataAttributeValue::info(const Text *&text,
1027 const StringC *&) const
1028 {
1029 text = &text_;
1030 return cdata;
1031 }
1032
text() const1033 const Text *CdataAttributeValue::text() const
1034 {
1035 return &text_;
1036 }
1037
recoverUnquoted(const StringC & str,const Location & strLoc,AttributeContext & context,const StringC &)1038 Boolean CdataAttributeValue::recoverUnquoted(const StringC &str,
1039 const Location &strLoc,
1040 AttributeContext &context,
1041 const StringC &)
1042 {
1043 TextIter iter(text_);
1044 TextItem::Type type;
1045 const Char *s;
1046 size_t len;
1047 const Location *loc;
1048 if (iter.next(type, s, len, loc)
1049 && type == TextItem::data
1050 && len == text_.size()
1051 && loc->origin().pointer() == strLoc.origin().pointer()
1052 && loc->index() + len == strLoc.index()
1053 && !iter.next(type, s, len, loc)) {
1054 text_.addChars(str, strLoc);
1055 context.Messenger::setNextLocation(strLoc);
1056 context.message(ParserMessages::unquotedAttributeValue);
1057 return 1;
1058 }
1059 return 0;
1060 }
1061
Attribute()1062 Attribute::Attribute()
1063 : specIndexPlus_(0)
1064 {
1065 }
1066
clear()1067 void Attribute::clear()
1068 {
1069 specIndexPlus_ = 0;
1070 value_.clear();
1071 semantics_.clear();
1072 }
1073
AttributeList(const ConstPtr<AttributeDefinitionList> & def)1074 AttributeList::AttributeList(const ConstPtr<AttributeDefinitionList> &def)
1075 : def_(def), vec_(def.isNull() ? 0 : def->size()), nSpec_(0), conref_(0),
1076 nIdrefs_(0), nEntityNames_(0)
1077 {
1078 }
1079
AttributeList()1080 AttributeList::AttributeList()
1081 : nSpec_(0), conref_(0)
1082 {
1083 }
1084
init(const ConstPtr<AttributeDefinitionList> & def)1085 void AttributeList::init(const ConstPtr<AttributeDefinitionList> &def)
1086 {
1087 def_ = def;
1088 nSpec_ = 0;
1089 conref_ = 0;
1090 nIdrefs_ = 0;
1091 nEntityNames_ = 0;
1092 if (def_.isNull())
1093 vec_.resize(0);
1094 else {
1095 size_t newLength = def_->size();
1096 size_t clearLim = vec_.size();
1097 if (clearLim > newLength)
1098 clearLim = newLength;
1099 vec_.resize(newLength);
1100 for (size_t i = 0; i < clearLim; i++)
1101 vec_[i].clear();
1102 }
1103 }
1104
changeDef(const ConstPtr<AttributeDefinitionList> & def)1105 void AttributeList::changeDef(const ConstPtr<AttributeDefinitionList> &def)
1106 {
1107 vec_.resize(def.isNull() ? 0 : def->size());
1108 def_ = def;
1109 }
1110
swap(AttributeList & to)1111 void AttributeList::swap(AttributeList &to)
1112 {
1113 vec_.swap(to.vec_);
1114 def_.swap(to.def_);
1115 {
1116 unsigned tem = to.nIdrefs_;
1117 to.nIdrefs_ = nIdrefs_;
1118 nIdrefs_ = tem;
1119 }
1120 {
1121 unsigned tem = to.nEntityNames_;
1122 to.nEntityNames_ = nEntityNames_;
1123 nEntityNames_ = tem;
1124 }
1125 {
1126 size_t tem = to.nSpec_;
1127 to.nSpec_ = nSpec_;
1128 nSpec_ = tem;
1129 }
1130 {
1131 PackedBoolean tem = to.conref_;
1132 to.conref_ = conref_;
1133 conref_ = tem;
1134 }
1135 }
1136
finish(AttributeContext & context)1137 void AttributeList::finish(AttributeContext &context)
1138 {
1139 for (size_t i = 0; i < vec_.size(); i++)
1140 if (!vec_[i].specified()) {
1141 ConstPtr<AttributeValue> value
1142 = def(i)->makeMissingValue(context);
1143 if (!conref_ || def_->notationIndex() != i) {
1144 vec_[i].setValue(value);
1145 if (!value.isNull())
1146 vec_[i].setSemantics(def(i)->makeSemantics(value.pointer(),
1147 context,
1148 nIdrefs_,
1149 nEntityNames_));
1150 }
1151 }
1152 const Syntax &syntax = context.attributeSyntax();
1153 if (nIdrefs_ > syntax.grpcnt())
1154 context.message(ParserMessages::idrefGrpcnt,
1155 NumberMessageArg(syntax.grpcnt()));
1156 if (nEntityNames_ > syntax.grpcnt())
1157 context.message(ParserMessages::entityNameGrpcnt,
1158 NumberMessageArg(syntax.grpcnt()));
1159 if (context.validate()
1160 && conref_
1161 && def_->notationIndex() != size_t(-1)
1162 && specified(def_->notationIndex()))
1163 context.message(ParserMessages::conrefNotation);
1164 }
1165
setSpec(unsigned i,AttributeContext & context)1166 void AttributeList::setSpec(unsigned i, AttributeContext &context)
1167 {
1168 if (vec_[i].specified())
1169 context.message(ParserMessages::duplicateAttributeSpec,
1170 StringMessageArg(def(i)->name()));
1171 else
1172 vec_[i].setSpec(nSpec_++);
1173 }
1174
noteInvalidSpec()1175 void AttributeList::noteInvalidSpec()
1176 {
1177 // This is needed for error recovery.
1178 // We don't want nSpec_ to be > 0, if there is no attribute definition.
1179 if (nSpec_)
1180 nSpec_++;
1181 }
1182
setValue(unsigned i,Text & text,AttributeContext & context,unsigned & specLength)1183 Boolean AttributeList::setValue(unsigned i, Text &text,
1184 AttributeContext &context,
1185 unsigned &specLength)
1186 {
1187 AttributeValue *value = def(i)->makeValue(text, context, specLength);
1188 if (def(i)->isConref())
1189 conref_ = 1;
1190 vec_[i].setValue(value);
1191 if (value)
1192 vec_[i].setSemantics(def(i)->makeSemantics(value, context,
1193 nIdrefs_, nEntityNames_));
1194 else if (AttributeValue::handleAsUnterminated(text, context))
1195 return 0;
1196 return 1;
1197 }
1198
setValueToken(unsigned i,Text & text,AttributeContext & context,unsigned & specLength)1199 void AttributeList::setValueToken(unsigned i, Text &text,
1200 AttributeContext &context,
1201 unsigned &specLength)
1202 {
1203 AttributeValue *value = def(i)->makeValueFromToken(text, context,
1204 specLength);
1205 if (def(i)->isConref())
1206 conref_ = 1;
1207 vec_[i].setValue(value);
1208 if (value)
1209 vec_[i].setSemantics(def(i)->makeSemantics(value, context,
1210 nIdrefs_, nEntityNames_));
1211 }
1212
getId() const1213 const StringC *AttributeList::getId() const
1214 {
1215 // Check for no attributes
1216 if (def_.isNull())
1217 return 0;
1218 // Check for no ID declared
1219 size_t i = def_->idIndex();
1220 if (i == size_t(-1))
1221 return 0;
1222 // Check for invalid value
1223 const AttributeValue *v = value(i);
1224 if (!v)
1225 return 0;
1226 // Check for implied value
1227 const Text *t = v->text();
1228 if (!t)
1229 return 0;
1230 return &t->string();
1231 }
1232
recoverUnquoted(const StringC & str,const Location & strLoc,AttributeContext & context)1233 Boolean AttributeList::recoverUnquoted(const StringC &str,
1234 const Location &strLoc,
1235 AttributeContext &context)
1236 {
1237 if (nSpec_ > 0) {
1238 for (size_t i = 0; i < vec_.size(); i++)
1239 if (vec_[i].specified() && vec_[i].specIndex() == nSpec_ - 1) {
1240 const AttributeValue *val = vec_[i].value();
1241 if (val)
1242 // I wish I could avoid casting away const here.
1243 return ((AttributeValue *)val)->recoverUnquoted(str, strLoc, context,
1244 name(i));
1245 break;
1246 }
1247 return 1;
1248 }
1249 return 0;
1250 }
1251
handleAsUnterminated(AttributeContext & context)1252 Boolean AttributeList::handleAsUnterminated(AttributeContext &context)
1253 {
1254 if (nSpec_ > 0) {
1255 for (size_t i = 0; i < vec_.size(); i++) {
1256 if (vec_[i].specified() && vec_[i].specIndex() == nSpec_ - 1) {
1257 const AttributeValue *val = vec_[i].value();
1258 const Text *ptr;
1259 if (val && (ptr = val->text()) != 0
1260 && AttributeValue::handleAsUnterminated(*ptr, context))
1261 return 1;
1262 break;
1263 }
1264 }
1265 }
1266 return 0;
1267 }
1268
1269 // This tries to guess this attribute value looks like if it had
1270 // a missing ending quote.
1271
handleAsUnterminated(const Text & text,AttributeContext & context)1272 Boolean AttributeValue::handleAsUnterminated(const Text &text,
1273 AttributeContext &context)
1274 {
1275 TextIter iter(text);
1276 const Char *lastStr = 0;
1277 size_t lastLen;
1278 Location startLoc;
1279 const Location *loc;
1280 TextItem::Type type;
1281 const Char *str;
1282 size_t len;
1283 while (iter.next(type, str, len, loc)) {
1284 if (startLoc.origin().isNull() && !loc->origin().isNull())
1285 startLoc = *loc;
1286 switch (type) {
1287 case TextItem::data:
1288 if (len != 1 || *str != context.attributeSyntax().space()) {
1289 lastStr = str;
1290 lastLen = len;
1291 }
1292 break;
1293 case TextItem::endDelim:
1294 case TextItem::endDelimA:
1295 case TextItem::ignore:
1296 break;
1297 default:
1298 lastStr = 0;
1299 break;
1300 }
1301 }
1302 if (lastStr) {
1303 while (lastLen > 0
1304 && lastStr[lastLen - 1] == context.attributeSyntax().space())
1305 lastLen--;
1306 const StringC &vi = context.attributeSyntax().delimGeneral(Syntax::dVI);
1307 if (lastLen >= vi.size()
1308 && (vi
1309 == StringC(lastStr + (lastLen - vi.size()), vi.size()))) {
1310 context.Messenger::setNextLocation(startLoc);
1311 context.message(ParserMessages::literalClosingDelimiter);
1312 return 1;
1313 }
1314 }
1315 return 0;
1316 }
1317
AttributeContext()1318 AttributeContext::AttributeContext()
1319 : mayDefaultAttribute_(0), validate_(1)
1320 {
1321 }
1322
~AttributeContext()1323 AttributeContext::~AttributeContext()
1324 {
1325 }
1326
defineId(const StringC &,const Location &,Location &)1327 Boolean AttributeContext::defineId(const StringC &, const Location &,
1328 Location &)
1329 {
1330 return 1;
1331 }
1332
noteIdref(const StringC &,const Location &)1333 void AttributeContext::noteIdref(const StringC &, const Location &)
1334 {
1335 }
1336
noteCurrentAttribute(size_t,AttributeValue *)1337 void AttributeContext::noteCurrentAttribute(size_t, AttributeValue *)
1338 {
1339 }
1340
getCurrentAttribute(size_t) const1341 ConstPtr<AttributeValue> AttributeContext::getCurrentAttribute(size_t) const
1342 {
1343 return 0;
1344 }
1345
getAttributeEntity(const StringC &,const Location &)1346 ConstPtr<Entity> AttributeContext::getAttributeEntity(const StringC &,
1347 const Location &)
1348 {
1349 return 0;
1350 }
1351
getAttributeNotation(const StringC &,const Location &)1352 ConstPtr<Notation> AttributeContext::getAttributeNotation(const StringC &,
1353 const Location &)
1354 {
1355 return 0;
1356 }
1357
makeImpliedAttributeValue()1358 ConstPtr<AttributeValue> AttributeContext::makeImpliedAttributeValue()
1359 {
1360 if (impliedAttributeValue_.isNull())
1361 impliedAttributeValue_ = new ImpliedAttributeValue;
1362 return impliedAttributeValue_;
1363 }
1364
1365 #ifdef SP_NAMESPACE
1366 }
1367 #endif
1368