Link with C++ linker
[oweals/cde.git] / cde / programs / nsgmls / parseAttribute.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: parseAttribute.C /main/1 1996/07/29 17:09:07 cde-hp $ */
24 // Copyright (c) 1994, 1995 James Clark
25 // See the file COPYING for copying permission.
26
27 #include "splib.h"
28 #include "Parser.h"
29 #include "MessageArg.h"
30 #include "token.h"
31 #include "macros.h"
32 #include "ParserMessages.h"
33
34 #ifdef SP_NAMESPACE
35 namespace SP_NAMESPACE {
36 #endif
37
38 Boolean Parser::parseAttributeSpec(Boolean inDecl,
39                                    AttributeList &atts,
40                                    Boolean &netEnabling)
41
42 {
43   unsigned specLength = 0;
44   AttributeParameter::Type curParm;
45
46   if (!parseAttributeParameter(inDecl, 0, curParm, netEnabling))
47     return 0;
48   while (curParm != AttributeParameter::end) {
49     switch (curParm) {
50     case AttributeParameter::name:
51       {
52         Text text;
53         text.addChars(currentInput()->currentTokenStart(),
54                       currentInput()->currentTokenLength(),
55                       currentLocation());
56         size_t nameMarkupIndex;
57         if (currentMarkup())
58           nameMarkupIndex = currentMarkup()->size() - 1;
59         text.subst(*syntax().generalSubstTable(), syntax().space());
60         if (!parseAttributeParameter(inDecl, 1, curParm, netEnabling))
61           return 0;
62         if (curParm == AttributeParameter::vi) {
63           specLength += text.size() + syntax().normsep();
64           if (!parseAttributeValueSpec(inDecl, text.string(), atts,     
65                                        specLength))
66             return 0;
67           // setup for next attribute
68           if (!parseAttributeParameter(inDecl, 0, curParm, netEnabling))
69             return 0;
70         }
71         else {
72           if (currentMarkup())
73             currentMarkup()->changeToAttributeValue(nameMarkupIndex);
74           if (!handleAttributeNameToken(text, atts, specLength))
75             return 0;
76         }
77       }
78       break;
79     case AttributeParameter::nameToken:
80       {
81         Text text;
82         text.addChars(currentInput()->currentTokenStart(),
83                       currentInput()->currentTokenLength(),
84                       currentLocation());
85         text.subst(*syntax().generalSubstTable(), syntax().space());
86         if (!handleAttributeNameToken(text, atts, specLength))
87           return 0;
88         if (!parseAttributeParameter(inDecl, 0, curParm, netEnabling))
89           return 0;
90       }
91       break;
92     case AttributeParameter::recoverUnquoted:
93       {
94         if (!atts.recoverUnquoted(currentToken(), currentLocation(), *this)) {
95           // Don't treat it as an unquoted attribute value.
96           currentInput()->endToken(1);
97           if (!atts.handleAsUnterminated(*this))
98             message(ParserMessages::attributeSpecCharacter,
99                     StringMessageArg(currentToken()));
100           return 0;
101         }
102         if (!parseAttributeParameter(inDecl, 0, curParm, netEnabling))
103           return 0;
104       }
105       break;
106     default:
107       CANNOT_HAPPEN();
108     }
109   }
110   atts.finish(*this);
111   if (specLength > syntax().attsplen())
112     message(ParserMessages::attsplen,
113             NumberMessageArg(syntax().attsplen()),
114             NumberMessageArg(specLength));
115   return 1;
116 }
117
118 Boolean Parser::handleAttributeNameToken(Text &text,
119                                          AttributeList &atts,
120                                          unsigned &specLength)
121 {
122   unsigned index;
123   if (!atts.tokenIndex(text.string(), index)) {
124     if (atts.handleAsUnterminated(*this))
125       return 0;
126     atts.noteInvalidSpec();
127     message(ParserMessages::noSuchAttributeToken,
128             StringMessageArg(text.string()));
129   }
130   else {
131     if (!sd().shorttag())
132       message(ParserMessages::attributeNameShorttag);
133     atts.setSpec(index, *this);
134     atts.setValueToken(index, text, *this, specLength);
135   }
136   return 1;
137 }
138
139 Boolean Parser::parseAttributeValueSpec(Boolean inDecl,
140                                         const StringC &name,
141                                         AttributeList &atts,
142                                         unsigned &specLength)
143 {
144   Mode mode = inDecl ? asMode : tagMode;
145   Markup *markup = currentMarkup();
146   Token token = getToken(mode);
147   if (token == tokenS) {
148     if (markup) {
149       do {
150         markup->addS(currentChar());
151         token = getToken(mode);
152       } while (token == tokenS);
153     }
154     else {
155       do {
156         token = getToken(mode);
157       } while (token == tokenS);
158     }
159   }
160   unsigned index;
161   Boolean valid = atts.attributeIndex(name, index);
162   if (!valid) {
163     message(ParserMessages::noSuchAttribute, StringMessageArg(name));
164     atts.noteInvalidSpec();
165   }
166   else
167     atts.setSpec(index, *this);
168   Text text;
169   switch (token) {
170   case tokenUnrecognized:
171     if (reportNonSgmlCharacter())
172       return 0;
173     // fall through
174   case tokenEtago:
175   case tokenStago:
176   case tokenNet:
177     message(ParserMessages::unquotedAttributeValue);
178     extendUnquotedAttributeValue();
179     if (markup)
180       markup->addAttributeValue(currentInput());
181     text.addChars(currentInput()->currentTokenStart(),
182                   currentInput()->currentTokenLength(),
183                   currentLocation());
184     break;
185   case tokenEe:
186     message(ParserMessages::attributeSpecEntityEnd);
187     return 0;
188   case tokenTagc:
189   case tokenDsc:
190   case tokenVi:
191     message(ParserMessages::attributeValueExpected);
192     return 0;
193   case tokenNameStart:
194   case tokenDigit:
195   case tokenLcUcNmchar:
196     if (!sd().shorttag())
197       message(ParserMessages::attributeValueShorttag);
198     extendNameToken(syntax().litlen() >= syntax().normsep()
199                     ? syntax().litlen() - syntax().normsep()
200                     : 0,
201                     ParserMessages::attributeValueLength);
202     if (markup)
203       markup->addAttributeValue(currentInput());
204     text.addChars(currentInput()->currentTokenStart(),
205                   currentInput()->currentTokenLength(),
206                   currentLocation());
207     break;
208   case tokenLit:
209   case tokenLita:
210     Boolean lita;
211     lita = (token == tokenLita);
212     if (!((valid ? atts.tokenized(index) : 1)
213           ? parseTokenizedAttributeValueLiteral(lita, text)
214           : parseAttributeValueLiteral(lita, text)))
215       return 0;
216     if (markup)
217       markup->addLiteral(text);
218     break;
219   default:
220       CANNOT_HAPPEN();
221   }
222   if (valid)
223     return atts.setValue(index, text, *this, specLength);
224   else
225     return !AttributeValue::handleAsUnterminated(text, *this);
226 }
227
228
229 Boolean Parser::parseAttributeParameter(Boolean inDecl,
230                                         Boolean allowVi,
231                                         AttributeParameter::Type &result,
232                                         Boolean &netEnabling)
233 {
234   Mode mode = inDecl ? asMode : tagMode;
235   Token token = getToken(mode);
236   Markup *markup = currentMarkup();
237   if (markup) {
238     while (token == tokenS) {
239       markup->addS(currentChar());
240       token = getToken(mode);
241     }
242   }
243   else {
244     while (token == tokenS)
245       token = getToken(mode);
246   }
247   switch (token) {
248   case tokenUnrecognized:
249     if (reportNonSgmlCharacter())
250       return 0;
251     extendUnquotedAttributeValue();
252     result = AttributeParameter::recoverUnquoted;
253     break;
254   case tokenEe:
255     message(ParserMessages::attributeSpecEntityEnd);
256     return 0;
257   case tokenEtago:
258   case tokenStago:
259     if (!sd().shorttag())
260       message(ParserMessages::minimizedStartTag);
261     else if (options().warnUnclosedTag)
262       message(ParserMessages::unclosedStartTag);
263     result = AttributeParameter::end;
264     currentInput()->ungetToken();
265     netEnabling = 0;
266     break;
267   case tokenNet:
268     if (markup)
269       markup->addDelim(Syntax::dNET);
270     if (!sd().shorttag())
271       message(ParserMessages::minimizedStartTag);
272     else if (options().warnNet)
273       message(ParserMessages::netStartTag);
274     netEnabling = 1;
275     result = AttributeParameter::end;
276     break;
277   case tokenTagc:
278     if (markup)
279       markup->addDelim(Syntax::dTAGC);
280     netEnabling = 0;
281     result = AttributeParameter::end;
282     break;
283   case tokenDsc:
284     if (markup)
285       markup->addDelim(Syntax::dDSC);
286     result = AttributeParameter::end;
287     break;
288   case tokenNameStart:
289     extendNameToken(syntax().namelen(), ParserMessages::nameTokenLength);
290     if (markup)
291       markup->addName(currentInput());
292     result = AttributeParameter::name;
293     break;
294   case tokenDigit:
295   case tokenLcUcNmchar:
296     extendNameToken(syntax().namelen(), ParserMessages::nameTokenLength);
297     if (markup)
298       markup->addName(currentInput());
299     result = AttributeParameter::nameToken;
300     break;
301   case tokenLit:
302   case tokenLita:
303     message(allowVi
304             ? ParserMessages::attributeSpecLiteral
305             : ParserMessages::attributeSpecNameTokenExpected);
306     return 0;
307   case tokenVi:
308     if (!allowVi) {
309       message(ParserMessages::attributeSpecNameTokenExpected);
310       return 0;
311     }
312     if (markup)
313       markup->addDelim(Syntax::dVI);
314     result = AttributeParameter::vi;
315     break;
316   default:
317     CANNOT_HAPPEN();
318   }
319   return 1;
320 }
321
322 void Parser::extendUnquotedAttributeValue()
323 {
324   InputSource *in = currentInput();
325   size_t length = in->currentTokenLength();
326   const Syntax &syn = syntax();
327   for (;;) {
328     Xchar c = in->tokenChar(messenger());
329     if (syn.isS(c)
330         || !syn.isSgmlChar(c)
331         || c == InputSource::eE
332         || c == syn.delimGeneral(Syntax::dTAGC)[0])
333       break;
334     length++;
335   }
336   in->endToken(length);
337 }
338
339 Boolean Parser::parseAttributeValueLiteral(Boolean lita, Text &text)
340 {
341   size_t maxLength = (syntax().litlen() > syntax().normsep()
342                       ? syntax().litlen() - syntax().normsep()
343                       : 0);
344   if (parseLiteral(lita ? alitaMode : alitMode, aliteMode,
345                    maxLength,
346                    ParserMessages::attributeValueLength,
347                    (wantMarkup() ? unsigned(literalDelimInfo) : 0),
348                    text)) {
349     if (text.size() == 0
350         && syntax().normsep() > syntax().litlen())
351       message(ParserMessages::attributeValueLengthNeg,
352               NumberMessageArg(syntax().normsep() - syntax().litlen()));
353     return 1;
354   }
355   else
356     return 0;
357 }
358
359 Boolean Parser::parseTokenizedAttributeValueLiteral(Boolean lita, Text &text)
360 {
361   size_t maxLength = (syntax().litlen() > syntax().normsep()
362                       ? syntax().litlen() - syntax().normsep()
363                       : 0);
364   if (parseLiteral(lita ? talitaMode : talitMode, taliteMode,
365                    maxLength,
366                    ParserMessages::tokenizedAttributeValueLength,
367                    literalSingleSpace
368                    | (wantMarkup() ? unsigned(literalDelimInfo) : 0),
369                    text)) {
370     if (text.size() == 0
371         && syntax().normsep() > syntax().litlen())
372       message(ParserMessages::tokenizedAttributeValueLengthNeg,
373               NumberMessageArg(syntax().normsep() - syntax().litlen()));
374     return 1;
375   }
376   else
377     return 0;
378 }
379
380
381 Boolean Parser::skipAttributeSpec()
382 {
383   AttributeParameter::Type parm;
384   Boolean netEnabling;
385   if (!parseAttributeParameter(0, 0, parm, netEnabling))
386     return 0;
387   while (parm != AttributeParameter::end) {
388     if (parm == AttributeParameter::name) {
389       size_t nameMarkupIndex = 0;
390       if (currentMarkup())
391         nameMarkupIndex = currentMarkup()->size() - 1;
392       if (!parseAttributeParameter(0, 1, parm, netEnabling))
393         return 0;
394       if (parm == AttributeParameter::vi) {
395         Token token = getToken(tagMode);
396         while (token == tokenS) {
397           if (currentMarkup())
398             currentMarkup()->addS(currentChar());
399           token = getToken(tagMode);
400         }
401         switch (token) {
402         case tokenUnrecognized:
403           if (!reportNonSgmlCharacter())
404             message(ParserMessages::attributeSpecCharacter,
405                     StringMessageArg(currentToken()));
406           return 0;
407         case tokenEe:
408           message(ParserMessages::attributeSpecEntityEnd);
409           return 0;
410         case tokenEtago:
411         case tokenStago:
412         case tokenNet:
413         case tokenTagc:
414         case tokenDsc:
415         case tokenVi:
416           message(ParserMessages::attributeValueExpected);
417           return 0;
418         case tokenNameStart:
419         case tokenDigit:
420         case tokenLcUcNmchar:
421           if (!sd().shorttag())
422             message(ParserMessages::attributeValueShorttag);
423           extendNameToken(syntax().litlen() >= syntax().normsep()
424                           ? syntax().litlen() - syntax().normsep()
425                           : 0,
426                           ParserMessages::attributeValueLength);
427           if (currentMarkup())
428             currentMarkup()->addAttributeValue(currentInput());
429           break;
430         case tokenLit:
431         case tokenLita:
432           {
433             Text text;
434             if (!parseLiteral(token == tokenLita ? talitaMode : talitMode,
435                               taliteMode,
436                               syntax().litlen(),
437                               ParserMessages::tokenizedAttributeValueLength,
438                               (currentMarkup() ? literalDelimInfo : 0)
439                               | literalNoProcess,
440                               text))
441               return 0;
442             if (currentMarkup())
443               currentMarkup()->addLiteral(text);
444           }
445           break;
446         default:
447           CANNOT_HAPPEN();
448         }
449         if (!parseAttributeParameter(0, 0, parm, netEnabling))
450           return 0;
451       }
452       else {
453         if (currentMarkup())
454           currentMarkup()->changeToAttributeValue(nameMarkupIndex);
455         if (!sd().shorttag())
456           message(ParserMessages::attributeNameShorttag);
457       }
458     }
459     else {
460       // It's a name token.
461       if (!parseAttributeParameter(0, 0, parm, netEnabling))
462         return 0;
463       if (!sd().shorttag())
464         message(ParserMessages::attributeNameShorttag);
465     }
466   }
467   if (netEnabling)
468     message(ParserMessages::startTagGroupNet);
469   return 1;
470 }
471
472 #ifdef SP_NAMESPACE
473 }
474 #endif